提交 02a57ae3 编写于 作者: E Eric Blake 提交者: Markus Armbruster

qapi: Forbid empty unions and useless alternates

Empty unions serve no purpose, and while we compile with gcc
which permits them, strict C99 forbids them.  We happen to inject
a dummy 'void *data' member into the C unions that represent QAPI
unions and alternates, but we want to get rid of that member (it
pollutes the namespace for no good reason), which would leave us
with an empty union if the user didn't provide any branches.  While
empty structs make sense in QAPI, empty unions don't add any
expressiveness to the QMP language.  So prohibit them at parse
time.  Update the documentation and testsuite to match.

Note that the documentation already mentioned that alternates
should have "two or more JSON data types"; so this also fixes
the code to enforce that.  However, we have existing uses of a
union type with only one branch, so the 2-or-more strictness
is intentionally limited to alternates.
Signed-off-by: NEric Blake <eblake@redhat.com>
Message-Id: <1455778109-6278-3-git-send-email-eblake@redhat.com>
Signed-off-by: NMarkus Armbruster <armbru@redhat.com>
上级 f96493b1
...@@ -187,11 +187,11 @@ prevent incomplete include files. ...@@ -187,11 +187,11 @@ prevent incomplete include files.
Usage: { 'struct': STRING, 'data': DICT, '*base': STRUCT-NAME } Usage: { 'struct': STRING, 'data': DICT, '*base': STRUCT-NAME }
A struct is a dictionary containing a single 'data' key whose A struct is a dictionary containing a single 'data' key whose value is
value is a dictionary. This corresponds to a struct in C or an Object a dictionary; the dictionary may be empty. This corresponds to a
in JSON. Each value of the 'data' dictionary must be the name of a struct in C or an Object in JSON. Each value of the 'data' dictionary
type, or a one-element array containing a type name. An example of a must be the name of a type, or a one-element array containing a type
struct is: name. An example of a struct is:
{ 'struct': 'MyType', { 'struct': 'MyType',
'data': { 'member1': 'str', 'member2': 'int', '*member3': 'str' } } 'data': { 'member1': 'str', 'member2': 'int', '*member3': 'str' } }
...@@ -288,9 +288,10 @@ or: { 'union': STRING, 'data': DICT, 'base': STRUCT-NAME, ...@@ -288,9 +288,10 @@ or: { 'union': STRING, 'data': DICT, 'base': STRUCT-NAME,
Union types are used to let the user choose between several different 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), 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. paragraphs. The data dictionary for either type of union must not
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:
......
...@@ -590,7 +590,10 @@ def check_union(expr, expr_info): ...@@ -590,7 +590,10 @@ def check_union(expr, expr_info):
"Discriminator '%s' must be of enumeration " "Discriminator '%s' must be of enumeration "
"type" % discriminator) "type" % discriminator)
# Check every branch # Check every branch; don't allow an empty union
if len(members) == 0:
raise QAPIExprError(expr_info,
"Union '%s' cannot have empty 'data'" % name)
for (key, value) in members.items(): for (key, value) in members.items():
check_name(expr_info, "Member of union '%s'" % name, key) check_name(expr_info, "Member of union '%s'" % name, key)
...@@ -613,7 +616,11 @@ def check_alternate(expr, expr_info): ...@@ -613,7 +616,11 @@ def check_alternate(expr, expr_info):
members = expr['data'] members = expr['data']
types_seen = {} types_seen = {}
# Check every branch # Check every branch; require at least two branches
if len(members) < 2:
raise QAPIExprError(expr_info,
"Alternate '%s' should have at least two branches "
"in 'data'" % name)
for (key, value) in members.items(): for (key, value) in members.items():
check_name(expr_info, "Member of alternate '%s'" % name, key) check_name(expr_info, "Member of alternate '%s'" % name, key)
...@@ -1059,6 +1066,7 @@ class QAPISchemaObjectTypeVariants(object): ...@@ -1059,6 +1066,7 @@ 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
......
tests/qapi-schema/alternate-empty.json:2: Alternate 'Alt' should have at least two branches in 'data'
# FIXME - alternates should list at least two types to be useful # alternates must list at least two types to be useful
{ 'alternate': 'Alt', 'data': { 'i': 'int' } } { 'alternate': 'Alt', 'data': { 'i': 'int' } }
object :empty
alternate Alt
case i: int
enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
prefix QTYPE
tests/qapi-schema/flat-union-empty.json:4: Union 'Union' cannot have empty 'data'
# FIXME - flat unions should not be empty # flat unions 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': { } }
object :empty
object Base
member type: Empty optional=False
enum Empty []
enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
prefix QTYPE
object Union
base Base
tag type
tests/qapi-schema/union-empty.json:2: Union 'Union' cannot have empty 'data'
# FIXME - unions should not be empty # unions cannot be empty
{ 'union': 'Union', 'data': { } } { 'union': 'Union', 'data': { } }
object :empty
enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
prefix QTYPE
object Union
member type: UnionKind optional=False
enum UnionKind []
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册