From d7504419093aef9fae802ad599cff1bf9022e24d Mon Sep 17 00:00:00 2001 From: Mathieu Duponchelle Date: Wed, 1 Jul 2020 23:34:34 +0200 Subject: giscanner: parse block comments for members and fields There was previously no mechanism for tagging enum members and struct fields with Since tags (or other, eg deprecation tags). While the customary place to add Since tags for these symbols is inline in the parent symbol's documentation eg: /** * Foo: * * @FOO_BAR: some bar. Since X.Y */ And variations on that theme, implementing parsing for that scheme would result in a pretty ambiguous grammar, especially if we also want support for multiple tags. Instead, the solution implemented here is to allow providing documentation for individual members and fields through their own separate block, as is done for virtual functions already. Inline comments are still used, with a lower precedence. Fixes #348 --- giscanner/annotationparser.py | 34 ++++- giscanner/girwriter.py | 2 + giscanner/maintransformer.py | 49 ++++--- .../Regress.AnnotationFields-field4.page | 14 ++ .../Regress.AnnotationBitfield.page | 4 + .../Regress.AnnotationFields-field4.page | 18 +++ .../Regress.AnnotationFields.page | 1 + .../Regress.AnnotationBitfield.page | 4 + .../Regress.AnnotationFields-field4.page | 14 ++ tests/scanner/Regress-1.0-expected.gir | 159 +++++++++++---------- tests/scanner/annotation.h | 17 ++- 11 files changed, 221 insertions(+), 95 deletions(-) create mode 100644 tests/scanner/Regress-1.0-C-expected/Regress.AnnotationFields-field4.page create mode 100644 tests/scanner/Regress-1.0-Gjs-expected/Regress.AnnotationFields-field4.page create mode 100644 tests/scanner/Regress-1.0-Python-expected/Regress.AnnotationFields-field4.page diff --git a/giscanner/annotationparser.py b/giscanner/annotationparser.py index 63212963..f8257206 100644 --- a/giscanner/annotationparser.py +++ b/giscanner/annotationparser.py @@ -461,6 +461,27 @@ ACTION_RE = re.compile( ''', re.UNICODE | re.VERBOSE) +# Pattern matching struct fields. +FIELD_RE = re.compile( + r''' + ^ # start + \s* # 0 or more whitespace characters + (?P[\w]+) # class name + \s* # 0 or more whitespace characters + \.{1} # 1 required dot + \s* # 0 or more whitespace characters + (?P[\w-]*\w) # field name + \s* # 0 or more whitespace characters + (?P:?) # delimiter + \s* # 0 or more whitespace characters + (?P.*?) # annotations + description + \s* # 0 or more whitespace characters + :? # invalid delimiter + \s* # 0 or more whitespace characters + $ # end + ''', + re.UNICODE | re.VERBOSE) + # Pattern matching parameters. PARAMETER_RE = re.compile( r''' @@ -1368,13 +1389,22 @@ class GtkDocCommentBlockParser(object): identifier_fields = None identifier_fields_start = None else: - result = SYMBOL_RE.match(line) + result = FIELD_RE.match(line) if result: - identifier_name = '%s' % (result.group('symbol_name'), ) + identifier_name = '%s.%s' % (result.group('class_name'), + result.group('field_name')) identifier_delimiter = result.group('delimiter') identifier_fields = result.group('fields') identifier_fields_start = result.start('fields') + else: + result = SYMBOL_RE.match(line) + + if result: + identifier_name = '%s' % (result.group('symbol_name'), ) + identifier_delimiter = result.group('delimiter') + identifier_fields = result.group('fields') + identifier_fields_start = result.start('fields') if result: in_part = PART_IDENTIFIER diff --git a/giscanner/girwriter.py b/giscanner/girwriter.py index d1333cb7..41d3ead2 100644 --- a/giscanner/girwriter.py +++ b/giscanner/girwriter.py @@ -437,6 +437,7 @@ class GIRWriter(XMLWriter): attrs = [('name', member.name), ('value', str(member.value)), ('c:identifier', member.symbol)] + self._append_version(member, attrs) if member.nick is not None: attrs.append(('glib:nick', member.nick)) with self.tagcontext('member', attrs): @@ -626,6 +627,7 @@ class GIRWriter(XMLWriter): raise AssertionError("Unknown field anonymous: %r" % (field.anonymous_node, )) else: attrs = [('name', field.name)] + self._append_version(field, attrs) self._append_node_generic(field, attrs) # Fields are assumed to be read-only # (see also girparser.c and generate.c) diff --git a/giscanner/maintransformer.py b/giscanner/maintransformer.py index 9468751d..3c4ef695 100644 --- a/giscanner/maintransformer.py +++ b/giscanner/maintransformer.py @@ -858,19 +858,28 @@ class MainTransformer(object): self._apply_annotations_params(node, node.parameters, block) self._apply_annotations_return(node, node.retval, block) - def _apply_annotations_field(self, parent, block, field): - if not block: - return - tag = block.params.get(field.name) - if not tag: + def _apply_annotations_field(self, parent, parent_block, field): + block = self._blocks.get('%s.%s' % (self._get_annotation_name(parent), field.name)) + + # Prioritize block level documentation + if block: + self._apply_annotations_annotated(field, block) + annotations = block.annotations + elif not parent_block: return - type_annotation = tag.annotations.get(ANN_TYPE) + else: + tag = parent_block.params.get(field.name) + if not tag: + return + annotations = tag.annotations + field.doc = tag.description + field.doc_position = tag.position + + type_annotation = annotations.get(ANN_TYPE) if type_annotation: field.type = self._transformer.create_type_from_user_string(type_annotation[0]) - field.doc = tag.description - field.doc_position = tag.position try: - self._adjust_container_type(parent, field, tag.annotations) + self._adjust_container_type(parent, field, annotations) except AttributeError as ex: print(ex) @@ -938,15 +947,21 @@ class MainTransformer(object): if value_annotation: node.value = value_annotation[0] - def _apply_annotations_enum_members(self, node, block): - if block is None: - return - + def _apply_annotations_enum_members(self, node, parent_block): for m in node.members: - param = block.params.get(m.symbol, None) - if param and param.description: - m.doc = param.description - m.doc_position = param.position + block = self._blocks.get(m.symbol) + # Prioritize block-level documentation + if block: + self._apply_annotations_annotated(m, block) + elif parent_block: + param = parent_block.params.get(m.symbol) + + if not param: + continue + + if param.description: + m.doc = param.description + m.doc_position = param.position def _pass_read_annotations2(self, node, chain): if isinstance(node, ast.Function): diff --git a/tests/scanner/Regress-1.0-C-expected/Regress.AnnotationFields-field4.page b/tests/scanner/Regress-1.0-C-expected/Regress.AnnotationFields-field4.page new file mode 100644 index 00000000..163d420f --- /dev/null +++ b/tests/scanner/Regress-1.0-C-expected/Regress.AnnotationFields-field4.page @@ -0,0 +1,14 @@ + + + + + + Regress.AnnotationFields->field4 +

