Skip to content

Commit

Permalink
qapi: Track owner of each object member
Browse files Browse the repository at this point in the history
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 <[email protected]>
Message-Id: <[email protected]>
[Incorrect & unused -wrapper case in _pretty_owner() dropped]
Signed-off-by: Markus Armbruster <[email protected]>
  • Loading branch information
ebblake authored and Markus Armbruster committed Dec 17, 2015
1 parent 61a9466 commit 88d4ef8
Showing 1 changed file with 38 additions and 2 deletions.
40 changes: 38 additions & 2 deletions scripts/qapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -957,8 +957,10 @@ def __init__(self, name, info, base, local_members, variants):
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
Expand Down Expand Up @@ -1013,6 +1015,8 @@ def visit(self, visitor):


class QAPISchemaObjectTypeMember(object):
role = 'member'

def __init__(self, name, typ, optional):
assert isinstance(name, str)
assert isinstance(typ, str)
Expand All @@ -1021,8 +1025,14 @@ def __init__(self, name, typ, optional):
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

Expand All @@ -1031,6 +1041,23 @@ def check_clash(self, seen):
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):
Expand All @@ -1047,6 +1074,10 @@ def __init__(self, tag_name, tag_member, variants):
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]
Expand All @@ -1066,6 +1097,8 @@ def check_clash(self, schema, seen):


class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
role = 'branch'

def __init__(self, name, typ):
QAPISchemaObjectTypeMember.__init__(self, name, typ, False)

Expand All @@ -1085,6 +1118,8 @@ def __init__(self, name, info, variants):
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):
Expand Down Expand Up @@ -1217,6 +1252,7 @@ def _make_array_type(self, element_type, info):
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,
Expand Down

0 comments on commit 88d4ef8

Please sign in to comment.