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: Eric Blake <eblake@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com>
This commit is contained in:
parent
a7f5966b29
commit
ff55d72eaf
@ -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:
|
||||
|
@ -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
|
||||
|
@ -0,0 +1 @@
|
||||
tests/qapi-schema/flat-union-branch-clash.json:10: Member name 'name' of branch 'value1' clashes with base 'Base'
|
@ -1 +1 @@
|
||||
0
|
||||
1
|
||||
|
@ -1,8 +1,8 @@
|
||||
# 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',
|
||||
|
@ -1,9 +0,0 @@
|
||||
[OrderedDict([('enum', 'TestEnum'), ('data', ['value1', 'value2'])]),
|
||||
OrderedDict([('struct', 'Base'), ('data', OrderedDict([('enum1', 'TestEnum'), ('name', 'str')]))]),
|
||||
OrderedDict([('struct', 'Branch1'), ('data', OrderedDict([('name', 'str')]))]),
|
||||
OrderedDict([('struct', 'Branch2'), ('data', OrderedDict([('value', 'int')]))]),
|
||||
OrderedDict([('union', 'TestUnion'), ('base', 'Base'), ('discriminator', 'enum1'), ('data', OrderedDict([('value1', 'Branch1'), ('value2', 'Branch2')]))])]
|
||||
[{'enum_name': 'TestEnum', 'enum_values': ['value1', 'value2']}]
|
||||
[OrderedDict([('struct', 'Base'), ('data', OrderedDict([('enum1', 'TestEnum'), ('name', 'str')]))]),
|
||||
OrderedDict([('struct', 'Branch1'), ('data', OrderedDict([('name', 'str')]))]),
|
||||
OrderedDict([('struct', 'Branch2'), ('data', OrderedDict([('value', 'int')]))])]
|
1
tests/qapi-schema/struct-base-clash-deep.err
Normal file
1
tests/qapi-schema/struct-base-clash-deep.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/struct-base-clash-deep.json:7: Member name 'name' clashes with base 'Base'
|
1
tests/qapi-schema/struct-base-clash-deep.exit
Normal file
1
tests/qapi-schema/struct-base-clash-deep.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
9
tests/qapi-schema/struct-base-clash-deep.json
Normal file
9
tests/qapi-schema/struct-base-clash-deep.json
Normal file
@ -0,0 +1,9 @@
|
||||
# we check for no duplicate keys with indirect base
|
||||
{ 'struct': 'Base',
|
||||
'data': { 'name': 'str' } }
|
||||
{ 'struct': 'Mid',
|
||||
'base': 'Base',
|
||||
'data': { 'value': 'int' } }
|
||||
{ 'struct': 'Sub',
|
||||
'base': 'Mid',
|
||||
'data': { '*name': 'str' } }
|
0
tests/qapi-schema/struct-base-clash-deep.out
Normal file
0
tests/qapi-schema/struct-base-clash-deep.out
Normal file
1
tests/qapi-schema/struct-base-clash.err
Normal file
1
tests/qapi-schema/struct-base-clash.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/struct-base-clash.json:4: Member name 'name' clashes with base 'Base'
|
1
tests/qapi-schema/struct-base-clash.exit
Normal file
1
tests/qapi-schema/struct-base-clash.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
6
tests/qapi-schema/struct-base-clash.json
Normal file
6
tests/qapi-schema/struct-base-clash.json
Normal file
@ -0,0 +1,6 @@
|
||||
# we check for no duplicate keys with base
|
||||
{ 'struct': 'Base',
|
||||
'data': { 'name': 'str' } }
|
||||
{ 'struct': 'Sub',
|
||||
'base': 'Base',
|
||||
'data': { 'name': 'str' } }
|
0
tests/qapi-schema/struct-base-clash.out
Normal file
0
tests/qapi-schema/struct-base-clash.out
Normal file
Loading…
Reference in New Issue
Block a user