A new field, breaking ABI is fun!

+

Since 1.4

+
diff --git a/tests/scanner/Regress-1.0-Gjs-expected/Regress.AnnotationBitfield.page b/tests/scanner/Regress-1.0-Gjs-expected/Regress.AnnotationBitfield.page index 355066b2..6f96ddb2 100644 --- a/tests/scanner/Regress-1.0-Gjs-expected/Regress.AnnotationBitfield.page +++ b/tests/scanner/Regress-1.0-Gjs-expected/Regress.AnnotationBitfield.page @@ -19,6 +19,10 @@ <code>AnnotationBitfield.BAR</code> + + +<code>AnnotationBitfield.FOOBAR</code> + + + + + field4 + + Regress.AnnotationFields.field4 + +AnnotationFields.field4: Number(guint) (Read / Write) + +

A new field, breaking ABI is fun!

+

Since 1.4

+
diff --git a/tests/scanner/Regress-1.0-Gjs-expected/Regress.AnnotationFields.page b/tests/scanner/Regress-1.0-Gjs-expected/Regress.AnnotationFields.page index 585a97c1..7cd55257 100644 --- a/tests/scanner/Regress-1.0-Gjs-expected/Regress.AnnotationFields.page +++ b/tests/scanner/Regress-1.0-Gjs-expected/Regress.AnnotationFields.page @@ -16,6 +16,7 @@ let annotationFields = new Regress.AnnotationFields({ field1: value arr: value len: value + field4: value });

