[gobject-introspection] [scanner] Warn for invalid scanner annotations



commit e2b95cdb39d6e5f287e23dbf30a04031b49a230f
Author: Johan Dahlin <johan gnome org>
Date:   Thu Sep 23 17:13:29 2010 -0300

    [scanner] Warn for invalid scanner annotations
    
    Warn for invalid annotations.
    Change so that custom attributes have to use the annotation
    keyword.

 giscanner/annotationparser.py             |   81 +++++++++++++++++++++++++---
 giscanner/maintransformer.py              |   14 +++---
 tests/scanner/Annotation-1.0-expected.gir |    4 +-
 tests/scanner/annotation.c                |   10 ++--
 tests/warn/Makefile.am                    |    1 +
 tests/warn/invalid-option.h               |    8 +++
 6 files changed, 95 insertions(+), 23 deletions(-)
---
diff --git a/giscanner/annotationparser.py b/giscanner/annotationparser.py
index aad17e8..937b9be 100644
--- a/giscanner/annotationparser.py
+++ b/giscanner/annotationparser.py
@@ -22,7 +22,7 @@
 
 import re
 
-from .message import Position
+from . import message
 from .odict import odict
 
 # All gtk-doc comments needs to start with this:
@@ -45,7 +45,11 @@ TAG_GET_VALUE_FUNC = 'get value func'
 # Options - annotations for parameters and return values
 OPT_ALLOW_NONE = 'allow-none'
 OPT_ARRAY = 'array'
+OPT_ATTRIBUTE = 'attribute'
+OPT_CLOSURE = 'closure'
+OPT_DESTROY = 'destroy'
 OPT_ELEMENT_TYPE = 'element-type'
+OPT_FOREIGN = 'foreign'
 OPT_IN = 'in'
 OPT_INOUT = 'inout'
 OPT_INOUT_ALT = 'in-out'
@@ -53,10 +57,24 @@ OPT_OUT = 'out'
 OPT_SCOPE = 'scope'
 OPT_TRANSFER = 'transfer'
 OPT_TYPE = 'type'
-OPT_CLOSURE = 'closure'
-OPT_DESTROY = 'destroy'
 OPT_SKIP = 'skip'
-OPT_FOREIGN = 'foreign'
+
+ALL_OPTIONS = [
+    OPT_ALLOW_NONE,
+    OPT_ARRAY,
+    OPT_ATTRIBUTE,
+    OPT_CLOSURE,
+    OPT_DESTROY,
+    OPT_ELEMENT_TYPE,
+    OPT_FOREIGN,
+    OPT_IN,
+    OPT_INOUT,
+    OPT_INOUT_ALT,
+    OPT_OUT,
+    OPT_SCOPE,
+    OPT_TRANSFER,
+    OPT_TYPE,
+    OPT_SKIP]
 
 # Array options - array specific annotations
 OPT_ARRAY_FIXED_SIZE = 'fixed-size'
@@ -71,11 +89,12 @@ class DocBlock(object):
 
     def __init__(self, name):
         self.name = name
-        self.options = {}
+        self.options = DocOptions()
         self.value = None
         self.tags = odict()
         self.comment = None
         self.params = []
+        self.position = None
 
     def __repr__(self):
         return '<DocBlock %r %r>' % (self.name, self.options)
@@ -83,19 +102,62 @@ class DocBlock(object):
     def get(self, name):
         return self.tags.get(name)
 
+    def validate(self):
+        for tag in self.tags.values():
+            tag.validate()
+
 
 class DocTag(object):
 
     def __init__(self, block, name):
         self.block = block
         self.name = name
-        self.options = {}
+        self.options = DocOptions()
         self.comment = None
         self.value = ''
+        self.position = None
 
     def __repr__(self):
         return '<DocTag %r %r>' % (self.name, self.options)
 
