Browse files

Split parameter and tag storage in annotationparser parse tree

This avoids tags overwriting parameters if they happen to
share the same name. For example, this was triggered by
valid code in libgnome-keyring.

tests/scanner/regress.c and tests/scanner/regress.h test
written by Colin Walters <walters@verbum.org>.

https://bugzilla.gnome.org/show_bug.cgi?id=672254
  • Loading branch information...
1 parent c93dafe commit 4019e078b3f20872cc11b70ae4686ce5f74c1bb9 @dieterv committed Apr 5, 2012
View
56 giscanner/annotationparser.py
@@ -145,7 +145,7 @@ def __init__(self, name):
self.value = None
self.tags = odict()
self.comment = None
- self.params = []
+ self.params = odict()
self.position = None
def __cmp__(self, other):
@@ -158,9 +158,12 @@ def set_position(self, position):
self.position = position
self.options.position = position
- def get(self, name):
+ def get_tag(self, name):
return self.tags.get(name)
+ def get_param(self, name):
+ return self.params.get(name)
+
def to_gtk_doc(self):
options = ''
if self.options:
@@ -170,19 +173,14 @@ def to_gtk_doc(self):
if 'SECTION' not in self.name:
lines[0] += ':'
lines[0] += options
- tags = []
- for name, tag in self.tags.iteritems():
- if name in self.params:
- lines.append(tag.to_gtk_doc_param())
- else:
- tags.append(tag)
-
+ for param in self.params.values():
+ lines.append(param.to_gtk_doc_param())
lines.append('')
for l in self.comment.split('\n'):
lines.append(l)
- if tags:
+ if self.tags:
lines.append('')
- for tag in tags:
+ for tag in self.tags.values():
lines.append(tag.to_gtk_doc_tag())
comment = ''
@@ -197,6 +195,9 @@ def to_gtk_doc(self):
return comment
def validate(self):
+ for param in self.params.values():
+ param.validate()
+
for tag in self.tags.values():
tag.validate()
@@ -720,7 +721,7 @@ def _parse_comment_block(self, comment_lines, filename, lineno):
message.warn("encountered multiple 'Returns' parameters or tags for "
"'%s'." % (comment_block.name),
position)
- elif param_name in comment_block.tags.keys():
+ elif param_name in comment_block.params.keys():
column = result.start('parameter_name') + column_offset
marker = ' '*column + '^'
message.warn("multiple '@%s' parameters for identifier '%s':\n%s\n%s" %
@@ -732,9 +733,10 @@ def _parse_comment_block(self, comment_lines, filename, lineno):
tag.comment = param_description
if param_annotations:
tag.options = self.parse_options(tag, param_annotations)
- comment_block.tags[param_name] = tag
- if not param_name == TAG_RETURNS:
- comment_block.params.append(param_name)
+ if param_name == TAG_RETURNS:
+ comment_block.tags[param_name] = tag
+ else:
+ comment_block.params[param_name] = tag
current_param = tag
continue
@@ -785,7 +787,7 @@ def _parse_comment_block(self, comment_lines, filename, lineno):
comment_block.tags[TAG_RETURNS] = tag
current_tag = tag
continue
- elif tag_name.lower() in _ALL_TAGS:
+ else:
if tag_name.lower() in comment_block.tags.keys():
column = result.start('tag_name') + column_offset
marker = ' '*column + '^'
@@ -858,20 +860,26 @@ def _parse_comment_block(self, comment_lines, filename, lineno):
comment_block.comment = ''
for tag in comment_block.tags.itervalues():
- if tag.comment:
- tag.comment = tag.comment.strip()
- else:
- tag.comment = None
+ self._clean_comment_block_part(tag)
- if tag.value:
- tag.value = tag.value.strip()
- else:
- tag.value = ''
+ for param in comment_block.params.itervalues():
+ self._clean_comment_block_part(param)
# Validate and store block.
comment_block.validate()
return comment_block
+ def _clean_comment_block_part(self, part):
+ if part.comment:
+ part.comment = part.comment.strip()
+ else:
+ part.comment = None
+
+ if part.value:
+ part.value = part.value.strip()
+ else:
+ part.value = ''
+
def _validate_multiline_annotation_continuation(self, line, original_line,
column_offset, position):
'''
View
2 giscanner/introspectablepass.py
@@ -58,7 +58,7 @@ def _parameter_warning(self, parent, param, text, position=None):
else:
context = "return value: "
if block:
- return_tag = block.get(TAG_RETURNS)
+ return_tag = block.get_tag(TAG_RETURNS)
if return_tag:
position = return_tag.position
message.warn_node(parent, prefix + context + text,
View
40 giscanner/maintransformer.py
@@ -24,7 +24,7 @@
from .annotationparser import (TAG_VFUNC, TAG_SINCE, TAG_DEPRECATED, TAG_RETURNS,
TAG_ATTRIBUTES, TAG_RENAME_TO, TAG_TYPE,
TAG_UNREF_FUNC, TAG_REF_FUNC, TAG_SET_VALUE_FUNC,
- TAG_GET_VALUE_FUNC, TAG_VALUE)
+ TAG_GET_VALUE_FUNC, TAG_VALUE, TAG_TRANSFER)
from .annotationparser import (OPT_ALLOW_NONE, OPT_ARRAY, OPT_ATTRIBUTE,
OPT_ELEMENT_TYPE, OPT_IN, OPT_INOUT,
OPT_INOUT_ALT, OPT_OUT, OPT_SCOPE,
@@ -141,7 +141,7 @@ def _get_validate_parameter_name(self, parent, param_name, origin):
def _apply_annotation_rename_to(self, node, chain, block):
if not block:
return
- rename_to = block.get(TAG_RENAME_TO)
+ rename_to = block.get_tag(TAG_RENAME_TO)
if not rename_to:
return
rename_to = rename_to.value
@@ -231,13 +231,13 @@ def _pass_read_annotations(self, node, chain):
if isinstance(node, ast.Class):
block = self._get_block(node)
if block:
- tag = block.get(TAG_UNREF_FUNC)
+ tag = block.get_tag(TAG_UNREF_FUNC)
node.unref_func = tag.value if tag else None
- tag = block.get(TAG_REF_FUNC)
+ tag = block.get_tag(TAG_REF_FUNC)
node.ref_func = tag.value if tag else None
- tag = block.get(TAG_SET_VALUE_FUNC)
+ tag = block.get_tag(TAG_SET_VALUE_FUNC)
node.set_value_func = tag.value if tag else None
- tag = block.get(TAG_GET_VALUE_FUNC)
+ tag = block.get_tag(TAG_GET_VALUE_FUNC)
node.get_value_func = tag.value if tag else None
if isinstance(node, ast.Constant):
self._apply_annotations_constant(node)
@@ -319,7 +319,7 @@ def _get_position(self, func, param):
block = self._blocks.get(func.symbol)
if block:
if isinstance(param, ast.Parameter):
- tag = block.tags.get(param.argname)
+ tag = block.params.get(param.argname)
elif isinstance(param, ast.Return):
tag = block.tags.get(TAG_RETURNS)
else:
@@ -599,11 +599,11 @@ def _apply_annotations_annotated(self, node, block):
node.doc = block.comment
- since_tag = block.get(TAG_SINCE)
+ since_tag = block.get_tag(TAG_SINCE)
if since_tag is not None:
node.version = since_tag.value
- deprecated_tag = block.get(TAG_DEPRECATED)
+ deprecated_tag = block.get_tag(TAG_DEPRECATED)
if deprecated_tag is not None:
value = deprecated_tag.value
if ': ' in value:
@@ -617,7 +617,7 @@ def _apply_annotations_annotated(self, node, block):
if version is not None:
node.deprecated_version = version
- annos_tag = block.get(TAG_ATTRIBUTES)
+ annos_tag = block.get_tag(TAG_ATTRIBUTES)
if annos_tag is not None:
for key, value in annos_tag.options.iteritems():
if value:
@@ -679,7 +679,7 @@ def _apply_annotations_param(self, parent, param, tag):
def _apply_annotations_return(self, parent, return_, block):
if block:
- tag = block.get(TAG_RETURNS)
+ tag = block.get_tag(TAG_RETURNS)
else:
tag = None
self._apply_annotations_param_ret_common(parent, return_, tag)
@@ -690,7 +690,7 @@ def _apply_annotations_params(self, parent, params, block):
declparams.add(parent.instance_parameter.argname)
for param in params:
if block:
- tag = block.get(param.argname)
+ tag = block.get_param(param.argname)
else:
tag = None
self._apply_annotations_param(parent, param, tag)
@@ -716,7 +716,7 @@ def _apply_annotations_params(self, parent, params, block):
text = ', should be one of %s' % (
', '.join(repr(p) for p in unused), )
- tag = block.get(doc_name)
+ tag = block.get_param(doc_name)
message.warn(
'%s: unknown parameter %r in documentation comment%s' % (
block.name, doc_name, text),
@@ -744,7 +744,7 @@ def _check_arg_annotations(self, parent, params, block):
def _apply_annotations_field(self, parent, block, field):
if not block:
return
- tag = block.get(field.name)
+ tag = block.get_param(field.name)
if not tag:
return
t = tag.options.get(OPT_TYPE)
@@ -762,15 +762,15 @@ def _apply_annotations_property(self, parent, prop):
self._apply_annotations_annotated(prop, block)
if not block:
return
- transfer_tag = block.get(OPT_TRANSFER)
+ transfer_tag = block.get_tag(TAG_TRANSFER)
if transfer_tag is not None:
transfer = transfer_tag.value
if transfer == OPT_TRANSFER_FLOATING:
transfer = OPT_TRANSFER_NONE
prop.transfer = transfer
else:
prop.transfer = self._get_transfer_default(parent, prop)
- type_tag = block.get(TAG_TYPE)
+ type_tag = block.get_tag(TAG_TYPE)
if type_tag:
prop.type = self._resolve_toplevel(type_tag.value, prop.type, prop, parent)
@@ -781,8 +781,8 @@ def _apply_annotations_signal(self, parent, signal):
# We're only attempting to name the signal parameters if
# the number of parameter tags (@foo) is the same or greater
# than the number of signal parameters
- if block and len(block.tags) > len(signal.parameters):
- names = block.tags.items()
+ if block and len(block.params) > len(signal.parameters):
+ names = block.params.items()
# Resolve real parameter names early, so that in later
# phase we can refer to them while resolving annotations.
for i, param in enumerate(signal.parameters):
@@ -806,7 +806,7 @@ def _apply_annotations_constant(self, node):
block = self._blocks.get(node.ctype)
if not block:
return
- tag = block.get(TAG_VALUE)
+ tag = block.get_tag(TAG_VALUE)
if tag:
node.value = tag.value
@@ -824,7 +824,7 @@ def _apply_annotations2_function(self, node, chain):
parent = chain[-1] if chain else None
if not (block and parent):
return
- virtual = block.get(TAG_VFUNC)
+ virtual = block.get_tag(TAG_VFUNC)
if not virtual:
return
invoker_name = virtual.value
View
20 tests/scanner/Regress-1.0-expected.gir
@@ -1528,6 +1528,26 @@ Use with regress_test_obj_emit_sig_with_obj</doc>
</parameter>
</parameters>
</function>
+ <function name="has_parameter_named_attrs"
+ c:identifier="regress_has_parameter_named_attrs">
+ <doc xml:whitespace="preserve">This test case mirrors GnomeKeyringPasswordSchema from
+libgnome-keyring.</doc>
+ <return-value transfer-ownership="none">
+ <type name="none" c:type="void"/>
+ </return-value>
+ <parameters>
+ <parameter name="foo" transfer-ownership="none">
+ <doc xml:whitespace="preserve">some int</doc>
+ <type name="gint" c:type="int"/>
+ </parameter>
+ <parameter name="attributes" transfer-ownership="none">
+ <doc xml:whitespace="preserve">list of attributes</doc>
+ <array zero-terminated="0" c:type="gpointer" fixed-size="32">
+ <type name="guint32" c:type="gpointer"/>
+ </array>
+ </parameter>
+ </parameters>
+ </function>
<function name="introspectable_via_alias"
c:identifier="regress_introspectable_via_alias">
<return-value transfer-ownership="none">
View
14 tests/scanner/regress.c
@@ -3610,3 +3610,17 @@ regress_test_struct_fixed_array_frob (RegressTestStructFixedArray *str)
for (i = 0; i < G_N_ELEMENTS(str->array); i++)
str->array[i] = 42 + i;
}
+
+/**
+ * regress_has_parameter_named_attrs:
+ * @foo: some int
+ * @attributes: (type guint32) (array fixed-size=32): list of attributes
+ *
+ * This test case mirrors GnomeKeyringPasswordSchema from
+ * libgnome-keyring.
+ */
+void
+regress_has_parameter_named_attrs (int foo,
+ gpointer attributes)
+{
+}
View
3 tests/scanner/regress.h
@@ -783,4 +783,7 @@ void regress_test_struct_fixed_array_frob (RegressTestStructFixedArray *str);
"POWERSHARE,PRODIGY,TLX,X400,GIF,CGM,WMF,BMP,MET,PMB,DIB,PICT,TIFF," \
"PDF,PS,JPEG,QTIME,MPEG,MPEG2,AVI,WAVE,AIFF,PCM,X509,PGP"
+void regress_has_parameter_named_attrs (int foo,
+ gpointer attributes);
+
#endif /* __GITESTTYPES_H__ */

0 comments on commit 4019e07

Please sign in to comment.