qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: "Marc-André Lureau" <marcandre.lureau@redhat.com>
To: John Snow <jsnow@redhat.com>
Cc: qemu-devel <qemu-devel@nongnu.org>,
	"Armbruster, Markus" <armbru@redhat.com>
Subject: Re: [PATCH v3 6/9] qapi: normalize 'if' condition to IfPredicate tree
Date: Mon, 17 May 2021 15:18:49 +0400	[thread overview]
Message-ID: <CAMxuvax5Csa01V_WZE3QLCrhuWYh79KgZsfLTNcidXEPTe79Gg@mail.gmail.com> (raw)
In-Reply-To: <ba1046ca-3d92-40c2-e1e5-58ba0ac9baaf@redhat.com>

[-- Attachment #1: Type: text/plain, Size: 21410 bytes --]

Hi

On Thu, May 13, 2021 at 3:47 AM John Snow <jsnow@redhat.com> wrote:

> On 4/29/21 9:40 AM, marcandre.lureau@redhat.com wrote:
> > From: Marc-André Lureau <marcandre.lureau@redhat.com>
> >
> > Modify check_if() to build an IfPredicate tree (the schema
> > documentation is updated in a following patch).
> >
>
> I'm wondering if check_if() is the right place to do this. It's
> certainly convenient, but we don't build any other domain-specific types
> here at all -- that all happens in schema.py.
>

Ok, I moved the conversion to schema.py, like other _make_foo() there


>
> Before this patch, the return value from expr.py is conceivably
> something you'd get "exactly as-is" from a JSON parser. This patch would
> end that, and collapses the waveform.
>
> I think we should build a function that turns the raw (or slightly
> normalized) 'ifcond' AST into the IfPredicate object like we do for
> other structures, like Members, Features, etc.
>
>
There is now a _make_if() to convert the raw json to SchemaIfCond.

I'd also like the documentation changes to eventually be squashed
> directly into this patch if it changes syntax, but keeping it separate
> during review makes sense.
>
> Tentatively, I think the expanded "IF" syntax makes sense.
>
> 'if': 'COND'
> 'if': ['COND']
> 'if': { 'any': ['COND'] }
>

Actually, a simple list is short form for { 'all': [] }


> Seems fine. I want to play around a little bit with a JsonSchema for it
> though to make sure that it's something I can provide good IntelliSense
> tooltips for in e.g. vscode. (A bit of a pipe-dream side project, I
> admit, but if you'll humor me I'd like the chance to give it a shot.
> Some constructs are simply easier to type and validate than others.)
>
> > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>
> Tested-by: John Snow <jsnow@redhat.com>
>
> > ---
> >   tests/unit/test-qmp-cmds.c                    |  1 +
> >   scripts/qapi/expr.py                          | 62 ++++++++++++++-----
> >   scripts/qapi/schema.py                        | 13 +---
> >   tests/qapi-schema/bad-if.err                  |  3 +-
> >   tests/qapi-schema/doc-good.out                | 12 ++--
> >   tests/qapi-schema/enum-if-invalid.err         |  3 +-
> >   tests/qapi-schema/features-if-invalid.err     |  2 +-
> >   tests/qapi-schema/qapi-schema-test.json       | 20 +++---
> >   tests/qapi-schema/qapi-schema-test.out        | 59 ++++++++++--------
> >   .../qapi-schema/struct-member-if-invalid.err  |  2 +-
> >   10 files changed, 106 insertions(+), 71 deletions(-)
> >
> > diff --git a/tests/unit/test-qmp-cmds.c b/tests/unit/test-qmp-cmds.c
> > index 1b0b7d99df..83efa39720 100644
> > --- a/tests/unit/test-qmp-cmds.c
> > +++ b/tests/unit/test-qmp-cmds.c
> > @@ -51,6 +51,7 @@ FeatureStruct1 *qmp_test_features0(bool has_fs0,
> FeatureStruct0 *fs0,
> >                                      bool has_cfs1, CondFeatureStruct1
> *cfs1,
> >                                      bool has_cfs2, CondFeatureStruct2
> *cfs2,
> >                                      bool has_cfs3, CondFeatureStruct3
> *cfs3,
> > +                                   bool has_cfs4, CondFeatureStruct4
> *cfs4,
> >                                      Error **errp)
> >   {
> >       return g_new0(FeatureStruct1, 1);
> > diff --git a/scripts/qapi/expr.py b/scripts/qapi/expr.py
> > index 496f7e0333..0a97a6f020 100644
> > --- a/scripts/qapi/expr.py
> > +++ b/scripts/qapi/expr.py
> > @@ -42,7 +42,14 @@
> >       cast,
> >   )
> >
> > -from .common import c_name
> > +from .common import (
> > +    IfAll,
> > +    IfAny,
> > +    IfNot,
> > +    IfOption,
> > +    IfPredicate,
> > +    c_name,
> > +)
> >   from .error import QAPISemError
> >   from .parser import QAPIDoc
> >   from .source import QAPISourceInfo
> > @@ -261,6 +268,10 @@ def check_if(expr: _JSONObject, info:
> QAPISourceInfo, source: str) -> None:
> >       """
> >       Normalize and validate the ``if`` member of an object.
> >
> > +    The ``if`` field may be either a ``str``, a ``List[str]`` or a dict.
> > +    A ``str`` element or a ``List[str]`` will be normalized to
> > +    ``IfAll([str])``.
> > +
> >       The ``if`` member may be either a ``str`` or a ``List[str]``.
> >       A ``str`` value will be normalized to ``List[str]``.
> >
> > @@ -281,25 +292,44 @@ def check_if(expr: _JSONObject, info:
> QAPISourceInfo, source: str) -> None:
> >       if ifcond is None:
> >           return
> >
> > -    if isinstance(ifcond, list):
> > -        if not ifcond:
> > -            raise QAPISemError(
> > -                info, "'if' condition [] of %s is useless" % source)
> > -    else:
> > -        # Normalize to a list
> > -        ifcond = expr['if'] = [ifcond]
> > -
> > -    for elt in ifcond:
> > -        if not isinstance(elt, str):
> > +    def normalize(cond: Union[str, List[str], object]) -> IfPredicate:
> > +        if isinstance(cond, str):
> > +            if not cond.strip():
> > +                raise QAPISemError(
> > +                    info,
> > +                    "'if' condition '%s' of %s makes no sense"
> > +                    % (cond, source))
> > +            return IfOption(cond)
> > +        if isinstance(cond, list):
> > +            cond = {"all": cond}
> > +        if not isinstance(cond, dict):
> >               raise QAPISemError(
> >                   info,
> > -                "'if' condition of %s must be a string or a list of
> strings"
> > -                % source)
> > -        if not elt.strip():
> > +                "'if' condition of %s must be a string, "
> > +                "a list of strings or a dict" % source)
> > +        if len(cond) != 1:
> >               raise QAPISemError(
> >                   info,
> > -                "'if' condition '%s' of %s makes no sense"
> > -                % (elt, source))
> > +                "'if' condition dict of %s must have one key: "
> > +                "'all', 'any' or 'not'" % source)
> > +        check_keys(cond, info, "'if' condition", [],
> > +                   ["all", "any", "not"])
> > +        oper, operands = next(iter(cond.items()))
> > +        if oper == "not":
> > +            return IfNot(normalize(operands))
> > +        if not operands:
> > +            raise QAPISemError(
> > +                info, "'if' condition [] of %s is useless" % source)
> > +        if not isinstance(operands, list):
> > +            raise QAPISemError(
> > +                info, "'%s' condition of %s must be a list" % (oper,
> source))
> > +        operands = [normalize(o) for o in operands]
> > +        return IfAll(operands) if oper == "all" else IfAny(operands)
> > +
> > +    ifcond = expr.get('if')
> > +    if ifcond is None:
> > +        return
> > +    expr['if'] = normalize(ifcond)
> >
> >
> >   def normalize_members(members: object) -> None:
> > diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py
> > index 366a53ab64..61664a4c5e 100644
> > --- a/scripts/qapi/schema.py
> > +++ b/scripts/qapi/schema.py
> > @@ -19,22 +19,15 @@
> >   import re
> >   from typing import Optional
> >
> > -from .common import (
> > -    POINTER_SUFFIX,
> > -    IfAll,
> > -    IfOption,
> > -    c_name,
> > -    mcgen,
> > -)
> > +from .common import POINTER_SUFFIX, c_name, mcgen
> >   from .error import QAPISemError, QAPISourceError
> >   from .expr import check_exprs
> >   from .parser import QAPISchemaParser
> >
> >
> >   class QAPISchemaIfCond:
> > -    def __init__(self, ifcond=None):
> > -        pred_list = [IfOption(opt) for opt in ifcond or []]
> > -        self.pred = IfAll(pred_list)
> > +    def __init__(self, pred=None):
> > +        self.pred = pred
> >
> >       def gen_doc(self):
> >           if self.pred:
> > diff --git a/tests/qapi-schema/bad-if.err b/tests/qapi-schema/bad-if.err
> > index f83dee65da..454fbae387 100644
> > --- a/tests/qapi-schema/bad-if.err
> > +++ b/tests/qapi-schema/bad-if.err
> > @@ -1,2 +1,3 @@
> >   bad-if.json: In struct 'TestIfStruct':
> > -bad-if.json:2: 'if' condition of struct must be a string or a list of
> strings
> > +bad-if.json:2: 'if' condition has unknown key 'value'
> > +Valid keys are 'all', 'any', 'not'.
> > diff --git a/tests/qapi-schema/doc-good.out
> b/tests/qapi-schema/doc-good.out
> > index 6bf996f539..ca7e53f3b5 100644
> > --- a/tests/qapi-schema/doc-good.out
> > +++ b/tests/qapi-schema/doc-good.out
> > @@ -12,15 +12,15 @@ enum QType
> >   module doc-good.json
> >   enum Enum
> >       member one
> > -        if IfAll(['defined(IFONE)'])
> > +        if 'defined(IFONE)'
> >       member two
> > -    if IfAll(['defined(IFCOND)'])
> > +    if 'defined(IFCOND)'
> >       feature enum-feat
> >   object Base
> >       member base1: Enum optional=False
> >   object Variant1
> >       member var1: str optional=False
> > -        if IfAll(['defined(IFSTR)'])
> > +        if 'defined(IFSTR)'
> >           feature member-feat
> >       feature variant1-feat
> >   object Variant2
> > @@ -29,7 +29,7 @@ object Object
> >       tag base1
> >       case one: Variant1
> >       case two: Variant2
> > -        if IfAll(['IFTWO'])
> > +        if 'IFTWO'
> >       feature union-feat1
> >   object q_obj_Variant1-wrapper
> >       member data: Variant1 optional=False
> > @@ -38,13 +38,13 @@ object q_obj_Variant2-wrapper
> >   enum SugaredUnionKind
> >       member one
> >       member two
> > -        if IfAll(['IFTWO'])
> > +        if 'IFTWO'
> >   object SugaredUnion
> >       member type: SugaredUnionKind optional=False
> >       tag type
> >       case one: q_obj_Variant1-wrapper
> >       case two: q_obj_Variant2-wrapper
> > -        if IfAll(['IFTWO'])
> > +        if 'IFTWO'
> >       feature union-feat2
> >   alternate Alternate
> >       tag type
> > diff --git a/tests/qapi-schema/enum-if-invalid.err
> b/tests/qapi-schema/enum-if-invalid.err
> > index 0556dc967b..3bb84075a9 100644
> > --- a/tests/qapi-schema/enum-if-invalid.err
> > +++ b/tests/qapi-schema/enum-if-invalid.err
> > @@ -1,2 +1,3 @@
> >   enum-if-invalid.json: In enum 'TestIfEnum':
> > -enum-if-invalid.json:2: 'if' condition of 'data' member 'bar' must be a
> string or a list of strings
> > +enum-if-invalid.json:2: 'if' condition has unknown key 'val'
> > +Valid keys are 'all', 'any', 'not'.
> > diff --git a/tests/qapi-schema/features-if-invalid.err
> b/tests/qapi-schema/features-if-invalid.err
> > index f63b89535e..724a810086 100644
> > --- a/tests/qapi-schema/features-if-invalid.err
> > +++ b/tests/qapi-schema/features-if-invalid.err
> > @@ -1,2 +1,2 @@
> >   features-if-invalid.json: In struct 'Stru':
> > -features-if-invalid.json:2: 'if' condition of 'features' member 'f'
> must be a string or a list of strings
> > +features-if-invalid.json:2: 'if' condition of 'features' member 'f'
> must be a string, a list of strings or a dict
> > diff --git a/tests/qapi-schema/qapi-schema-test.json
> b/tests/qapi-schema/qapi-schema-test.json
> > index 84b9d41f15..2d5e480b44 100644
> > --- a/tests/qapi-schema/qapi-schema-test.json
> > +++ b/tests/qapi-schema/qapi-schema-test.json
> > @@ -231,8 +231,8 @@
> >
> >   { 'union': 'TestIfUnion', 'data':
> >     { 'foo': 'TestStruct',
> > -    'bar': { 'type': 'str', 'if': 'defined(TEST_IF_UNION_BAR)'} },
> > -  'if': 'defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)' }
> > +    'union-bar': { 'type': 'str', 'if': 'defined(TEST_IF_UNION_BAR)'} },
> > +  'if': ['defined(TEST_IF_UNION)', 'defined(TEST_IF_STRUCT)'] }
> >
> >   { 'command': 'test-if-union-cmd',
> >     'data': { 'union-cmd-arg': 'TestIfUnion' },
> > @@ -241,11 +241,10 @@
> >   { 'alternate': 'TestIfAlternate', 'data':
> >     { 'foo': 'int',
> >       'bar': { 'type': 'TestStruct', 'if': 'defined(TEST_IF_ALT_BAR)'} },
> > -  'if': 'defined(TEST_IF_ALT) && defined(TEST_IF_STRUCT)' }
> > +  'if': {'all': ['defined(TEST_IF_ALT)', 'defined(TEST_IF_STRUCT)'] } }
> >
> > -{ 'command': 'test-if-alternate-cmd',
> > -  'data': { 'alt-cmd-arg': 'TestIfAlternate' },
> > -  'if': 'defined(TEST_IF_ALT)' }
> > +{ 'command': 'test-if-alternate-cmd', 'data': { 'alt-cmd-arg':
> 'TestIfAlternate' },
> > +  'if': {'all': ['defined(TEST_IF_ALT)', {'not':
> 'defined(TEST_IF_NOT_ALT)'}] } }
> >
> >   { 'command': 'test-if-cmd',
> >     'data': {
> > @@ -259,7 +258,7 @@
> >   { 'event': 'TEST_IF_EVENT', 'data':
> >     { 'foo': 'TestIfStruct',
> >       'bar': { 'type': ['TestIfEnum'], 'if': 'defined(TEST_IF_EVT_BAR)'
> } },
> > -  'if': 'defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)' }
> > +  'if': ['defined(TEST_IF_EVT)', 'defined(TEST_IF_STRUCT)'] }
> >
> >   # test 'features'
> >
> > @@ -290,6 +289,10 @@
> >     'data': { 'foo': 'int' },
> >     'features': [ { 'name': 'feature1', 'if': [
> 'defined(TEST_IF_COND_1)',
> >
>  'defined(TEST_IF_COND_2)'] } ] }
> > +{ 'struct': 'CondFeatureStruct4',
> > +  'data': { 'foo': 'int' },
> > +  'features': [ { 'name': 'feature1', 'if': {'any':
> ['defined(TEST_IF_COND_1)',
> > +
>  'defined(TEST_IF_COND_2)'] } } ] }
> >
> >   { 'enum': 'FeatureEnum1',
> >     'data': [ 'eins', 'zwei', 'drei' ],
> > @@ -313,7 +316,8 @@
> >               '*fs4': 'FeatureStruct4',
> >               '*cfs1': 'CondFeatureStruct1',
> >               '*cfs2': 'CondFeatureStruct2',
> > -            '*cfs3': 'CondFeatureStruct3' },
> > +            '*cfs3': 'CondFeatureStruct3',
> > +            '*cfs4': 'CondFeatureStruct4' },
> >     'returns': 'FeatureStruct1',
> >     'features': [] }
> >
> > diff --git a/tests/qapi-schema/qapi-schema-test.out
> b/tests/qapi-schema/qapi-schema-test.out
> > index c2d303aa18..f859bf648d 100644
> > --- a/tests/qapi-schema/qapi-schema-test.out
> > +++ b/tests/qapi-schema/qapi-schema-test.out
> > @@ -298,49 +298,49 @@ command __org.qemu_x-command
> q_obj___org.qemu_x-command-arg -> __org.qemu_x-Unio
> >   object TestIfStruct
> >       member foo: int optional=False
> >       member bar: int optional=False
> > -        if IfAll(['defined(TEST_IF_STRUCT_BAR)'])
> > -    if IfAll(['defined(TEST_IF_STRUCT)'])
> > +        if 'defined(TEST_IF_STRUCT_BAR)'
> > +    if 'defined(TEST_IF_STRUCT)'
> >   enum TestIfEnum
> >       member foo
> >       member bar
> > -        if IfAll(['defined(TEST_IF_ENUM_BAR)'])
> > -    if IfAll(['defined(TEST_IF_ENUM)'])
> > +        if 'defined(TEST_IF_ENUM_BAR)'
> > +    if 'defined(TEST_IF_ENUM)'
> >   object q_obj_TestStruct-wrapper
> >       member data: TestStruct optional=False
> >   enum TestIfUnionKind
> >       member foo
> > -    member bar
> > -        if IfAll(['defined(TEST_IF_UNION_BAR)'])
> > -    if IfAll(['defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)'])
> > +    member union-bar
> > +        if 'defined(TEST_IF_UNION_BAR)'
> > +    if IfAll(['defined(TEST_IF_UNION)', 'defined(TEST_IF_STRUCT)'])
> >   object TestIfUnion
> >       member type: TestIfUnionKind optional=False
> >       tag type
> >       case foo: q_obj_TestStruct-wrapper
> > -    case bar: q_obj_str-wrapper
> > -        if IfAll(['defined(TEST_IF_UNION_BAR)'])
> > -    if IfAll(['defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)'])
> > +    case union-bar: q_obj_str-wrapper
> > +        if 'defined(TEST_IF_UNION_BAR)'
> > +    if IfAll(['defined(TEST_IF_UNION)', 'defined(TEST_IF_STRUCT)'])
> >   object q_obj_test-if-union-cmd-arg
> >       member union-cmd-arg: TestIfUnion optional=False
> > -    if IfAll(['defined(TEST_IF_UNION)'])
> > +    if 'defined(TEST_IF_UNION)'
> >   command test-if-union-cmd q_obj_test-if-union-cmd-arg -> None
> >       gen=True success_response=True boxed=False oob=False
> preconfig=False
> > -    if IfAll(['defined(TEST_IF_UNION)'])
> > +    if 'defined(TEST_IF_UNION)'
> >   alternate TestIfAlternate
> >       tag type
> >       case foo: int
> >       case bar: TestStruct
> > -        if IfAll(['defined(TEST_IF_ALT_BAR)'])
> > -    if IfAll(['defined(TEST_IF_ALT) && defined(TEST_IF_STRUCT)'])
> > +        if 'defined(TEST_IF_ALT_BAR)'
> > +    if IfAll(['defined(TEST_IF_ALT)', 'defined(TEST_IF_STRUCT)'])
> >   object q_obj_test-if-alternate-cmd-arg
> >       member alt-cmd-arg: TestIfAlternate optional=False
> > -    if IfAll(['defined(TEST_IF_ALT)'])
> > +    if IfAll(['defined(TEST_IF_ALT)',
> IfNot('defined(TEST_IF_NOT_ALT)')])
> >   command test-if-alternate-cmd q_obj_test-if-alternate-cmd-arg -> None
> >       gen=True success_response=True boxed=False oob=False
> preconfig=False
> > -    if IfAll(['defined(TEST_IF_ALT)'])
> > +    if IfAll(['defined(TEST_IF_ALT)',
> IfNot('defined(TEST_IF_NOT_ALT)')])
> >   object q_obj_test-if-cmd-arg
> >       member foo: TestIfStruct optional=False
> >       member bar: TestIfEnum optional=False
> > -        if IfAll(['defined(TEST_IF_CMD_BAR)'])
> > +        if 'defined(TEST_IF_CMD_BAR)'
> >       if IfAll(['defined(TEST_IF_CMD)', 'defined(TEST_IF_STRUCT)'])
> >   command test-if-cmd q_obj_test-if-cmd-arg -> UserDefThree
> >       gen=True success_response=True boxed=False oob=False
> preconfig=False
> > @@ -348,15 +348,15 @@ command test-if-cmd q_obj_test-if-cmd-arg ->
> UserDefThree
> >   command test-cmd-return-def-three None -> UserDefThree
> >       gen=True success_response=True boxed=False oob=False
> preconfig=False
> >   array TestIfEnumList TestIfEnum
> > -    if IfAll(['defined(TEST_IF_ENUM)'])
> > +    if 'defined(TEST_IF_ENUM)'
> >   object q_obj_TEST_IF_EVENT-arg
> >       member foo: TestIfStruct optional=False
> >       member bar: TestIfEnumList optional=False
> > -        if IfAll(['defined(TEST_IF_EVT_BAR)'])
> > -    if IfAll(['defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)'])
> > +        if 'defined(TEST_IF_EVT_BAR)'
> > +    if IfAll(['defined(TEST_IF_EVT)', 'defined(TEST_IF_STRUCT)'])
> >   event TEST_IF_EVENT q_obj_TEST_IF_EVENT-arg
> >       boxed=False
> > -    if IfAll(['defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)'])
> > +    if IfAll(['defined(TEST_IF_EVT)', 'defined(TEST_IF_STRUCT)'])
> >   object FeatureStruct0
> >       member foo: int optional=False
> >   object FeatureStruct1
> > @@ -379,17 +379,21 @@ object FeatureStruct4
> >   object CondFeatureStruct1
> >       member foo: int optional=False
> >       feature feature1
> > -        if IfAll(['defined(TEST_IF_FEATURE_1)'])
> > +        if 'defined(TEST_IF_FEATURE_1)'
> >   object CondFeatureStruct2
> >       member foo: int optional=False
> >       feature feature1
> > -        if IfAll(['defined(TEST_IF_FEATURE_1)'])
> > +        if 'defined(TEST_IF_FEATURE_1)'
> >       feature feature2
> > -        if IfAll(['defined(TEST_IF_FEATURE_2)'])
> > +        if 'defined(TEST_IF_FEATURE_2)'
> >   object CondFeatureStruct3
> >       member foo: int optional=False
> >       feature feature1
> >           if IfAll(['defined(TEST_IF_COND_1)',
> 'defined(TEST_IF_COND_2)'])
> > +object CondFeatureStruct4
> > +    member foo: int optional=False
> > +    feature feature1
> > +        if IfAny(['defined(TEST_IF_COND_1)', 'defined(TEST_IF_COND_2)'])
> >   enum FeatureEnum1
> >       member eins
> >       member zwei
> > @@ -417,6 +421,7 @@ object q_obj_test-features0-arg
> >       member cfs1: CondFeatureStruct1 optional=True
> >       member cfs2: CondFeatureStruct2 optional=True
> >       member cfs3: CondFeatureStruct3 optional=True
> > +    member cfs4: CondFeatureStruct4 optional=True
> >   command test-features0 q_obj_test-features0-arg -> FeatureStruct1
> >       gen=True success_response=True boxed=False oob=False
> preconfig=False
> >   command test-command-features1 None -> None
> > @@ -429,13 +434,13 @@ command test-command-features3 None -> None
> >   command test-command-cond-features1 None -> None
> >       gen=True success_response=True boxed=False oob=False
> preconfig=False
> >       feature feature1
> > -        if IfAll(['defined(TEST_IF_FEATURE_1)'])
> > +        if 'defined(TEST_IF_FEATURE_1)'
> >   command test-command-cond-features2 None -> None
> >       gen=True success_response=True boxed=False oob=False
> preconfig=False
> >       feature feature1
> > -        if IfAll(['defined(TEST_IF_FEATURE_1)'])
> > +        if 'defined(TEST_IF_FEATURE_1)'
> >       feature feature2
> > -        if IfAll(['defined(TEST_IF_FEATURE_2)'])
> > +        if 'defined(TEST_IF_FEATURE_2)'
> >   command test-command-cond-features3 None -> None
> >       gen=True success_response=True boxed=False oob=False
> preconfig=False
> >       feature feature1
> > diff --git a/tests/qapi-schema/struct-member-if-invalid.err
> b/tests/qapi-schema/struct-member-if-invalid.err
> > index 42e7fdae3c..c18157c1f9 100644
> > --- a/tests/qapi-schema/struct-member-if-invalid.err
> > +++ b/tests/qapi-schema/struct-member-if-invalid.err
> > @@ -1,2 +1,2 @@
> >   struct-member-if-invalid.json: In struct 'Stru':
> > -struct-member-if-invalid.json:2: 'if' condition of 'data' member
> 'member' must be a string or a list of strings
> > +struct-member-if-invalid.json:2: 'if' condition of 'data' member
> 'member' must be a string, a list of strings or a dict
> >
>
>

[-- Attachment #2: Type: text/html, Size: 28510 bytes --]

  reply	other threads:[~2021-05-17 11:33 UTC|newest]

Thread overview: 40+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-04-29 13:40 [PATCH v3 0/9] qapi: untie 'if' conditions from C preprocessor marcandre.lureau
2021-04-29 13:40 ` [PATCH v3 1/9] qapi: replace List[str] by QAPISchemaIfCond marcandre.lureau
2021-05-12 20:53   ` John Snow
2021-05-17 11:17     ` Marc-André Lureau
2021-05-17 16:30       ` John Snow
2021-04-29 13:40 ` [PATCH v3 2/9] qapi: move gen_if/gen_endif to QAPISchemaIfCond marcandre.lureau
2021-05-11 16:39   ` Stefan Hajnoczi
2021-05-12 12:36     ` Marc-André Lureau
2021-05-12 18:53     ` John Snow
2021-05-17 11:18       ` Marc-André Lureau
2021-05-12 21:01   ` John Snow
2021-05-21 14:35     ` Markus Armbruster
2021-04-29 13:40 ` [PATCH v3 3/9] qapi: start building an 'if' predicate tree marcandre.lureau
2021-05-12 21:39   ` John Snow
2021-05-17 11:18     ` Marc-André Lureau
2021-05-17 16:34       ` John Snow
2021-05-21 14:48     ` Markus Armbruster
2021-05-21 16:18       ` John Snow
2021-06-08 13:57         ` Markus Armbruster
2021-04-29 13:40 ` [PATCH v3 4/9] qapi: introduce IfPredicateList and IfAny marcandre.lureau
2021-05-12 23:26   ` John Snow
2021-05-17 11:18     ` Marc-André Lureau
2021-05-17 16:35       ` John Snow
2021-04-29 13:40 ` [PATCH v3 5/9] qapi: add IfNot marcandre.lureau
2021-05-12 23:32   ` John Snow
2021-04-29 13:40 ` [PATCH v3 6/9] qapi: normalize 'if' condition to IfPredicate tree marcandre.lureau
2021-05-12 23:47   ` John Snow
2021-05-17 11:18     ` Marc-André Lureau [this message]
2021-05-17 16:41       ` John Snow
2021-04-29 13:40 ` [PATCH v3 7/9] qapi: convert 'if' C-expressions to the new syntax tree marcandre.lureau
2021-05-12 23:51   ` John Snow
2021-05-17 11:20     ` Marc-André Lureau
2021-04-29 13:40 ` [PATCH v3 8/9] qapi: make 'if' condition strings simple identifiers marcandre.lureau
2021-05-12 23:56   ` John Snow
2021-04-29 13:40 ` [PATCH v3 9/9] docs: update the documentation about schema configuration marcandre.lureau
2021-05-13  0:01   ` John Snow
2021-05-11 16:52 ` [PATCH v3 0/9] qapi: untie 'if' conditions from C preprocessor Stefan Hajnoczi
2021-05-12 12:39   ` Marc-André Lureau
2021-05-12 17:43     ` Markus Armbruster
2021-05-12 18:58       ` John Snow

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=CAMxuvax5Csa01V_WZE3QLCrhuWYh79KgZsfLTNcidXEPTe79Gg@mail.gmail.com \
    --to=marcandre.lureau@redhat.com \
    --cc=armbru@redhat.com \
    --cc=jsnow@redhat.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).