A future patch will move some error checking from the parser to the various QAPISchema*.check() methods, which run only after parsing completes. It will thus be possible to create a python instance representing an implicit QAPI type that parses fine but will fail validation during check(). Since all errors have to have an associated 'info' location, we need a location to be associated with those implicit types. The intuitive info to use is the location of the enclosing entity that caused the creation of the implicit type.
Note that we do not anticipate builtin types being used in an error message (as they are not part of the user's QAPI input, the user can't cause a semantic error in their behavior), so we exempt those types from requiring info, by setting a flag to track the completion of _def_predefineds(), and tracking that flag in _def_entity(). No change to the generated code. Signed-off-by: Eric Blake <ebl...@redhat.com> --- v7: better commit message and comments, fix info assertion to use instance flag rather than ugly leaky abstraction static flag v6: improve commit message, track implicit enum info, rebase on new lazy array handling --- scripts/qapi.py | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/scripts/qapi.py b/scripts/qapi.py index f5040da..e49f72b 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -795,9 +795,9 @@ class QAPISchemaEntity(object): self.name = name # For explicitly defined entities, info points to the (explicit) # definition. For builtins (and their arrays), info is None. - # TODO For other implicitly defined entities, it should point to - # a place that triggers implicit definition; there may be more - # than one such place. + # For other implicitly defined entities, it points to a place + # that triggers implicit definition; there may be more than one + # such place. self.info = info def c_name(self): @@ -1153,7 +1153,9 @@ class QAPISchema(object): try: self.exprs = check_exprs(QAPISchemaParser(open(fname, "r")).exprs) self._entity_dict = {} + self._predefined_done = False self._def_predefineds() + self._predefined_done = True self._def_exprs() self.check() except (QAPISchemaError, QAPIExprError), err: @@ -1161,6 +1163,9 @@ class QAPISchema(object): exit(1) def _def_entity(self, ent): + if self._predefined_done: + # Only the predefined types are allowed to not have info + assert ent.info assert ent.name not in self._entity_dict self._entity_dict[ent.name] = ent @@ -1203,9 +1208,9 @@ class QAPISchema(object): [], None) self._def_entity(self.the_empty_object_type) - def _make_implicit_enum_type(self, name, values): + def _make_implicit_enum_type(self, name, info, values): name = name + 'Kind' - self._def_entity(QAPISchemaEnumType(name, None, values, None)) + self._def_entity(QAPISchemaEnumType(name, info, values, None)) return name def _make_array_type(self, element_type, info): @@ -1214,12 +1219,12 @@ class QAPISchema(object): self._def_entity(QAPISchemaArrayType(name, info, element_type)) return name - def _make_implicit_object_type(self, name, role, members): + def _make_implicit_object_type(self, name, info, role, members): if not members: return None name = ':obj-%s-%s' % (name, role) if not self.lookup_entity(name, QAPISchemaObjectType): - self._def_entity(QAPISchemaObjectType(name, None, None, + self._def_entity(QAPISchemaObjectType(name, info, None, members, None)) return name @@ -1259,11 +1264,11 @@ class QAPISchema(object): assert len(typ) == 1 typ = self._make_array_type(typ[0], info) typ = self._make_implicit_object_type( - typ, 'wrapper', [self._make_member('data', typ, info)]) + typ, info, 'wrapper', [self._make_member('data', typ, info)]) return QAPISchemaObjectTypeVariant(case, typ) - def _make_implicit_tag(self, type_name, variants): - typ = self._make_implicit_enum_type(type_name, + def _make_implicit_tag(self, type_name, info, variants): + typ = self._make_implicit_enum_type(type_name, info, [v.name for v in variants]) return QAPISchemaObjectTypeUnionTag('type', typ, False) @@ -1279,7 +1284,7 @@ class QAPISchema(object): else: variants = [self._make_simple_variant(key, value, info) for (key, value) in data.iteritems()] - tag_enum = self._make_implicit_tag(name, variants) + tag_enum = self._make_implicit_tag(name, info, variants) self._def_entity( QAPISchemaObjectType(name, info, base, self._make_members(OrderedDict(), info), @@ -1292,7 +1297,7 @@ class QAPISchema(object): data = expr['data'] variants = [self._make_variant(key, value) for (key, value) in data.iteritems()] - tag_enum = self._make_implicit_tag(name, variants) + tag_enum = self._make_implicit_tag(name, info, variants) self._def_entity( QAPISchemaAlternateType(name, info, QAPISchemaObjectTypeVariants(None, @@ -1307,7 +1312,7 @@ class QAPISchema(object): success_response = expr.get('success-response', True) if isinstance(data, OrderedDict): data = self._make_implicit_object_type( - name, 'arg', self._make_members(data, info)) + name, info, 'arg', self._make_members(data, info)) if isinstance(rets, list): assert len(rets) == 1 rets = self._make_array_type(rets[0], info) @@ -1319,7 +1324,7 @@ class QAPISchema(object): data = expr.get('data') if isinstance(data, OrderedDict): data = self._make_implicit_object_type( - name, 'arg', self._make_members(data, info)) + name, info, 'arg', self._make_members(data, info)) self._def_entity(QAPISchemaEvent(name, info, data)) def _def_exprs(self): -- 2.4.3