+    def validate(self):
+        for option in self.options:
+            if not option in ALL_OPTIONS:
+                message.warn('invalid option: %s' % (option, ),
+                             positions=self.position)
+
+
+class DocOptions(object):
+    def __init__(self):
+        self.values = []
+
+    def __getitem__(self, item):
+        for key, value in self.values:
+            if key == item:
+                return value
+        raise KeyError
+
+    def __iter__(self):
+        return (k for k, v in self.values)
+
+    def add(self, name, value):
+        self.values.append((name, value))
+
+    def get(self, item, default=None):
+        for key, value in self.values:
+            if key == item:
+                return value
+        return default
+
+    def getall(self, item):
+        for key, value in self.values:
+            if key == item:
+                yield value
+
+    def iteritems(self):
+        return iter(self.values)
+
+
 class DocOption(object):
 
     def __init__(self, tag, option):
@@ -176,7 +238,7 @@ class AnnotationParser(object):
         if cpos:
             block_name = block_name[:cpos]
         block = DocBlock(block_name)
-        block.position = Position(filename, lineno)
+        block.position = message.Position(filename, lineno)
         if cpos:
             block.options = self.parse_options(block, block_header[cpos+2:])
         comment_lines = []
@@ -271,6 +333,7 @@ class AnnotationParser(object):
                 comment_lines.append(line)
             lineno += 1
         block.comment = '\n'.join(comment_lines)
+        block.validate()
         self._blocks[block.name] = block
 
     @classmethod
@@ -278,7 +341,7 @@ class AnnotationParser(object):
         # (foo)
         # (bar opt1 opt2...)
         opened = -1
-        options = {}
+        options = DocOptions()
         last = None
         for i, c in enumerate(value):
             if c == '(' and opened == -1:
@@ -295,7 +358,7 @@ class AnnotationParser(object):
                     raise AssertionError
                 if option is not None:
                     option = DocOption(tag, option)
-                options[name] = option
+                options.add(name, option)
                 last = i + 2
                 opened = -1
 
diff --git a/giscanner/maintransformer.py b/giscanner/maintransformer.py
index a50951a..605c5d3 100644
--- a/giscanner/maintransformer.py
+++ b/giscanner/maintransformer.py
@@ -25,8 +25,8 @@ from .annotationparser import (TAG_VFUNC, TAG_SINCE, TAG_DEPRECATED, TAG_RETURNS
                                TAG_ATTRIBUTES, TAG_RENAME_TO, TAG_TYPE, TAG_TRANSFER,
                                TAG_UNREF_FUNC, TAG_REF_FUNC, TAG_SET_VALUE_FUNC,
                                TAG_GET_VALUE_FUNC)