This is a struct for testing field documentation and annotations

diff --git a/tests/scanner/Regress-1.0-Python-expected/Regress.AnnotationBitfield.page b/tests/scanner/Regress-1.0-Python-expected/Regress.AnnotationBitfield.page index fa303bb4..a9003221 100644 --- a/tests/scanner/Regress-1.0-Python-expected/Regress.AnnotationBitfield.page +++ b/tests/scanner/Regress-1.0-Python-expected/Regress.AnnotationBitfield.page @@ -19,6 +19,10 @@ <code>AnnotationBitfield.BAR</code> + + +<code>AnnotationBitfield.FOOBAR</code> + diff --git a/tests/scanner/Regress-1.0-Python-expected/Regress.AnnotationFields-field4.page b/tests/scanner/Regress-1.0-Python-expected/Regress.AnnotationFields-field4.page new file mode 100644 index 00000000..163d420f --- /dev/null +++ b/tests/scanner/Regress-1.0-Python-expected/Regress.AnnotationFields-field4.page @@ -0,0 +1,14 @@ + + + + + + Regress.AnnotationFields->field4 +

A new field, breaking ABI is fun!

+

Since 1.4

+
diff --git a/tests/scanner/Regress-1.0-expected.gir b/tests/scanner/Regress-1.0-expected.gir index cef3b124..62f456f8 100644 --- a/tests/scanner/Regress-1.0-expected.gir +++ b/tests/scanner/Regress-1.0-expected.gir @@ -58,7 +58,7 @@ and/or use gtk-doc annotations. --> - + version="1.4"> Constant to define a calculated large value - + line="297">Constant to define a calculated large value + c:type="REGRESS_ANNOTATION_CALCULATED_LARGE_DIV"> Constant to define a calculated large value - + line="306">Constant to define a calculated large value + - + + + This is a callback. - + line="20">This is a callback. + array of ints + line="25">array of ints array of ints + line="22">array of ints @@ -141,18 +146,18 @@ and/or use gtk-doc annotations. --> This is a struct for testing field documentation and annotations - + line="252">This is a struct for testing field documentation and annotations + Some documentation + line="254">Some documentation an array of length @len + line="255">an array of length @len @@ -160,13 +165,19 @@ and/or use gtk-doc annotations. --> the length of array + line="256">the length of array + + A new field, breaking ABI is fun! + + - + @@ -190,12 +201,12 @@ and/or use gtk-doc annotations. --> c:type="RegressAnnotationListCallback"> This is a callback taking a list. - + line="29">This is a callback taking a list. + list of strings + line="34">list of strings @@ -204,7 +215,7 @@ and/or use gtk-doc annotations. --> list of strings + line="31">list of strings @@ -214,9 +225,9 @@ and/or use gtk-doc annotations. --> This is a callback with a 'closure' argument that is not named + line="38">This is a callback with a 'closure' argument that is not named 'user_data' and hence has to be annotated. - + @@ -228,7 +239,7 @@ and/or use gtk-doc annotations. --> closure="0"> The user data + line="40">The user data @@ -243,11 +254,11 @@ and/or use gtk-doc annotations. --> This is an object used to test annotations. - + line="47">This is an object used to test annotations. + - + This is a test for out arguments; GObject defaults to transfer - + This is a test for out arguments, one transferred, other not - + Test taking a zero-terminated array - + @@ -369,7 +380,7 @@ and/or use gtk-doc annotations. --> Test taking an array with length parameter - + @@ -402,7 +413,7 @@ and/or use gtk-doc annotations. --> Test taking a zero-terminated array with length parameter - + @@ -435,7 +446,7 @@ are zero-terminated Test returning a caller-owned object - + deprecated="1" deprecated-version="0.12"> Use regress_annotation_object_create_object() instead. - + - + @@ -489,7 +500,7 @@ are zero-terminated Test taking a call-scoped callback - + @@ -527,7 +538,7 @@ are zero-terminated filename="annotation.c" line="349">This is a test for returning a hash table mapping strings to objects. - + line="378">This is a test for returning a list of objects. The list itself should be freed, but not the internal objects, intentionally similar example to gtk_container_get_children - + filename="annotation.c" line="331">This is a test for returning a list of strings, where each string needs to be freed. - + - + @@ -614,7 +625,7 @@ each string needs to be freed. This is a test for in arguments - + This is a test for out arguments - + This is a second test for out arguments - + This is a 3th test for out arguments - + - + - + This is a test for out arguments - + Test taking a zero-terminated array with length parameter - + @@ -831,7 +842,7 @@ each string needs to be freed. Test taking a guchar * with a length. - + @@ -863,7 +874,7 @@ each string needs to be freed. Test taking a gchar * with a length. - + @@ -896,7 +907,7 @@ each string needs to be freed. filename="annotation.c" line="550">Test taking a gchar * with a length, overriding the array element type. - + @@ -928,7 +939,7 @@ type. Test returning a string as an out parameter - + - + @@ -979,7 +990,7 @@ type. filename="annotation.c" line="606">This is here just for the sake of being overriden by its regress_annotation_object_watch_full(). - + @@ -1014,7 +1025,7 @@ regress_annotation_object_watch_full(). Test overriding via the "Rename To" annotation. - + @@ -1055,7 +1066,7 @@ regress_annotation_object_watch_full(). - + @@ -1196,7 +1207,7 @@ it says it's pointer but it's actually a string. - + @@ -1204,8 +1215,8 @@ it says it's pointer but it's actually a string. This is a test of an array of object in an field of a struct. - + line="242">This is a test of an array of object in an field of a struct. + @@ -5648,7 +5659,7 @@ the introspection client langage. - + @@ -5680,7 +5691,7 @@ the introspection client langage. filename="annotation.c" line="719">Test messing up the heuristic of closure/destroy-notification detection, and fixing it via annotations. - + @@ -5712,7 +5723,7 @@ detection, and fixing it via annotations. - + - + @@ -5750,7 +5761,7 @@ detection, and fixing it via annotations. - + @@ -5765,7 +5776,7 @@ detection, and fixing it via annotations. - + @@ -5782,7 +5793,7 @@ detection, and fixing it via annotations. - + - + - + @@ -5833,14 +5844,14 @@ detection, and fixing it via annotations. Explicitly test having a space after the ** here. - + - + @@ -5857,7 +5868,7 @@ detection, and fixing it via annotations. - + - + @@ -5889,7 +5900,7 @@ detection, and fixing it via annotations. See https://bugzilla.gnome.org/show_bug.cgi?id=630862 - + - + - + diff --git a/tests/scanner/annotation.h b/tests/scanner/annotation.h index 92c0b6bd..5c23d77c 100644 --- a/tests/scanner/annotation.h +++ b/tests/scanner/annotation.h @@ -8,7 +8,13 @@ typedef enum /*< flags,prefix=ANN >*/ { ANN_FLAG_FOO = 1, - ANN_FLAG_BAR = 2 + ANN_FLAG_BAR = 2, + /** + * ANN_FLAG_FOOBAR: + * + * Since: 1.4 + */ + ANN_FLAG_FOOBAR = 3, } RegressAnnotationBitfield; /** @@ -256,9 +262,16 @@ struct RegressAnnotationFields int field1; guchar *arr; gulong len; + /** + * RegressAnnotationFields.field4: + * + * A new field, breaking ABI is fun! + * + * Since: 1.4 + */ + guint field4; }; - _GI_TEST_EXTERN void regress_annotation_ptr_array (GPtrArray *array); -- cgit v1.2.1