aboutsummaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
authorMarkus Armbruster <armbru@redhat.com>2023-03-16 08:13:25 +0100
committerMarkus Armbruster <armbru@redhat.com>2023-04-24 15:21:39 +0200
commitde3b3f529d453dfaa1f8b437c1a1f0766d8108e4 (patch)
treec6b9dab2c6dceed6fa6012e969374a619e9a1778 /scripts
parent713d921aed52a802c62f02dadd59da5a9f9466b1 (diff)
qapi: Require boxed for conditional command and event arguments
The C code generator fails to honor 'if' conditions of command and event arguments. For instance, tests/qapi-schema/qapi-schema-test.json has { 'event': 'TEST_IF_EVENT', 'data': { 'foo': 'TestIfStruct', 'bar': { 'type': ['str'], 'if': 'TEST_IF_EVT_ARG' } }, 'if': { 'all': ['TEST_IF_EVT', 'TEST_IF_STRUCT'] } } Generated tests/test-qapi-events.h fails to honor the TEST_IF_EVT_ARG condition: #if defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT) void qapi_event_send_test_if_event(TestIfStruct *foo, strList *bar); #endif /* defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT) */ Only uses so far are in tests/. We could fix the generator to emit something like #if defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT) void qapi_event_send_test_if_event(TestIfStruct *foo #if defined(TEST_IF_EVT_ARG) , strList *bar #endif ); #endif /* defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT) */ Ugly. Calls become similarly ugly. Not worth fixing. Conditional arguments work fine with 'boxed': true, simply because complex types with conditional members work fine. Not worth breaking. Reject conditional arguments unless boxed. Move the tests cases covering unboxed conditional arguments out of tests/qapi-schema/qapi-schema-test.json. Cover boxed conditional arguments there instead. Signed-off-by: Markus Armbruster <armbru@redhat.com> Message-Id: <20230316071325.492471-15-armbru@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com>
Diffstat (limited to 'scripts')
-rw-r--r--scripts/qapi/commands.py1
-rw-r--r--scripts/qapi/gen.py1
-rw-r--r--scripts/qapi/schema.py14
3 files changed, 16 insertions, 0 deletions
diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py
index a079378d1b..d1fdf4182c 100644
--- a/scripts/qapi/commands.py
+++ b/scripts/qapi/commands.py
@@ -66,6 +66,7 @@ def gen_call(name: str,
elif arg_type:
assert not arg_type.variants
for memb in arg_type.members:
+ assert not memb.ifcond.is_present()
if memb.need_has():
argstr += 'arg.has_%s, ' % c_name(memb.name)
argstr += 'arg.%s, ' % c_name(memb.name)
diff --git a/scripts/qapi/gen.py b/scripts/qapi/gen.py
index b5a8d03e8e..8f8f784f4a 100644
--- a/scripts/qapi/gen.py
+++ b/scripts/qapi/gen.py
@@ -119,6 +119,7 @@ def build_params(arg_type: Optional[QAPISchemaObjectType],
elif arg_type:
assert not arg_type.variants
for memb in arg_type.members:
+ assert not memb.ifcond.is_present()
ret += sep
sep = ', '
if memb.need_has():
diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py
index 719152fe49..8f31f8832f 100644
--- a/scripts/qapi/schema.py
+++ b/scripts/qapi/schema.py
@@ -486,6 +486,10 @@ class QAPISchemaObjectType(QAPISchemaType):
assert self.members is not None
return not self.members and not self.variants
+ def has_conditional_members(self):
+ assert self.members is not None
+ return any(m.ifcond.is_present() for m in self.members)
+
def c_name(self):
assert self.name != 'q_empty'
return super().c_name()
@@ -817,6 +821,11 @@ class QAPISchemaCommand(QAPISchemaEntity):
self.info,
"command's 'data' can take %s only with 'boxed': true"
% self.arg_type.describe())
+ self.arg_type.check(schema)
+ if self.arg_type.has_conditional_members() and not self.boxed:
+ raise QAPISemError(
+ self.info,
+ "conditional command arguments require 'boxed': true")
if self._ret_type_name:
self.ret_type = schema.resolve_type(
self._ret_type_name, self.info, "command's 'returns'")
@@ -872,6 +881,11 @@ class QAPISchemaEvent(QAPISchemaEntity):
self.info,
"event's 'data' can take %s only with 'boxed': true"
% self.arg_type.describe())
+ self.arg_type.check(schema)
+ if self.arg_type.has_conditional_members() and not self.boxed:
+ raise QAPISemError(
+ self.info,
+ "conditional event arguments require 'boxed': true")
def connect_doc(self, doc=None):
super().connect_doc(doc)