-from .annotationparser import (OPT_ALLOW_NONE,
-                               OPT_ARRAY, OPT_ELEMENT_TYPE, OPT_IN, OPT_INOUT,
+from .annotationparser import (OPT_ALLOW_NONE, OPT_ARRAY, OPT_ATTRIBUTE,
+                               OPT_ELEMENT_TYPE, OPT_IN, OPT_INOUT,
                                OPT_INOUT_ALT, OPT_OUT, OPT_SCOPE,
                                OPT_TYPE, OPT_CLOSURE, OPT_DESTROY, OPT_SKIP,
                                OPT_FOREIGN, OPT_ARRAY_FIXED_SIZE,
@@ -114,6 +114,8 @@ usage is void (*_gtk_reserved1)(void);"""
                                  ast.Record, ast.Union)):
             return True
         for field in node.fields:
+            if field is None:
+                continue
             if (field.name.startswith('_')
                 and field.anonymous_node is not None
                 and isinstance(field.anonymous_node, ast.Callback)):
@@ -506,11 +508,9 @@ usage is void (*_gtk_reserved1)(void);"""
         if tag is not None and tag.comment is not None:
             node.doc = tag.comment
 
-        for key in options:
-            if '.' in key:
-                value = options.get(key)
-                if value:
-                    node.attributes.append((key, value.one()))
+        if options:
+            for attribute in options.getall(OPT_ATTRIBUTE):
+                node.attributes.append(attribute.flat())
 
     def _apply_annotations_annotated(self, node, block):
         if block is None:
diff --git a/tests/scanner/Annotation-1.0-expected.gir b/tests/scanner/Annotation-1.0-expected.gir
index 6d72dd4..f922d58 100644
--- a/tests/scanner/Annotation-1.0-expected.gir
+++ b/tests/scanner/Annotation-1.0-expected.gir
@@ -609,8 +609,8 @@ it says it's pointer but it's actually a string.</doc>
     </record>
     <function name="attribute_func" c:identifier="annotation_attribute_func">
       <return-value transfer-ownership="none">
-        <attribute name="yet.another.annotation" value="another_value"/>
         <attribute name="some.other.annotation" value="value2"/>
+        <attribute name="yet.another.annotation" value="another_value"/>
         <doc xml:whitespace="preserve">The return value.</doc>
         <type name="gint" c:type="gint"/>
       </return-value>
@@ -620,8 +620,8 @@ it says it's pointer but it's actually a string.</doc>
           <type name="Object" c:type="AnnotationObject*"/>
         </parameter>
         <parameter name="data" transfer-ownership="none">
-          <attribute name="another.annotation" value="blahvalue"/>
           <attribute name="some.annotation" value="value"/>
+          <attribute name="another.annotation" value="blahvalue"/>
           <doc xml:whitespace="preserve">Some data.</doc>
           <type name="utf8" c:type="gchar*"/>
         </parameter>
diff --git a/tests/scanner/annotation.c b/tests/scanner/annotation.c
index 40df086..e2e0991 100644
--- a/tests/scanner/annotation.c
+++ b/tests/scanner/annotation.c
@@ -122,12 +122,12 @@ annotation_object_class_init (AnnotationObjectClass *klass)
   /**
    * AnnotationObject::attribute-signal:
    * @annotation: the annotation object
-   * @arg1: (some.annotation.foo1 val1): a value
-   * @arg2: (some.annotation.foo2 val2): another value
+   * @arg1: (attribute some.annotation.foo1 val1): a value
+   * @arg2: (attribute some.annotation.foo2 val2): another value
    *
    * This signal tests a signal with attributes.
    *
-   * Returns: (some.annotation.foo3 val3): the return value
+   * Returns: (attribute some.annotation.foo3 val3): the return value
    */
   annotation_object_signals[ATTRIBUTE_SIGNAL] =
     g_signal_new ("attribute-signal",
@@ -734,9 +734,9 @@ annotation_ptr_array (GPtrArray *array)
 /**
  * annotation_attribute_func:
  * @object: A #AnnotationObject.
- * @data: (some.annotation value) (another.annotation blahvalue): Some data.
+ * @data: (attribute some.annotation value) (attribute another.annotation blahvalue): Some data.
  *
- * Returns: (some.other.annotation value2) (yet.another.annotation another_value): The return value.
+ * Returns: (attribute some.other.annotation value2) (attribute yet.another.annotation another_value): The return value.
  */
 gint
 annotation_attribute_func (AnnotationObject *object,
diff --git a/tests/warn/Makefile.am b/tests/warn/Makefile.am
index 81c7442..44515d7 100644
--- a/tests/warn/Makefile.am
+++ b/tests/warn/Makefile.am
@@ -4,6 +4,7 @@ TESTS = \
 	callback-invalid-scope.h \
 	callback-missing-scope.h \
 	return-gobject.h \
+	invalid-option.h \
 	unknown-parameter.h \
 	unresolved-element-type.h \
 	unresolved-type.h
diff --git a/tests/warn/invalid-option.h b/tests/warn/invalid-option.h
new file mode 100644
index 0000000..22483bb
--- /dev/null
+++ b/tests/warn/invalid-option.h
@@ -0,0 +1,8 @@
+
+/**
+ * func:
+ * @param: (invalid-annotation-option):
+ */
+void test_func(int param);
+
+// EXPECT:4: Warning: Test: invalid option: invalid-annotation-option



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]