Commit 16884391 authored by Peter Maydell's avatar Peter Maydell
Browse files

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: default avatarPeter Maydell <peter.maydell@linaro.org>
parents 1cfe28cd e151941d
Loading
Loading
Loading
Loading
+0 −2
Original line number Diff line number Diff line
@@ -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",
+12 −0
Original line number Diff line number Diff line
@@ -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
+41 −34
Original line number Diff line number Diff line
@@ -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,9 +246,8 @@ 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,
        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,
@@ -237,38 +255,27 @@ class QAPISchemaGenDocVisitor(QAPISchemaVisitor):
        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:
+5 −27
Original line number Diff line number Diff line
@@ -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)

+22 −7
Original line number Diff line number Diff line
@@ -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()

        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,
                "the following documented members are not in "
                "the declaration: %s" % ", ".join(bogus))
                    "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')
Loading