aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Blake <eblake@redhat.com>2015-11-18 01:52:50 -0700
committerMarkus Armbruster <armbru@redhat.com>2015-12-17 08:21:27 +0100
commit88d4ef8b5cbf9d3336564b1d3ac7a91cbe4aee0e (patch)
tree341b8f7d44ba916ebe75f36ce5d028a6c4458e03
parent61a946611b77b05936c60775eaaef87b65ec9f09 (diff)
qapi: Track owner of each object member
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 Variants.set_owner() method does not set the owner for the tag_member field; this field is set earlier either as part of an object's non-variant members, or explicitly by alternates. 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' (parameter 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(). No change to generated code. Signed-off-by: Eric Blake <eblake@redhat.com> Message-Id: <1447836791-369-16-git-send-email-eblake@redhat.com> [Incorrect & unused -wrapper case in _pretty_owner() dropped] Signed-off-by: Markus Armbruster <armbru@redhat.com>
-rw-r--r--scripts/qapi.py40
1 files changed, 38 insertions, 2 deletions
diff --git a/scripts/qapi.py b/scripts/qapi.py
index 6fc14beaa0..77d3e0a34a 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -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
@@ -1013,6 +1015,8 @@ class QAPISchemaObjectType(QAPISchemaType):
class QAPISchemaObjectTypeMember(object):
+ role = 'member'
+
def __init__(self, name, typ, optional):
assert isinstance(name, str)
assert isinstance(typ, str)
@@ -1021,8 +1025,14 @@ 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):
+ assert self.owner
self.type = schema.lookup_type(self._type_name)
assert self.type
@@ -1031,6 +1041,23 @@ class QAPISchemaObjectTypeMember(object):
assert self.name not in seen
seen[self.name] = self
+ def _pretty_owner(self):
+ owner = self.owner
+ if owner.startswith(':obj-'):
+ # See QAPISchema._make_implicit_object_type() - reverse the
+ # mapping there to create a nice human-readable description
+ owner = owner[5:]
+ if owner.endswith('-arg'):
+ return '(parameter of %s)' % owner[:-4]
+ else:
+ assert owner.endswith('-wrapper')
+ # Unreachable and not implemented
+ assert False
+ 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):
@@ -1047,6 +1074,10 @@ class QAPISchemaObjectTypeVariants(object):
self.tag_member = tag_member
self.variants = variants
+ def set_owner(self, name):
+ for v in self.variants:
+ v.set_owner(name)
+
def check(self, schema, seen):
if not self.tag_member: # flat union
self.tag_member = seen[self.tag_name]
@@ -1066,6 +1097,8 @@ class QAPISchemaObjectTypeVariants(object):
class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
+ role = 'branch'
+
def __init__(self, name, typ):
QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
@@ -1085,6 +1118,8 @@ class QAPISchemaAlternateType(QAPISchemaType):
QAPISchemaType.__init__(self, name, info)
assert isinstance(variants, QAPISchemaObjectTypeVariants)
assert not variants.tag_name
+ variants.set_owner(name)
+ variants.tag_member.set_owner(self.name)
self.variants = variants
def check(self, schema):
@@ -1217,6 +1252,7 @@ class QAPISchema(object):
def _make_implicit_object_type(self, name, info, role, members):
if not members:
return None
+ # See also QAPISchemaObjectTypeMember._pretty_owner()
name = ':obj-%s-%s' % (name, role)
if not self.lookup_entity(name, QAPISchemaObjectType):
self._def_entity(QAPISchemaObjectType(name, info, None,