diff options
author | Peter Maydell | 2019-10-29 21:06:08 +0100 |
---|---|---|
committer | Peter Maydell | 2019-10-29 21:06:08 +0100 |
commit | 16884391c750d0c5e863f55ad7aaaa146fc5181e (patch) | |
tree | 7909241a593eea7f9cafaea658e9b101a7682626 | |
parent | Merge remote-tracking branch 'remotes/jasowang/tags/net-pull-request' into st... (diff) | |
parent | qapi: Check feature documentation against the schema (diff) | |
download | qemu-16884391c750d0c5e863f55ad7aaaa146fc5181e.tar.gz qemu-16884391c750d0c5e863f55ad7aaaa146fc5181e.tar.xz qemu-16884391c750d0c5e863f55ad7aaaa146fc5181e.zip |
Merge remote-tracking branch 'remotes/armbru/tags/pull-qapi-2019-10-29' into staging
QAPI patches for 2019-10-29
# gpg: Signature made Tue 29 Oct 2019 06:40:56 GMT
# gpg: using RSA key 354BC8B3D7EB2A6B68674E5F3870B400EB918653
# gpg: issuer "armbru@redhat.com"
# gpg: Good signature from "Markus Armbruster <armbru@redhat.com>" [full]
# gpg: aka "Markus Armbruster <armbru@pond.sub.org>" [full]
# Primary key fingerprint: 354B C8B3 D7EB 2A6B 6867 4E5F 3870 B400 EB91 8653
* remotes/armbru/tags/pull-qapi-2019-10-29:
qapi: Check feature documentation against the schema
qapi: Polish reporting of bogus member documentation
qapi: Lift features into QAPISchemaEntity
qapi: Fold normalize_enum() into check_enum()
qapi: Fold normalize_features() into check_features()
qapi: Fold normalize_if() into check_if()
qapi: Eliminate .check_doc() overrides
qapi: Simplify ._make_implicit_object_type()
qapi: Fix doc comment checking for commands and events
qapi: Clean up doc comment checking for implicit union base
qapi: Fix enum doc comment checking
qapi: Split .connect_doc(), .check_doc() off .check()
qapi: De-duplicate entity documentation generation code
qapi: Implement boxed event argument documentation
qemu-doc: Belatedly document QMP command deprecation
tests/qapi-schema: Fix feature documentation testing
tests/qapi-schema: Cover alternate documentation comments
tests/qapi-schema: Demonstrate command and event doc comment bugs
tests/qapi-schema: Demonstrate feature and enum doc comment bugs
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
29 files changed, 270 insertions, 111 deletions
diff --git a/qapi/net.json b/qapi/net.json index 728990f4fb..4c96137811 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -723,8 +723,6 @@ # Trigger generation of broadcast RARP frames to update network switches. # This can be useful when network bonds fail-over the active slave. # -# @params: AnnounceParameters giving timing and repetition count of announce -# # Example: # # -> { "execute": "announce-self", diff --git a/qemu-deprecated.texi b/qemu-deprecated.texi index 7239e0959d..f727bd3932 100644 --- a/qemu-deprecated.texi +++ b/qemu-deprecated.texi @@ -149,6 +149,18 @@ QEMU 4.1 has three options, please migrate to one of these three: @section QEMU Machine Protocol (QMP) commands +@subsection change (since 2.5.0) + +Use ``blockdev-change-medium'' or ``change-vnc-password'' instead. + +@subsection migrate_set_downtime and migrate_set_speed (since 2.8.0) + +Use ``migrate-set-parameters'' instead. + +@subsection migrate-set-cache-size and query-migrate-cache-size (since 2.11.0) + +Use ``migrate-set-parameters'' and ``query-migrate-parameters'' instead. + @subsection query-block result field dirty-bitmaps[i].status (since 4.0) The ``status'' field of the ``BlockDirtyInfo'' structure, returned by diff --git a/scripts/qapi/doc.py b/scripts/qapi/doc.py index 6d5726cf6e..6f1c17f71f 100644 --- a/scripts/qapi/doc.py +++ b/scripts/qapi/doc.py @@ -12,7 +12,7 @@ from qapi.gen import QAPIGenDoc, QAPISchemaVisitor MSG_FMT = """ @deftypefn {type} {{}} {name} -{body} +{body}{members}{features}{sections} @end deftypefn """.format @@ -20,7 +20,7 @@ MSG_FMT = """ TYPE_FMT = """ @deftp {{{type}}} {name} -{body} +{body}{members}{features}{sections} @end deftp """.format @@ -149,7 +149,8 @@ def texi_member(member, desc, suffix): suffix, desc, texi_if(member.ifcond, prefix='@*')) -def texi_members(doc, what, base, variants, member_func): +def texi_members(doc, what, base=None, variants=None, + member_func=texi_member): """Format the table of members""" items = '' for section in doc.args.values(): @@ -182,6 +183,14 @@ def texi_members(doc, what, base, variants, member_func): return '\n@b{%s:}\n@table @asis\n%s@end table\n' % (what, items) +def texi_arguments(doc, boxed_arg_type): + if boxed_arg_type: + assert not doc.args + return ('\n@b{Arguments:} the members of @code{%s}\n' + % boxed_arg_type.name) + return texi_members(doc, 'Arguments') + + def texi_features(doc): """Format the table of features""" items = '' @@ -208,12 +217,22 @@ def texi_sections(doc, ifcond): return body -def texi_entity(doc, what, ifcond, base=None, variants=None, - member_func=texi_member): - return (texi_body(doc) - + texi_members(doc, what, base, variants, member_func) - + texi_features(doc) - + texi_sections(doc, ifcond)) +def texi_type(typ, doc, ifcond, members): + return TYPE_FMT(type=typ, + name=doc.symbol, + body=texi_body(doc), + members=members, + features=texi_features(doc), + sections=texi_sections(doc, ifcond)) + + +def texi_msg(typ, doc, ifcond, members): + return MSG_FMT(type=typ, + name=doc.symbol, + body=texi_body(doc), + members=members, + features=texi_features(doc), + sections=texi_sections(doc, ifcond)) class QAPISchemaGenDocVisitor(QAPISchemaVisitor): @@ -227,48 +246,36 @@ class QAPISchemaGenDocVisitor(QAPISchemaVisitor): def visit_enum_type(self, name, info, ifcond, members, prefix): doc = self.cur_doc - self._gen.add(TYPE_FMT(type='Enum', - name=doc.symbol, - body=texi_entity(doc, 'Values', ifcond, - member_func=texi_enum_value))) + self._gen.add(texi_type('Enum', doc, ifcond, + texi_members(doc, 'Values', + member_func=texi_enum_value))) def visit_object_type(self, name, info, ifcond, base, members, variants, features): doc = self.cur_doc if base and base.is_implicit(): base = None - self._gen.add(TYPE_FMT(type='Object', - name=doc.symbol, - body=texi_entity(doc, 'Members', ifcond, - base, variants))) + self._gen.add(texi_type('Object', doc, ifcond, + texi_members(doc, 'Members', base, variants))) def visit_alternate_type(self, name, info, ifcond, variants): doc = self.cur_doc - self._gen.add(TYPE_FMT(type='Alternate', - name=doc.symbol, - body=texi_entity(doc, 'Members', ifcond))) + self._gen.add(texi_type('Alternate', doc, ifcond, + texi_members(doc, 'Members'))) def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, success_response, boxed, allow_oob, allow_preconfig, features): doc = self.cur_doc - if boxed: - body = texi_body(doc) - body += ('\n@b{Arguments:} the members of @code{%s}\n' - % arg_type.name) - body += texi_features(doc) - body += texi_sections(doc, ifcond) - else: - body = texi_entity(doc, 'Arguments', ifcond) - self._gen.add(MSG_FMT(type='Command', - name=doc.symbol, - body=body)) + self._gen.add(texi_msg('Command', doc, ifcond, + texi_arguments(doc, + arg_type if boxed else None))) def visit_event(self, name, info, ifcond, arg_type, boxed): doc = self.cur_doc - self._gen.add(MSG_FMT(type='Event', - name=doc.symbol, - body=texi_entity(doc, 'Arguments', ifcond))) + self._gen.add(texi_msg('Event', doc, ifcond, + texi_arguments(doc, + arg_type if boxed else None))) def symbol(self, doc, entity): if self._gen._body: diff --git a/scripts/qapi/expr.py b/scripts/qapi/expr.py index 7c7394f835..d7a289eded 100644 --- a/scripts/qapi/expr.py +++ b/scripts/qapi/expr.py @@ -95,12 +95,6 @@ def check_flags(expr, info): info, "flag '%s' may only use true value" % key) -def normalize_if(expr): - ifcond = expr.get('if') - if isinstance(ifcond, str): - expr['if'] = [ifcond] - - def check_if(expr, info, source): def check_if_str(ifcond, info): @@ -126,6 +120,7 @@ def check_if(expr, info, source): check_if_str(elt, info) else: check_if_str(ifcond, info) + expr['if'] = [ifcond] def normalize_members(members): @@ -175,21 +170,16 @@ def check_type(value, info, source, raise QAPISemError(info, "%s uses reserved name" % key_source) check_keys(arg, info, key_source, ['type'], ['if']) check_if(arg, info, key_source) - normalize_if(arg) check_type(arg['type'], info, key_source, allow_array=True) -def normalize_features(features): - if isinstance(features, list): - features[:] = [f if isinstance(f, dict) else {'name': f} - for f in features] - - def check_features(features, info): if features is None: return if not isinstance(features, list): raise QAPISemError(info, "'features' must be an array") + features[:] = [f if isinstance(f, dict) else {'name': f} + for f in features] for f in features: source = "'features' member" assert isinstance(f, dict) @@ -198,13 +188,6 @@ def check_features(features, info): source = "%s '%s'" % (source, f['name']) check_name_str(f['name'], info, source) check_if(f, info, source) - normalize_if(f) - - -def normalize_enum(expr): - if isinstance(expr['data'], list): - expr['data'] = [m if isinstance(m, dict) else {'name': m} - for m in expr['data']] def check_enum(expr, info): @@ -219,6 +202,8 @@ def check_enum(expr, info): permit_upper = name in info.pragma.name_case_whitelist + members[:] = [m if isinstance(m, dict) else {'name': m} + for m in members] for member in members: source = "'data' member" check_keys(member, info, source, ['name'], ['if']) @@ -227,7 +212,6 @@ def check_enum(expr, info): check_name_str(member['name'], info, source, enum_member=True, permit_upper=permit_upper) check_if(member, info, source) - normalize_if(member) def check_struct(expr, info): @@ -259,7 +243,6 @@ def check_union(expr, info): check_name_str(key, info, source) check_keys(value, info, source, ['type'], ['if']) check_if(value, info, source) - normalize_if(value) check_type(value['type'], info, source, allow_array=not base) @@ -273,7 +256,6 @@ def check_alternate(expr, info): check_name_str(key, info, source) check_keys(value, info, source, ['type'], ['if']) check_if(value, info, source) - normalize_if(value) check_type(value['type'], info, source) @@ -339,7 +321,6 @@ def check_exprs(exprs): if meta == 'enum': check_keys(expr, info, meta, ['enum', 'data'], ['if', 'prefix']) - normalize_enum(expr) check_enum(expr, info) elif meta == 'union': check_keys(expr, info, meta, @@ -357,7 +338,6 @@ def check_exprs(exprs): check_keys(expr, info, meta, ['struct', 'data'], ['base', 'if', 'features']) normalize_members(expr['data']) - normalize_features(expr.get('features')) check_struct(expr, info) elif meta == 'command': check_keys(expr, info, meta, @@ -366,7 +346,6 @@ def check_exprs(exprs): 'gen', 'success-response', 'allow-oob', 'allow-preconfig']) normalize_members(expr.get('data')) - normalize_features(expr.get('features')) check_command(expr, info) elif meta == 'event': check_keys(expr, info, meta, @@ -376,7 +355,6 @@ def check_exprs(exprs): else: assert False, 'unexpected meta type' - normalize_if(expr) check_if(expr, info, meta) check_flags(expr, info) diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py index e800876ad1..342792e410 100644 --- a/scripts/qapi/parser.py +++ b/scripts/qapi/parser.py @@ -555,16 +555,31 @@ class QAPIDoc(object): self.args[member.name] = QAPIDoc.ArgSection(member.name) self.args[member.name].connect(member) + def connect_feature(self, feature): + if feature.name not in self.features: + raise QAPISemError(feature.info, + "feature '%s' lacks documentation" + % feature.name) + self.features[feature.name] = QAPIDoc.ArgSection(feature.name) + self.features[feature.name].connect(feature) + def check_expr(self, expr): if self.has_section('Returns') and 'command' not in expr: raise QAPISemError(self.info, "'Returns:' is only valid for commands") def check(self): - bogus = [name for name, section in self.args.items() - if not section.member] - if bogus: - raise QAPISemError( - self.info, - "the following documented members are not in " - "the declaration: %s" % ", ".join(bogus)) + + def check_args_section(args, info, what): + bogus = [name for name, section in args.items() + if not section.member] + if bogus: + raise QAPISemError( + self.info, + "documented member%s '%s' %s not exist" + % ("s" if len(bogus) > 1 else "", + "', '".join(bogus), + "do" if len(bogus) > 1 else "does")) + + check_args_section(self.args, self.info, 'members') + check_args_section(self.features, self.info, 'features') diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index f7d68a35f4..cf0045f34e 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -27,8 +27,11 @@ from qapi.parser import QAPISchemaParser class QAPISchemaEntity(object): meta = None - def __init__(self, name, info, doc, ifcond=None): + def __init__(self, name, info, doc, ifcond=None, features=None): assert name is None or isinstance(name, str) + for f in features or []: + assert isinstance(f, QAPISchemaFeature) + f.set_defined_in(name) self.name = name self._module = None # For explicitly defined entities, info points to the (explicit) @@ -39,6 +42,7 @@ class QAPISchemaEntity(object): self.info = info self.doc = doc self._ifcond = ifcond or [] + self.features = features or [] self._checked = False def c_name(self): @@ -49,8 +53,21 @@ class QAPISchemaEntity(object): if self.info: self._module = os.path.relpath(self.info.fname, os.path.dirname(schema.fname)) + seen = {} + for f in self.features: + f.check_clash(self.info, seen) + if self.doc: + self.doc.connect_feature(f) + self._checked = True + def connect_doc(self, doc=None): + pass + + def check_doc(self): + if self.doc: + self.doc.check() + @property def ifcond(self): assert self._checked @@ -217,8 +234,12 @@ class QAPISchemaEnumType(QAPISchemaType): seen = {} for m in self.members: m.check_clash(self.info, seen) - if self.doc: - self.doc.connect_member(m) + + def connect_doc(self, doc=None): + doc = doc or self.doc + if doc: + for m in self.members: + doc.connect_member(m) def is_implicit(self): # See QAPISchema._make_implicit_enum_type() and ._def_predefineds() @@ -296,7 +317,7 @@ class QAPISchemaObjectType(QAPISchemaType): # struct has local_members, optional base, and no variants # flat union has base, variants, and no local_members # simple union has local_members, variants, and no base - QAPISchemaType.__init__(self, name, info, doc, ifcond) + QAPISchemaType.__init__(self, name, info, doc, ifcond, features) self.meta = 'union' if variants else 'struct' assert base is None or isinstance(base, str) for m in local_members: @@ -305,15 +326,11 @@ class QAPISchemaObjectType(QAPISchemaType): if variants is not None: assert isinstance(variants, QAPISchemaObjectTypeVariants) variants.set_defined_in(name) - for f in features: - assert isinstance(f, QAPISchemaFeature) - f.set_defined_in(name) self._base_name = base self.base = None self.local_members = local_members self.variants = variants self.members = None - self.features = features def check(self, schema): # This calls another type T's .check() exactly when the C @@ -345,22 +362,12 @@ class QAPISchemaObjectType(QAPISchemaType): for m in self.local_members: m.check(schema) m.check_clash(self.info, seen) - if self.doc: - self.doc.connect_member(m) members = seen.values() if self.variants: self.variants.check(schema, seen) self.variants.check_clash(self.info, seen) - # Features are in a name space separate from members - seen = {} - for f in self.features: - f.check_clash(self.info, seen) - - if self.doc: - self.doc.check() - self.members = members # mark completed # Check that the members of this type do not cause duplicate JSON members, @@ -372,6 +379,14 @@ class QAPISchemaObjectType(QAPISchemaType): for m in self.members: m.check_clash(info, seen) + def connect_doc(self, doc=None): + doc = doc or self.doc + if doc: + if self.base and self.base.is_implicit(): + self.base.connect_doc(doc) + for m in self.local_members: + doc.connect_member(m) + @property def ifcond(self): assert self._checked @@ -639,10 +654,12 @@ class QAPISchemaAlternateType(QAPISchemaType): "%s can't be distinguished from '%s'" % (v.describe(self.info), types_seen[qt])) types_seen[qt] = v.name - if self.doc: - self.doc.connect_member(v) - if self.doc: - self.doc.check() + + def connect_doc(self, doc=None): + doc = doc or self.doc + if doc: + for v in self.variants.variants: + doc.connect_member(v) def c_type(self): return c_name(self.name) + pointer_suffix @@ -662,12 +679,9 @@ class QAPISchemaCommand(QAPISchemaEntity): def __init__(self, name, info, doc, ifcond, arg_type, ret_type, gen, success_response, boxed, allow_oob, allow_preconfig, features): - QAPISchemaEntity.__init__(self, name, info, doc, ifcond) + QAPISchemaEntity.__init__(self, name, info, doc, ifcond, features) assert not arg_type or isinstance(arg_type, str) assert not ret_type or isinstance(ret_type, str) - for f in features: - assert isinstance(f, QAPISchemaFeature) - f.set_defined_in(name) self._arg_type_name = arg_type self.arg_type = None self._ret_type_name = ret_type @@ -677,7 +691,6 @@ class QAPISchemaCommand(QAPISchemaEntity): self.boxed = boxed self.allow_oob = allow_oob self.allow_preconfig = allow_preconfig - self.features = features def check(self, schema): QAPISchemaEntity.check(self, schema) @@ -707,10 +720,11 @@ class QAPISchemaCommand(QAPISchemaEntity): "command's 'returns' cannot take %s" % self.ret_type.describe()) - # Features are in a name space separate from members - seen = {} - for f in self.features: - f.check_clash(self.info, seen) + def connect_doc(self, doc=None): + doc = doc or self.doc + if doc: + if self.arg_type and self.arg_type.is_implicit(): + self.arg_type.connect_doc(doc) def visit(self, visitor): QAPISchemaEntity.visit(self, visitor) @@ -748,6 +762,12 @@ class QAPISchemaEvent(QAPISchemaEntity): "event's 'data' can take %s only with 'boxed': true" % self.arg_type.describe()) + def connect_doc(self, doc=None): + doc = doc or self.doc + if doc: + if self.arg_type and self.arg_type.is_implicit(): + self.arg_type.connect_doc(doc) + def visit(self, visitor): QAPISchemaEntity.visit(self, visitor) visitor.visit_event(self.name, self.info, self.ifcond, @@ -873,8 +893,7 @@ class QAPISchema(object): self._def_entity(QAPISchemaArrayType(name, info, element_type)) return name - def _make_implicit_object_type(self, name, info, doc, ifcond, - role, members): + def _make_implicit_object_type(self, name, info, ifcond, role, members): if not members: return None # See also QAPISchemaObjectTypeMember.describe() @@ -892,7 +911,7 @@ class QAPISchema(object): # TODO kill simple unions or implement the disjunction assert (ifcond or []) == typ._ifcond # pylint: disable=protected-access else: - self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, + self._def_entity(QAPISchemaObjectType(name, info, None, ifcond, None, members, None, [])) return name @@ -939,7 +958,7 @@ class QAPISchema(object): assert len(typ) == 1 typ = self._make_array_type(typ[0], info) typ = self._make_implicit_object_type( - typ, info, None, self.lookup_type(typ), + typ, info, self.lookup_type(typ), 'wrapper', [self._make_member('data', typ, None, info)]) return QAPISchemaObjectTypeVariant(case, info, typ, ifcond) @@ -952,7 +971,7 @@ class QAPISchema(object): tag_member = None if isinstance(base, dict): base = self._make_implicit_object_type( - name, info, doc, ifcond, + name, info, ifcond, 'base', self._make_members(base, info)) if tag_name: variants = [self._make_variant(key, value['type'], @@ -999,7 +1018,7 @@ class QAPISchema(object): features = expr.get('features', []) if isinstance(data, OrderedDict): data = self._make_implicit_object_type( - name, info, doc, ifcond, 'arg', self._make_members(data, info)) + name, info, ifcond, 'arg', self._make_members(data, info)) if isinstance(rets, list): assert len(rets) == 1 rets = self._make_array_type(rets[0], info) @@ -1015,7 +1034,7 @@ class QAPISchema(object): ifcond = expr.get('if') if isinstance(data, OrderedDict): data = self._make_implicit_object_type( - name, info, doc, ifcond, 'arg', self._make_members(data, info)) + name, info, ifcond, 'arg', self._make_members(data, info)) self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed)) def _def_exprs(self, exprs): @@ -1043,6 +1062,8 @@ class QAPISchema(object): def check(self): for ent in self._entity_list: ent.check(self) + ent.connect_doc() + ent.check_doc() def visit(self, visitor): visitor.visit_begin(self) diff --git a/tests/Makefile.include b/tests/Makefile.include index 7f487f65e7..34ec03391c 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -341,7 +341,11 @@ qapi-schema += base-cycle-indirect.json qapi-schema += command-int.json qapi-schema += comments.json qapi-schema += doc-bad-alternate-member.json +qapi-schema += doc-bad-boxed-command-arg.json qapi-schema += doc-bad-command-arg.json +qapi-schema += doc-bad-enum-member.json +qapi-schema += doc-bad-event-arg.json +qapi-schema += doc-bad-feature.json qapi-schema += doc-bad-section.json qapi-schema += doc-bad-symbol.json qapi-schema += doc-bad-union-member.json @@ -365,6 +369,7 @@ qapi-schema += doc-missing-expr.json qapi-schema += doc-missing-space.json qapi-schema += doc-missing.json qapi-schema += doc-no-symbol.json +qapi-schema += doc-undoc-feature.json qapi-schema += double-type.json qapi-schema += duplicate-key.json qapi-schema += empty.json diff --git a/tests/qapi-schema/doc-bad-alternate-member.err b/tests/qapi-schema/doc-bad-alternate-member.err index a1c282f935..d7286bb57c 100644 --- a/tests/qapi-schema/doc-bad-alternate-member.err +++ b/tests/qapi-schema/doc-bad-alternate-member.err @@ -1 +1 @@ -doc-bad-alternate-member.json:3: the following documented members are not in the declaration: aa, bb +doc-bad-alternate-member.json:3: documented members 'aa', 'bb' do not exist diff --git a/tests/qapi-schema/doc-bad-boxed-command-arg.err b/tests/qapi-schema/doc-bad-boxed-command-arg.err new file mode 100644 index 0000000000..7137af3ec9 --- /dev/null +++ b/tests/qapi-schema/doc-bad-boxed-command-arg.err @@ -0,0 +1 @@ +doc-bad-boxed-command-arg.json:9: documented member 'a' does not exist diff --git a/tests/qapi-schema/doc-bad-boxed-command-arg.json b/tests/qapi-schema/doc-bad-boxed-command-arg.json new file mode 100644 index 0000000000..bd143241ec --- /dev/null +++ b/tests/qapi-schema/doc-bad-boxed-command-arg.json @@ -0,0 +1,14 @@ +# Boxed arguments are not to be documented with the command + +## +# @Args: +# @a: an argument +## +{ 'struct': 'Args', 'data': { 'a': 'int' } } + +## +# @cmd-boxed: +# @a: bogus +## +{ 'command': 'cmd-boxed', 'boxed': true, + 'data': 'Args' } diff --git a/tests/qapi-schema/doc-bad-boxed-command-arg.out b/tests/qapi-schema/doc-bad-boxed-command-arg.out new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/qapi-schema/doc-bad-boxed-command-arg.out diff --git a/tests/qapi-schema/doc-bad-command-arg.err b/tests/qapi-schema/doc-bad-command-arg.err index 153ea0330a..18ed076cef 100644 --- a/tests/qapi-schema/doc-bad-command-arg.err +++ b/tests/qapi-schema/doc-bad-command-arg.err @@ -1 +1 @@ -doc-bad-command-arg.json:3: the following documented members are not in the declaration: b +doc-bad-command-arg.json:3: documented member 'b' does not exist diff --git a/tests/qapi-schema/doc-bad-enum-member.err b/tests/qapi-schema/doc-bad-enum-member.err new file mode 100644 index 0000000000..7efeb47363 --- /dev/null +++ b/tests/qapi-schema/doc-bad-enum-member.err @@ -0,0 +1 @@ +doc-bad-enum-member.json:3: documented member 'a' does not exist diff --git a/tests/qapi-schema/doc-bad-enum-member.json b/tests/qapi-schema/doc-bad-enum-member.json new file mode 100644 index 0000000000..9cab35c6e8 --- /dev/null +++ b/tests/qapi-schema/doc-bad-enum-member.json @@ -0,0 +1,8 @@ +# Members listed in the doc comment must exist in the actual schema + +## +# @Foo: +# @a: a +# @b: b +## +{ 'enum': 'Foo', 'data': [ 'b' ] } diff --git a/tests/qapi-schema/doc-bad-enum-member.out b/tests/qapi-schema/doc-bad-enum-member.out new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/qapi-schema/doc-bad-enum-member.out diff --git a/tests/qapi-schema/doc-bad-event-arg.err b/tests/qapi-schema/doc-bad-event-arg.err new file mode 100644 index 0000000000..d13cacf21f --- /dev/null +++ b/tests/qapi-schema/doc-bad-event-arg.err @@ -0,0 +1 @@ +doc-bad-event-arg.json:3: documented member 'a' does not exist diff --git a/tests/qapi-schema/doc-bad-event-arg.json b/tests/qapi-schema/doc-bad-event-arg.json new file mode 100644 index 0000000000..23c83cc81f --- /dev/null +++ b/tests/qapi-schema/doc-bad-event-arg.json @@ -0,0 +1,7 @@ +# Arguments listed in the doc comment must exist in the actual schema + +## +# @FOO: +# @a: a +## +{ 'event': 'FOO' } diff --git a/tests/qapi-schema/doc-bad-event-arg.out b/tests/qapi-schema/doc-bad-event-arg.out new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/qapi-schema/doc-bad-event-arg.out diff --git a/tests/qapi-schema/doc-bad-feature.err b/tests/qapi-schema/doc-bad-feature.err new file mode 100644 index 0000000000..e4c62adfa3 --- /dev/null +++ b/tests/qapi-schema/doc-bad-feature.err @@ -0,0 +1 @@ +doc-bad-feature.json:3: documented member 'a' does not exist diff --git a/tests/qapi-schema/doc-bad-feature.json b/tests/qapi-schema/doc-bad-feature.json new file mode 100644 index 0000000000..3d49b8e607 --- /dev/null +++ b/tests/qapi-schema/doc-bad-feature.json @@ -0,0 +1,9 @@ +# Features listed in the doc comment must exist in the actual schema + +## +# @foo: +# +# Features: +# @a: a +## +{ 'command': 'foo' } diff --git a/tests/qapi-schema/doc-bad-feature.out b/tests/qapi-schema/doc-bad-feature.out new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/qapi-schema/doc-bad-feature.out diff --git a/tests/qapi-schema/doc-bad-union-member.err b/tests/qapi-schema/doc-bad-union-member.err index 8b9d36eab1..6dd2726a65 100644 --- a/tests/qapi-schema/doc-bad-union-member.err +++ b/tests/qapi-schema/doc-bad-union-member.err @@ -1 +1 @@ -doc-bad-union-member.json:3: the following documented members are not in the declaration: a, b +doc-bad-union-member.json:3: documented members 'a', 'b' do not exist diff --git a/tests/qapi-schema/doc-good.json b/tests/qapi-schema/doc-good.json index 7dc21e58a3..d992e713d9 100644 --- a/tests/qapi-schema/doc-good.json +++ b/tests/qapi-schema/doc-good.json @@ -99,6 +99,14 @@ 'data': { 'one': 'Variant1', 'two': { 'type': 'Variant2', 'if': 'IFTWO' } } } ## +# @Alternate: +# @i: an integer +# @b is undocumented +## +{ 'alternate': 'Alternate', + 'data': { 'i': 'int', 'b': 'bool' } } + +## # == Another subsection ## @@ -149,3 +157,9 @@ { 'command': 'cmd-boxed', 'boxed': true, 'data': 'Object', 'features': [ 'cmd-feat1', 'cmd-feat2' ] } + +## +# @EVT-BOXED: +## +{ 'event': 'EVT-BOXED', 'boxed': true, + 'data': 'Object' } diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out index f78fdef6a9..4c9406a464 100644 --- a/tests/qapi-schema/doc-good.out +++ b/tests/qapi-schema/doc-good.out @@ -42,6 +42,10 @@ object SugaredUnion case one: q_obj_Variant1-wrapper case two: q_obj_Variant2-wrapper if ['IFTWO'] +alternate Alternate + tag type + case i: int + case b: bool object q_obj_cmd-arg member arg1: int optional=False member arg2: str optional=True @@ -54,6 +58,8 @@ command cmd-boxed Object -> None gen=True success_response=True boxed=True oob=False preconfig=False feature cmd-feat1 feature cmd-feat2 +event EVT-BOXED Object + boxed=True doc freeform body= = Section @@ -120,6 +126,8 @@ A paragraph Another paragraph (but no @var: line) arg=var1 + feature=variant1-feat +a feature doc symbol=Variant2 body= @@ -131,6 +139,14 @@ doc symbol=SugaredUnion arg=type +doc symbol=Alternate + body= + + arg=i +an integer +@b is undocumented + arg=b + doc freeform body= == Another subsection @@ -144,6 +160,10 @@ the second argument arg=arg3 + feature=cmd-feat1 +a feature + feature=cmd-feat2 +another feature section=Note @arg3 is undocumented section=Returns @@ -166,7 +186,14 @@ Duis aute irure dolor doc symbol=cmd-boxed body= If you're bored enough to read this, go see a video of boxed cats + feature=cmd-feat1 +a feature + feature=cmd-feat2 +another feature section=Example -> in <- out +doc symbol=EVT-BOXED + body= + diff --git a/tests/qapi-schema/doc-good.texi b/tests/qapi-schema/doc-good.texi index 2ce8b883c9..d4b15dabf0 100644 --- a/tests/qapi-schema/doc-good.texi +++ b/tests/qapi-schema/doc-good.texi @@ -170,6 +170,23 @@ One of @t{"one"}, @t{"two"} @end deftp + +@deftp {Alternate} Alternate + + + +@b{Members:} +@table @asis +@item @code{i: int} +an integer +@code{b} is undocumented +@item @code{b: boolean} +Not documented +@end table + +@end deftp + + @subsection Another subsection @@ -258,3 +275,13 @@ another feature @end deftypefn + + +@deftypefn Event {} EVT-BOXED + + + +@b{Arguments:} the members of @code{Object} + +@end deftypefn + diff --git a/tests/qapi-schema/doc-undoc-feature.err b/tests/qapi-schema/doc-undoc-feature.err new file mode 100644 index 0000000000..62fc82d2b9 --- /dev/null +++ b/tests/qapi-schema/doc-undoc-feature.err @@ -0,0 +1,2 @@ +doc-undoc-feature.json: In command 'foo': +doc-undoc-feature.json:9: feature 'undoc' lacks documentation diff --git a/tests/qapi-schema/doc-undoc-feature.json b/tests/qapi-schema/doc-undoc-feature.json new file mode 100644 index 0000000000..c52f88e2cd --- /dev/null +++ b/tests/qapi-schema/doc-undoc-feature.json @@ -0,0 +1,9 @@ +# Doc comment must cover all features + +## +# @foo: +# +# Features: +# @doc: documented feature +## +{ 'command': 'foo', 'features': ['undoc', 'doc'] } diff --git a/tests/qapi-schema/doc-undoc-feature.out b/tests/qapi-schema/doc-undoc-feature.out new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/qapi-schema/doc-undoc-feature.out diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py index 2bd9fd8742..bad14edb47 100755 --- a/tests/qapi-schema/test-qapi.py +++ b/tests/qapi-schema/test-qapi.py @@ -117,6 +117,8 @@ def test_frontend(fname): print(' body=\n%s' % doc.body.text) for arg, section in doc.args.items(): print(' arg=%s\n%s' % (arg, section.text)) + for feat, section in doc.features.items(): + print(' feature=%s\n%s' % (feat, section.text)) for section in doc.sections: print(' section=%s\n%s' % (section.name, section.text)) |