@@ -957,8 +957,10 @@ class QAPISchemaObjectType(QAPISchemaType):
assert base is None or isinstance(base, str)
for m in local_members:
assert isinstance(m, QAPISchemaObjectTypeMember)
- assert (variants is None or
- isinstance(variants, QAPISchemaObjectTypeVariants))
+ m.set_owner(name)
+ if variants is not None:
+ assert isinstance(variants, QAPISchemaObjectTypeVariants)
+ variants.set_owner(name)
self._base_name = base
self.base = None
self.local_members = local_members
@@ -1011,6 +1013,8 @@ class QAPISchemaObjectType(QAPISchemaType):
class QAPISchemaObjectTypeMember(object):
+ role = 'member'
+
def __init__(self, name, typ, optional):
assert isinstance(name, str)
assert isinstance(typ, str)
@@ -1019,11 +1023,17 @@ class QAPISchemaObjectTypeMember(object):
self._type_name = typ
self.type = None
self.optional = optional
+ self.owner = None
+
+ def set_owner(self, name):
+ assert not self.owner
+ self.owner = name
def check(self, schema, seen):
# seen is a map of names we must not collide with (either QMP
# names, when called by ObjectType, or case names, when called
# by Variant). This method is safe to call over multiple 'seen'.
+ assert self.owner
assert self.name not in seen
self.type = schema.lookup_type(self._type_name)
assert self.type
@@ -1032,6 +1042,25 @@ class QAPISchemaObjectTypeMember(object):
def c_type(self):
return self.type.c_type()
+ def _pretty_owner(self):
+ # See QAPISchema._make_implicit_object_type() - reverse the
+ # mapping there to create a nice human-readable description
+ owner = self.owner
+ if owner.startswith(':obj-'):
+ owner = owner[5:]
+ if owner.endswith('-arg'):
+ return '(argument of %s)' % owner[:-4]
+ elif owner.endswith('-data'):
+ return '(data of %s)' % owner[:-5]
+ else:
+ assert owner.endswith('-wrapper')
+ return '(branch of %s)' % owner[:-8]
+ else:
+ return '(%s of %s)' % (self.role, owner)
+
+ def describe(self):
+ return "'%s' %s" % (self.name, self._pretty_owner())
+
class QAPISchemaObjectTypeVariants(object):
def __init__(self, tag_name, tag_member, variants):
@@ -1049,6 +1078,12 @@ class QAPISchemaObjectTypeVariants(object):
self.tag_member = tag_member
self.variants = variants
+ def set_owner(self, name):
+ if isinstance(self.tag_member, QAPISchemaAlternateTypeTag):
+ self.tag_member.set_owner(name)
+ for v in self.variants:
+ v.set_owner(name)
+
def check(self, schema, seen):
if self.tag_name: # flat union
self.tag_member = seen[self.tag_name]
@@ -1067,6 +1102,8 @@ class QAPISchemaObjectTypeVariants(object):
class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
+ role = 'branch'
+
def __init__(self, name, typ):
QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
@@ -1094,6 +1131,7 @@ class QAPISchemaAlternateType(QAPISchemaType):
QAPISchemaType.__init__(self, name, info)
assert isinstance(variants, QAPISchemaObjectTypeVariants)
assert not variants.tag_name
+ variants.set_owner(name)
self.variants = variants
def check(self, schema):
@@ -1111,6 +1149,7 @@ class QAPISchemaAlternateTypeTag(QAPISchemaObjectTypeMember):
QAPISchemaObjectTypeMember.__init__(self, 'type', '', False)
def check(self, schema, seen):
+ assert self.owner
assert len(seen) == 0
seen[self.name] = self
@@ -1235,6 +1274,7 @@ class QAPISchema(object):
def _make_implicit_object_type(self, name, info, role, members):
if not members:
return None
+ # See also QAPISchemaObjectTypeMember.describe()
name = ':obj-%s-%s' % (name, role)
if not self.lookup_entity(name, QAPISchemaObjectType):
self._def_entity(QAPISchemaObjectType(name, info, None,
@@ -1338,7 +1378,7 @@ class QAPISchema(object):
data = expr.get('data')
if isinstance(data, OrderedDict):
data = self._make_implicit_object_type(
- name, info, 'arg', self._make_members(data, info))
+ name, info, 'data', self._make_members(data, info))
self._def_entity(QAPISchemaEvent(name, info, data))
def _def_exprs(self):
@@ -1,9 +1,9 @@
object :empty
-object :obj-EVENT_C-arg
+object :obj-EVENT_C-data
member a: int optional=True
member b: UserDefOne optional=True
member c: str optional=False
-object :obj-EVENT_D-arg
+object :obj-EVENT_D-data
member a: EventStructOne optional=False
member b: str optional=False
member c: str optional=True
@@ -82,8 +82,8 @@ alternate AltStrNum
case n: number
event EVENT_A None
event EVENT_B None
-event EVENT_C :obj-EVENT_C-arg
-event EVENT_D :obj-EVENT_D-arg
+event EVENT_C :obj-EVENT_C-data
+event EVENT_D :obj-EVENT_D-data
object Empty1
object Empty2
base Empty1
Future commits will migrate semantic checking away from parsing and over to the various QAPISchema*.check() methods. But to report an error message about an incorrect semantic use of a member of an object type, it helps to know which type, command, or event owns the member. In particular, when a member is inherited from a base type, it is desirable to associate the member name with the base type (and not the type calling member.check()). Rather than packing additional information into the seen array passed to each member.check() (as in seen[m.name] = {'member':m, 'owner':type}), it is easier to have each member track the name of the owner type in the first place (keeping things simpler with the existing seen[m.name] = m). The new member.owner field is set via a new set_owner() method, called when registering the members and variants arrays with an object or variant type. Track only a name, and not the actual type object, to avoid creating a circular python reference chain. Note that the set_owner() method for variants has to know whether the tag_member field is owned elsewhere (by the base of flat unions, and the local_members of simple unions) or must be set directly (for alternates); it decides this based on the subclass of the tag_member field. The source information is intended for human consumption in error messages, and a new describe() method is added to access the resulting information. For example, given the qapi: { 'command': 'foo', 'data': { 'string': 'str' } } an implementation of visit_command() that calls arg_type.members[0].describe() will see "'string' (argument of foo)". To make the human-readable name of implicit types work without duplicating efforts, the describe() method has to reverse the name of implicit types, via the helper _pretty_owner(), plus a tweak to report event data separately from command arguments. No change to generated code. Signed-off-by: Eric Blake <eblake@redhat.com> --- v9 (now in subset D): rebase to earlier changes, hoist 'role' to top of class, split out _pretty_helper(), manage owner when tag_member appears as part of local_members for unions v8: don't munge implicit type names [except for event data], and instead make describe() create nicer messages. Add set_owner(), and use field 'role' instead of method _describe() v7: total rewrite: rework implicit object names, assign owner when initializing owner type rather than when creating member python object v6: rebase on new lazy array creation and simple union 'type' motion; tweak commit message --- scripts/qapi.py | 46 +++++++++++++++++++++++++++++++--- tests/qapi-schema/qapi-schema-test.out | 8 +++--- 2 files changed, 47 insertions(+), 7 deletions(-)