[gobject-introspection] giscanner: flesh out annotation parsing and storage
- From: Colin Walters <walters src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gobject-introspection] giscanner: flesh out annotation parsing and storage
- Date: Wed, 9 Oct 2013 16:58:09 +0000 (UTC)
commit a2b22ce75937d2d996ef90e0ab683d36031365d0
Author: Dieter Verfaillie <dieterv optionexplicit be>
Date: Mon Aug 12 16:54:11 2013 +0200
giscanner: flesh out annotation parsing and storage
- remove annotations regex, restore proper parens parsing
- drop weird DocOption() storage class and use lists/dicts
as appropriate
- make GtkDocAnnotations a simple OrderedDict subclass instead
of a weird hybrid dict/list storage class
- Deprecate Attribute: tag, replace with (attributes) annotation
on the identifier
giscanner/annotationparser.py | 978 ++++++++++++--------
giscanner/maintransformer.py | 117 ++--
tests/scanner/Makefile.am | 1 +
tests/scanner/annotation.c | 14 +-
tests/scanner/annotation.h | 4 +-
.../annotationparser/gi/annotation_attributes.xml | 395 ++++++++
.../gi/annotation_element_type.xml | 2 +-
.../annotationparser/gi/annotation_in_out.xml | 7 +-
.../annotationparser/gi/identifier_symbol.xml | 6 +-
.../scanner/annotationparser/gi/tag_deprecated.xml | 2 +-
tests/scanner/annotationparser/gi/tag_since.xml | 2 +-
.../scanner/annotationparser/gi/tag_stability.xml | 2 +-
tests/scanner/annotationparser/test_parser.py | 92 ++-
tests/scanner/annotationparser/test_patterns.py | 221 ++---
tests/warn/invalid-element-type.h | 10 +-
15 files changed, 1238 insertions(+), 615 deletions(-)
---
diff --git a/giscanner/annotationparser.py b/giscanner/annotationparser.py
index 26e001e..25445b4 100644
--- a/giscanner/annotationparser.py
+++ b/giscanner/annotationparser.py
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
# -*- Mode: Python -*-
+
# GObject-Introspection - a framework for introspecting GObject libraries
# Copyright (C) 2008-2010 Johan Dahlin
# Copyright (C) 2012-2013 Dieter Verfaillie <dieterv optionexplicit be>
@@ -109,7 +110,7 @@ import os
import re
from .collections import OrderedDict
-from .message import Position, warn
+from .message import Position, warn, error
# GTK-Doc comment block parts
@@ -148,7 +149,6 @@ DEPRECATED_GI_TAGS = [TAG_RETURN,
TAG_RETURNS_VALUE]
# 4) GObject-Introspection annotation tags.
-TAG_ATTRIBUTES = 'attributes'
TAG_GET_VALUE_FUNC = 'get value func'
TAG_REF_FUNC = 'ref func'
TAG_RENAME_TO = 'rename to'
@@ -159,8 +159,7 @@ TAG_UNREF_FUNC = 'unref func'
TAG_VALUE = 'value'
TAG_VFUNC = 'virtual'
-GI_ANN_TAGS = [TAG_ATTRIBUTES,
- TAG_GET_VALUE_FUNC,
+GI_ANN_TAGS = [TAG_GET_VALUE_FUNC,
TAG_REF_FUNC,
TAG_RENAME_TO,
TAG_SET_VALUE_FUNC,
@@ -170,12 +169,28 @@ GI_ANN_TAGS = [TAG_ATTRIBUTES,
TAG_VALUE,
TAG_VFUNC]
-ALL_TAGS = GTKDOC_TAGS + DEPRECATED_GTKDOC_TAGS + DEPRECATED_GI_TAGS + GI_ANN_TAGS
+# 5) Deprecated GObject-Introspection annotation tags.
+# Accepted by old versions of this module while they should have been
+# annotations on the identifier part instead.
+# Note: This list can not be extended ever again. The GObject-Introspection project is not
+# allowed to invent GTK-Doc tags. Please create new annotations instead.
+TAG_ATTRIBUTES = 'attributes'
+
+DEPRECATED_GI_ANN_TAGS = [TAG_ATTRIBUTES]
+
+ALL_TAGS = (GTKDOC_TAGS + DEPRECATED_GTKDOC_TAGS + DEPRECATED_GI_TAGS + GI_ANN_TAGS +
+ DEPRECATED_GI_ANN_TAGS)
+
+# GObject-Introspection annotation start/end tokens
+ANN_LPAR = '('
+ANN_RPAR = ')'
# GObject-Introspection annotations
+# 1) Supported annotations
+# Note: when adding new annotations, GTK-Doc project's gtkdoc-mkdb needs to be modified too!
ANN_ALLOW_NONE = 'allow-none'
ANN_ARRAY = 'array'
-ANN_ATTRIBUTE = 'attribute'
+ANN_ATTRIBUTES = 'attributes'
ANN_CLOSURE = 'closure'
ANN_CONSTRUCTOR = 'constructor'
ANN_DESTROY = 'destroy'
@@ -183,7 +198,6 @@ ANN_ELEMENT_TYPE = 'element-type'
ANN_FOREIGN = 'foreign'
ANN_IN = 'in'
ANN_INOUT = 'inout'
-ANN_INOUT_ALT = 'in-out'
ANN_METHOD = 'method'
ANN_OUT = 'out'
ANN_SCOPE = 'scope'
@@ -191,24 +205,33 @@ ANN_SKIP = 'skip'
ANN_TRANSFER = 'transfer'
ANN_TYPE = 'type'
-ALL_ANNOTATIONS = [
- ANN_ALLOW_NONE,
- ANN_ARRAY,
- ANN_ATTRIBUTE,
- ANN_CLOSURE,
- ANN_CONSTRUCTOR,
- ANN_DESTROY,
- ANN_ELEMENT_TYPE,
- ANN_FOREIGN,
- ANN_IN,
- ANN_INOUT,
- ANN_INOUT_ALT,
- ANN_METHOD,
- ANN_OUT,
- ANN_SCOPE,
- ANN_SKIP,
- ANN_TRANSFER,
- ANN_TYPE]
+GI_ANNS = [ANN_ALLOW_NONE,
+ ANN_ARRAY,
+ ANN_ATTRIBUTES,
+ ANN_CLOSURE,
+ ANN_CONSTRUCTOR,
+ ANN_DESTROY,
+ ANN_ELEMENT_TYPE,
+ ANN_FOREIGN,
+ ANN_IN,
+ ANN_INOUT,
+ ANN_METHOD,
+ ANN_OUT,
+ ANN_SCOPE,
+ ANN_SKIP,
+ ANN_TRANSFER,
+ ANN_TYPE]
+
+# 2) Deprecated GObject-Introspection annotations
+ANN_ATTRIBUTE = 'attribute'
+ANN_INOUT_ALT = 'in-out'
+
+DEPRECATED_GI_ANNS = [ANN_ATTRIBUTE,
+ ANN_INOUT_ALT]
+
+ALL_ANNOTATIONS = GI_ANNS + DEPRECATED_GI_ANNS
+DICT_ANNOTATIONS = [ANN_ARRAY, ANN_ATTRIBUTES]
+LIST_ANNOTATIONS = [ann for ann in ALL_ANNOTATIONS if ann not in DICT_ANNOTATIONS]
# (array) annotation options
OPT_ARRAY_FIXED_SIZE = 'fixed-size'
@@ -318,12 +341,7 @@ SECTION_RE = re.compile(
''',
re.UNICODE | re.VERBOSE)
-# Program matching symbol (function, constant, struct and enum) identifiers.
-#
-# Results in 3 symbolic groups:
-# - group 1 = symbol_name
-# - group 2 = delimiter
-# - group 3 = annotations
+# Pattern matching symbol (function, constant, struct and enum) identifiers.
SYMBOL_RE = re.compile(
r'''
^ # start
@@ -332,44 +350,36 @@ SYMBOL_RE = re.compile(
\s* # 0 or more whitespace characters
(?P<delimiter>:?) # delimiter
\s* # 0 or more whitespace characters
- (?P<annotations>(?:\(.*?\)\s*)*) # annotations
+ (?P<fields>.*?) # annotations + description
+ \s* # 0 or more whitespace characters
+ :? # invalid delimiter
\s* # 0 or more whitespace characters
$ # end
''',
re.UNICODE | re.VERBOSE)
-# Program matching property identifiers.
-#
-# Results in 4 symbolic groups:
-# - group 1 = class_name
-# - group 2 = property_name
-# - group 3 = delimiter
-# - group 4 = annotations
+# Pattern matching property identifiers.
PROPERTY_RE = re.compile(
r'''
^ # start
\s* # 0 or more whitespace characters
(?P<class_name>[\w]+) # class name
\s* # 0 or more whitespace characters
- :{1} # required colon
+ :{1} # 1 required colon
\s* # 0 or more whitespace characters
(?P<property_name>[\w-]*\w) # property name
\s* # 0 or more whitespace characters
(?P<delimiter>:?) # delimiter
\s* # 0 or more whitespace characters
- (?P<annotations>(?:\(.*?\)\s*)*) # annotations
+ (?P<fields>.*?) # annotations + description
+ \s* # 0 or more whitespace characters
+ :? # invalid delimiter
\s* # 0 or more whitespace characters
$ # end
''',
re.UNICODE | re.VERBOSE)
-# Program matching signal identifiers.
-#
-# Results in 4 symbolic groups:
-# - group 1 = class_name
-# - group 2 = signal_name
-# - group 3 = delimiter
-# - group 4 = annotations
+# Pattern matching signal identifiers.
SIGNAL_RE = re.compile(
r'''
^ # start
@@ -382,19 +392,15 @@ SIGNAL_RE = re.compile(
\s* # 0 or more whitespace characters
(?P<delimiter>:?) # delimiter
\s* # 0 or more whitespace characters
- (?P<annotations>(?:\(.*?\)\s*)*) # annotations
+ (?P<fields>.*?) # annotations + description
+ \s* # 0 or more whitespace characters
+ :? # invalid delimiter
\s* # 0 or more whitespace characters
$ # end
''',
re.UNICODE | re.VERBOSE)
-# Program matching parameters.
-#
-# Results in 4 symbolic groups:
-# - group 1 = parameter_name
-# - group 2 = annotations
-# - group 3 = delimiter
-# - group 4 = description
+# Pattern matching parameters.
PARAMETER_RE = re.compile(
r'''
^ # start
@@ -402,63 +408,30 @@ PARAMETER_RE = re.compile(
@ # @ character
(?P<parameter_name>[\w-]*\w|.*?\.\.\.) # parameter name
\s* # 0 or more whitespace characters
- :{1} # required colon
+ :{1} # 1 required delimiter
\s* # 0 or more whitespace characters
- (?P<annotations>(?:\(.*?\)\s*)*) # annotations
- (?P<delimiter>:?) # delimiter
- \s* # 0 or more whitespace characters
- (?P<description>.*?) # description
+ (?P<fields>.*?) # annotations + description
\s* # 0 or more whitespace characters
$ # end
''',
re.UNICODE | re.VERBOSE)
-# Program matching tags.
-#
-# Results in 4 symbolic groups:
-# - group 1 = tag_name
-# - group 2 = annotations
-# - group 3 = delimiter
-# - group 4 = description
-_all_tags = '|'.join(ALL_TAGS).replace(' ', '\\ ')
+# Pattern matching tags.
+_all_tags = '|'.join(ALL_TAGS).replace(' ', r'\s')
TAG_RE = re.compile(
r'''
^ # start
\s* # 0 or more whitespace characters
(?P<tag_name>''' + _all_tags + r''') # tag name
\s* # 0 or more whitespace characters
- :{1} # required colon
- \s* # 0 or more whitespace characters
- (?P<annotations>(?:\(.*?\)\s*)*) # annotations
- (?P<delimiter>:?) # delimiter
+ :{1} # 1 required delimiter
\s* # 0 or more whitespace characters
- (?P<description>.*?) # description
+ (?P<fields>.*?) # annotations + value + description
\s* # 0 or more whitespace characters
$ # end
''',
re.UNICODE | re.VERBOSE | re.IGNORECASE)
-# Program matching multiline annotation continuations.
-# This is used on multiline parameters and tags (but not on the first line) to
-# generate warnings about invalid annotations spanning multiple lines.
-#
-# Results in 3 symbolic groups:
-# - group 2 = annotations
-# - group 3 = delimiter
-# - group 4 = description
-MULTILINE_ANNOTATION_CONTINUATION_RE = re.compile(
- r'''
- ^ # start
- \s* # 0 or more whitespace characters
- (?P<annotations>(?:\(.*?\)\s*)*) # annotations
- (?P<delimiter>:) # delimiter
- \s* # 0 or more whitespace characters
- (?P<description>.*?) # description
- \s* # 0 or more whitespace characters
- $ # end
- ''',
- re.UNICODE | re.VERBOSE)
-
# Pattern matching value and description fields for TAG_DEPRECATED & TAG_SINCE tags.
TAG_VALUE_VERSION_RE = re.compile(
r'''
@@ -490,82 +463,21 @@ TAG_VALUE_STABILITY_RE = re.compile(
re.UNICODE | re.VERBOSE | re.IGNORECASE)
-class DocOption(object):
-
- __slots__ = ('_array', '_dict')
-
- def __init__(self, option):
- self._array = []
- self._dict = OrderedDict()
- # (annotation option1=value1 option2=value2) etc
- for p in option.split(' '):
- if '=' in p:
- name, value = p.split('=', 1)
- else:
- name = p
- value = None
- self._dict[name] = value
- if value is None:
- self._array.append(name)
- else:
- self._array.append((name, value))
-
- def __repr__(self):
- return '<DocOption %r>' % (self._array, )
-
- def length(self):
- return len(self._array)
-
- def one(self):
- assert len(self._array) == 1
- return self._array[0]
-
- def flat(self):
- return self._array
-
- def all(self):
- return self._dict
-
-
-class GtkDocAnnotations(object):
-
- __slots__ = ('values', 'position')
-
- def __init__(self):
- self.values = []
- self.position = None
-
- def __repr__(self):
- return '<GtkDocAnnotations %r>' % (self.values, )
-
- def __getitem__(self, item):
- for key, value in self.values:
- if key == item:
- return value
- raise KeyError
-
- def __nonzero__(self):
- return bool(self.values)
-
- def __iter__(self):
- return (k for k, v in self.values)
-
- def add(self, name, value):
- self.values.append((name, value))
+class GtkDocAnnotations(OrderedDict):
+ '''
+ An ordered dictionary mapping annotation names to annotation options (if any). Annotation
+ options can be either a :class:`list`, a :class:`giscanner.collections.OrderedDict`
+ (depending on the annotation name)or :const:`None`.
+ '''
- def get(self, item, default=None):
- for key, value in self.values:
- if key == item:
- return value
- return default
+ __slots__ = ('position')
- def getall(self, item):
- for key, value in self.values:
- if key == item:
- yield value
+ def __init__(self, position=None):
+ OrderedDict.__init__(self)
- def items(self):
- return iter(self.values)
+ #: A :class:`giscanner.message.Position` instance specifying the location of the
+ #: annotations in the source file or :const:`None`.
+ self.position = position
class GtkDocTag(object):
@@ -584,7 +496,7 @@ class GtkDocTag(object):
def _validate_annotation(self, ann_name, options, required=False,
n_params=None, choices=None):
- if required and options is None:
+ if required and len(options) == 0:
warn('%s annotation needs a value' % (ann_name, ), self.position)
return
@@ -595,28 +507,25 @@ class GtkDocTag(object):
s = 'one value'
else:
s = '%d values' % (n_params, )
- if ((n_params > 0 and (options is None or options.length() != n_params))
- or n_params == 0 and options is not None):
- if options is None:
- length = 0
- else:
- length = options.length()
+ if ((n_params > 0 and (len(options) == 0 or len(options) != n_params))
+ or n_params == 0 and len(options) != 0):
+ length = len(options)
warn('%s annotation needs %s, not %d' % (ann_name, s, length),
self.position)
return
if choices is not None:
- option = options.one()
+ option = options[0]
if option not in choices:
warn('invalid %s annotation value: %r' % (ann_name, option, ),
self.position)
return
def _validate_array(self, ann_name, options):
- if options is None:
+ if len(options) == 0:
return
- for option, value in options.all().items():
+ for option, value in options.items():
if option in [OPT_ARRAY_ZERO_TERMINATED, OPT_ARRAY_FIXED_SIZE]:
try:
int(value)
@@ -637,29 +546,29 @@ class GtkDocTag(object):
self.position)
def _validate_closure(self, ann_name, options):
- if options is not None and options.length() > 1:
- warn('closure takes at most 1 value, %d given' % (options.length(), ),
+ if len(options) != 0 and len(options) > 1:
+ warn('closure takes at most 1 value, %d given' % (len(options), ),
self.position)
def _validate_element_type(self, ann_name, options):
self._validate_annotation(ann_name, options, required=True)
- if options is None:
+ if len(options) == 0:
warn('element-type takes at least one value, none given',
self.position)
return
- if options.length() > 2:
- warn('element-type takes at most 2 values, %d given' % (options.length(), ),
+ if len(options) > 2:
+ warn('element-type takes at most 2 values, %d given' % (len(options), ),
self.position)
return
def _validate_out(self, ann_name, options):
- if options is None:
+ if len(options) == 0:
return
- if options.length() > 1:
- warn('out annotation takes at most 1 value, %d given' % (options.length(), ),
+ if len(options) > 1:
+ warn('out annotation takes at most 1 value, %d given' % (len(options), ),
self.position)
return
- option = options.one()
+ option = options[0]
if option not in [OPT_OUT_CALLEE_ALLOCATES,
OPT_OUT_CALLER_ALLOCATES]:
warn("out annotation value is invalid: %r" % (option, ),
@@ -670,8 +579,11 @@ class GtkDocTag(object):
def serialize_one(option, value, fmt, fmt2):
if value:
if type(value) != str:
- value = ' '.join((serialize_one(k, v, '%s=%s', '%s')
- for k, v in value.all().items()))
+ if isinstance(value, list):
+ value = ' '.join(value)
+ else:
+ value = ' '.join((serialize_one(k, v, '%s=%s', '%s')
+ for k, v in value.items()))
return fmt % (option, value)
else:
return fmt2 % (option, )
@@ -698,18 +610,14 @@ class GtkDocTag(object):
self.description or '')
def validate(self):
- if self.name == TAG_ATTRIBUTES:
- # The 'Attributes:' tag allows free form annotations so the
- # validation below is most certainly going to fail.
- return
-
for ann_name, value in self.annotations.items():
if ann_name == ANN_ALLOW_NONE:
self._validate_annotation(ann_name, value, n_params=0)
elif ann_name == ANN_ARRAY:
self._validate_array(ann_name, value)
- elif ann_name == ANN_ATTRIBUTE:
- self._validate_annotation(ann_name, value, n_params=2)
+ elif ann_name == ANN_ATTRIBUTES:
+ # The 'attributes' annotation allows free form annotations.
+ pass
elif ann_name == ANN_CLOSURE:
self._validate_closure(ann_name, value)
elif ann_name == ANN_DESTROY:
@@ -827,73 +735,34 @@ class GtkDocCommentBlock(object):
class GtkDocCommentBlockParser(object):
- """
- GTK-Doc comment block parser.
-
+ '''
Parse GTK-Doc comment blocks into a parse tree built out of :class:`GtkDocCommentBlock`,
- :class:`GtkDocTag`, :class:`GtkDocAnnotations` and :class:`DocOption` objects. This
- parser tries to accept malformed input whenever possible and does not emit
- syntax errors. However, it does emit warnings at the slightest indication
- of malformed input when possible. It is usually a good idea to heed these
- warnings as malformed input is known to result in invalid GTK-Doc output.
-
- A GTK-Doc comment block can be constructed out of multiple parts that can
- be combined to write different types of documentation.
- See `GTK-Doc's documentation`_ to learn more about possible valid combinations.
- Each part can be further divided into fields which are separated by `:` characters.
-
- Possible parts and the fields they are constructed from look like the
- following (optional fields are enclosed in square brackets):
-
- .. code-block:: c
- /**
- * identifier_name [:annotations]
- * @parameter_name [:annotations] [:description]
- *
- * comment_block_description
- * tag_name [:annotations] [:description]
- */
-
- The order in which the different parts have to be specified is important::
-
- - There has to be exactly 1 `identifier` part on the first line of the
- comment block which consists of:
- * an `identifier_name` field
- * an optional `annotations` field
- - Followed by 0 or more `parameters` parts, each consisting of:
- * a `parameter_name` field
- * an optional `annotations` field
- * an optional `description` field
- - Followed by at least 1 empty line signaling the beginning of
- the `comment_block_description` part
- - Followed by an optional `comment block description` part.
- - Followed by 0 or more `tag` parts, each consisting of:
- * a `tag_name` field
- * an optional `annotations` field
- * an optional `description` field
-
- Additionally, the following restrictions are in effect::
-
- - Parts can optionally be separated by an empty line, except between
- the `parameter` parts and the `comment block description` part where
- an empty line is required (see above).
- - Parts and fields cannot span multiple lines, except for
- `parameter descriptions`, `tag descriptions` and the
- `comment_block_description` fields.
- - `parameter descriptions` fields can not span multiple paragraphs.
- - `tag descriptions` and `comment block description` fields can
- span multiple paragraphs.
+ :class:`GtkDocParameter`, :class:`GtkDocTag` and :class:`GtkDocAnnotations`
+ objects. This parser tries to accept malformed input whenever possible and does
+ not cause the process to exit on syntax errors. It does however emit:
+
+ * warning messages at the slightest indication of recoverable malformed input and
+ * error messages for unrecoverable malformed input
+
+ whenever possible. Recoverable, in this context, means that we can serialize the
+ :class:`GtkDocCommentBlock` instance using a :class:`GtkDocCommentBlockWriter` without
+ information being lost. It is usually a good idea to heed these warning and error messages
+ as malformed input can result in both:
+
+ * invalid GTK-Doc output (HTML, pdf, ...) when the comment blocks are parsed
+ with GTK-Doc's gtkdoc-mkdb
+ * unexpected introspection behavior, for example missing parameters in the
+ generated .gir and .typelib files
.. NOTE:: :class:`GtkDocCommentBlockParser` functionality is heavily based on gtkdoc-mkdb's
`ScanSourceFile()`_ function and is currently in sync with GTK-Doc
commit `47abcd5`_.
- .. _GTK-Doc's documentation:
- http://developer.gnome.org/gtk-doc-manual/1.18/documenting.html.en
.. _ScanSourceFile():
- http://git.gnome.org/browse/gtk-doc/tree/gtkdoc-mkdb.in#n3722
- .. _47abcd5: 47abcd53b8489ebceec9e394676512a181c1f1f6
- """
+ http://git.gnome.org/browse/gtk-doc/tree/gtkdoc-mkdb.in#n3722
+ .. _47abcd5:
+ https://git.gnome.org/browse/gtk-doc/commit/?id=47abcd53b8489ebceec9e394676512a181c1f1f6
+ '''
def parse_comment_blocks(self, comments):
'''
@@ -1030,32 +899,40 @@ class GtkDocCommentBlockParser(object):
####################################################################
# Check for GTK-Doc comment block identifier.
####################################################################
- if not comment_block:
+ if comment_block is None:
result = SECTION_RE.match(line)
if result:
identifier_name = 'SECTION:%s' % (result.group('section_name'), )
- column = result.start('section_name') + column_offset
+ identifier_delimiter = None
+ identifier_fields = None
+ identifier_fields_start = None
else:
result = PROPERTY_RE.match(line)
if result:
identifier_name = '%s:%s' % (result.group('class_name'),
result.group('property_name'))
- column = result.start('property_name') + column_offset
+ identifier_delimiter = result.group('delimiter')
+ identifier_fields = result.group('fields')
+ identifier_fields_start = result.start('fields')
else:
result = SIGNAL_RE.match(line)
if result:
identifier_name = '%s::%s' % (result.group('class_name'),
result.group('signal_name'))
- column = result.start('signal_name') + column_offset
+ 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'), )
- column = result.start('symbol_name') + column_offset
+ identifier_delimiter = result.group('delimiter')
+ identifier_fields = result.group('fields')
+ identifier_fields_start = result.start('fields')
if result:
in_part = PART_IDENTIFIER
@@ -1064,17 +941,26 @@ class GtkDocCommentBlockParser(object):
comment_block = GtkDocCommentBlock(identifier_name)
comment_block.position = position
- if 'annotations' in result.groupdict() and result.group('annotations') != '':
- comment_block.annotations = self.parse_annotations(comment_block,
- result.group('annotations'))
+ if identifier_fields:
+ (a, d) = self._parse_fields(position,
+ column_offset + identifier_fields_start,
+ original_line,
+ identifier_fields, True, False)
+ if d:
+ # Not an identifier due to invalid trailing description field
+ in_part = None
+ part_indent = None
+ comment_block = None
+ result = None
+ else:
+ comment_block.annotations = a
- if 'delimiter' in result.groupdict() and result.group('delimiter') != ':':
- delimiter_start = result.start('delimiter')
- delimiter_column = column_offset + delimiter_start
- marker = ' ' * delimiter_column + '^'
- warn("missing ':' at column %s:\n%s\n%s" %
- (delimiter_column + 1, original_line, marker),
- position)
+ if not identifier_delimiter and a:
+ marker_position = column_offset + result.start('delimiter')
+ marker = ' ' * marker_position + '^'
+ warn('missing ":" at column %s:\n%s\n%s' %
+ (marker_position + 1, original_line, marker),
+ position)
if not result:
# Emit a single warning when the identifier is not found on the first line
@@ -1091,10 +977,11 @@ class GtkDocCommentBlockParser(object):
####################################################################
result = PARAMETER_RE.match(line)
if result:
- marker = ' ' * (result.start('parameter_name') + column_offset) + '^'
param_name = result.group('parameter_name')
- param_annotations = result.group('annotations')
- param_description = result.group('description')
+ param_name_lower = param_name.lower()
+ param_fields = result.group('fields')
+ param_fields_start = result.start('fields')
+ marker = ' ' * (result.start('parameter_name') + column_offset) + '^'
if in_part == PART_IDENTIFIER:
in_part = PART_PARAMETERS
@@ -1109,7 +996,7 @@ class GtkDocCommentBlockParser(object):
# Old style GTK-Doc allowed return values to be specified as
# parameters instead of tags.
- if param_name.lower() == TAG_RETURNS:
+ if param_name_lower == TAG_RETURNS:
param_name = TAG_RETURNS
if not returns_seen:
@@ -1133,9 +1020,14 @@ class GtkDocCommentBlockParser(object):
tag = GtkDocTag(param_name)
tag.position = position
- tag.description = param_description
- if param_annotations:
- tag.annotations = self.parse_annotations(tag, param_annotations)
+
+ if param_fields:
+ (a, d) = self._parse_fields(position,
+ column_offset + param_fields_start,
+ original_line, param_fields)
+ tag.annotations = a
+ tag.description = d
+
if param_name == TAG_RETURNS:
comment_block.tags[param_name] = tag
else:
@@ -1146,9 +1038,13 @@ class GtkDocCommentBlockParser(object):
####################################################################
# Check for comment block description.
#
- # When we are parsing comment block parameters or the comment block
- # identifier (when there are no parameters) and encounter an empty
- # line, we must be parsing the comment block description.
+ # When we are parsing parameter parts or the identifier part (when
+ # there are no parameters) and encounter an empty line, we must be
+ # parsing the comment block description.
+ #
+ # Note: it is unclear why GTK-Doc does not allow paragraph breaks
+ # at this location as those might be handy describing
+ # parameters from time to time...
####################################################################
if (EMPTY_LINE_RE.match(line) and in_part in [PART_IDENTIFIER, PART_PARAMETERS]):
in_part = PART_DESCRIPTION
@@ -1160,42 +1056,99 @@ class GtkDocCommentBlockParser(object):
####################################################################
result = TAG_RE.match(line)
if result and line_indent <= part_indent:
+ part_indent = line_indent
tag_name = result.group('tag_name')
- tag_annotations = result.group('annotations')
- tag_description = result.group('description')
-
+ tag_name_lower = tag_name.lower()
+ tag_fields = result.group('fields')
+ tag_fields_start = result.start('fields')
marker = ' ' * (result.start('tag_name') + column_offset) + '^'
- # Deprecated GTK-Doc Description: tag
- if tag_name.lower() == TAG_DESCRIPTION:
- warn("GTK-Doc tag \"Description:\" has been deprecated:\n%s\n%s" %
+ if tag_name_lower in DEPRECATED_GI_ANN_TAGS:
+ # Deprecated GObject-Introspection specific tags.
+ # Emit a warning and transform these into annotations on the identifier
+ # instead, as agreed upon in http://bugzilla.gnome.org/show_bug.cgi?id=676133
+ warn('GObject-Introspection specific GTK-Doc tag "%s" '
+ 'has been deprecated, please use annotations on the identifier '
+ 'instead:\n%s\n%s' % (tag_name, original_line, marker),
+ position)
+
+ # Translate deprecated tag name into corresponding annotation name
+ ann_name = tag_name_lower.replace(' ', '-')
+
+ if tag_name_lower == TAG_ATTRIBUTES:
+ transformed = ''
+ (a, d) = self._parse_fields(position,
+ result.start('tag_name') + column_offset,
+ line,
+ tag_fields.strip(),
+ False,
+ False)
+
+ if a:
+ for annotation in a:
+ ann_options = self._parse_annotation_options_list(position, marker,
+ line, annotation)
+ n_options = len(ann_options)
+ if n_options == 1:
+ transformed = '%s %s' % (transformed, ann_options[0], )
+ elif n_options == 2:
+ transformed = '%s %s=%s' % (transformed, ann_options[0],
+ ann_options[1])
+ else:
+ # Malformed Attributes: tag
+ error('malformed "Attributes:" tag will be ignored:\n%s\n%s' %
+ (original_line, marker),
+ position)
+ transformed = None
+
+ if transformed:
+ transformed = '%s %s' % (ann_name, transformed.strip())
+ ann_name, docannotation = self._parse_annotation(
+ position,
+ column_offset + tag_fields_start,
+ original_line,
+ transformed)
+ stored_annotation = comment_block.annotations.get('attributes')
+ if stored_annotation:
+ error('Duplicate "Attributes:" annotation will '
+ 'be ignored:\n%s\n%s' % (original_line, marker),
+ position)
+ else:
+ comment_block.annotations[ann_name] = docannotation
+ else:
+ ann_name, options = self._parse_annotation(position,
+ column_offset + tag_fields_start,
+ line,
+ '%s %s' % (ann_name, tag_fields))
+ comment_block.annotations[ann_name] = options
+
+ continue
+ elif tag_name_lower == TAG_DESCRIPTION:
+ # Deprecated GTK-Doc Description: tag
+ warn('GTK-Doc tag "Description:" has been deprecated:\n%s\n%s' %
(original_line, marker),
position)
in_part = PART_DESCRIPTION
- part_indent = line_indent
if not comment_block.description:
- comment_block.description = tag_description
+ comment_block.description = tag_fields
else:
- comment_block.description += '\n' + tag_description
+ comment_block.description += '\n' + tag_fields
continue
# Now that the deprecated stuff is out of the way, continue parsing real tags
if in_part == PART_DESCRIPTION:
in_part = PART_TAGS
- part_indent = line_indent
-
if in_part != PART_TAGS:
column = result.start('tag_name') + column_offset
- marker = ' ' * column + '^'
warn("'%s:' tag unexpected at this location:\n%s\n%s" %
(tag_name, original_line, marker),
position)
- if tag_name.lower() in [TAG_RETURN, TAG_RETURNS,
- TAG_RETURN_VALUE, TAG_RETURNS_VALUE]:
+ if tag_name_lower in [TAG_RETURN, TAG_RETURNS,
+ TAG_RETURN_VALUE, TAG_RETURNS_VALUE]:
if not returns_seen:
returns_seen = True
else:
@@ -1205,44 +1158,49 @@ class GtkDocCommentBlockParser(object):
tag = GtkDocTag(TAG_RETURNS)
tag.position = position
- tag.description = tag_description
- if tag_annotations:
- tag.annotations = self.parse_annotations(tag, tag_annotations)
+
+ if tag_fields:
+ (a, d) = self._parse_fields(position,
+ column_offset + tag_fields_start,
+ original_line,
+ tag_fields)
+ tag.annotations = a
+ tag.description = d
+
comment_block.tags[TAG_RETURNS] = tag
current_tag = tag
continue
else:
- if tag_name.lower() in comment_block.tags.keys():
- column = result.start('tag_name') + column_offset
- marker = ' ' * column + '^'
+ if tag_name_lower in comment_block.tags.keys():
warn("multiple '%s:' tags for identifier '%s':\n%s\n%s" %
(tag_name, comment_block.name, original_line, marker),
position)
- tag = GtkDocTag(tag_name.lower())
+ tag = GtkDocTag(tag_name_lower)
tag.position = position
- if tag_annotations:
- if tag_name.lower() == TAG_ATTRIBUTES:
- tag.annotations = self.parse_annotations(tag, tag_annotations)
- else:
- warn("annotations not supported for tag '%s:'." %
- (tag_name, ),
- position)
-
- if tag_name.lower() in [TAG_DEPRECATED, TAG_SINCE]:
- result = TAG_VALUE_VERSION_RE.match(tag_description)
- tag.value = result.group('value')
- tag.description = result.group('description')
- elif tag_name.lower() == TAG_STABILITY:
- result = TAG_VALUE_STABILITY_RE.match(tag_description)
- tag.value = result.group('value').capitalize()
- tag.description = result.group('description')
- elif tag_name.lower() in GI_ANN_TAGS:
- tag.value = tag_description
- tag.description = ''
-
- comment_block.tags[tag_name.lower()] = tag
+ if tag_fields:
+ (a, d) = self._parse_fields(position,
+ column_offset + tag_fields_start,
+ original_line,
+ tag_fields)
+ if a:
+ error('annotations not supported for tag "%s:".' % (tag_name, ),
+ position)
+
+ if tag_name_lower in [TAG_DEPRECATED, TAG_SINCE]:
+ result = TAG_VALUE_VERSION_RE.match(d)
+ tag.value = result.group('value')
+ tag.description = result.group('description')
+ elif tag_name_lower == TAG_STABILITY:
+ result = TAG_VALUE_STABILITY_RE.match(d)
+ tag.value = result.group('value').capitalize()
+ tag.description = result.group('description')
+ elif tag_name_lower in GI_ANN_TAGS:
+ tag.value = d
+ tag.description = ''
+
+ comment_block.tags[tag_name_lower] = tag
current_tag = tag
continue
@@ -1257,16 +1215,22 @@ class GtkDocCommentBlockParser(object):
comment_block.description += '\n' + line
continue
elif in_part == PART_PARAMETERS:
- self._validate_multiline_annotation_continuation(line, original_line,
- column_offset, position)
+ if not current_param.description:
+ self._validate_multiline_annotation_continuation(line, original_line,
+ column_offset, position)
# Append to parameter description.
- current_param.description += ' ' + line.strip()
+ if current_param.description is None:
+ current_param.description = line
+ else:
+ current_param.description += ' ' + line.strip()
continue
elif in_part == PART_TAGS:
- self._validate_multiline_annotation_continuation(line, original_line,
- column_offset, position)
+ if not current_tag.description:
+ self._validate_multiline_annotation_continuation(line, original_line,
+ column_offset, position)
+
+ # Append to tag description.
current_tag.description += ' ' + line.strip()
- continue
########################################################################
# Finished parsing this comment block.
@@ -1303,48 +1267,328 @@ class GtkDocCommentBlockParser(object):
def _validate_multiline_annotation_continuation(self, line, original_line,
column_offset, position):
'''
- Validate parameters and tags (except the first line) and generate
- warnings about invalid annotations spanning multiple lines.
+ Validate annotatable parts' source text ensuring annotations don't span multiple lines.
+ For example, the following comment block would result in a warning being emitted for
+ the forth line::
+
+ /**
+ * shiny_function:
+ * @array_: (out caller-allocates) (array)
+ * (element-type utf8) (transfer full): A beautiful array
+ */
+
+ :param line: line to validate, stripped from ("``*/``") at start of the line.
+ :param original_line: original line (including ("``*/``")) being validated
+ :param column_offset: number of characters stripped from `line` when ("``*/``")
+ was removed
+ :param position: :class:`giscanner.message.Position` of `line` in the source file
+ '''
+
+ success, annotations, start_pos, end_pos = self._parse_annotations(position, column_offset,
+ original_line, line,
+ False)
+ if annotations:
+ marker = ' ' * (start_pos + column_offset) + '^'
+ warn('ignoring invalid multiline annotation continuation:\n%s\n%s' %
+ (original_line, marker),
+ position)
- :param line: line to validate, stripped from ' * ' at start of the line.
- :param original_line: original line to validate (used in warning messages)
- :param column_offset: column width of ' * ' at the time it was stripped from `line`
- :param position: position of `line` in the source file
+ def _parse_annotation_options_list(self, position, column, line, options):
+ '''
+ Parse annotation options into a list. For example::
+
+ ┌──────────────────────────────────────────────────────────────┐
+ │ 'option1 option2 option3' │ ─▷ source
+ ├──────────────────────────────────────────────────────────────┤
+ │ ['option1', 'option2', 'option3'] │ ◁─ parsed options
+ └──────────────────────────────────────────────────────────────┘
+
+ :param position: :class:`giscanner.message.Position` of `line` in the source file
+ :param column: start column of the `options` in the source file
+ :param line: complete source line
+ :param options: annotation options to parse
+ :returns: a list of annotation options
'''
- result = MULTILINE_ANNOTATION_CONTINUATION_RE.match(line)
- if result:
- column = result.start('annotations') + column_offset
- marker = ' ' * column + '^'
- warn('ignoring invalid multiline annotation continuation:\n'
- '%s\n%s' % (original_line, marker),
+ parsed = []
+
+ if options:
+ result = options.find('=')
+ if result >= 0:
+ marker = ' ' * (column + result) + '^'
+ warn('invalid annotation options: expected a "list" but '
+ 'received "key=value pairs":\n%s\n%s' % (line, marker),
+ position)
+ parsed = self._parse_annotation_options_unknown(position, column, line, options)
+ else:
+ parsed = options.split(' ')
+
+ return parsed
+
+ def _parse_annotation_options_dict(self, position, column, line, options):
+ '''
+ Parse annotation options into a dict. For example::
+
+ ┌──────────────────────────────────────────────────────────────┐
+ │ 'option1=value1 option2 option3=value2' │ ─▷ source
+ ├──────────────────────────────────────────────────────────────┤
+ │ {'option1': 'value1', 'option2': None, 'option3': 'value2'} │ ◁─ parsed options
+ └──────────────────────────────────────────────────────────────┘
+
+ :param position: :class:`giscanner.message.Position` of `line` in the source file
+ :param column: start column of the `options` in the source file
+ :param line: complete source line
+ :param options: annotation options to parse
+ :returns: an ordered dictionary of annotation options
+ '''
+
+ parsed = OrderedDict()
+
+ if options:
+ for p in options.split(' '):
+ parts = p.split('=', 1)
+ key = parts[0]
+ value = parts[1] if len(parts) == 2 else None
+ parsed[key] = value
+
+ return parsed
+
+ def _parse_annotation_options_unknown(self, position, column, line, options):
+ '''
+ Parse annotation options into a list holding a single item. This is used when the
+ annotation options to parse in not known to be a list nor dict. For example::
+
+ ┌──────────────────────────────────────────────────────────────┐
+ │ ' option1 option2 option3=value1 ' │ ─▷ source
+ ├──────────────────────────────────────────────────────────────┤
+ │ ['option1 option2 option3=value1'] │ ◁─ parsed options
+ └──────────────────────────────────────────────────────────────┘
+
+ :param position: :class:`giscanner.message.Position` of `line` in the source file
+ :param column: start column of the `options` in the source file
+ :param line: complete source line
+ :param options: annotation options to parse
+ :returns: a list of annotation options
+ '''
+
+ if options:
+ return [options.strip()]
+
+ def _parse_annotation(self, position, column, line, annotation):
+ '''
+ Parse an annotation into the annotation name and a list or dict (depending on the
+ name of the annotation) holding the options. For example::
+
+ ┌──────────────────────────────────────────────────────────────┐
+ │ 'name opt1=value1 opt2=value2 opt3' │ ─▷ source
+ ├──────────────────────────────────────────────────────────────┤
+ │ 'name', {'opt1': 'value1', 'opt2':'value2', 'opt3':None} │ ◁─ parsed annotation
+ └──────────────────────────────────────────────────────────────┘
+
+ ┌──────────────────────────────────────────────────────────────┐
+ │ 'name opt1 opt2' │ ─▷ source
+ ├──────────────────────────────────────────────────────────────┤
+ │ 'name', ['opt1', 'opt2'] │ ◁─ parsed annotation
+ └──────────────────────────────────────────────────────────────┘
+
+ ┌──────────────────────────────────────────────────────────────┐
+ │ 'unkownname unknown list of options' │ ─▷ source
+ ├──────────────────────────────────────────────────────────────┤
+ │ 'unkownname', ['unknown list of options'] │ ◁─ parsed annotation
+ └──────────────────────────────────────────────────────────────┘
+
+ :param position: :class:`giscanner.message.Position` of `line` in the source file
+ :param column: start column of the `annotation` in the source file
+ :param line: complete source line
+ :param annotation: annotation to parse
+ :returns: a tuple containing the annotation name and options
+ '''
+
+ # Transform deprecated type syntax "tokens"
+ annotation = annotation.replace('<', ANN_LPAR).replace('>', ANN_RPAR)
+
+ parts = annotation.split(' ', 1)
+ ann_name = parts[0].lower()
+ ann_options = parts[1] if len(parts) == 2 else None
+
+ if ann_name == ANN_INOUT_ALT:
+ marker = ' ' * (column) + '^'
+ warn('"%s" annotation has been deprecated, please use "%s" instead:\n%s\n%s' %
+ (ANN_INOUT_ALT, ANN_INOUT, line, marker),
position)
- @classmethod
- def parse_annotations(cls, tag, value):
- # (annotation)
- # (annotation opt1 opt2 ...)
- # (annotation opt1=value1 opt2=value2 ...)
- opened = -1
- annotations = GtkDocAnnotations()
- annotations.position = tag.position
-
- for i, c in enumerate(value):
- if c == '(' and opened == -1:
- opened = i + 1
- if c == ')' and opened != -1:
- segment = value[opened:i]
- parts = segment.split(' ', 1)
- if len(parts) == 2:
- name, option = parts
- elif len(parts) == 1:
- name = parts[0]
- option = None
+ ann_name = ANN_INOUT
+ elif ann_name == ANN_ATTRIBUTE:
+ marker = ' ' * (column) + '^'
+ warn('"%s" annotation has been deprecated, please use "%s" instead:\n%s\n%s' %
+ (ANN_ATTRIBUTE, ANN_ATTRIBUTES, line, marker),
+ position)
+
+ ann_name = ANN_ATTRIBUTES
+ ann_options = self._parse_annotation_options_list(position, column, line, ann_options)
+ n_options = len(ann_options)
+ if n_options == 1:
+ ann_options = ann_options[0]
+ elif n_options == 2:
+ ann_options = '%s=%s' % (ann_options[0], ann_options[1])
+ else:
+ marker = ' ' * (column) + '^'
+ error('malformed "(attribute)" annotation will be ignored:\n%s\n%s' %
+ (line, marker),
+ position)
+ return None, None
+
+ column += len(ann_name) + 2
+
+ if ann_name in LIST_ANNOTATIONS:
+ ann_options = self._parse_annotation_options_list(position, column, line, ann_options)
+ elif ann_name in DICT_ANNOTATIONS:
+ ann_options = self._parse_annotation_options_dict(position, column, line, ann_options)
+ else:
+ ann_options = self._parse_annotation_options_unknown(position, column, line,
+ ann_options)
+
+ return ann_name, ann_options
+
+ def _parse_annotations(self, position, column, line, fields, parse_options=True):
+ '''
+ Parse annotations into a :class:`GtkDocAnnotations` object.
+
+ :param position: :class:`giscanner.message.Position` of `line` in the source file
+ :param column: start column of the `annotations` in the source file
+ :param line: complete source line
+ :param fields: string containing the fields to parse
+ :param parse_options: whether options will be parsed into a :class:`GtkDocAnnotations`
+ object or into a :class:`list`
+ :returns: if `parse_options` evaluates to True a :class:`GtkDocAnnotations` object,
+ a :class:`list` otherwise. If `line` does not contain any annotations,
+ :const:`None`
+ '''
+
+ if parse_options:
+ parsed_annotations = GtkDocAnnotations(position)
+ else:
+ parsed_annotations = []
+
+ i = 0
+ parens_level = 0
+ prev_char = ''
+ char_buffer = []
+ start_pos = 0
+ end_pos = 0
+
+ for i, cur_char in enumerate(fields):
+ cur_char_is_space = cur_char.isspace()
+
+ if cur_char == ANN_LPAR:
+ parens_level += 1
+
+ if parens_level == 1:
+ start_pos = i
+
+ if prev_char == ANN_LPAR:
+ marker = ' ' * (column + i) + '^'
+ error('unexpected parentheses, annotations will be ignored:\n%s\n%s' %
+ (line, marker),
+ position)
+ return (False, None, None, None)
+ elif parens_level > 1:
+ char_buffer.append(cur_char)
+ elif cur_char == ANN_RPAR:
+ parens_level -= 1
+
+ if prev_char == ANN_LPAR:
+ marker = ' ' * (column + i) + '^'
+ error('unexpected parentheses, annotations will be ignored:\n%s\n%s' %
+ (line, marker),
+ position)
+ return (False, None, None, None)
+ elif parens_level < 0:
+ marker = ' ' * (column + i) + '^'
+ error('unbalanced parentheses, annotations will be ignored:\n%s\n%s' %
+ (line, marker),
+ position)
+ return (False, None, None, None)
+ elif parens_level == 0:
+ end_pos = i + 1
+
+ if parse_options is True:
+ name, options = self._parse_annotation(position,
+ column + start_pos,
+ line,
+ ''.join(char_buffer).strip())
+ if name is not None:
+ if name in parsed_annotations:
+ marker = ' ' * (column + i) + '^'
+ error('multiple "%s" annotations:\n%s\n%s' %
+ (name, line, marker), position)
+ parsed_annotations[name] = options
+ else:
+ parsed_annotations.append(''.join(char_buffer).strip())
+
+ char_buffer = []
+ else:
+ char_buffer.append(cur_char)
+ elif cur_char_is_space:
+ if parens_level > 0:
+ char_buffer.append(cur_char)
+ else:
+ if parens_level == 0:
+ break
else:
- raise AssertionError
- if option is not None:
- option = DocOption(option)
- annotations.add(name, option)
- opened = -1
+ char_buffer.append(cur_char)
+
+ prev_char = cur_char
+
+ if parens_level > 0:
+ marker = ' ' * (column + i) + '^'
+ error('unbalanced parentheses, annotations will be ignored:\n%s\n%s' %
+ (line, marker),
+ position)
+ return (False, None, None, None)
+ else:
+ return (True, parsed_annotations, start_pos, end_pos)
+
+ def _parse_fields(self, position, column, line, fields, parse_options=True,
+ validate_description_field=True):
+ '''
+ Parse annotations out of field data. For example::
+
+ ┌──────────────────────────────────────────────────────────────┐
+ │ '(skip): description of some parameter │ ─▷ source
+ ├──────────────────────────────────────────────────────────────┤
+ │ ({'skip': []}, 'description of some parameter') │ ◁─ annotations and
+ └──────────────────────────────────────────────────────────────┘ remaining fields
+
+ :param position: :class:`giscanner.message.Position` of `line` in the source file
+ :param column: start column of `fields` in the source file
+ :param line: complete source line
+ :param fields: string containing the fields to parse
+ :param parse_options: whether options will be parsed into a :class:`GtkDocAnnotations`
+ object or into a :class:`list`
+ :param validate_description_field: :const:`True` to validate the description field
+ :returns: if `parse_options` evaluates to True a :class:`GtkDocAnnotations` object,
+ a :class:`list` otherwise. If `line` does not contain any annotations,
+ :const:`None` and a string holding the remaining fields
+ '''
+ description_field = ''
+ success, annotations, start_pos, end_pos = self._parse_annotations(position,
+ column,
+ line,
+ fields,
+ parse_options)
+ if success:
+ description_field = fields[end_pos:].strip()
+
+ if description_field and validate_description_field:
+ if description_field.startswith(':'):
+ description_field = description_field[1:]
+ else:
+ if end_pos > 0:
+ marker_position = column + end_pos
+ marker = ' ' * marker_position + '^'
+ warn('missing ":" at column %s:\n%s\n%s' %
+ (marker_position + 1, line, marker),
+ position)
- return annotations
+ return (annotations, description_field)
diff --git a/giscanner/maintransformer.py b/giscanner/maintransformer.py
index af9a0a1..6dd35bc 100644
--- a/giscanner/maintransformer.py
+++ b/giscanner/maintransformer.py
@@ -26,7 +26,7 @@ from .annotationparser import (TAG_VFUNC, TAG_SINCE, TAG_DEPRECATED, TAG_RETURNS
TAG_UNREF_FUNC, TAG_REF_FUNC, TAG_SET_VALUE_FUNC,
TAG_GET_VALUE_FUNC, TAG_VALUE, TAG_TRANSFER,
TAG_STABILITY)
-from .annotationparser import (ANN_ALLOW_NONE, ANN_ARRAY, ANN_ATTRIBUTE,
+from .annotationparser import (ANN_ALLOW_NONE, ANN_ARRAY, ANN_ATTRIBUTES,
ANN_ELEMENT_TYPE, ANN_IN, ANN_INOUT,
ANN_INOUT_ALT, ANN_OUT, ANN_SCOPE,
ANN_TYPE, ANN_CLOSURE, ANN_DESTROY, ANN_TRANSFER, ANN_SKIP,
@@ -140,6 +140,8 @@ class MainTransformer(object):
if not rename_to:
return
rename_to = rename_to.value
+ if not rename_to:
+ return
target = self._namespace.get_by_symbol(rename_to)
if not target:
message.warn_node(node,
@@ -349,15 +351,9 @@ class MainTransformer(object):
annotations.position)
def _apply_annotations_array(self, parent, node, annotations):
- array_opt = annotations.get(ANN_ARRAY)
- if array_opt:
- array_values = array_opt.all()
- else:
- array_values = {}
-
- element_type = annotations.get(ANN_ELEMENT_TYPE)
- if element_type is not None:
- element_type_node = self._resolve(element_type.one(),
+ element_type_options = annotations.get(ANN_ELEMENT_TYPE)
+ if element_type_options:
+ element_type_node = self._resolve(element_type_options[0],
node.type, node, parent)
elif isinstance(node.type, ast.Array):
element_type_node = node.type.element_type
@@ -366,24 +362,24 @@ class MainTransformer(object):
# and no (element-type) means array of Foo
element_type_node = node.type.clone()
# The element's ctype is the array's dereferenced
- if element_type_node.ctype is not None and \
- element_type_node.ctype.endswith('*'):
+ if element_type_node.ctype is not None and element_type_node.ctype.endswith('*'):
element_type_node.ctype = element_type_node.ctype[:-1]
if isinstance(node.type, ast.Array):
array_type = node.type.array_type
else:
array_type = None
- container_type = ast.Array(array_type, element_type_node,
- ctype=node.type.ctype,
- is_const=node.type.is_const)
- if OPT_ARRAY_ZERO_TERMINATED in array_values:
- container_type.zeroterminated = array_values.get(
- OPT_ARRAY_ZERO_TERMINATED) == '1'
+
+ array_options = annotations.get(ANN_ARRAY)
+ container_type = ast.Array(array_type, element_type_node, ctype=node.type.ctype,
+ is_const=node.type.is_const)
+ if OPT_ARRAY_ZERO_TERMINATED in array_options:
+ container_type.zeroterminated = array_options.get(OPT_ARRAY_ZERO_TERMINATED) == '1'
else:
container_type.zeroterminated = False
- length = array_values.get(OPT_ARRAY_LENGTH)
- if length is not None:
+
+ length = array_options.get(OPT_ARRAY_LENGTH)
+ if length:
paramname = self._get_validate_parameter_name(parent, length, node)
if paramname:
param = parent.get_parameter(paramname)
@@ -391,11 +387,11 @@ class MainTransformer(object):
if param.direction == ast.PARAM_DIRECTION_OUT:
param.transfer = ast.PARAM_TRANSFER_FULL
container_type.length_param_name = param.argname
- fixed = array_values.get(OPT_ARRAY_FIXED_SIZE)
+ fixed = array_options.get(OPT_ARRAY_FIXED_SIZE)
if fixed:
try:
container_type.size = int(fixed)
- except ValueError:
+ except (TypeError, ValueError):
# Already warned in annotationparser.py
return
node.type = container_type
@@ -410,34 +406,33 @@ class MainTransformer(object):
return
if isinstance(node.type, ast.List):
- if element_type_opt.length() != 1:
+ if len(element_type_opt) != 1:
message.warn(
'element-type annotation for a list must have exactly '
- 'one option, not %d options' % (element_type_opt.length(), ),
+ 'one option, not %d options' % (len(element_type_opt), ),
annotations.position)
return
- node.type.element_type = self._resolve(element_type_opt.one(),
+ node.type.element_type = self._resolve(element_type_opt[0],
node.type, node, parent)
elif isinstance(node.type, ast.Map):
- if element_type_opt.length() != 2:
+ if len(element_type_opt) != 2:
message.warn(
'element-type annotation for a hash table must have exactly '
- 'two options, not %d option(s)' % (element_type_opt.length(), ),
+ 'two options, not %d option(s)' % (len(element_type_opt), ),
annotations.position)
return
- element_type = element_type_opt.flat()
- node.type.key_type = self._resolve(element_type[0],
+ node.type.key_type = self._resolve(element_type_opt[0],
node.type, node, parent)
- node.type.value_type = self._resolve(element_type[1],
+ node.type.value_type = self._resolve(element_type_opt[1],
node.type, node, parent)
elif isinstance(node.type, ast.Array):
- if element_type_opt.length() != 1:
+ if len(element_type_opt) != 1:
message.warn(
'element-type annotation for an array must have exactly '
- 'one option, not %d options' % (element_type_opt.length(), ),
+ 'one option, not %d options' % (len(element_type_opt), ),
annotations.position)
return
- node.type.element_type = self._resolve(element_type_opt.one(),
+ node.type.element_type = self._resolve(element_type_opt[0],
node.type, node, parent)
else:
message.warn_node(parent,
@@ -529,19 +524,18 @@ class MainTransformer(object):
param_type = annotations.get(ANN_TYPE)
if param_type:
- node.type = self._resolve_toplevel(param_type.one(),
+ node.type = self._resolve_toplevel(param_type[0],
node.type, node, parent)
caller_allocates = False
annotated_direction = None
- if (ANN_INOUT in annotations or ANN_INOUT_ALT in annotations):
+ if ANN_INOUT in annotations:
annotated_direction = ast.PARAM_DIRECTION_INOUT
elif ANN_OUT in annotations:
- subtype = annotations[ANN_OUT]
- if subtype is not None:
- subtype = subtype.one()
annotated_direction = ast.PARAM_DIRECTION_OUT
- if subtype in (None, ''):
+
+ options = annotations[ANN_OUT]
+ if len(options) == 0:
if node.type.target_giname and node.type.ctype:
target = self._transformer.lookup_giname(node.type.target_giname)
target = self._transformer.resolve_aliases(target)
@@ -550,10 +544,12 @@ class MainTransformer(object):
caller_allocates = (not has_double_indirection and is_structure_or_union)
else:
caller_allocates = False
- elif subtype == OPT_OUT_CALLER_ALLOCATES:
- caller_allocates = True
- elif subtype == OPT_OUT_CALLEE_ALLOCATES:
- caller_allocates = False
+ else:
+ option = options[0]
+ if option == OPT_OUT_CALLER_ALLOCATES:
+ caller_allocates = True
+ elif option == OPT_OUT_CALLEE_ALLOCATES:
+ caller_allocates = False
elif ANN_IN in annotations:
annotated_direction = ast.PARAM_DIRECTION_IN
@@ -564,8 +560,8 @@ class MainTransformer(object):
node.transfer = self._get_transfer_default(parent, node)
transfer_tag = annotations.get(ANN_TRANSFER)
- if transfer_tag and transfer_tag.length() == 1:
- transfer = transfer_tag.one()
+ if transfer_tag and len(transfer_tag) == 1:
+ transfer = transfer_tag[0]
if transfer == OPT_TRANSFER_FLOATING:
transfer = OPT_TRANSFER_NONE
node.transfer = transfer
@@ -584,8 +580,11 @@ class MainTransformer(object):
node.skip = True
if annotations:
- for attribute in annotations.getall(ANN_ATTRIBUTE):
- node.attributes.append(attribute.flat())
+ attributes_annotation = annotations.get(ANN_ATTRIBUTES)
+ if attributes_annotation is not None:
+ for key, value in attributes_annotation.items():
+ if value:
+ node.attributes.append((key, value))
def _apply_annotations_annotated(self, node, block):
if block is None:
@@ -610,11 +609,11 @@ class MainTransformer(object):
if stability_tag.value:
node.stability = stability_tag.value
- annos_tag = block.tags.get(TAG_ATTRIBUTES)
- if annos_tag is not None:
- for ann_name, options in annos_tag.annotations.items():
- if options:
- node.attributes.append((ann_name, options.one()))
+ attributes_annotation = block.annotations.get(ANN_ATTRIBUTES)
+ if attributes_annotation is not None:
+ for key, value in attributes_annotation.items():
+ if value:
+ node.attributes.append((key, value))
if ANN_SKIP in block.annotations:
node.skip = True
@@ -637,14 +636,14 @@ class MainTransformer(object):
if isinstance(parent, (ast.Function, ast.VFunction)):
scope = annotations.get(ANN_SCOPE)
- if scope and scope.length() == 1:
- param.scope = scope.one()
+ if scope and len(scope) == 1:
+ param.scope = scope[0]
param.transfer = ast.PARAM_TRANSFER_NONE
destroy = annotations.get(ANN_DESTROY)
if destroy:
param.destroy_name = self._get_validate_parameter_name(parent,
- destroy.one(),
+ destroy[0],
param)
if param.destroy_name is not None:
param.scope = ast.PARAM_SCOPE_NOTIFIED
@@ -654,9 +653,9 @@ class MainTransformer(object):
# since we don't have a way right now to flag this callback a destroy.
destroy_param.scope = ast.PARAM_SCOPE_NOTIFIED
closure = annotations.get(ANN_CLOSURE)
- if closure and closure.length() == 1:
+ if closure and len(closure) == 1:
param.closure_name = self._get_validate_parameter_name(parent,
- closure.one(),
+ closure[0],
param)
elif isinstance(parent, ast.Callback):
if ANN_CLOSURE in annotations:
@@ -721,7 +720,7 @@ class MainTransformer(object):
return
t = tag.annotations.get(ANN_TYPE)
if t:
- field.type = self._transformer.create_type_from_user_string(t.one())
+ field.type = self._transformer.create_type_from_user_string(t[0])
try:
self._adjust_container_type(parent, field, tag.annotations)
@@ -775,7 +774,7 @@ class MainTransformer(object):
annotations = getattr(tag, 'annotations', {})
param_type = annotations.get(ANN_TYPE)
if param_type:
- param.type = self._resolve_toplevel(param_type.one(), param.type,
+ param.type = self._resolve_toplevel(param_type[0], param.type,
param, parent)
else:
tag = None
diff --git a/tests/scanner/Makefile.am b/tests/scanner/Makefile.am
index 0957a5f..aa5b55a 100644
--- a/tests/scanner/Makefile.am
+++ b/tests/scanner/Makefile.am
@@ -202,6 +202,7 @@ EXTRA_DIST += \
annotationparser/tests.xsd \
annotationparser/gi/annotation_allow_none.xml \
annotationparser/gi/annotation_array.xml \
+ annotationparser/gi/annotation_attributes.xml \
annotationparser/gi/annotation_closure.xml \
annotationparser/gi/annotation_constructor.xml \
annotationparser/gi/annotation_destroy.xml \
diff --git a/tests/scanner/annotation.c b/tests/scanner/annotation.c
index 0a92d3a..32187d3 100644
--- a/tests/scanner/annotation.c
+++ b/tests/scanner/annotation.c
@@ -128,12 +128,12 @@ regress_annotation_object_class_init (RegressAnnotationObjectClass *klass)
/**
* RegressAnnotationObject::attribute-signal:
* @regress_annotation: the regress_annotation object
- * @arg1: (attribute some.annotation.foo1 val1): a value
- * @arg2: (attribute some.annotation.foo2 val2): another value
+ * @arg1: (attributes some.annotation.foo1=val1): a value
+ * @arg2: (attributes some.annotation.foo2=val2): another value
*
* This signal tests a signal with attributes.
*
- * Returns: (attribute some.annotation.foo3 val3): the return value
+ * Returns: (attributes some.annotation.foo3=val3): the return value
*/
regress_annotation_object_signals[ATTRIBUTE_SIGNAL] =
g_signal_new ("attribute-signal",
@@ -707,9 +707,7 @@ regress_annotation_string_array_length (guint n_properties, const gchar * const
}
/**
- * regress_annotation_object_extra_annos:
- *
- * Attributes: (org.foobar testvalue)
+ * regress_annotation_object_extra_annos: (attributes org.foobar=testvalue)
*/
void
regress_annotation_object_extra_annos (RegressAnnotationObject *object)
@@ -763,9 +761,9 @@ regress_annotation_ptr_array (GPtrArray *array)
/**
* regress_annotation_attribute_func:
* @object: A #RegressAnnotationObject.
- * @data: (attribute some.annotation value) (attribute another.annotation blahvalue): Some data.
+ * @data: (attributes some.annotation=value another.annotation=blahvalue): Some data.
*
- * Returns: (attribute some.other.annotation value2) (attribute yet.another.annotation another_value): The
return value.
+ * Returns: (attributes some.other.annotation=value2 yet.another.annotation=another_value): The return value.
*/
gint
regress_annotation_attribute_func (RegressAnnotationObject *object,
diff --git a/tests/scanner/annotation.h b/tests/scanner/annotation.h
index 66470cb..b982a5b 100644
--- a/tests/scanner/annotation.h
+++ b/tests/scanner/annotation.h
@@ -37,11 +37,9 @@ typedef GList* (*RegressAnnotationListCallback) (GList *in);
typedef void (*RegressAnnotationNotifyFunc) (gpointer data);
/**
- * RegressAnnotationObject:
+ * RegressAnnotationObject: (attributes org.example.Test=cows)
*
* This is an object used to test annotations.
- *
- * Attributes: (org.example.Test cows)
*/
typedef struct _RegressAnnotationObject RegressAnnotationObject;
typedef struct _RegressAnnotationObjectClass RegressAnnotationObjectClass;
diff --git a/tests/scanner/annotationparser/gi/annotation_attributes.xml
b/tests/scanner/annotationparser/gi/annotation_attributes.xml
new file mode 100644
index 0000000..e7c4689
--- /dev/null
+++ b/tests/scanner/annotationparser/gi/annotation_attributes.xml
@@ -0,0 +1,395 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<tests xmlns="http://schemas.gnome.org/gobject-introspection/2013/test">
+
+<test>
+ <input>/**
+ * AnnotationObject: (attributes org.example.test1=horses org.example.test2 org.example.test3=cows)
+ *
+ * This is an object used to test annotations.
+ */</input>
+ <parser>
+ <docblock>
+ <identifier>
+ <name>AnnotationObject</name>
+ <annotations>
+ <annotation>
+ <name>attributes</name>
+ <options>
+ <option>
+ <name>org.example.test1</name>
+ <value>horses</value>
+ </option>
+ <option>
+ <name>org.example.test2</name>
+ </option>
+ <option>
+ <name>org.example.test3</name>
+ <value>cows</value>
+ </option>
+ </options>
+ </annotation>
+ </annotations>
+ </identifier>
+ <description>This is an object used to test annotations.</description>
+ </docblock>
+ </parser>
+</test>
+
+<test>
+ <!--
+ Deprecated "Attributes:" tag
+ -->
+ <input>/**
+ * AnnotationObject:
+ *
+ * This is an object used to test annotations.
+ *
+ * Attributes: (org.example.test1 horses) (org.example.test2) (org.example.test3 cows)
+ */</input>
+ <parser>
+ <docblock>
+ <identifier>
+ <name>AnnotationObject</name>
+ <annotations>
+ <annotation>
+ <name>attributes</name>
+ <options>
+ <option>
+ <name>org.example.test1</name>
+ <value>horses</value>
+ </option>
+ <option>
+ <name>org.example.test2</name>
+ </option>
+ <option>
+ <name>org.example.test3</name>
+ <value>cows</value>
+ </option>
+ </options>
+ </annotation>
+ </annotations>
+ </identifier>
+ <description>This is an object used to test annotations.</description>
+ </docblock>
+ <messages>
+ <message>6: Warning: Test: GObject-Introspection specific GTK-Doc tag "Attributes" has been
deprecated, please use annotations on the identifier instead:
+ * Attributes: (org.example.test1 horses) (org.example.test2) (org.example.test3 cows)
+ ^</message>
+ </messages>
+ </parser>
+</test>
+
+<test>
+ <!--
+ (attributes) annotation on the identifier together with a
+ deprecated "Attributes:" tag.
+ -->
+ <input>/**
+ * AnnotationObject: (attributes org.example.test1=horses)
+ *
+ * This is an object used to test annotations.
+ *
+ * Attributes: (org.example.test1 horses) (org.example.test2 cows)
+ */</input>
+ <parser>
+ <docblock>
+ <identifier>
+ <name>AnnotationObject</name>
+ <annotations>
+ <annotation>
+ <name>attributes</name>
+ <options>
+ <option>
+ <name>org.example.test1</name>
+ <value>horses</value>
+ </option>
+ </options>
+ </annotation>
+ </annotations>
+ </identifier>
+ <description>This is an object used to test annotations.</description>
+ </docblock>
+ <messages>
+ <message>6: Warning: Test: GObject-Introspection specific GTK-Doc tag "Attributes" has been
deprecated, please use annotations on the identifier instead:
+ * Attributes: (org.example.test1 horses) (org.example.test2 cows)
+ ^</message>
+ <message>6: Error: Test: Duplicate "Attributes:" annotation will be ignored:
+ * Attributes: (org.example.test1 horses) (org.example.test2 cows)
+ ^</message>
+ </messages>
+ </parser>
+</test>
+
+<test>
+ <!--
+ Deprecated "Attributes:" tag in the wrong location
+ -->
+ <input>/**
+ * AnnotationObject:
+ *
+ * Attributes: (org.example.Test horses) (org.example.test2 cows)
+ *
+ * This is an object used to test annotations.
+ */</input>
+ <parser>
+ <docblock>
+ <identifier>
+ <name>AnnotationObject</name>
+ <annotations>
+ <annotation>
+ <name>attributes</name>
+ <options>
+ <option>
+ <name>org.example.Test</name>
+ <value>horses</value>
+ </option>
+ <option>
+ <name>org.example.test2</name>
+ <value>cows</value>
+ </option>
+ </options>
+ </annotation>
+ </annotations>
+ </identifier>
+ <description>This is an object used to test annotations.</description>
+ </docblock>
+ <messages>
+ <message>4: Warning: Test: GObject-Introspection specific GTK-Doc tag "Attributes" has been
deprecated, please use annotations on the identifier instead:
+ * Attributes: (org.example.Test horses) (org.example.test2 cows)
+ ^</message>
+ </messages>
+ </parser>
+</test>
+
+<test>
+ <input>/**
+ * AnnotationObject:
+ *
+ * Attributes: (org.example.Test horses cows)
+ *
+ * This is an object used to test annotations.
+ */</input>
+ <parser>
+ <docblock>
+ <identifier>
+ <name>AnnotationObject</name>
+ </identifier>
+ <description>This is an object used to test annotations.</description>
+ </docblock>
+ <messages>
+ <message>4: Warning: Test: GObject-Introspection specific GTK-Doc tag "Attributes" has been
deprecated, please use annotations on the identifier instead:
+ * Attributes: (org.example.Test horses cows)
+ ^</message>
+ <message>4: Error: Test: malformed "Attributes:" tag will be ignored:
+ * Attributes: (org.example.Test horses cows)
+ ^</message>
+ </messages>
+ </parser>
+</test>
+
+<test>
+ <input>/**
+ * AnnotationObject::attribute-signal:
+ * @annotation: the annotation object
+ * @arg1: (attributes some.annotation.foo1=val1): a value
+ * @arg2: (attributes some.annotation.foo2=val2): another value
+ * @arg3: (array fixed-size=2): a third value
+ *
+ * This signal tests a signal with attributes.
+ *
+ * Returns: (attributes some.annotation.foo3=val3): the return value
+ */</input>
+ <parser>
+ <docblock>
+ <identifier>
+ <name>AnnotationObject::attribute-signal</name>
+ </identifier>
+ <parameters>
+ <parameter>
+ <name>annotation</name>
+ <description>the annotation object</description>
+ </parameter>
+ <parameter>
+ <name>arg1</name>
+ <annotations>
+ <annotation>
+ <name>attributes</name>
+ <options>
+ <option>
+ <name>some.annotation.foo1</name>
+ <value>val1</value>
+ </option>
+ </options>
+ </annotation>
+ </annotations>
+ <description>a value</description>
+ </parameter>
+ <parameter>
+ <name>arg2</name>
+ <annotations>
+ <annotation>
+ <name>attributes</name>
+ <options>
+ <option>
+ <name>some.annotation.foo2</name>
+ <value>val2</value>
+ </option>
+ </options>
+ </annotation>
+ </annotations>
+ <description>another value</description>
+ </parameter>
+ <parameter>
+ <name>arg3</name>
+ <annotations>
+ <annotation>
+ <name>array</name>
+ <options>
+ <option>
+ <name>fixed-size</name>
+ <value>2</value>
+ </option>
+ </options>
+ </annotation>
+ </annotations>
+ <description>a third value</description>
+ </parameter>
+ </parameters>
+ <description>This signal tests a signal with attributes.</description>
+ <tags>
+ <tag>
+ <name>returns</name>
+ <annotations>
+ <annotation>
+ <name>attributes</name>
+ <options>
+ <option>
+ <name>some.annotation.foo3</name>
+ <value>val3</value>
+ </option>
+ </options>
+ </annotation>
+ </annotations>
+ <description>the return value</description>
+ </tag>
+ </tags>
+ </docblock>
+ </parser>
+</test>
+
+<test>
+ <!--
+ Deprecated (attribute) annotation.
+ -->
+ <input>/**
+ * AnnotationObject::attribute-signal:
+ * @annotation: the annotation object
+ * @arg1: (attribute some.annotation.foo1): a value
+ * @arg2: (attribute some.annotation.foo2 val2): another value
+ * @arg3: (attribute x y z): something special
+ * @arg4: (array fixed-size=2): a third value
+ *
+ * This signal tests a signal with attributes.
+ *
+ * Returns: (attribute some.annotation.foo3 val3): the return value
+ */</input>
+ <parser>
+ <docblock>
+ <identifier>
+ <name>AnnotationObject::attribute-signal</name>
+ </identifier>
+ <parameters>
+ <parameter>
+ <name>annotation</name>
+ <description>the annotation object</description>
+ </parameter>
+ <parameter>
+ <name>arg1</name>
+ <annotations>
+ <annotation>
+ <name>attributes</name>
+ <options>
+ <option>
+ <name>some.annotation.foo1</name>
+ </option>
+ </options>
+ </annotation>
+ </annotations>
+ <description>a value</description>
+ </parameter>
+ <parameter>
+ <name>arg2</name>
+ <annotations>
+ <annotation>
+ <name>attributes</name>
+ <options>
+ <option>
+ <name>some.annotation.foo2</name>
+ <value>val2</value>
+ </option>
+ </options>
+ </annotation>
+ </annotations>
+ <description>another value</description>
+ </parameter>
+ <parameter>
+ <name>arg3</name>
+ <description>something special</description>
+ </parameter>
+ <parameter>
+ <name>arg4</name>
+ <annotations>
+ <annotation>
+ <name>array</name>
+ <options>
+ <option>
+ <name>fixed-size</name>
+ <value>2</value>
+ </option>
+ </options>
+ </annotation>
+ </annotations>
+ <description>a third value</description>
+ </parameter>
+ </parameters>
+ <description>This signal tests a signal with attributes.</description>
+ <tags>
+ <tag>
+ <name>returns</name>
+ <annotations>
+ <annotation>
+ <name>attributes</name>
+ <options>
+ <option>
+ <name>some.annotation.foo3</name>
+ <value>val3</value>
+ </option>
+ </options>
+ </annotation>
+ </annotations>
+ <description>the return value</description>
+ </tag>
+ </tags>
+ </docblock>
+ <messages>
+ <message>4: Warning: Test: "attribute" annotation has been deprecated, please use "attributes" instead:
+ * @arg1: (attribute some.annotation.foo1): a value
+ ^</message>
+ <message>5: Warning: Test: "attribute" annotation has been deprecated, please use "attributes" instead:
+ * @arg2: (attribute some.annotation.foo2 val2): another value
+ ^</message>
+ <message>6: Warning: Test: "attribute" annotation has been deprecated, please use "attributes" instead:
+ * @arg3: (attribute x y z): something special
+ ^</message>
+ <message>6: Error: Test: malformed "(attribute)" annotation will be ignored:
+ * @arg3: (attribute x y z): something special
+ ^</message>
+ <message>11: Warning: Test: "attribute" annotation has been deprecated, please use "attributes" instead:
+ * Returns: (attribute some.annotation.foo3 val3): the return value
+ ^</message>
+ </messages>
+ </parser>
+</test>
+
+</tests>
diff --git a/tests/scanner/annotationparser/gi/annotation_element_type.xml
b/tests/scanner/annotationparser/gi/annotation_element_type.xml
index 30fffaf..84337b7 100644
--- a/tests/scanner/annotationparser/gi/annotation_element_type.xml
+++ b/tests/scanner/annotationparser/gi/annotation_element_type.xml
@@ -110,7 +110,7 @@ element-type annotation.</description>
<name>utf8</name>
</option>
<option>
- <name><![CDATA[GLib.HashTable<utf8,utf8>]]></name>
+ <name>GLib.HashTable(utf8,utf8)</name>
</option>
</options>
</annotation>
diff --git a/tests/scanner/annotationparser/gi/annotation_in_out.xml
b/tests/scanner/annotationparser/gi/annotation_in_out.xml
index cb48957..5530a6a 100644
--- a/tests/scanner/annotationparser/gi/annotation_in_out.xml
+++ b/tests/scanner/annotationparser/gi/annotation_in_out.xml
@@ -36,7 +36,7 @@
<name>inoutarg2</name>
<annotations>
<annotation>
- <name>in-out</name>
+ <name>inout</name>
</annotation>
</annotations>
<description>This is an argument test</description>
@@ -50,6 +50,11 @@
</tag>
</tags>
</docblock>
+ <messages>
+ <message>5: Warning: Test: "in-out" annotation has been deprecated, please use "inout" instead:
+ * @inoutarg2: (in-out): This is an argument test
+ ^</message>
+ </messages>
</parser>
</test>
diff --git a/tests/scanner/annotationparser/gi/identifier_symbol.xml
b/tests/scanner/annotationparser/gi/identifier_symbol.xml
index 7e31776..4a97936 100644
--- a/tests/scanner/annotationparser/gi/identifier_symbol.xml
+++ b/tests/scanner/annotationparser/gi/identifier_symbol.xml
@@ -74,7 +74,7 @@
<description>Missing colon will result in a warning.</description>
</docblock>
<messages>
- <message>2: Warning: Test: missing ':' at column 30:
+ <message>2: Warning: Test: missing ":" at column 30:
* test_symbol_missing_colon (skip)
^</message>
</messages>
@@ -153,7 +153,7 @@
<description>Missing colon will result in a warning.</description>
</docblock>
<messages>
- <message>2: Warning: Test: missing ':' at column 42:
+ <message>2: Warning: Test: missing ":" at column 42:
* GtkWidget:test_property_missing_colon (skip)
^</message>
</messages>
@@ -232,7 +232,7 @@
<description>Missing colon will result in a warning.</description>
</docblock>
<messages>
- <message>2: Warning: Test: missing ':' at column 41:
+ <message>2: Warning: Test: missing ":" at column 41:
* GtkWidget::test_signal_missing_colon (skip)
^</message>
</messages>
diff --git a/tests/scanner/annotationparser/gi/tag_deprecated.xml
b/tests/scanner/annotationparser/gi/tag_deprecated.xml
index b33509c..fbd342b 100644
--- a/tests/scanner/annotationparser/gi/tag_deprecated.xml
+++ b/tests/scanner/annotationparser/gi/tag_deprecated.xml
@@ -67,7 +67,7 @@
</tags>
</docblock>
<messages>
- <message>6: Warning: Test: annotations not supported for tag 'Deprecated:'.</message>
+ <message>6: Error: Test: annotations not supported for tag "Deprecated:".</message>
</messages>
</parser>
</test>
diff --git a/tests/scanner/annotationparser/gi/tag_since.xml b/tests/scanner/annotationparser/gi/tag_since.xml
index e736aba..502d8b6 100644
--- a/tests/scanner/annotationparser/gi/tag_since.xml
+++ b/tests/scanner/annotationparser/gi/tag_since.xml
@@ -66,7 +66,7 @@
</tags>
</docblock>
<messages>
- <message>6: Warning: Test: annotations not supported for tag 'Since:'.</message>
+ <message>6: Error: Test: annotations not supported for tag "Since:".</message>
</messages>
</parser>
</test>
diff --git a/tests/scanner/annotationparser/gi/tag_stability.xml
b/tests/scanner/annotationparser/gi/tag_stability.xml
index 27976fd..ec87b02 100644
--- a/tests/scanner/annotationparser/gi/tag_stability.xml
+++ b/tests/scanner/annotationparser/gi/tag_stability.xml
@@ -108,7 +108,7 @@
</tags>
</docblock>
<messages>
- <message>6: Warning: Test: annotations not supported for tag 'Stability:'.</message>
+ <message>6: Error: Test: annotations not supported for tag "Stability:".</message>
</messages>
</parser>
</test>
diff --git a/tests/scanner/annotationparser/test_parser.py b/tests/scanner/annotationparser/test_parser.py
index 98ae787..c7ff532 100644
--- a/tests/scanner/annotationparser/test_parser.py
+++ b/tests/scanner/annotationparser/test_parser.py
@@ -130,20 +130,25 @@ class TestCommentBlock(unittest.TestCase):
parsed += ' <identifier>\n'
parsed += ' <name>%s</name>\n' % (docblock.name, )
- if docblock.annotations.values:
+ if docblock.annotations:
parsed += ' <annotations>\n'
- for key, value in docblock.annotations.values:
+ for ann_name, ann_options in docblock.annotations.items():
parsed += ' <annotation>\n'
- parsed += ' <name>%s</name>\n' % (key, )
- if value is not None:
- options = value.all()
+ parsed += ' <name>%s</name>\n' % (ann_name, )
+ if ann_options:
parsed += ' <options>\n'
- for option in options:
- parsed += ' <option>\n'
- parsed += ' <name>%s</name>\n' % (option, )
- if options[option] is not None:
- parsed += ' <value>%s</value>\n' % (options[option], )
- parsed += ' </option>\n'
+ if isinstance(ann_options, list):
+ for option in ann_options:
+ parsed += ' <option>\n'
+ parsed += ' <name>%s</name>\n' % (option, )
+ parsed += ' </option>\n'
+ else:
+ for (option, value) in ann_options.items():
+ parsed += ' <option>\n'
+ parsed += ' <name>%s</name>\n' % (option, )
+ if value:
+ parsed += ' <value>%s</value>\n' % (value, )
+ parsed += ' </option>\n'
parsed += ' </options>\n'
parsed += ' </annotation>\n'
parsed += ' </annotations>\n'
@@ -155,20 +160,25 @@ class TestCommentBlock(unittest.TestCase):
param = docblock.params.get(param_name)
parsed += ' <parameter>\n'
parsed += ' <name>%s</name>\n' % (param_name, )
- if param.annotations.values:
+ if param.annotations:
parsed += ' <annotations>\n'
- for key, value in param.annotations.values:
+ for ann_name, ann_options in param.annotations.items():
parsed += ' <annotation>\n'
- parsed += ' <name>%s</name>\n' % (key, )
- if value is not None:
- options = value.all()
+ parsed += ' <name>%s</name>\n' % (ann_name, )
+ if ann_options:
parsed += ' <options>\n'
- for option in options:
- parsed += ' <option>\n'
- parsed += ' <name>%s</name>\n' % (option, )
- if options[option] is not None:
- parsed += ' <value>%s</value>\n' % (options[option], )
- parsed += ' </option>\n'
+ if isinstance(ann_options, list):
+ for option in ann_options:
+ parsed += ' <option>\n'
+ parsed += ' <name>%s</name>\n' % (option, )
+ parsed += ' </option>\n'
+ else:
+ for (option, value) in ann_options.items():
+ parsed += ' <option>\n'
+ parsed += ' <name>%s</name>\n' % (option, )
+ if value:
+ parsed += ' <value>%s</value>\n' % (value, )
+ parsed += ' </option>\n'
parsed += ' </options>\n'
parsed += ' </annotation>\n'
parsed += ' </annotations>\n'
@@ -186,20 +196,25 @@ class TestCommentBlock(unittest.TestCase):
tag = docblock.tags.get(tag_name)
parsed += ' <tag>\n'
parsed += ' <name>%s</name>\n' % (tag_name, )
- if tag.annotations.values:
+ if tag.annotations:
parsed += ' <annotations>\n'
- for key, value in tag.annotations.values:
+ for ann_name, ann_options in tag.annotations.items():
parsed += ' <annotation>\n'
- parsed += ' <name>%s</name>\n' % (key, )
- if value is not None:
- options = value.all()
+ parsed += ' <name>%s</name>\n' % (ann_name, )
+ if ann_options:
parsed += ' <options>\n'
- for option in options:
- parsed += ' <option>\n'
- parsed += ' <name>%s</name>\n' % (option, )
- if options[option] is not None:
- parsed += ' <value>%s</value>\n' % (options[option], )
- parsed += ' </option>\n'
+ if isinstance(ann_options, list):
+ for option in ann_options:
+ parsed += ' <option>\n'
+ parsed += ' <name>%s</name>\n' % (option, )
+ parsed += ' </option>\n'
+ else:
+ for (option, value) in ann_options.items():
+ parsed += ' <option>\n'
+ parsed += ' <name>%s</name>\n' % (option, )
+ if value:
+ parsed += ' <value>%s</value>\n' % (value, )
+ parsed += ' </option>\n'
parsed += ' </options>\n'
parsed += ' </annotation>\n'
parsed += ' </annotations>\n'
@@ -235,8 +250,9 @@ class TestCommentBlock(unittest.TestCase):
expected += ' <options>\n'
for option in annotation.findall(ns('{}options/{}option')):
expected += ' <option>\n'
- expected += ' <name>%s</name>\n' %
(option.find(ns('{}name')).text, )
- if option.find('value') is not None:
+ if option.find(ns('{}name')) is not None:
+ expected += ' <name>%s</name>\n' %
(option.find(ns('{}name')).text, )
+ if option.find(ns('{}value')) is not None:
expected += ' <value>%s</value>\n' %
(option.find(ns('{}value')).text, )
expected += ' </option>\n'
expected += ' </options>\n'
@@ -260,7 +276,8 @@ class TestCommentBlock(unittest.TestCase):
expected += ' <options>\n'
for option in annotation.findall(ns('{}options/{}option')):
expected += ' <option>\n'
- expected += ' <name>%s</name>\n' %
(option.find(ns('{}name')).text, )
+ if option.find(ns('{}name')) is not None:
+ expected += ' <name>%s</name>\n' %
(option.find(ns('{}name')).text, )
if option.find(ns('{}value')) is not None:
expected += ' <value>%s</value>\n' %
(option.find(ns('{}value')).text, )
expected += ' </option>\n'
@@ -292,7 +309,8 @@ class TestCommentBlock(unittest.TestCase):
expected += ' <options>\n'
for option in annotation.findall(ns('{}options/{}option')):
expected += ' <option>\n'
- expected += ' <name>%s</name>\n' %
(option.find(ns('{}name')).text, )
+ if option.find(ns('{}name')) is not None:
+ expected += ' <name>%s</name>\n' %
(option.find(ns('{}name')).text, )
if option.find(ns('{}value')) is not None:
expected += ' <value>%s</value>\n' %
(option.find(ns('{}value')).text, )
expected += ' </option>\n'
diff --git a/tests/scanner/annotationparser/test_patterns.py b/tests/scanner/annotationparser/test_patterns.py
index d121ae6..023f61f 100644
--- a/tests/scanner/annotationparser/test_patterns.py
+++ b/tests/scanner/annotationparser/test_patterns.py
@@ -85,182 +85,182 @@ identifier_symbol_tests = [
(SYMBOL_RE, 'GBaseFinalizeFunc:',
{'delimiter': ':',
'symbol_name': 'GBaseFinalizeFunc',
- 'annotations': ''}),
+ 'fields': ''}),
(SYMBOL_RE, 'gtk_widget_show ',
{'delimiter': '',
'symbol_name': 'gtk_widget_show',
- 'annotations': ''}),
+ 'fields': ''}),
(SYMBOL_RE, ' gtk_widget_show',
{'delimiter': '',
'symbol_name': 'gtk_widget_show',
- 'annotations': ''}),
+ 'fields': ''}),
(SYMBOL_RE, ' gtk_widget_show ',
{'delimiter': '',
'symbol_name': 'gtk_widget_show',
- 'annotations': ''}),
+ 'fields': ''}),
(SYMBOL_RE, 'gtk_widget_show:',
{'delimiter': ':',
'symbol_name': 'gtk_widget_show',
- 'annotations': ''}),
+ 'fields': ''}),
(SYMBOL_RE, 'gtk_widget_show :',
{'delimiter': ':',
'symbol_name': 'gtk_widget_show',
- 'annotations': ''}),
+ 'fields': ''}),
(SYMBOL_RE, 'gtk_widget_show: ',
{'delimiter': ':',
'symbol_name': 'gtk_widget_show',
- 'annotations': ''}),
+ 'fields': ''}),
(SYMBOL_RE, 'gtk_widget_show : ',
{'delimiter': ':',
'symbol_name': 'gtk_widget_show',
- 'annotations': ''}),
+ 'fields': ''}),
(SYMBOL_RE, ' gtk_widget_show:',
{'delimiter': ':',
'symbol_name': 'gtk_widget_show',
- 'annotations': ''}),
+ 'fields': ''}),
(SYMBOL_RE, ' gtk_widget_show :',
{'delimiter': ':',
'symbol_name': 'gtk_widget_show',
- 'annotations': ''}),
+ 'fields': ''}),
(SYMBOL_RE, ' gtk_widget_show: ',
{'delimiter': ':',
'symbol_name': 'gtk_widget_show',
- 'annotations': ''}),
+ 'fields': ''}),
(SYMBOL_RE, ' gtk_widget_show : ',
{'delimiter': ':',
'symbol_name': 'gtk_widget_show',
- 'annotations': ''}),
+ 'fields': ''}),
(SYMBOL_RE, 'gtk_widget_show (skip)',
{'delimiter': '',
'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip)'}),
+ 'fields': '(skip)'}),
(SYMBOL_RE, 'gtk_widget_show: (skip)',
{'delimiter': ':',
'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip)'}),
+ 'fields': '(skip)'}),
(SYMBOL_RE, 'gtk_widget_show : (skip)',
{'delimiter': ':',
'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip)'}),
+ 'fields': '(skip)'}),
(SYMBOL_RE, 'gtk_widget_show: (skip)',
{'delimiter': ':',
'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip)'}),
+ 'fields': '(skip)'}),
(SYMBOL_RE, 'gtk_widget_show : (skip)',
{'delimiter': ':',
'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip)'}),
+ 'fields': '(skip)'}),
(SYMBOL_RE, ' gtk_widget_show:(skip)',
{'delimiter': ':',
'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip)'}),
+ 'fields': '(skip)'}),
(SYMBOL_RE, ' gtk_widget_show :(skip)',
{'delimiter': ':',
'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip)'}),
+ 'fields': '(skip)'}),
(SYMBOL_RE, ' gtk_widget_show: (skip)',
{'delimiter': ':',
'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip)'}),
+ 'fields': '(skip)'}),
(SYMBOL_RE, ' gtk_widget_show : (skip) \t ',
{'delimiter': ':',
'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip) \t '}),
+ 'fields': '(skip)'}),
(SYMBOL_RE, ' gtk_widget_show : (skip) \t ',
{'delimiter': ':',
'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip) \t '}),
+ 'fields': '(skip)'}),
(SYMBOL_RE, 'gtk_widget_show:(skip)(test1)',
{'delimiter': ':',
'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip)(test1)'}),
+ 'fields': '(skip)(test1)'}),
(SYMBOL_RE, 'gtk_widget_show (skip)(test1)',
{'delimiter': '',
'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip)(test1)'}),
+ 'fields': '(skip)(test1)'}),
(SYMBOL_RE, 'gtk_widget_show: (skip) (test1)',
{'delimiter': ':',
'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip) (test1)'}),
+ 'fields': '(skip) (test1)'}),
(SYMBOL_RE, 'gtk_widget_show : (skip) (test1)',
{'delimiter': ':',
'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip) (test1)'}),
+ 'fields': '(skip) (test1)'}),
(SYMBOL_RE, 'gtk_widget_show: (skip) (test1)',
{'delimiter': ':',
'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip) (test1)'}),
+ 'fields': '(skip) (test1)'}),
(SYMBOL_RE, 'gtk_widget_show : (skip) (test1)',
{'delimiter': ':',
'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip) (test1)'}),
+ 'fields': '(skip) (test1)'}),
(SYMBOL_RE, ' gtk_widget_show:(skip) (test1)',
{'delimiter': ':',
'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip) (test1)'}),
+ 'fields': '(skip) (test1)'}),
(SYMBOL_RE, ' gtk_widget_show :(skip) (test1)',
{'delimiter': ':',
'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip) (test1)'}),
+ 'fields': '(skip) (test1)'}),
(SYMBOL_RE, ' gtk_widget_show: (skip) (test1)',
{'delimiter': ':',
'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip) (test1)'}),
+ 'fields': '(skip) (test1)'}),
(SYMBOL_RE, ' gtk_widget_show : (skip) (test1) ',
{'delimiter': ':',
'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip) (test1) '}),
+ 'fields': '(skip) (test1)'}),
(SYMBOL_RE, 'gtk_widget_show: (skip) (test1) (test-2)',
{'delimiter': ':',
'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip) (test1) (test-2)'}),
+ 'fields': '(skip) (test1) (test-2)'}),
(SYMBOL_RE, 'gtk_widget_show : (skip) (test1) (test-2)',
{'delimiter': ':',
'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip) (test1) (test-2)'}),
+ 'fields': '(skip) (test1) (test-2)'}),
(SYMBOL_RE, 'gtk_widget_show: (skip) (test1) (test-2)',
{'delimiter': ':',
'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip) (test1) (test-2)'}),
+ 'fields': '(skip) (test1) (test-2)'}),
(SYMBOL_RE, 'gtk_widget_show : (skip) (test1) (test-2)',
{'delimiter': ':',
'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip) (test1) (test-2)'}),
+ 'fields': '(skip) (test1) (test-2)'}),
(SYMBOL_RE, ' gtk_widget_show:(skip) (test1) (test-2)',
{'delimiter': ':',
'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip) (test1) (test-2)'}),
+ 'fields': '(skip) (test1) (test-2)'}),
(SYMBOL_RE, ' gtk_widget_show :(skip) (test1) (test-2)',
{'delimiter': ':',
'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip) (test1) (test-2)'}),
+ 'fields': '(skip) (test1) (test-2)'}),
(SYMBOL_RE, ' gtk_widget_show: (skip) (test1) (test-2)',
{'delimiter': ':',
'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip) (test1) (test-2)'}),
+ 'fields': '(skip) (test1) (test-2)'}),
(SYMBOL_RE, ' gtk_widget_show : (skip) (test1) (test-2) ',
{'delimiter': ':',
'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip) (test1) (test-2) '}),
+ 'fields': '(skip) (test1) (test-2)'}),
(SYMBOL_RE, ' gtk_widget_show : (skip) (test1) (test-2) ',
{'delimiter': ':',
'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip) (test1) (test-2) '}),
+ 'fields': '(skip) (test1) (test-2)'}),
# constants
(SYMBOL_RE, 'MY_CONSTANT:',
{'delimiter': ':',
'symbol_name': 'MY_CONSTANT',
- 'annotations': ''}),
+ 'fields': ''}),
# structs
(SYMBOL_RE, 'FooWidget:',
{'delimiter': ':',
'symbol_name': 'FooWidget',
- 'annotations': ''}),
+ 'fields': ''}),
# enums
(SYMBOL_RE, 'Something:',
{'delimiter': ':',
'symbol_name': 'Something',
- 'annotations': ''})]
+ 'fields': ''})]
identifier_property_tests = [
# simple property name
@@ -268,57 +268,57 @@ identifier_property_tests = [
{'class_name': 'GtkWidget',
'property_name': 'name',
'delimiter': '',
- 'annotations': '(skip)'}),
+ 'fields': '(skip)'}),
(PROPERTY_RE, 'GtkWidget:name',
{'class_name': 'GtkWidget',
'property_name': 'name',
'delimiter': '',
- 'annotations': ''}),
+ 'fields': ''}),
(PROPERTY_RE, ' GtkWidget :name',
{'class_name': 'GtkWidget',
'property_name': 'name',
'delimiter': '',
- 'annotations': ''}),
+ 'fields': ''}),
(PROPERTY_RE, 'GtkWidget: name ',
{'class_name': 'GtkWidget',
'property_name': 'name',
'delimiter': '',
- 'annotations': ''}),
+ 'fields': ''}),
(PROPERTY_RE, ' GtkWidget : name ',
{'class_name': 'GtkWidget',
'property_name': 'name',
'delimiter': '',
- 'annotations': ''}),
+ 'fields': ''}),
(PROPERTY_RE, 'GtkWidget:name:',
{'class_name': 'GtkWidget',
'property_name': 'name',
'delimiter': ':',
- 'annotations': ''}),
+ 'fields': ''}),
(PROPERTY_RE, 'GtkWidget:name: ',
{'class_name': 'GtkWidget',
'property_name': 'name',
'delimiter': ':',
- 'annotations': ''}),
+ 'fields': ''}),
(PROPERTY_RE, ' GtkWidget:name:',
{'class_name': 'GtkWidget',
'property_name': 'name',
'delimiter': ':',
- 'annotations': ''}),
+ 'fields': ''}),
(PROPERTY_RE, 'Something:name:',
{'class_name': 'Something',
'property_name': 'name',
'delimiter': ':',
- 'annotations': ''}),
+ 'fields': ''}),
(PROPERTY_RE, 'Something:name: ',
{'class_name': 'Something',
'property_name': 'name',
'delimiter': ':',
- 'annotations': ''}),
+ 'fields': ''}),
(PROPERTY_RE, ' Something:name:',
{'class_name': 'Something',
'property_name': 'name',
'delimiter': ':',
- 'annotations': ''}),
+ 'fields': ''}),
(PROPERTY_RE, 'Weird-thing:name:',
None),
(PROPERTY_RE, 'really-weird_thing:name:',
@@ -327,63 +327,63 @@ identifier_property_tests = [
{'class_name': 'GWin32InputStream',
'property_name': 'handle',
'delimiter': ':',
- 'annotations': ''}),
+ 'fields': ''}),
# property name that contains a dash
(PROPERTY_RE, 'GtkWidget:double-buffered (skip)',
{'class_name': 'GtkWidget',
'property_name': 'double-buffered',
'delimiter': '',
- 'annotations': '(skip)'}),
+ 'fields': '(skip)'}),
(PROPERTY_RE, 'GtkWidget:double-buffered',
{'class_name': 'GtkWidget',
'property_name': 'double-buffered',
'delimiter': '',
- 'annotations': ''}),
+ 'fields': ''}),
(PROPERTY_RE, ' GtkWidget :double-buffered',
{'class_name': 'GtkWidget',
'property_name': 'double-buffered',
'delimiter': '',
- 'annotations': ''}),
+ 'fields': ''}),
(PROPERTY_RE, 'GtkWidget: double-buffered ',
{'class_name': 'GtkWidget',
'property_name': 'double-buffered',
'delimiter': '',
- 'annotations': ''}),
+ 'fields': ''}),
(PROPERTY_RE, ' GtkWidget : double-buffered ',
{'class_name': 'GtkWidget',
'property_name': 'double-buffered',
'delimiter': '',
- 'annotations': ''}),
+ 'fields': ''}),
(PROPERTY_RE, 'GtkWidget:double-buffered:',
{'class_name': 'GtkWidget',
'property_name': 'double-buffered',
'delimiter': ':',
- 'annotations': ''}),
+ 'fields': ''}),
(PROPERTY_RE, 'GtkWidget:double-buffered: ',
{'class_name': 'GtkWidget',
'property_name': 'double-buffered',
'delimiter': ':',
- 'annotations': ''}),
+ 'fields': ''}),
(PROPERTY_RE, ' GtkWidget:double-buffered:',
{'class_name': 'GtkWidget',
'property_name': 'double-buffered',
'delimiter': ':',
- 'annotations': ''}),
+ 'fields': ''}),
(PROPERTY_RE, 'Something:double-buffered:',
{'class_name': 'Something',
'property_name': 'double-buffered',
'delimiter': ':',
- 'annotations': ''}),
+ 'fields': ''}),
(PROPERTY_RE, 'Something:double-buffered: ',
{'class_name': 'Something',
'property_name': 'double-buffered',
'delimiter': ':',
- 'annotations': ''}),
+ 'fields': ''}),
(PROPERTY_RE, ' Something:double-buffered:',
{'class_name': 'Something',
'property_name': 'double-buffered',
'delimiter': ':',
- 'annotations': ''}),
+ 'fields': ''}),
(PROPERTY_RE, 'Weird-thing:double-buffered:',
None),
(PROPERTY_RE, 'really-weird_thing:double-buffered:',
@@ -392,7 +392,7 @@ identifier_property_tests = [
{'class_name': 'GMemoryOutputStream',
'property_name': 'realloc-function',
'delimiter': ':',
- 'annotations': '(skip)'})]
+ 'fields': '(skip)'})]
identifier_signal_tests = [
# simple property name
@@ -400,17 +400,17 @@ identifier_signal_tests = [
{'class_name': 'GtkWidget',
'signal_name': 'changed',
'delimiter': ':',
- 'annotations': '(skip)'}),
+ 'fields': '(skip)'}),
(SIGNAL_RE, 'GtkWidget::changed:',
{'class_name': 'GtkWidget',
'signal_name': 'changed',
'delimiter': ':',
- 'annotations': ''}),
+ 'fields': ''}),
(SIGNAL_RE, 'Something::changed:',
{'class_name': 'Something',
'signal_name': 'changed',
'delimiter': ':',
- 'annotations': ''}),
+ 'fields': ''}),
(SIGNAL_RE, 'Weird-thing::changed:',
None),
(SIGNAL_RE, 'really-weird_thing::changed:',
@@ -420,17 +420,17 @@ identifier_signal_tests = [
{'class_name': 'GtkWidget',
'signal_name': 'hierarchy-changed',
'delimiter': ':',
- 'annotations': '(skip)'}),
+ 'fields': '(skip)'}),
(SIGNAL_RE, 'GtkWidget::hierarchy-changed:',
{'class_name': 'GtkWidget',
'signal_name': 'hierarchy-changed',
'delimiter': ':',
- 'annotations': ''}),
+ 'fields': ''}),
(SIGNAL_RE, 'Something::hierarchy-changed:',
{'class_name': 'Something',
'signal_name': 'hierarchy-changed',
'delimiter': ':',
- 'annotations': ''}),
+ 'fields': ''}),
(SIGNAL_RE, 'Weird-thing::hierarchy-changed:',
None),
(SIGNAL_RE, 'really-weird_thing::hierarchy-changed:',
@@ -439,106 +439,71 @@ identifier_signal_tests = [
parameter_tests = [
(PARAMETER_RE, '@Short_description: Base class for all widgets ',
{'parameter_name': 'Short_description',
- 'annotations': '',
- 'delimiter': '',
- 'description': 'Base class for all widgets'}),
+ 'fields': 'Base class for all widgets'}),
(PARAMETER_RE, '@...: the value of the first property, followed optionally by more',
{'parameter_name': '...',
- 'annotations': '',
- 'delimiter': '',
- 'description': 'the value of the first property, followed optionally by more'}),
+ 'fields': 'the value of the first property, followed optionally by more'}),
(PARAMETER_RE, '@widget: a #GtkWidget',
{'parameter_name': 'widget',
- 'annotations': '',
- 'delimiter': '',
- 'description': 'a #GtkWidget'}),
+ 'fields': 'a #GtkWidget'}),
(PARAMETER_RE, '@widget_pointer: (inout) (transfer none): '
'address of a variable that contains @widget',
{'parameter_name': 'widget_pointer',
- 'annotations': '(inout) (transfer none)',
- 'delimiter': ':',
- 'description': 'address of a variable that contains @widget'}),
+ 'fields': '(inout) (transfer none): address of a variable that contains @widget'}),
(PARAMETER_RE, '@weird_thing: (inout) (transfer none) (allow-none) (attribute) (destroy) '
'(foreign) (inout) (out) (transfer) (skip) (method): some weird @thing',
{'parameter_name': 'weird_thing',
- 'annotations': '(inout) (transfer none) (allow-none) (attribute) (destroy) '
- '(foreign) (inout) (out) (transfer) (skip) (method)',
- 'delimiter': ':',
- 'description': 'some weird @thing'}),
+ 'fields': '(inout) (transfer none) (allow-none) (attribute) (destroy) '
+ '(foreign) (inout) (out) (transfer) (skip) (method): '
+ 'some weird @thing'}),
(PARAMETER_RE, '@data: a pointer to the element data. The data may be moved as elements '
'are added to the #GByteArray.',
{'parameter_name': 'data',
- 'annotations': '',
- 'delimiter': '',
- 'description': 'a pointer to the element data. The data may be moved as elements '
+ 'fields': 'a pointer to the element data. The data may be moved as elements '
'are added to the #GByteArray.'}),
(PARAMETER_RE, '@a: a #GSequenceIter',
{'parameter_name': 'a',
- 'annotations': '',
- 'delimiter': '',
- 'description': 'a #GSequenceIter'}),
+ 'fields': 'a #GSequenceIter'}),
(PARAMETER_RE, '@keys: (array length=n_keys) (element-type GQuark) (allow-none):',
{'parameter_name': 'keys',
- 'annotations': '(array length=n_keys) (element-type GQuark) (allow-none)',
- 'delimiter': ':',
- 'description': ''})]
+ 'fields': '(array length=n_keys) (element-type GQuark) (allow-none):'})]
tag_tests = [
(TAG_RE, 'Since 3.0',
None),
(TAG_RE, 'Since: 3.0',
{'tag_name': 'Since',
- 'annotations': '',
- 'delimiter': '',
- 'description': '3.0'}),
+ 'fields': '3.0'}),
(TAG_RE, 'Attributes: (inout) (transfer none): some note about attributes',
{'tag_name': 'Attributes',
- 'annotations': '(inout) (transfer none)',
- 'delimiter': ':',
- 'description': 'some note about attributes'}),
+ 'fields': '(inout) (transfer none): some note about attributes'}),
(TAG_RE, 'Rename to: something_else',
{'tag_name': 'Rename to',
- 'annotations': '',
- 'delimiter': '',
- 'description': 'something_else'}),
+ 'fields': 'something_else'}),
(TAG_RE, '@Deprecated: Since 2.8, reference counting is done atomically',
None),
(TAG_RE, 'Returns %TRUE and does weird things',
None),
(TAG_RE, 'Returns: a #GtkWidget',
{'tag_name': 'Returns',
- 'annotations': '',
- 'delimiter': '',
- 'description': 'a #GtkWidget'}),
+ 'fields': 'a #GtkWidget'}),
(TAG_RE, 'Return value: (transfer none): The binary data that @text responds. '
'This pointer',
{'tag_name': 'Return value',
- 'annotations': '(transfer none)',
- 'delimiter': ':',
- 'description': 'The binary data that @text responds. This pointer'}),
+ 'fields': '(transfer none): The binary data that @text responds. This pointer'}),
(TAG_RE, 'Return value: (transfer full) (array length=out_len) (element-type guint8):',
{'tag_name': 'Return value',
- 'annotations': '(transfer full) (array length=out_len) (element-type guint8)',
- 'delimiter': ':',
- 'description': ''}),
+ 'fields': '(transfer full) (array length=out_len) (element-type guint8):'}),
(TAG_RE, 'Returns: A boolean value, but let me tell you a bit about this boolean. It',
{'tag_name': 'Returns',
- 'annotations': '',
- 'delimiter': '',
- 'description': 'A boolean value, but let me tell you a bit about this boolean. '
- 'It'}),
+ 'fields': 'A boolean value, but let me tell you a bit about this boolean. It'}),
(TAG_RE, 'Returns: (transfer container) (element-type GObject.ParamSpec): a',
{'tag_name': 'Returns',
- 'annotations': '(transfer container) (element-type GObject.ParamSpec)',
- 'delimiter': ':',
- 'description': 'a'}),
+ 'fields': '(transfer container) (element-type GObject.ParamSpec): a'}),
(TAG_RE, 'Return value: (type GLib.HashTable<utf8,GLib.HashTable<utf8,utf8>>) '
'(transfer full):',
{'tag_name': 'Return value',
- 'annotations': '(type GLib.HashTable<utf8,GLib.HashTable<utf8,utf8>>) '
- '(transfer full)',
- 'delimiter': ':',
- 'description': ''})]
+ 'fields': '(type GLib.HashTable<utf8,GLib.HashTable<utf8,utf8>>) (transfer full):'})]
tag_value_version_tests = [
(TAG_VALUE_VERSION_RE, ' abc',
diff --git a/tests/warn/invalid-element-type.h b/tests/warn/invalid-element-type.h
index bcd9123..97b8281 100644
--- a/tests/warn/invalid-element-type.h
+++ b/tests/warn/invalid-element-type.h
@@ -74,16 +74,16 @@ GList* test_unresolved_element_type(void);
GPtrArray* test_unresolved_value_element_type(void);
-// EXPECT:5: Warning: Test: element-type annotation takes at least one option, none given
+// EXPECT:5: Warning: Test: element-type annotation for a list must have exactly one option, not 0 options
// EXPECT:6: Warning: Test: element-type annotation for a list must have exactly one option, not 2 options
-// EXPECT:16: Warning: Test: element-type annotation takes at least one option, none given
// EXPECT:20: Warning: Test: Unknown container Type(target_fundamental=utf8, ctype=char*) for element-type
annotation
-// EXPECT:27: Warning: Test: element-type annotation takes at least one option, none given
+// EXPECT:20: Warning: Test: Unknown container Type(target_fundamental=utf8, ctype=char*) for element-type
annotation
+// EXPECT:27: Warning: Test: element-type annotation for a hash table must have exactly two options, not 0
option(s)
// EXPECT:28: Warning: Test: element-type annotation for a hash table must have exactly two options, not 1
option(s)
// EXPECT:29: Warning: Test: element-type annotation for a hash table must have exactly two options, not 3
option(s)
-// EXPECT:40: Warning: Test: element-type annotation takes at least one option, none given
+// EXPECT:40: Warning: Test: element-type annotation for an array must have exactly one option, not 0 options
// EXPECT:41: Warning: Test: invalid (element-type) for a GByteArray, must be one of guint8, gint8 or gchar
-// EXPECT:51: Warning: Test: element-type annotation takes at least one option, none given
+// EXPECT:51: Warning: Test: element-type annotation for an array must have exactly one option, not 0 options
// EXPECT:52: Warning: Test: invalid (element-type) for a GPtrArray, must be a pointer
// EXPECT:63: Warning: Test: test_unresolved_element_type: Unknown type: 'Unresolved'
// EXPECT:71: Warning: Test: test_unresolved_value_element_type: Unknown type: 'GLib.Value'
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]