Signed-off-by: Markus Armbruster <armbru(a)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 8368745a3e..2fb845303b 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):
@@ -963,7 +976,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:]
@@ -971,10 +984,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):
@@ -997,7 +1012,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