qapi: Permit omitting all flat union branches
Absent flat union branches default to the empty struct (since commit
800877bb16
"qapi: allow empty branches in flat unions"). But an
attempt to omit all of them is rejected with "Union 'FOO' has no
branches". Harmless oddity, but it's easy to avoid, so do that.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-Id: <20190913201349.24332-11-armbru@redhat.com>
[Commit message typo fixed]
This commit is contained in:
parent
f03255362a
commit
0ced9531f1
@ -436,8 +436,7 @@ Union types are used to let the user choose between several different
|
|||||||
variants for an object. There are two flavors: simple (no
|
variants for an object. There are two flavors: simple (no
|
||||||
discriminator or base), and flat (both discriminator and base). A union
|
discriminator or base), and flat (both discriminator and base). A union
|
||||||
type is defined using a data dictionary as explained in the following
|
type is defined using a data dictionary as explained in the following
|
||||||
paragraphs. The data dictionary for either type of union must not
|
paragraphs. Unions must have at least one branch.
|
||||||
be empty.
|
|
||||||
|
|
||||||
A simple union type defines a mapping from automatic discriminator
|
A simple union type defines a mapping from automatic discriminator
|
||||||
values to data types like in this example:
|
values to data types like in this example:
|
||||||
|
@ -852,7 +852,7 @@ def check_union(expr, info):
|
|||||||
|
|
||||||
# With no discriminator it is a simple union.
|
# With no discriminator it is a simple union.
|
||||||
if discriminator is None:
|
if discriminator is None:
|
||||||
enum_define = None
|
enum_values = members.keys()
|
||||||
allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
|
allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
|
||||||
if base is not None:
|
if base is not None:
|
||||||
raise QAPISemError(info, "Simple union '%s' must not have a base" %
|
raise QAPISemError(info, "Simple union '%s' must not have a base" %
|
||||||
@ -885,16 +885,17 @@ def check_union(expr, info):
|
|||||||
'must not be conditional' %
|
'must not be conditional' %
|
||||||
(base, discriminator, name))
|
(base, discriminator, name))
|
||||||
enum_define = enum_types.get(discriminator_value['type'])
|
enum_define = enum_types.get(discriminator_value['type'])
|
||||||
allow_metas = ['struct']
|
|
||||||
# Do not allow string discriminator
|
# Do not allow string discriminator
|
||||||
if not enum_define:
|
if not enum_define:
|
||||||
raise QAPISemError(info,
|
raise QAPISemError(info,
|
||||||
"Discriminator '%s' must be of enumeration "
|
"Discriminator '%s' must be of enumeration "
|
||||||
"type" % discriminator)
|
"type" % discriminator)
|
||||||
|
enum_values = enum_get_names(enum_define)
|
||||||
|
allow_metas = ['struct']
|
||||||
|
|
||||||
|
if (len(enum_values) == 0):
|
||||||
|
raise QAPISemError(info, "Union '%s' has no branches" % name)
|
||||||
|
|
||||||
# Check every branch; don't allow an empty union
|
|
||||||
if len(members) == 0:
|
|
||||||
raise QAPISemError(info, "Union '%s' cannot have empty 'data'" % name)
|
|
||||||
for (key, value) in members.items():
|
for (key, value) in members.items():
|
||||||
check_name(info, "Member of union '%s'" % name, key)
|
check_name(info, "Member of union '%s'" % name, key)
|
||||||
|
|
||||||
@ -907,8 +908,8 @@ def check_union(expr, info):
|
|||||||
|
|
||||||
# If the discriminator names an enum type, then all members
|
# If the discriminator names an enum type, then all members
|
||||||
# of 'data' must also be members of the enum type.
|
# of 'data' must also be members of the enum type.
|
||||||
if enum_define:
|
if discriminator is not None:
|
||||||
if key not in enum_get_names(enum_define):
|
if key not in enum_values:
|
||||||
raise QAPISemError(info,
|
raise QAPISemError(info,
|
||||||
"Discriminator value '%s' is not found in "
|
"Discriminator value '%s' is not found in "
|
||||||
"enum '%s'"
|
"enum '%s'"
|
||||||
@ -1578,7 +1579,6 @@ class QAPISchemaObjectTypeVariants(object):
|
|||||||
assert bool(tag_member) != bool(tag_name)
|
assert bool(tag_member) != bool(tag_name)
|
||||||
assert (isinstance(tag_name, str) or
|
assert (isinstance(tag_name, str) or
|
||||||
isinstance(tag_member, QAPISchemaObjectTypeMember))
|
isinstance(tag_member, QAPISchemaObjectTypeMember))
|
||||||
assert len(variants) > 0
|
|
||||||
for v in variants:
|
for v in variants:
|
||||||
assert isinstance(v, QAPISchemaObjectTypeVariant)
|
assert isinstance(v, QAPISchemaObjectTypeVariant)
|
||||||
self._tag_name = tag_name
|
self._tag_name = tag_name
|
||||||
|
@ -1 +1 @@
|
|||||||
tests/qapi-schema/flat-union-empty.json:4: Union 'Union' cannot have empty 'data'
|
tests/qapi-schema/flat-union-empty.json:4: Union 'Union' has no branches
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# flat unions cannot be empty
|
# flat union discriminator cannot be empty
|
||||||
{ 'enum': 'Empty', 'data': [ ] }
|
{ 'enum': 'Empty', 'data': [ ] }
|
||||||
{ 'struct': 'Base', 'data': { 'type': 'Empty' } }
|
{ 'struct': 'Base', 'data': { 'type': 'Empty' } }
|
||||||
{ 'union': 'Union', 'base': 'Base', 'discriminator': 'type', 'data': { } }
|
{ 'union': 'Union', 'base': 'Base', 'discriminator': 'type', 'data': { } }
|
||||||
|
@ -25,6 +25,11 @@
|
|||||||
{ 'struct': 'Empty1', 'data': { } }
|
{ 'struct': 'Empty1', 'data': { } }
|
||||||
{ 'struct': 'Empty2', 'base': 'Empty1', 'data': { } }
|
{ 'struct': 'Empty2', 'base': 'Empty1', 'data': { } }
|
||||||
|
|
||||||
|
# Likewise for an empty flat union
|
||||||
|
{ 'union': 'Union',
|
||||||
|
'base': { 'type': 'EnumOne' }, 'discriminator': 'type',
|
||||||
|
'data': { } }
|
||||||
|
|
||||||
{ 'command': 'user_def_cmd0', 'data': 'Empty2', 'returns': 'Empty2' }
|
{ 'command': 'user_def_cmd0', 'data': 'Empty2', 'returns': 'Empty2' }
|
||||||
|
|
||||||
# for testing override of default naming heuristic
|
# for testing override of default naming heuristic
|
||||||
|
@ -23,6 +23,15 @@ enum MyEnum
|
|||||||
object Empty1
|
object Empty1
|
||||||
object Empty2
|
object Empty2
|
||||||
base Empty1
|
base Empty1
|
||||||
|
object q_obj_Union-base
|
||||||
|
member type: EnumOne optional=False
|
||||||
|
object Union
|
||||||
|
base q_obj_Union-base
|
||||||
|
tag type
|
||||||
|
case value1: q_empty
|
||||||
|
case value2: q_empty
|
||||||
|
case value3: q_empty
|
||||||
|
case value4: q_empty
|
||||||
command user_def_cmd0 Empty2 -> Empty2
|
command user_def_cmd0 Empty2 -> Empty2
|
||||||
gen=True success_response=True boxed=False oob=False preconfig=False
|
gen=True success_response=True boxed=False oob=False preconfig=False
|
||||||
enum QEnumTwo
|
enum QEnumTwo
|
||||||
|
@ -1 +1 @@
|
|||||||
tests/qapi-schema/union-empty.json:2: Union 'Union' cannot have empty 'data'
|
tests/qapi-schema/union-empty.json:2: Union 'Union' has no branches
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
# unions cannot be empty
|
# simple unions cannot be empty
|
||||||
{ 'union': 'Union', 'data': { } }
|
{ 'union': 'Union', 'data': { } }
|
||||||
|
Loading…
Reference in New Issue
Block a user