diff options
author | Dieter Verfaillie <dieterv@optionexplicit.be> | 2013-08-21 12:16:30 +0200 |
---|---|---|
committer | Dieter Verfaillie <dieterv@optionexplicit.be> | 2013-10-08 20:55:56 +0200 |
commit | 35278b955304eb62565b5578b6848494721847bd (patch) | |
tree | 101e6c9846232daaa36cc394c6f538912473e697 | |
parent | 4ff3c660de64ba423659bd796fbd944b7af1913d (diff) | |
download | gobject-introspection-35278b955304eb62565b5578b6848494721847bd.tar.gz |
giscanner: refactor annotation validation
- annotations on the identifier (formerly g-i specific tags) have
never been validated before, so fix this
- removes duplicate validation code from GtkDocTag and GtkDocParameter
- remove repeated validation code doing the same thing as
annotationparser from maintransformer...
-rw-r--r-- | giscanner/annotationparser.py | 645 | ||||
-rw-r--r-- | giscanner/maintransformer.py | 17 | ||||
-rw-r--r-- | tests/scanner/annotationparser/gi/tag_returns.xml | 32 | ||||
-rw-r--r-- | tests/warn/callback-invalid-scope.h | 7 | ||||
-rw-r--r-- | tests/warn/invalid-array.h | 12 | ||||
-rw-r--r-- | tests/warn/invalid-closure.h | 2 | ||||
-rw-r--r-- | tests/warn/invalid-element-type.h | 47 | ||||
-rw-r--r-- | tests/warn/invalid-out.h | 2 | ||||
-rw-r--r-- | tests/warn/invalid-transfer.h | 6 |
9 files changed, 460 insertions, 310 deletions
diff --git a/giscanner/annotationparser.py b/giscanner/annotationparser.py index ce1f7236..3c377c75 100644 --- a/giscanner/annotationparser.py +++ b/giscanner/annotationparser.py @@ -109,6 +109,8 @@ Refer to the `GTK-Doc manual`_ for more detailed usage information. import os import re +from operator import ne, gt, lt + from .collections import OrderedDict from .message import Position, warn, error @@ -248,21 +250,37 @@ OPT_ARRAY_FIXED_SIZE = 'fixed-size' OPT_ARRAY_LENGTH = 'length' OPT_ARRAY_ZERO_TERMINATED = 'zero-terminated' +ARRAY_OPTIONS = [OPT_ARRAY_FIXED_SIZE, + OPT_ARRAY_LENGTH, + OPT_ARRAY_ZERO_TERMINATED] + # (out) annotation options OPT_OUT_CALLEE_ALLOCATES = 'callee-allocates' OPT_OUT_CALLER_ALLOCATES = 'caller-allocates' +OUT_OPTIONS = [OPT_OUT_CALLEE_ALLOCATES, + OPT_OUT_CALLER_ALLOCATES] + # (scope) annotation options OPT_SCOPE_ASYNC = 'async' OPT_SCOPE_CALL = 'call' OPT_SCOPE_NOTIFIED = 'notified' +SCOPE_OPTIONS = [OPT_SCOPE_ASYNC, + OPT_SCOPE_CALL, + OPT_SCOPE_NOTIFIED] + # (transfer) annotation options OPT_TRANSFER_CONTAINER = 'container' OPT_TRANSFER_FLOATING = 'floating' OPT_TRANSFER_FULL = 'full' OPT_TRANSFER_NONE = 'none' +TRANSFER_OPTIONS = [OPT_TRANSFER_CONTAINER, + OPT_TRANSFER_FLOATING, + OPT_TRANSFER_FULL, + OPT_TRANSFER_NONE] + # Program matching the start of a comment block. # @@ -490,53 +508,125 @@ class GtkDocAnnotations(OrderedDict): self.position = position -class GtkDocParameter(object): +class GtkDocAnnotatable(object): ''' - Represents a GTK-Doc parameter part. + Base class for GTK-Doc comment block parts that can be annotated. ''' - __slots__ = ('name', 'annotations', 'description', 'position') + __slots__ = ('position', 'annotations') - def __init__(self, name, position=None): - #: Parameter name. - self.name = name + #: A :class:`tuple` of annotation name constants that are valid for this object. Annotation + #: names not in this :class:`tuple` will be reported as *unknown* by :func:`validate`. The + #: :attr:`valid_annotations` class attribute should be overridden by subclasses. + valid_annotations = () - #: Parameter description or :const:`None`. - self.description = None + def __init__(self, position=None): + #: A :class:`giscanner.message.Position` instance specifying the location of the + #: annotatable comment block part in the source file or :const:`None`. + self.position = position + #: A :class:`GtkDocAnnotations` instance representing the annotations + #: applied to this :class:`GtkDocAnnotatable` instance. self.annotations = GtkDocAnnotations() - self.position = position def __repr__(self): - return '<GtkDocParameter %r %r>' % (self.name, self.annotations) + return '<GtkDocAnnotatable %r %r>' % (self.annotations, ) - def _validate_annotation(self, ann_name, options, required=False, - n_params=None, choices=None): - if required and len(options) == 0: - warn('%s annotation needs a value' % (ann_name, ), self.position) - return + def validate(self): + ''' + Validate annotations stored by the :class:`GtkDocAnnotatable` instance, if any. + ''' - if n_params is not None: - if n_params == 0: - s = 'no value' - elif n_params == 1: - s = 'one value' - else: - s = '%d values' % (n_params, ) - if len(options) != n_params: - length = len(options) - warn('%s annotation needs %s, not %d' % (ann_name, s, length), - self.position) - return + if self.annotations: + for ann_name, options in self.annotations.items(): + if ann_name in self.valid_annotations: + validate = getattr(self, '_do_validate_' + ann_name.replace('-', '_')) + validate(ann_name, options) + elif ann_name in ALL_ANNOTATIONS: + # Not error() as ann_name might be valid in some newer + # GObject-Instrospection version. + warn('unexpected annotation: %s' % (ann_name, ), self.position) + else: + # Not error() as ann_name might be valid in some newer + # GObject-Instrospection version. + warn('unknown annotation: %s' % (ann_name, ), self.position) + + def _validate_options(self, ann_name, n_options, expected_n_options, operator, message): + ''' + Validate the number of options held by an annotation according to the test + ``operator(n_options, expected_n_options)``. + + :param ann_name: name of the annotation holding the options to validate + :param n_options: number of options held by the annotation + :param expected_n_options: number of expected options + :param operator: an operator function from python's :mod:`operator` module, for example + :func:`operator.ne` or :func:`operator.lt` + :param message: warning message used when the test + ``operator(n_options, expected_n_options)`` fails. + ''' + + if n_options == 0: + t = 'none' + else: + t = '%d' % (n_options, ) + + if expected_n_options == 0: + s = 'no options' + elif expected_n_options == 1: + s = 'one option' + else: + s = '%d options' % (expected_n_options, ) + + if operator(n_options, expected_n_options): + warn('"%s" annotation %s %s, %s given' % (ann_name, message, s, t), self.position) + + def _validate_annotation(self, ann_name, options, choices=None, + exact_n_options=None, min_n_options=None, max_n_options=None): + ''' + Validate an annotation. + + :param ann_name: name of the annotation holding the options to validate + :param options: annotation options to be validated + :param choices: an iterable of allowed option names or :const:`None` to skip this test + :param exact_n_options: exact number of expected options or :const:`None` to skip this test + :param min_n_options: minimum number of expected options or :const:`None` to skip this test + :param max_n_options: maximum number of expected options or :const:`None` to skip this test + ''' + + n_options = len(options) + + if exact_n_options is not None: + self._validate_options(ann_name, n_options, exact_n_options, ne, 'needs') + + if min_n_options is not None: + self._validate_options(ann_name, n_options, min_n_options, lt, 'takes at least') - if choices is not None: + if max_n_options is not None: + self._validate_options(ann_name, n_options, max_n_options, gt, 'takes at most') + + if options and choices is not None: option = options[0] if option not in choices: - warn('invalid %s annotation value: %r' % (ann_name, option, ), - self.position) - return + warn('invalid "%s" annotation option: "%s"' % (ann_name, option), self.position) + + def _do_validate_allow_none(self, ann_name, options): + ''' + Validate the ``(allow-none)`` annotation. + + :param ann_name: name of the annotation holding the options to validate + :param options: annotation options held by the annotation + ''' + + self._validate_annotation(ann_name, options, exact_n_options=0) + + def _do_validate_array(self, ann_name, options): + ''' + Validate the ``(array)`` annotation. + + :param ann_name: name of the annotation holding the options to validate + :param options: annotation options held by the annotation + ''' - def _validate_array(self, ann_name, options): if len(options) == 0: return @@ -546,49 +636,254 @@ class GtkDocParameter(object): int(value) except (TypeError, ValueError): if value is None: - warn('array option %s needs a value' % (option, ), - positions=self.position) + warn('"%s" annotation option "%s" needs a value' % (ann_name, option), + self.position) else: - warn('invalid array %s option value %r, ' - 'must be an integer' % (option, value, ), - positions=self.position) + warn('invalid "%s" annotation option "%s" value "%s", must be an integer' % + (ann_name, option, value), + self.position) elif option == OPT_ARRAY_LENGTH: if value is None: - warn('array option length needs a value', - positions=self.position) + warn('"%s" annotation option "length" needs a value' % (ann_name, ), + self.position) else: - warn('invalid array annotation value: %r' % (option, ), + warn('invalid "%s" annotation option: "%s"' % (ann_name, option), self.position) - def _validate_closure(self, ann_name, options): - if len(options) != 0 and len(options) > 1: - warn('closure takes at most 1 value, %d given' % (len(options), ), - self.position) + def _do_validate_attributes(self, ann_name, options): + ''' + Validate the ``(attributes)`` annotation. - def _validate_element_type(self, ann_name, options): - self._validate_annotation(ann_name, options, required=True) - if len(options) == 0: - warn('element-type takes at least one value, none given', - self.position) - return - if len(options) > 2: - warn('element-type takes at most 2 values, %d given' % (len(options), ), - self.position) - return + :param ann_name: name of the annotation holding the options to validate + :param options: annotation options to validate + ''' - def _validate_out(self, ann_name, options): - if len(options) == 0: - return - if len(options) > 1: - warn('out annotation takes at most 1 value, %d given' % (len(options), ), - self.position) - return - option = options[0] - if option not in [OPT_OUT_CALLEE_ALLOCATES, - OPT_OUT_CALLER_ALLOCATES]: - warn("out annotation value is invalid: %r" % (option, ), - self.position) - return + # The 'attributes' annotation allows free form annotations. + pass + + def _do_validate_closure(self, ann_name, options): + ''' + Validate the ``(closure)`` annotation. + + :param ann_name: name of the annotation holding the options to validate + :param options: annotation options to validate + ''' + + self._validate_annotation(ann_name, options, max_n_options=1) + + def _do_validate_constructor(self, ann_name, options): + ''' + Validate the ``(constructor)`` annotation. + + :param ann_name: name of the annotation holding the options to validate + :param options: annotation options to validate + ''' + + self._validate_annotation(ann_name, options, exact_n_options=0) + + def _do_validate_destroy(self, ann_name, options): + ''' + Validate the ``(destroy)`` annotation. + + :param ann_name: name of the annotation holding the options to validate + :param options: annotation options to validate + ''' + + self._validate_annotation(ann_name, options, exact_n_options=1) + + def _do_validate_element_type(self, ann_name, options): + ''' + Validate the ``(element)`` annotation. + + :param ann_name: name of the annotation holding the options to validate + :param options: annotation options to validate + ''' + + self._validate_annotation(ann_name, options, min_n_options=1, max_n_options=2) + + def _do_validate_foreign(self, ann_name, options): + ''' + Validate the ``(foreign)`` annotation. + + :param ann_name: name of the annotation holding the options to validate + :param options: annotation options to validate + ''' + + self._validate_annotation(ann_name, options, exact_n_options=0) + + def _do_validate_get_value_func(self, ann_name, options): + ''' + Validate the ``(value-func)`` annotation. + + :param ann_name: name of the annotation holding the options to validate + :param options: annotation options to validate + ''' + + self._validate_annotation(ann_name, options, exact_n_options=1) + + def _do_validate_in(self, ann_name, options): + ''' + Validate the ``(in)`` annotation. + + :param ann_name: name of the annotation holding the options to validate + :param options: annotation options to validate + ''' + + self._validate_annotation(ann_name, options, exact_n_options=0) + + def _do_validate_inout(self, ann_name, options): + ''' + Validate the ``(in-out)`` annotation. + + :param ann_name: name of the annotation holding the options to validate + :param options: annotation options to validate + ''' + + self._validate_annotation(ann_name, options, exact_n_options=0) + + def _do_validate_method(self, ann_name, options): + ''' + Validate the ``(method)`` annotation. + + :param ann_name: name of the annotation holding the options to validate + :param options: annotation options to validate + ''' + + self._validate_annotation(ann_name, options, exact_n_options=0) + + def _do_validate_out(self, ann_name, options): + ''' + Validate the ``(out)`` annotation. + + :param ann_name: name of the annotation holding the options to validate + :param options: annotation options to validate + ''' + + self._validate_annotation(ann_name, options, max_n_options=1, choices=OUT_OPTIONS) + + def _do_validate_ref_func(self, ann_name, options): + ''' + Validate the ``(ref-func)`` annotation. + + :param ann_name: name of the annotation holding the options to validate + :param options: annotation options to validate + ''' + + self._validate_annotation(ann_name, options, exact_n_options=1) + + def _do_validate_rename_to(self, ann_name, options): + ''' + Validate the ``(rename-to)`` annotation. + + :param ann_name: name of the annotation holding the options to validate + :param options: annotation options to validate + ''' + + self._validate_annotation(ann_name, options, exact_n_options=1) + + def _do_validate_scope(self, ann_name, options): + ''' + Validate the ``(scope)`` annotation. + + :param ann_name: name of the annotation holding the options to validate + :param options: annotation options to validate + ''' + + self._validate_annotation(ann_name, options, exact_n_options=1, choices=SCOPE_OPTIONS) + + def _do_validate_set_value_func(self, ann_name, options): + ''' + Validate the ``(value-func)`` annotation. + + :param ann_name: name of the annotation holding the options to validate + :param options: annotation options to validate + ''' + + self._validate_annotation(ann_name, options, exact_n_options=1) + + def _do_validate_skip(self, ann_name, options): + ''' + Validate the ``(skip)`` annotation. + + :param ann_name: name of the annotation holding the options to validate + :param options: annotation options to validate + ''' + + self._validate_annotation(ann_name, options, exact_n_options=0) + + def _do_validate_transfer(self, ann_name, options): + ''' + Validate the ``(transfer)`` annotation. + + :param ann_name: name of the annotation holding the options to validate + :param options: annotation options to validate + ''' + + self._validate_annotation(ann_name, options, exact_n_options=1, choices=TRANSFER_OPTIONS) + + def _do_validate_type(self, ann_name, options): + ''' + Validate the ``(type)`` annotation. + + :param ann_name: name of the annotation holding the options to validate + :param options: annotation options to validate + ''' + + self._validate_annotation(ann_name, options, exact_n_options=1) + + def _do_validate_unref_func(self, ann_name, options): + ''' + Validate the ``(unref-func)`` annotation. + + :param ann_name: name of the annotation holding the options to validate + :param options: annotation options to validate + ''' + + self._validate_annotation(ann_name, options, exact_n_options=1) + + def _do_validate_value(self, ann_name, options): + ''' + Validate the ``(value)`` annotation. + + :param ann_name: name of the annotation holding the options to validate + :param options: annotation options to validate + ''' + + self._validate_annotation(ann_name, options, exact_n_options=1) + + def _do_validate_virtual(self, ann_name, options): + ''' + Validate the ``(virtual)`` annotation. + + :param ann_name: name of the annotation holding the options to validate + :param options: annotation options to validate + ''' + + self._validate_annotation(ann_name, options, exact_n_options=1) + + +class GtkDocParameter(GtkDocAnnotatable): + ''' + Represents a GTK-Doc parameter part. + ''' + + __slots__ = ('name', 'description') + + valid_annotations = (ANN_ALLOW_NONE, ANN_ARRAY, ANN_ATTRIBUTES, ANN_CLOSURE, ANN_DESTROY, + ANN_ELEMENT_TYPE, ANN_IN, ANN_INOUT, ANN_OUT, ANN_SCOPE, ANN_SKIP, + ANN_TRANSFER, ANN_TYPE) + + def __init__(self, name, position=None): + GtkDocAnnotatable.__init__(self, position) + + #: Parameter name. + self.name = name + + #: Parameter description or :const:`None`. + self.description = None + + def __repr__(self): + return '<GtkDocParameter %r %r>' % (self.name, self.annotations) def _get_gtk_doc_value(self): def serialize_one(option, value, fmt, fmt2): @@ -615,66 +910,20 @@ class GtkDocParameter(object): def to_gtk_doc(self): return '@%s: %s%s' % (self.name, self._get_gtk_doc_value(), self.description) - def validate(self): - 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_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: - self._validate_annotation(ann_name, value, n_params=1) - elif ann_name == ANN_ELEMENT_TYPE: - self._validate_element_type(ann_name, value) - elif ann_name == ANN_FOREIGN: - self._validate_annotation(ann_name, value, n_params=0) - elif ann_name == ANN_IN: - self._validate_annotation(ann_name, value, n_params=0) - elif ann_name in [ANN_INOUT, ANN_INOUT_ALT]: - self._validate_annotation(ann_name, value, n_params=0) - elif ann_name == ANN_OUT: - self._validate_out(ann_name, value) - elif ann_name == ANN_SCOPE: - self._validate_annotation( - ann_name, value, required=True, - n_params=1, - choices=[OPT_SCOPE_ASYNC, - OPT_SCOPE_CALL, - OPT_SCOPE_NOTIFIED]) - elif ann_name == ANN_SKIP: - self._validate_annotation(ann_name, value, n_params=0) - elif ann_name == ANN_TRANSFER: - self._validate_annotation( - ann_name, value, required=True, - n_params=1, - choices=[OPT_TRANSFER_FULL, - OPT_TRANSFER_CONTAINER, - OPT_TRANSFER_NONE, - OPT_TRANSFER_FLOATING]) - elif ann_name == ANN_TYPE: - self._validate_annotation(ann_name, value, required=True, - n_params=1) - elif ann_name == ANN_CONSTRUCTOR: - self._validate_annotation(ann_name, value, n_params=0) - elif ann_name == ANN_METHOD: - self._validate_annotation(ann_name, value, n_params=0) - else: - warn('unknown annotation: %s' % (ann_name, ), - self.position) - -class GtkDocTag(object): +class GtkDocTag(GtkDocAnnotatable): ''' Represents a GTK-Doc tag part. ''' - __slots__ = ('name', 'annotations', 'value', 'description', 'position') + __slots__ = ('name', 'value', 'description') + + valid_annotations = (ANN_ALLOW_NONE, ANN_ARRAY, ANN_ATTRIBUTES, ANN_ELEMENT_TYPE, ANN_SKIP, + ANN_TRANSFER, ANN_TYPE) def __init__(self, name, position=None): + GtkDocAnnotatable.__init__(self, position) + #: Tag name. self.name = name @@ -684,92 +933,9 @@ class GtkDocTag(object): #: Tag description or :const:`None`. self.description = None - self.annotations = GtkDocAnnotations() - self.position = position - def __repr__(self): return '<GtkDocTag %r %r>' % (self.name, self.annotations) - def _validate_annotation(self, ann_name, options, required=False, - n_params=None, choices=None): - if required and len(options) == 0: - warn('%s annotation needs a value' % (ann_name, ), self.position) - return - - if n_params is not None: - if n_params == 0: - s = 'no value' - elif n_params == 1: - s = 'one value' - else: - s = '%d values' % (n_params, ) - if len(options) != n_params: - length = len(options) - warn('%s annotation needs %s, not %d' % (ann_name, s, length), - self.position) - return - - if choices is not None: - 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 len(options) == 0: - return - - for option, value in options.items(): - if option in [OPT_ARRAY_ZERO_TERMINATED, OPT_ARRAY_FIXED_SIZE]: - try: - int(value) - except (TypeError, ValueError): - if value is None: - warn('array option %s needs a value' % (option, ), - positions=self.position) - else: - warn('invalid array %s option value %r, ' - 'must be an integer' % (option, value, ), - positions=self.position) - elif option == OPT_ARRAY_LENGTH: - if value is None: - warn('array option length needs a value', - positions=self.position) - else: - warn('invalid array annotation value: %r' % (option, ), - self.position) - - def _validate_closure(self, ann_name, options): - 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 len(options) == 0: - warn('element-type takes at least one value, none given', - self.position) - return - 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 len(options) == 0: - return - if len(options) > 1: - warn('out annotation takes at most 1 value, %d given' % (len(options), ), - self.position) - return - option = options[0] - if option not in [OPT_OUT_CALLEE_ALLOCATES, - OPT_OUT_CALLER_ALLOCATES]: - warn("out annotation value is invalid: %r" % (option, ), - self.position) - return - def _get_gtk_doc_value(self): def serialize_one(option, value, fmt, fmt2): if value: @@ -801,66 +967,22 @@ class GtkDocTag(object): self._get_gtk_doc_value(), self.description or '') - def validate(self): - 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_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: - self._validate_annotation(ann_name, value, n_params=1) - elif ann_name == ANN_ELEMENT_TYPE: - self._validate_element_type(ann_name, value) - elif ann_name == ANN_FOREIGN: - self._validate_annotation(ann_name, value, n_params=0) - elif ann_name == ANN_IN: - self._validate_annotation(ann_name, value, n_params=0) - elif ann_name in [ANN_INOUT, ANN_INOUT_ALT]: - self._validate_annotation(ann_name, value, n_params=0) - elif ann_name == ANN_OUT: - self._validate_out(ann_name, value) - elif ann_name == ANN_SCOPE: - self._validate_annotation( - ann_name, value, required=True, - n_params=1, - choices=[OPT_SCOPE_ASYNC, - OPT_SCOPE_CALL, - OPT_SCOPE_NOTIFIED]) - elif ann_name == ANN_SKIP: - self._validate_annotation(ann_name, value, n_params=0) - elif ann_name == ANN_TRANSFER: - self._validate_annotation( - ann_name, value, required=True, - n_params=1, - choices=[OPT_TRANSFER_FULL, - OPT_TRANSFER_CONTAINER, - OPT_TRANSFER_NONE, - OPT_TRANSFER_FLOATING]) - elif ann_name == ANN_TYPE: - self._validate_annotation(ann_name, value, required=True, - n_params=1) - elif ann_name == ANN_CONSTRUCTOR: - self._validate_annotation(ann_name, value, n_params=0) - elif ann_name == ANN_METHOD: - self._validate_annotation(ann_name, value, n_params=0) - else: - warn('unknown annotation: %s' % (ann_name, ), - self.position) - -class GtkDocCommentBlock(object): +class GtkDocCommentBlock(GtkDocAnnotatable): ''' Represents a GTK-Doc comment block. ''' - __slots__ = ('name', 'annotations', 'tags', 'description', 'params', 'position') + __slots__ = ('name', 'params', 'description', 'tags') + + #: Valid annotation names for the GTK-Doc comment block identifier part. + valid_annotations = (ANN_ATTRIBUTES, ANN_CONSTRUCTOR, ANN_FOREIGN, ANN_GET_VALUE_FUNC, + ANN_METHOD, ANN_REF_FUNC, ANN_RENAME_TO, ANN_SET_VALUE_FUNC, + ANN_SKIP, ANN_TRANSFER, ANN_TYPE, ANN_UNREF_FUNC, ANN_VALUE, ANN_VFUNC) + + def __init__(self, name, position=None): + GtkDocAnnotatable.__init__(self, position) - def __init__(self, name): #: Identifier name. self.name = name @@ -875,9 +997,6 @@ class GtkDocCommentBlock(object): #: applied to this :class:`GtkDocCommentBlock`. self.tags = OrderedDict() - self.annotations = GtkDocAnnotations() - self.position = None - def __cmp__(self, other): # Note: This is used by g-ir-annotation-tool, which does a ``sorted(blocks.values())``, # meaning that keeping this around makes update-glib-annotations.py patches @@ -934,6 +1053,12 @@ class GtkDocCommentBlock(object): return comment def validate(self): + ''' + Validate annotations applied to the :class:`GtkDocCommentBlock` identifier, parameters + and tags. + ''' + GtkDocAnnotatable.validate(self) + for param in self.params.values(): param.validate() diff --git a/giscanner/maintransformer.py b/giscanner/maintransformer.py index b4418b64..ebb2b88e 100644 --- a/giscanner/maintransformer.py +++ b/giscanner/maintransformer.py @@ -140,7 +140,7 @@ class MainTransformer(object): target = self._namespace.get_by_symbol(rename_to) if not target: message.warn_node(node, - "Can't find symbol %r referenced by Rename annotation" % (rename_to, )) + "Can't find symbol %r referenced by \"rename-to\" annotation" % (rename_to, )) elif target.shadowed_by: message.warn_node(node, "Function %r already shadowed by %r, can't overwrite " @@ -394,16 +394,12 @@ class MainTransformer(object): def _apply_annotations_element_type(self, parent, node, annotations): element_type_opt = annotations.get(ANN_ELEMENT_TYPE) if element_type_opt is None: - message.warn( - 'element-type annotation takes at least one option, ' - 'none given', - annotations.position) return if isinstance(node.type, ast.List): if len(element_type_opt) != 1: message.warn( - 'element-type annotation for a list must have exactly ' + '"element-type" annotation for a list must have exactly ' 'one option, not %d options' % (len(element_type_opt), ), annotations.position) return @@ -412,7 +408,7 @@ class MainTransformer(object): elif isinstance(node.type, ast.Map): if len(element_type_opt) != 2: message.warn( - 'element-type annotation for a hash table must have exactly ' + '"element-type" annotation for a hash table must have exactly ' 'two options, not %d option(s)' % (len(element_type_opt), ), annotations.position) return @@ -423,15 +419,16 @@ class MainTransformer(object): elif isinstance(node.type, ast.Array): if len(element_type_opt) != 1: message.warn( - 'element-type annotation for an array must have exactly ' + '"element-type" annotation for an array must have exactly ' 'one option, not %d options' % (len(element_type_opt), ), annotations.position) return node.type.element_type = self._resolve(element_type_opt[0], node.type, node, parent) else: - message.warn_node(parent, - "Unknown container %r for element-type annotation" % (node.type, )) + message.warn( + "Unknown container %r for element-type annotation" % (node.type, ), + annotations.position) def _get_transfer_default_param(self, parent, node): if node.direction in [ast.PARAM_DIRECTION_INOUT, diff --git a/tests/scanner/annotationparser/gi/tag_returns.xml b/tests/scanner/annotationparser/gi/tag_returns.xml index b5643b37..96ee8049 100644 --- a/tests/scanner/annotationparser/gi/tag_returns.xml +++ b/tests/scanner/annotationparser/gi/tag_returns.xml @@ -333,4 +333,36 @@ parameter is encountered.</description> </parser> </test> +<test> + <input>/** + * annotation_object_string_out: + * + * Test returning a string as an out parameter + * + * Returns: (out): some boolean + **/</input> + <parser> + <docblock> + <identifier> + <name>annotation_object_string_out</name> + </identifier> + <description>Test returning a string as an out parameter</description> + <tags> + <tag> + <name>returns</name> + <annotations> + <annotation> + <name>out</name> + </annotation> + </annotations> + <description>some boolean</description> + </tag> + </tags> + </docblock> + <messages> + <message>6: Warning: Test: unexpected annotation: out</message> + </messages> + </parser> +</test> + </tests> diff --git a/tests/warn/callback-invalid-scope.h b/tests/warn/callback-invalid-scope.h index 583dc0ca..d07f172e 100644 --- a/tests/warn/callback-invalid-scope.h +++ b/tests/warn/callback-invalid-scope.h @@ -7,7 +7,7 @@ */ void test_callback_invalid(GCallback *callback, gpointer user_data); -// EXPECT:5: Warning: Test: invalid scope annotation value: 'invalid' +// EXPECT:5: Warning: Test: invalid "scope" annotation option: "invalid" /** * test_callback_invalid2: @@ -16,7 +16,7 @@ void test_callback_invalid(GCallback *callback, gpointer user_data); */ void test_callback_invalid2(GCallback *callback, gpointer user_data); -// EXPECT:14: Warning: Test: scope annotation needs a value +// EXPECT:14: Warning: Test: "scope" annotation needs one option, none given /** * test_callback_invalid3: @@ -25,7 +25,8 @@ void test_callback_invalid2(GCallback *callback, gpointer user_data); */ void test_callback_invalid3(GCallback *callback, gpointer user_data); -// EXPECT:23: Warning: Test: scope annotation needs one value, not 2 +// EXPECT:23: Warning: Test: "scope" annotation needs one option, 2 given +// EXPECT:23: Warning: Test: invalid "scope" annotation option: "invalid" // EXPECT:13: Warning: Test: test_callback_invalid2: argument callback: Missing (scope) annotation for callback without GDestroyNotify (valid: call, async) // EXPECT:22: Warning: Test: test_callback_invalid3: argument callback: Missing (scope) annotation for callback without GDestroyNotify (valid: call, async) diff --git a/tests/warn/invalid-array.h b/tests/warn/invalid-array.h index a4a4e47e..b9b828cf 100644 --- a/tests/warn/invalid-array.h +++ b/tests/warn/invalid-array.h @@ -7,7 +7,7 @@ void test_invalid_array (char ***out1); -// EXPECT:5: Warning: Test: invalid array annotation value: 'foobar' +// EXPECT:5: Warning: Test: invalid "array" annotation option: "foobar" /** * test_invalid_array_zero_terminated: @@ -18,8 +18,8 @@ void test_invalid_array_zero_terminated (char ***out1, char ***out2); -// EXPECT:14: Warning: Test: array option zero-terminated needs a value -// EXPECT:15: Warning: Test: invalid array zero-terminated option value 'foobar', must be an integer +// EXPECT:14: Warning: Test: "array" annotation option "zero-terminated" needs a value +// EXPECT:15: Warning: Test: invalid "array" annotation option "zero-terminated" value "foobar", must be an integer /** * test_invalid_array_fixed_size: @@ -30,8 +30,8 @@ void test_invalid_array_fixed_size (char ***out1, char ***out2); -// EXPECT:26: Warning: Test: array option fixed-size needs a value -// EXPECT:27: Warning: Test: invalid array fixed-size option value 'foobar', must be an integer +// EXPECT:26: Warning: Test: "array" annotation option "fixed-size" needs a value +// EXPECT:27: Warning: Test: invalid "array" annotation option "fixed-size" value "foobar", must be an integer /** * test_invalid_array_length: @@ -41,4 +41,4 @@ void test_invalid_array_length (char ***out1, char ***out2); -// EXPECT:38: Warning: Test: array option length needs a value +// EXPECT:38: Warning: Test: "array" annotation option "length" needs a value diff --git a/tests/warn/invalid-closure.h b/tests/warn/invalid-closure.h index 50ba0864..9769804b 100644 --- a/tests/warn/invalid-closure.h +++ b/tests/warn/invalid-closure.h @@ -5,4 +5,4 @@ */ void test_invalid_closure(int param); -// EXPECT:4: Warning: Test: closure takes at most 1 value, 2 given +// EXPECT:4: Warning: Test: "closure" annotation takes at most one option, 2 given diff --git a/tests/warn/invalid-element-type.h b/tests/warn/invalid-element-type.h index 97b8281d..8028fb71 100644 --- a/tests/warn/invalid-element-type.h +++ b/tests/warn/invalid-element-type.h @@ -8,8 +8,11 @@ void test_invalid_list_element_type(GList *l1, GList *l2); -// EXPECT:5: Warning: Test: element-type annotation needs a value -// EXPECT:5: Warning: Test: element-type takes at least one value, none given +// EXPECT:4: Warning: Test: test_invalid_list_element_type: argument l1: Missing (element-type) annotation +// EXPECT:4: Warning: Test: test_invalid_list_element_type: argument l2: Missing (element-type) annotation +// EXPECT:6: Warning: Test: "element-type" annotation for a list must have exactly one option, not 2 options +// 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 /** * test_invalid_array_element_type: @@ -19,8 +22,7 @@ void test_invalid_list_element_type(GList *l1, GList *l2); void test_invalid_array_element_type(const char *a1, const char *a2); -// EXPECT:16: Warning: Test: element-type annotation needs a value -// EXPECT:16: Warning: Test: element-type takes at least one value, none given +// EXPECT:19: Warning: Test: "element-type" annotation takes at least one option, none given /** * test_invalid_hash_element_type: @@ -31,9 +33,9 @@ void test_invalid_array_element_type(const char *a1, const char *a2); void test_invalid_hash_element_type(GHashTable *h1, GHashTable *h2, GHashTable *h3); -// EXPECT:27: Warning: Test: element-type annotation needs a value -// EXPECT:27: Warning: Test: element-type takes at least one value, none given -// EXPECT:29: Warning: Test: element-type takes at most 2 values, 3 given +// EXPECT:29: Warning: Test: "element-type" annotation takes at least one option, none given +// EXPECT:29: Warning: Test: "element-type" annotation for a hash table must have exactly two options, not 0 option(s) +// EXPECT:31: Warning: Test: "element-type" annotation takes at most 2 options, 3 given /** * test_invalid_bytearray_element_type: @@ -43,8 +45,8 @@ void test_invalid_hash_element_type(GHashTable *h1, GHashTable *h2, GHashTable * void test_invalid_bytearray_element_type(GByteArray *b1, GByteArray *b2); -// EXPECT:40: Warning: Test: element-type annotation needs a value -// EXPECT:40: Warning: Test: element-type takes at least one value, none given +// EXPECT:42: Warning: Test: "element-type" annotation takes at least one option, none given +// EXPECT:42: Warning: Test: "element-type" annotation for an array must have exactly one option, not 0 options /** * test_invalid_ptrarray_element_type: @@ -54,8 +56,8 @@ void test_invalid_bytearray_element_type(GByteArray *b1, GByteArray *b2); void test_invalid_ptrarray_element_type(GPtrArray *p1, GPtrArray *p2); -// EXPECT:51: Warning: Test: element-type annotation needs a value -// EXPECT:51: Warning: Test: element-type takes at least one value, none given +// EXPECT:53: Warning: Test: "element-type" annotation takes at least one option, none given +// EXPECT:53: Warning: Test: "element-type" annotation for an array must have exactly one option, not 0 options /** * test_unresolved_element_type: @@ -74,19 +76,12 @@ GList* test_unresolved_element_type(void); GPtrArray* test_unresolved_value_element_type(void); -// 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:20: Warning: Test: Unknown container Type(target_fundamental=utf8, ctype=char*) for element-type annotation +// EXPECT:19: Warning: Test: Unknown container Type(target_fundamental=utf8, ctype=char*) for element-type annotation // 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 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 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' -// EXPECT:4: Warning: Test: test_invalid_list_element_type: argument l1: Missing (element-type) annotation -// EXPECT:4: Warning: Test: test_invalid_list_element_type: argument l2: Missing (element-type) annotation -// EXPECT:50: Warning: Test: test_invalid_ptrarray_element_type: argument p1: Missing (element-type) annotation +// EXPECT:30: Warning: Test: "element-type" annotation for a hash table must have exactly two options, not 1 option(s) +// EXPECT:31: Warning: Test: "element-type" annotation for a hash table must have exactly two options, not 3 option(s) +// EXPECT:43: Warning: Test: invalid (element-type) for a GByteArray, must be one of guint8, gint8 or gchar +// EXPECT:52: Warning: Test: test_invalid_ptrarray_element_type: argument p1: Missing (element-type) annotation +// EXPECT:54: Warning: Test: invalid (element-type) for a GPtrArray, must be a pointer +// EXPECT:65: Warning: Test: test_unresolved_element_type: Unknown type: 'Unresolved' +// EXPECT:73: Warning: Test: test_unresolved_value_element_type: Unknown type: 'GLib.Value' diff --git a/tests/warn/invalid-out.h b/tests/warn/invalid-out.h index fcb4f70f..7e6ec34f 100644 --- a/tests/warn/invalid-out.h +++ b/tests/warn/invalid-out.h @@ -5,4 +5,4 @@ void test_invalid_out(int *out); -// EXPECT:3: Warning: Test: out annotation value is invalid: 'invalid' +// EXPECT:3: Warning: Test: invalid "out" annotation option: "invalid" diff --git a/tests/warn/invalid-transfer.h b/tests/warn/invalid-transfer.h index 3579ad15..ec43f2ac 100644 --- a/tests/warn/invalid-transfer.h +++ b/tests/warn/invalid-transfer.h @@ -7,6 +7,6 @@ */ void test_transfer_invalid(int param, int param2, int param3); -// EXPECT:4: Warning: Test: transfer annotation needs a value -// EXPECT:5: Warning: Test: invalid transfer annotation value: 'invalid' -// EXPECT:6: Warning: Test: transfer annotation needs one value, not 2 +// EXPECT:4: Warning: Test: "transfer" annotation needs one option, none given +// EXPECT:5: Warning: Test: invalid "transfer" annotation option: "invalid" +// EXPECT:6: Warning: Test: "transfer" annotation needs one option, 2 given |