Commit a2b22ce7 authored by Dieter Verfaillie's avatar Dieter Verfaillie

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
parent 839e4f10
This diff is collapsed.
......@@ -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
......
......@@ -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 \
......
......@@ -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,
......
......@@ -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;
......
<?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>