summaryrefslogtreecommitdiff
path: root/scripts/qapi.py
diff options
context:
space:
mode:
authorEric Blake <eblake@redhat.com>2015-11-18 01:52:46 -0700
committerMarkus Armbruster <armbru@redhat.com>2015-12-17 08:21:26 +0100
commitb807a1e1e3796adaf3ece2f7b69ea5ee28468ff4 (patch)
tree12de63c62e22824e86aa3a4a6d3d103d29172760 /scripts/qapi.py
parent14ff84619c6bb9b729dbf8b127c1e4c56ed8c500 (diff)
downloadqemu-b807a1e1e3796adaf3ece2f7b69ea5ee28468ff4.tar.gz
qapi: Check for QAPI collisions involving variant members
Right now, our ad hoc parser ensures that we cannot have a flat union that introduces any members that would clash with non-variant members inherited from the union's base type (see flat-union-clash-member.json). We want QAPISchemaObjectType.check() to make the same check, so we can later reduce some of the ad hoc checks. We already have a map 'seen' of all non-variant members. We still need to check for collisions between each variant type's members and the non-variant ones. To know the variant type's members, we need to call variant.type.check(). This also detects when a type contains itself in a variant, exactly like the existing base.check() detects when a type contains itself as a base. (Except that we currently forbid anything but a struct as the type of a variant, so we can't actually trigger this type of loop yet.) Slight complication: an alternate's variant can have arbitrary type, but only an object type's check() may be called outside QAPISchema.check(). We could either skip the call for variants of alternates, or skip it for non-object types. For now, do the latter, because it's easier. Then we call each variant member's check_clash() with the appropriate 'seen' map. Since members of different variants can't clash, we have to clone a fresh seen for each variant. Wrap this in a new helper method QAPISchemaObjectTypeVariants.check_clash(). Note that cloning 'seen' inside .check_clash() resembles the one we just removed from .check() in 'qapi: Drop obsolete tag value collision assertions'; the difference here is that we are now checking for clashes among the qapi members of the variant type, rather than for a single clash with the variant tag name itself. Note that, by construction, collisions can't actually happen for simple unions: each variant's type is a wrapper with a single member 'data', which will never collide with the only non-variant member 'type'. For alternates, there's nothing for a variant object type's members to clash with, and therefore no need to call the new variants.check_clash(). No change to generated code. Signed-off-by: Eric Blake <eblake@redhat.com> Message-Id: <1447836791-369-12-git-send-email-eblake@redhat.com> [Commit message tweaked] Signed-off-by: Markus Armbruster <armbru@redhat.com>
Diffstat (limited to 'scripts/qapi.py')
-rw-r--r--scripts/qapi.py15
1 files changed, 15 insertions, 0 deletions
diff --git a/scripts/qapi.py b/scripts/qapi.py
index c6cb17b14a..b2d071f92d 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -992,6 +992,7 @@ class QAPISchemaObjectType(QAPISchemaType):
if self.variants:
self.variants.check(schema, seen)
assert self.variants.tag_member in self.members
+ self.variants.check_clash(schema, seen)
def is_implicit(self):
# See QAPISchema._make_implicit_object_type()
@@ -1056,6 +1057,18 @@ class QAPISchemaObjectTypeVariants(object):
assert isinstance(self.tag_member.type, QAPISchemaEnumType)
for v in self.variants:
v.check(schema, self.tag_member.type)
+ if isinstance(v.type, QAPISchemaObjectType):
+ v.type.check(schema)
+
+ def check_clash(self, schema, seen):
+ for v in self.variants:
+ # Reset seen map for each variant, since qapi names from one
+ # branch do not affect another branch
+ vseen = dict(seen)
+ assert isinstance(v.type, QAPISchemaObjectType)
+ assert not v.type.variants # not implemented
+ for m in v.type.members:
+ m.check_clash(vseen)
class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
@@ -1086,6 +1099,8 @@ class QAPISchemaAlternateType(QAPISchemaType):
def check(self, schema):
self.variants.tag_member.check(schema)
+ # Not calling self.variants.check_clash(), because there's nothing
+ # to clash with
self.variants.check(schema, {})
def json_type(self):