Raise an error if an enum has no members.  Such enums cannot be populated
with a valid value, and therefore they can only be used as optional
members.  But QAPI leaves out optional members from the serialization
protocol, and therefore the existence of the enum is completely pointless.

The same limitation is not extended to an enum whose members are all
compiled out; these can still be useful as optional members.  However they
cannot implicitly block usage of parent structures because, by virtue
of having only conditional members, they are "enums with conditional
first item" and must therefore be optional.

Signed-off-by: Paolo Bonzini <[email protected]>
---
 scripts/qapi/schema.py                  | 4 ++++
 tests/qapi-schema/enum-empty.err        | 2 ++
 tests/qapi-schema/enum-empty.json       | 2 ++
 tests/qapi-schema/enum-empty.out        | 0
 tests/qapi-schema/meson.build           | 1 +
 tests/qapi-schema/qapi-schema-test.json | 3 ---
 tests/qapi-schema/qapi-schema-test.out  | 1 -
 tests/qapi-schema/union-empty-if.err    | 2 ++
 tests/qapi-schema/union-empty-if.json   | 6 ++++++
 tests/qapi-schema/union-empty-if.out    | 0
 tests/qapi-schema/union-empty.err       | 4 ++--
 11 files changed, 19 insertions(+), 6 deletions(-)
 create mode 100644 tests/qapi-schema/enum-empty.err
 create mode 100644 tests/qapi-schema/enum-empty.json
 create mode 100644 tests/qapi-schema/enum-empty.out
 create mode 100644 tests/qapi-schema/union-empty-if.err
 create mode 100644 tests/qapi-schema/union-empty-if.json
 create mode 100644 tests/qapi-schema/union-empty-if.out

diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py
index 71296b15de2..a5a11298817 100644
--- a/scripts/qapi/schema.py
+++ b/scripts/qapi/schema.py
@@ -428,6 +428,10 @@ def __init__(
 
     def check(self, schema: QAPISchema) -> None:
         super().check(schema)
+        if not self.members:
+            raise QAPISemError(
+                self.info,
+                "enum '%s' must have at least one value" % self.name)
         seen: Dict[str, QAPISchemaMember] = {}
         for m in self.members:
             m.check_clash(self.info, seen)
diff --git a/tests/qapi-schema/enum-empty.err b/tests/qapi-schema/enum-empty.err
new file mode 100644
index 00000000000..6070bf62cbd
--- /dev/null
+++ b/tests/qapi-schema/enum-empty.err
@@ -0,0 +1,2 @@
+enum-empty.json: In enum 'TestEmpty':
+enum-empty.json:2: enum 'TestEmpty' must have at least one value
diff --git a/tests/qapi-schema/enum-empty.json 
b/tests/qapi-schema/enum-empty.json
new file mode 100644
index 00000000000..3b3dfb2e3d8
--- /dev/null
+++ b/tests/qapi-schema/enum-empty.json
@@ -0,0 +1,2 @@
+# An enum must have at least one value
+{ 'enum': 'TestEmpty', 'data': [] }
diff --git a/tests/qapi-schema/enum-empty.out b/tests/qapi-schema/enum-empty.out
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/tests/qapi-schema/meson.build b/tests/qapi-schema/meson.build
index 3b0c16a8b67..1058b7ecfae 100644
--- a/tests/qapi-schema/meson.build
+++ b/tests/qapi-schema/meson.build
@@ -196,6 +196,7 @@ schemas = [
   'union-clash-member.json',
   'union-discriminator-bad-name.json',
   'union-empty.json',
+  'union-empty-if.json',
   'union-inline-invalid-dict.json',
   'union-int-branch.json',
   'union-invalid-base.json',
diff --git a/tests/qapi-schema/qapi-schema-test.json 
b/tests/qapi-schema/qapi-schema-test.json
index 8ca977c49d2..195f1c4847b 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -23,9 +23,6 @@
   'data': { 'enum1': 'EnumOne',   # Intentional forward reference
             '*enum2': 'EnumOne', 'enum3': 'EnumOne', '*enum4': 'EnumOne' } }
 
-# An empty enum, although unusual, is currently acceptable
-{ 'enum': 'MyEnum', 'data': [ ] }
-
 # Likewise for an empty struct, including an empty base
 { 'struct': 'Empty1', 'data': { } }
 { 'struct': 'Empty2', 'base': 'Empty1', 'data': { } }
diff --git a/tests/qapi-schema/qapi-schema-test.out 
b/tests/qapi-schema/qapi-schema-test.out
index 4617eb4e98a..ddd8bf80d66 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -18,7 +18,6 @@ object NestedEnumsOne
     member enum2: EnumOne optional=True
     member enum3: EnumOne optional=False
     member enum4: EnumOne optional=True
-enum MyEnum
 object Empty1
 object Empty2
     base Empty1
diff --git a/tests/qapi-schema/union-empty-if.err 
b/tests/qapi-schema/union-empty-if.err
new file mode 100644
index 00000000000..2b8691ab2f5
--- /dev/null
+++ b/tests/qapi-schema/union-empty-if.err
@@ -0,0 +1,2 @@
+union-empty-if.json: In struct 'Base':
+union-empty-if.json:5: enum type 'Empty' of member 'type' has a conditional 
first value and must be optional
diff --git a/tests/qapi-schema/union-empty-if.json 
b/tests/qapi-schema/union-empty-if.json
new file mode 100644
index 00000000000..36625dc4a89
--- /dev/null
+++ b/tests/qapi-schema/union-empty-if.json
@@ -0,0 +1,6 @@
+# union discriminator enum cannot have only conditional items
+{ 'enum': 'Empty',
+  'data': [ { 'name' : 'bar', 'if': { 'all': ['FOO'] } } ] }
+
+{ 'struct': 'Base', 'data': { 'type': 'Empty' } }
+{ 'union': 'Union', 'base': 'Base', 'discriminator': 'type', 'data': { } }
diff --git a/tests/qapi-schema/union-empty-if.out 
b/tests/qapi-schema/union-empty-if.out
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/tests/qapi-schema/union-empty.err 
b/tests/qapi-schema/union-empty.err
index d4284399621..c07dcf32a5a 100644
--- a/tests/qapi-schema/union-empty.err
+++ b/tests/qapi-schema/union-empty.err
@@ -1,2 +1,2 @@
-union-empty.json: In union 'Union':
-union-empty.json:4: union has no branches
+union-empty.json: In enum 'Empty':
+union-empty.json:2: enum 'Empty' must have at least one value
-- 
2.55.0


Reply via email to