diff options
author | Markus Armbruster <armbru@redhat.com> | 2023-03-16 08:13:25 +0100 |
---|---|---|
committer | Markus Armbruster <armbru@redhat.com> | 2023-04-24 15:21:39 +0200 |
commit | de3b3f529d453dfaa1f8b437c1a1f0766d8108e4 (patch) | |
tree | c6b9dab2c6dceed6fa6012e969374a619e9a1778 /scripts | |
parent | 713d921aed52a802c62f02dadd59da5a9f9466b1 (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.py | 1 | ||||
-rw-r--r-- | scripts/qapi/gen.py | 1 | ||||
-rw-r--r-- | scripts/qapi/schema.py | 14 |
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) |