]> xenbits.xensource.com Git - qemu-xen.git/commitdiff
qapi: Forbid empty unions and useless alternates
authorEric Blake <eblake@redhat.com>
Thu, 18 Feb 2016 06:48:16 +0000 (23:48 -0700)
committerMarkus Armbruster <armbru@redhat.com>
Fri, 19 Feb 2016 10:08:56 +0000 (11:08 +0100)
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: Eric Blake <eblake@redhat.com>
Message-Id: <1455778109-6278-3-git-send-email-eblake@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
14 files changed:
docs/qapi-code-gen.txt
scripts/qapi.py
tests/qapi-schema/alternate-empty.err
tests/qapi-schema/alternate-empty.exit
tests/qapi-schema/alternate-empty.json
tests/qapi-schema/alternate-empty.out
tests/qapi-schema/flat-union-empty.err
tests/qapi-schema/flat-union-empty.exit
tests/qapi-schema/flat-union-empty.json
tests/qapi-schema/flat-union-empty.out
tests/qapi-schema/union-empty.err
tests/qapi-schema/union-empty.exit
tests/qapi-schema/union-empty.json
tests/qapi-schema/union-empty.out

index 128f074a2de49e7cf200a2ff85487d20822ded05..999f3b98f021d2af25c2d7fd47873f49b42a39ff 100644 (file)
@@ -187,11 +187,11 @@ prevent incomplete include files.
 
 Usage: { 'struct': STRING, 'data': DICT, '*base': STRUCT-NAME }
 
-A struct is a dictionary containing a single 'data' key whose
-value is a dictionary.  This corresponds to a struct in C or an Object
-in JSON. Each value of the 'data' dictionary must be the name of a
-type, or a one-element array containing a type name.  An example of a
-struct is:
+A struct is a dictionary containing a single 'data' key whose value is
+a dictionary; the dictionary may be empty.  This corresponds to a
+struct in C or an Object in JSON. Each value of the 'data' dictionary
+must be the name of a type, or a one-element array containing a type
+name.  An example of a struct is:
 
  { 'struct': 'MyType',
    'data': { 'member1': 'str', 'member2': 'int', '*member3': 'str' } }
@@ -288,9 +288,10 @@ or:    { 'union': STRING, 'data': DICT, 'base': STRUCT-NAME,
 
 Union types are used to let the user choose between several different
 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
-paragraphs.
+paragraphs.  The data dictionary for either type of union must not
+be empty.
 
 A simple union type defines a mapping from automatic discriminator
 values to data types like in this example:
index f40dc9e777d809b387a833dd492e2d839459d94b..f97236f5098aea44f316090ae176fe153c39aa38 100644 (file)
@@ -590,7 +590,10 @@ def check_union(expr, expr_info):
                                 "Discriminator '%s' must be of enumeration "
                                 "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():
         check_name(expr_info, "Member of union '%s'" % name, key)
 
@@ -613,7 +616,11 @@ def check_alternate(expr, expr_info):
     members = expr['data']
     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():
         check_name(expr_info, "Member of alternate '%s'" % name, key)
 
@@ -1059,6 +1066,7 @@ class QAPISchemaObjectTypeVariants(object):
         assert bool(tag_member) != bool(tag_name)
         assert (isinstance(tag_name, str) or
                 isinstance(tag_member, QAPISchemaObjectTypeMember))
+        assert len(variants) > 0
         for v in variants:
             assert isinstance(v, QAPISchemaObjectTypeVariant)
         self.tag_name = tag_name
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..bb06c5bfec08a9b31324102c75790f2f05574319 100644 (file)
@@ -0,0 +1 @@
+tests/qapi-schema/alternate-empty.json:2: Alternate 'Alt' should have at least two branches in 'data'
index 573541ac9702dd3969c9bc859d2b91ec1f7e6e56..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d 100644 (file)
@@ -1 +1 @@
-0
+1
index db3820f841a5ebb8600acb5aaee7055b697f589f..fff15baf16c2ef48efc4619d21d4a3ded29e2325 100644 (file)
@@ -1,2 +1,2 @@
-# 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' } }
index f78f174111c18255a158ac2c58f9d495516b6b1c..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 (file)
@@ -1,5 +0,0 @@
-object :empty
-alternate Alt
-    case i: int
-enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
-    prefix QTYPE
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..15754f54eb9251724eaddb1dc98085fc941b781c 100644 (file)
@@ -0,0 +1 @@
+tests/qapi-schema/flat-union-empty.json:4: Union 'Union' cannot have empty 'data'
index 573541ac9702dd3969c9bc859d2b91ec1f7e6e56..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d 100644 (file)
@@ -1 +1 @@
-0
+1
index 67dd2978eb173ac639abb7c95306ebb1282a203d..77f1d9abfb8f9b39a0a9dd9a03be3cb4e4343c15 100644 (file)
@@ -1,4 +1,4 @@
-# FIXME - flat unions should not be empty
+# flat unions cannot be empty
 { 'enum': 'Empty', 'data': [ ] }
 { 'struct': 'Base', 'data': { 'type': 'Empty' } }
 { 'union': 'Union', 'base': 'Base', 'discriminator': 'type', 'data': { } }
index eade2d5ac6ec8e1c74d20d0ce58be97b65a3402f..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 (file)
@@ -1,9 +0,0 @@
-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
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..12c20221bdfb6f7b046a7deaaf1074aa6126b651 100644 (file)
@@ -0,0 +1 @@
+tests/qapi-schema/union-empty.json:2: Union 'Union' cannot have empty 'data'
index 573541ac9702dd3969c9bc859d2b91ec1f7e6e56..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d 100644 (file)
@@ -1 +1 @@
-0
+1
index 1785007113360147aa09969fce5351ecdfd4c7ac..1f0b13ca21ae25d34b9d67da84df14b88f35e959 100644 (file)
@@ -1,2 +1,2 @@
-# FIXME - unions should not be empty
+# unions cannot be empty
 { 'union': 'Union', 'data': { } }
index bdf17e5b29500650723dc8c06f5341ad03640d28..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 (file)
@@ -1,6 +0,0 @@
-object :empty
-enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
-    prefix QTYPE
-object Union
-    member type: UnionKind optional=False
-enum UnionKind []