Commit ff55d72e authored by Eric Blake's avatar Eric Blake Committed by Markus Armbruster
Browse files

qapi: Check for member name conflicts with a base class



Our type inheritance for both 'struct' and for flat 'union' merges
key/value pairs from the base class with those from the type in
question.  Although the C code currently boxes things so that there
is a distinction between which member is referred to, the QMP wire
format does not allow passing a key more than once in a single
object.  Besides, if we ever change the generated C code to not be
quite so boxy, we'd want to avoid duplicate member names there,
too.

Fix a testsuite entry added in an earlier patch, as well as adding
a couple more tests to ensure we have appropriate coverage.  Ensure
that collisions are detected, regardless of whether there is a
difference in opinion on whether the member name is optional.

Signed-off-by: default avatarEric Blake <eblake@redhat.com>
Reviewed-by: default avatarMarkus Armbruster <armbru@redhat.com>
Signed-off-by: default avatarMarkus Armbruster <armbru@redhat.com>
parent a7f5966b
Loading
Loading
Loading
Loading
+22 −1
Original line number Diff line number Diff line
@@ -414,6 +414,20 @@ def check_type(expr_info, source, value, allow_array = False,
                   allow_metas=['built-in', 'union', 'alternate', 'struct',
                                'enum'])

def check_member_clash(expr_info, base_name, data, source = ""):
    base = find_struct(base_name)
    assert base
    base_members = base['data']
    for key in data.keys():
        if key.startswith('*'):
            key = key[1:]
        if key in base_members or "*" + key in base_members:
            raise QAPIExprError(expr_info,
                                "Member name '%s'%s clashes with base '%s'"
                                % (key, source, base_name))
    if base.get('base'):
        check_member_clash(expr_info, base['base'], data, source)

def check_command(expr, expr_info):
    name = expr['command']
    allow_star = expr.has_key('gen')
@@ -503,9 +517,14 @@ def check_union(expr, expr_info):
        check_name(expr_info, "Member of union '%s'" % name, key)

        # Each value must name a known type; furthermore, in flat unions,
        # branches must be a struct
        # branches must be a struct with no overlapping member names
        check_type(expr_info, "Member '%s' of union '%s'" % (key, name),
                   value, allow_array=True, allow_metas=allow_metas)
        if base:
            branch_struct = find_struct(value)
            assert branch_struct
            check_member_clash(expr_info, base, branch_struct['data'],
                               " of branch '%s'" % key)

        # If the discriminator names an enum type, then all members
        # of 'data' must also be members of the enum type.
@@ -582,6 +601,8 @@ def check_struct(expr, expr_info):
               allow_dict=True, allow_optional=True)
    check_type(expr_info, "'base' for struct '%s'" % name, expr.get('base'),
               allow_metas=['struct'])
    if expr.get('base'):
        check_member_clash(expr_info, expr['base'], expr['data'])

def check_exprs(schema):
    for expr_elem in schema.exprs:
+2 −1
Original line number Diff line number Diff line
@@ -243,7 +243,8 @@ check-qapi-schema-y := $(addprefix tests/qapi-schema/, \
	include-simple.json include-relpath.json include-format-err.json \
	include-non-file.json include-no-file.json include-before-err.json \
	include-nested-err.json include-self-cycle.json include-cycle.json \
	include-repetition.json event-nest-struct.json event-case.json)
	include-repetition.json event-nest-struct.json event-case.json \
	struct-base-clash.json struct-base-clash-deep.json )

GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h \
		     tests/test-qmp-commands.h tests/test-qapi-event.h
+1 −0
Original line number Diff line number Diff line
tests/qapi-schema/flat-union-branch-clash.json:10: Member name 'name' of branch 'value1' clashes with base 'Base'
+1 −1
Original line number Diff line number Diff line
0
1
+2 −2
Original line number Diff line number Diff line
# FIXME: we should check for no duplicate keys between branches and base
# we check for no duplicate keys between branches and base
{ 'enum': 'TestEnum',
  'data': [ 'value1', 'value2' ] }
{ 'struct': 'Base',
  'data': { 'enum1': 'TestEnum', 'name': 'str' } }
  'data': { 'enum1': 'TestEnum', '*name': 'str' } }
{ 'struct': 'Branch1',
  'data': { 'name': 'str' } }
{ 'struct': 'Branch2',
Loading