On Sun, Mar 15, 2020 at 4:23 PM Markus Armbruster <arm...@redhat.com> wrote: > > Signed-off-by: Markus Armbruster <arm...@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lur...@redhat.com> > --- > docs/devel/qapi-code-gen.txt | 4 +++- > tests/qapi-schema/doc-good.texi | 2 ++ > qapi/introspect.json | 6 +++++- > scripts/qapi/expr.py | 3 ++- > scripts/qapi/introspect.py | 2 +- > scripts/qapi/schema.py | 25 ++++++++++++++++++++----- > tests/qapi-schema/doc-good.json | 5 ++++- > tests/qapi-schema/doc-good.out | 3 +++ > tests/qapi-schema/qapi-schema-test.json | 2 +- > tests/qapi-schema/qapi-schema-test.out | 1 + > tests/qapi-schema/test-qapi.py | 7 ++++--- > 11 files changed, 46 insertions(+), 14 deletions(-) > > diff --git a/docs/devel/qapi-code-gen.txt b/docs/devel/qapi-code-gen.txt > index 9fce78dcad..a1ef1cfd61 100644 > --- a/docs/devel/qapi-code-gen.txt > +++ b/docs/devel/qapi-code-gen.txt > @@ -234,7 +234,9 @@ Syntax: > '*features': FEATURES } > MEMBERS = { MEMBER, ... } > MEMBER = STRING : TYPE-REF > - | STRING : { 'type': TYPE-REF, '*if': COND } > + | STRING : { 'type': TYPE-REF, > + '*if': COND, > + '*features': FEATURES } > > Member 'struct' names the struct type. > > diff --git a/tests/qapi-schema/doc-good.texi b/tests/qapi-schema/doc-good.texi > index 76b396dae6..7f28fb7a0f 100644 > --- a/tests/qapi-schema/doc-good.texi > +++ b/tests/qapi-schema/doc-good.texi > @@ -132,6 +132,8 @@ Not documented > @table @asis > @item @code{variant1-feat} > a feature > +@item @code{member-feat} > +a member feature > @end table > > @end deftp > diff --git a/qapi/introspect.json b/qapi/introspect.json > index da3e176899..b1aabd4cfd 100644 > --- a/qapi/introspect.json > +++ b/qapi/introspect.json > @@ -206,11 +206,15 @@ > # Future extension: if present and non-null, the parameter > # is optional, and defaults to this value. > # > +# @features: names of features associated with the member, in no > +# particular order. (since 5.0) > +# > # Since: 2.5 > ## > { 'struct': 'SchemaInfoObjectMember', > - 'data': { 'name': 'str', 'type': 'str', '*default': 'any' } } > + 'data': { 'name': 'str', 'type': 'str', '*default': 'any', > # @default's type must be null or match @type > + '*features': [ 'str' ] } } > > ## > # @SchemaInfoObjectVariant: > diff --git a/scripts/qapi/expr.py b/scripts/qapi/expr.py > index f9c4448980..2942520399 100644 > --- a/scripts/qapi/expr.py > +++ b/scripts/qapi/expr.py > @@ -167,8 +167,9 @@ def check_type(value, info, source, > allow_optional=True, permit_upper=permit_upper) > if c_name(key, False) == 'u' or c_name(key, > False).startswith('has_'): > raise QAPISemError(info, "%s uses reserved name" % key_source) > - check_keys(arg, info, key_source, ['type'], ['if']) > + check_keys(arg, info, key_source, ['type'], ['if', 'features']) > check_if(arg, info, key_source) > + check_features(arg.get('features'), info) > check_type(arg['type'], info, key_source, allow_array=True) > > > diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py > index a3fa9865db..23652be810 100644 > --- a/scripts/qapi/introspect.py > +++ b/scripts/qapi/introspect.py > @@ -173,7 +173,7 @@ const QLitObject %(c_name)s = %(c_string)s; > obj = {'name': member.name, 'type': self._use_type(member.type)} > if member.optional: > obj['default'] = None > - return _make_tree(obj, member.ifcond, None) > + return _make_tree(obj, member.ifcond, member.features) > > def _gen_variants(self, tag_name, variants): > return {'tag': tag_name, > diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py > index 59e1f5a395..6ee3677215 100644 > --- a/scripts/qapi/schema.py > +++ b/scripts/qapi/schema.py > @@ -668,18 +668,31 @@ class QAPISchemaFeature(QAPISchemaMember): > > > class QAPISchemaObjectTypeMember(QAPISchemaMember): > - def __init__(self, name, info, typ, optional, ifcond=None): > + def __init__(self, name, info, typ, optional, ifcond=None, > features=None): > super().__init__(name, info, ifcond) > assert isinstance(typ, str) > assert isinstance(optional, bool) > + for f in features or []: > + assert isinstance(f, QAPISchemaFeature) > + f.set_defined_in(name) > self._type_name = typ > self.type = None > self.optional = optional > + self.features = features or [] > > def check(self, schema): > assert self.defined_in > self.type = schema.resolve_type(self._type_name, self.info, > self.describe) > + seen = {} > + for f in self.features: > + f.check_clash(self.info, seen) > + > + def connect_doc(self, doc): > + super().connect_doc(doc) > + if doc: > + for f in self.features: > + doc.connect_feature(f) > > > class QAPISchemaVariant(QAPISchemaObjectTypeMember): > @@ -962,7 +975,7 @@ class QAPISchema: > name, info, doc, ifcond, features, > self._make_enum_members(data, info), prefix)) > > - def _make_member(self, name, typ, ifcond, info): > + def _make_member(self, name, typ, ifcond, features, info): > optional = False > if name.startswith('*'): > name = name[1:] > @@ -970,10 +983,12 @@ class QAPISchema: > if isinstance(typ, list): > assert len(typ) == 1 > typ = self._make_array_type(typ[0], info) > - return QAPISchemaObjectTypeMember(name, info, typ, optional, ifcond) > + return QAPISchemaObjectTypeMember(name, info, typ, optional, ifcond, > + self._make_features(features, > info)) > > def _make_members(self, data, info): > - return [self._make_member(key, value['type'], value.get('if'), info) > + return [self._make_member(key, value['type'], value.get('if'), > + value.get('features'), info) > for (key, value) in data.items()] > > def _def_struct_type(self, expr, info, doc): > @@ -996,7 +1011,7 @@ class QAPISchema: > typ = self._make_array_type(typ[0], info) > typ = self._make_implicit_object_type( > typ, info, self.lookup_type(typ), > - 'wrapper', [self._make_member('data', typ, None, info)]) > + 'wrapper', [self._make_member('data', typ, None, None, info)]) > return QAPISchemaVariant(case, info, typ, ifcond) > > def _def_union_type(self, expr, info, doc): > diff --git a/tests/qapi-schema/doc-good.json b/tests/qapi-schema/doc-good.json > index 457b8b2cdf..ddd89d1233 100644 > --- a/tests/qapi-schema/doc-good.json > +++ b/tests/qapi-schema/doc-good.json > @@ -78,10 +78,13 @@ > # > # Features: > # @variant1-feat: a feature > +# @member-feat: a member feature > ## > { 'struct': 'Variant1', > 'features': [ 'variant1-feat' ], > - 'data': { 'var1': { 'type': 'str', 'if': 'defined(IFSTR)' } } } > + 'data': { 'var1': { 'type': 'str', > + 'features': [ 'member-feat' ], > + 'if': 'defined(IFSTR)' } } } > > ## > # @Variant2: > diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out > index 9bcb2b3e91..6757dd26a2 100644 > --- a/tests/qapi-schema/doc-good.out > +++ b/tests/qapi-schema/doc-good.out > @@ -21,6 +21,7 @@ object Base > object Variant1 > member var1: str optional=False > if ['defined(IFSTR)'] > + feature member-feat > feature variant1-feat > object Variant2 > object Object > @@ -135,6 +136,8 @@ Another paragraph (but no @var: line) > > feature=variant1-feat > a feature > + feature=member-feat > +a member feature > doc symbol=Variant2 > body= > > diff --git a/tests/qapi-schema/qapi-schema-test.json > b/tests/qapi-schema/qapi-schema-test.json > index fa4f3a15da..f576c337af 100644 > --- a/tests/qapi-schema/qapi-schema-test.json > +++ b/tests/qapi-schema/qapi-schema-test.json > @@ -258,7 +258,7 @@ > 'data': { 'foo': 'int' }, > 'features': [] } > { 'struct': 'FeatureStruct1', > - 'data': { 'foo': 'int' }, > + 'data': { 'foo': { 'type': 'int', 'features': [ 'member-feature1' ] } }, > 'features': [ 'feature1' ] } > { 'struct': 'FeatureStruct2', > 'data': { 'foo': 'int' }, > diff --git a/tests/qapi-schema/qapi-schema-test.out > b/tests/qapi-schema/qapi-schema-test.out > index 1cbd0802b3..cd863ae966 100644 > --- a/tests/qapi-schema/qapi-schema-test.out > +++ b/tests/qapi-schema/qapi-schema-test.out > @@ -359,6 +359,7 @@ object FeatureStruct0 > member foo: int optional=False > object FeatureStruct1 > member foo: int optional=False > + feature member-feature1 > feature feature1 > object FeatureStruct2 > member foo: int optional=False > diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py > index 8e09e54edb..f396b471eb 100755 > --- a/tests/qapi-schema/test-qapi.py > +++ b/tests/qapi-schema/test-qapi.py > @@ -55,6 +55,7 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): > print(' member %s: %s optional=%s' > % (m.name, m.type.name, m.optional)) > self._print_if(m.ifcond, 8) > + self._print_features(m.features, indent=8) > self._print_variants(variants) > self._print_if(ifcond) > self._print_features(features) > @@ -96,11 +97,11 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): > print('%sif %s' % (' ' * indent, ifcond)) > > @classmethod > - def _print_features(cls, features): > + def _print_features(cls, features, indent=4): > if features: > for f in features: > - print(' feature %s' % f.name) > - cls._print_if(f.ifcond, 8) > + print('%sfeature %s' % (' ' * indent, f.name)) > + cls._print_if(f.ifcond, indent + 4) > > > def test_frontend(fname): > -- > 2.21.1 > > -- Marc-André Lureau