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>
This commit is contained in:
parent
14ff84619c
commit
b807a1e1e3
|
@ -992,6 +992,7 @@ class QAPISchemaObjectType(QAPISchemaType):
|
||||||
if self.variants:
|
if self.variants:
|
||||||
self.variants.check(schema, seen)
|
self.variants.check(schema, seen)
|
||||||
assert self.variants.tag_member in self.members
|
assert self.variants.tag_member in self.members
|
||||||
|
self.variants.check_clash(schema, seen)
|
||||||
|
|
||||||
def is_implicit(self):
|
def is_implicit(self):
|
||||||
# See QAPISchema._make_implicit_object_type()
|
# See QAPISchema._make_implicit_object_type()
|
||||||
|
@ -1056,6 +1057,18 @@ class QAPISchemaObjectTypeVariants(object):
|
||||||
assert isinstance(self.tag_member.type, QAPISchemaEnumType)
|
assert isinstance(self.tag_member.type, QAPISchemaEnumType)
|
||||||
for v in self.variants:
|
for v in self.variants:
|
||||||
v.check(schema, self.tag_member.type)
|
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):
|
class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
|
||||||
|
@ -1086,6 +1099,8 @@ class QAPISchemaAlternateType(QAPISchemaType):
|
||||||
|
|
||||||
def check(self, schema):
|
def check(self, schema):
|
||||||
self.variants.tag_member.check(schema)
|
self.variants.tag_member.check(schema)
|
||||||
|
# Not calling self.variants.check_clash(), because there's nothing
|
||||||
|
# to clash with
|
||||||
self.variants.check(schema, {})
|
self.variants.check(schema, {})
|
||||||
|
|
||||||
def json_type(self):
|
def json_type(self):
|
||||||
|
|
Loading…
Reference in a new issue