A future patch will enable deferred error detection in the various QAPISchema*.check() methods (rather than the current ad hoc parse checks). But that means the user can request a QAPI entity that will only fail validation after it has been initialized. Since all errors have to have an associated 'info' location, we need a location to be associated with all user-triggered 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(). No change to the generated code. RFC: I used a class-level static flag to track whether we expected 'info is None' when creating a QAPISchemaEntity. This is gross, because the flag will only be set on the first QAPISchema() instance (it works because none of our client scripts ever instantiate more than one schema). But the only other thing I could think of would be passing the QAPISchema instance into the constructor for each QAPISchemaEntity, which is a lot of churn. Any better ideas on how best to do the assertion, or should I just drop it? Signed-off-by: Eric Blake <[email protected]> --- v6: improve commit message, track implicit enum info, rebase on new lazy array handling --- scripts/qapi.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/scripts/qapi.py b/scripts/qapi.py index 255001a..19cca97 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -792,6 +792,7 @@ class QAPISchemaEntity(object): def __init__(self, name, info): assert isinstance(name, str) self.name = name + assert info or not QAPISchema.predefined_initialized self.info = info def c_name(self): @@ -1114,6 +1115,8 @@ class QAPISchemaEvent(QAPISchemaEntity): class QAPISchema(object): + predefined_initialized = False + def __init__(self, fname): try: self.exprs = check_exprs(QAPISchemaParser(open(fname, "r")).exprs) @@ -1122,6 +1125,7 @@ class QAPISchema(object): exit(1) self._entity_dict = {} self._def_predefineds() + QAPISchema.predefined_initialized = True self._def_exprs() self.check() @@ -1163,9 +1167,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): @@ -1174,12 +1178,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 @@ -1218,13 +1222,13 @@ class QAPISchema(object): if isinstance(typ, list): assert len(typ) == 1 typ = self._make_array_type(typ[0], info) - typ = self._make_implicit_object_type(typ, 'wrapper', + typ = self._make_implicit_object_type(typ, info, 'wrapper', [self._make_member('data', typ, info)]) return QAPISchemaObjectTypeVariant(case, typ) - def _make_tag_enum(self, type_name, variants): - typ = self._make_implicit_enum_type(type_name, + def _make_tag_enum(self, type_name, info, variants): + typ = self._make_implicit_enum_type(type_name, info, [v.name for v in variants]) return QAPISchemaObjectTypeMember('type', typ, False) @@ -1240,7 +1244,7 @@ class QAPISchema(object): else: variants = [self._make_simple_variant(key, value, info) for (key, value) in data.iteritems()] - tag_enum = self._make_tag_enum(name, variants) + tag_enum = self._make_tag_enum(name, info, variants) self._def_entity( QAPISchemaObjectType(name, info, base, self._make_members(OrderedDict(), info), @@ -1253,7 +1257,7 @@ class QAPISchema(object): data = expr['data'] variants = [self._make_variant(key, value) for (key, value) in data.iteritems()] - tag_enum = self._make_tag_enum(name, variants) + tag_enum = self._make_tag_enum(name, info, variants) self._def_entity( QAPISchemaAlternateType(name, info, QAPISchemaObjectTypeVariants(None, @@ -1267,7 +1271,7 @@ class QAPISchema(object): gen = expr.get('gen', True) success_response = expr.get('success-response', True) if isinstance(data, OrderedDict): - data = self._make_implicit_object_type(name, 'arg', + data = self._make_implicit_object_type(name, info, 'arg', self._make_members(data, info)) if isinstance(rets, list): @@ -1280,7 +1284,7 @@ class QAPISchema(object): name = expr['event'] data = expr.get('data') if isinstance(data, OrderedDict): - data = self._make_implicit_object_type(name, 'arg', + data = self._make_implicit_object_type(name, info, 'arg', self._make_members(data, info)) self._def_entity(QAPISchemaEvent(name, info, data)) -- 2.4.3
