qapi: add qapi2texi script
authorMarc-André Lureau <marcandre.lureau@redhat.com>
Fri, 13 Jan 2017 14:41:29 +0000 (15:41 +0100)
committerMarkus Armbruster <armbru@redhat.com>
Mon, 16 Jan 2017 09:10:35 +0000 (10:10 +0100)
As the name suggests, the qapi2texi script converts JSON QAPI
description into a texi file suitable for different target
formats (info/man/txt/pdf/html...).

It parses the following kind of blocks:

Free-form:

  ##
  # = Section
  # == Subsection
  #
  # Some text foo with *emphasis*
  # 1. with a list
  # 2. like that
  #
  # And some code:
  # | $ echo foo
  # | -> do this
  # | <- get that
  #
  ##

Symbol description:

  ##
  # @symbol:
  #
  # Symbol body ditto ergo sum. Foo bar
  # baz ding.
  #
  # @param1: the frob to frobnicate
  # @param2: #optional how hard to frobnicate
  #
  # Returns: the frobnicated frob.
  #          If frob isn't frobnicatable, GenericError.
  #
  # Since: version
  # Notes: notes, comments can have
  #        - itemized list
  #        - like this
  #
  # Example:
  #
  # -> { "execute": "quit" }
  # <- { "return": {} }
  #
  ##

That's roughly following the following EBNF grammar:

api_comment = "##\n" comment "##\n"
comment = freeform_comment | symbol_comment
freeform_comment = { "# " text "\n" | "#\n" }
symbol_comment = "# @" name ":\n" { member | tag_section | freeform_comment }
member = "# @" name ':' [ text ] "\n" freeform_comment
tag_section = "# " ( "Returns:", "Since:", "Note:", "Notes:", "Example:", "Examples:" ) [ text ]  "\n" freeform_comment
text = free text with markup

Note that the grammar is ambiguous: a line "# @foo:\n" can be parsed
both as freeform_comment and as symbol_comment.  The actual parser
recognizes symbol_comment.

See docs/qapi-code-gen.txt for more details.

Deficiencies and limitations:
- the generated QMP documentation includes internal types
- union type support is lacking
- type information is lacking in generated documentation
- doc comment error message positions are imprecise, they point
  to the beginning of the comment.
- a few minor issues, all marked TODO/FIXME in the code

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Message-Id: <20170113144135.5150-16-marcandre.lureau@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
[test-qapi.py tweaked to avoid trailing empty lines in .out]
Signed-off-by: Markus Armbruster <armbru@redhat.com>
276 files changed:
docs/qapi-code-gen.txt
scripts/qapi.py
scripts/qapi2texi.py [new file with mode: 0755]
tests/Makefile.include
tests/qapi-schema/alternate-any.err
tests/qapi-schema/alternate-any.json
tests/qapi-schema/alternate-array.err
tests/qapi-schema/alternate-array.json
tests/qapi-schema/alternate-base.err
tests/qapi-schema/alternate-base.json
tests/qapi-schema/alternate-clash.err
tests/qapi-schema/alternate-clash.json
tests/qapi-schema/alternate-conflict-dict.err
tests/qapi-schema/alternate-conflict-dict.json
tests/qapi-schema/alternate-conflict-string.err
tests/qapi-schema/alternate-conflict-string.json
tests/qapi-schema/alternate-empty.err
tests/qapi-schema/alternate-empty.json
tests/qapi-schema/alternate-nested.err
tests/qapi-schema/alternate-nested.json
tests/qapi-schema/alternate-unknown.err
tests/qapi-schema/alternate-unknown.json
tests/qapi-schema/args-alternate.err
tests/qapi-schema/args-alternate.json
tests/qapi-schema/args-any.err
tests/qapi-schema/args-any.json
tests/qapi-schema/args-array-empty.err
tests/qapi-schema/args-array-empty.json
tests/qapi-schema/args-array-unknown.err
tests/qapi-schema/args-array-unknown.json
tests/qapi-schema/args-bad-boxed.err
tests/qapi-schema/args-bad-boxed.json
tests/qapi-schema/args-boxed-anon.err
tests/qapi-schema/args-boxed-anon.json
tests/qapi-schema/args-boxed-empty.err
tests/qapi-schema/args-boxed-empty.json
tests/qapi-schema/args-boxed-string.err
tests/qapi-schema/args-boxed-string.json
tests/qapi-schema/args-int.err
tests/qapi-schema/args-int.json
tests/qapi-schema/args-invalid.err
tests/qapi-schema/args-invalid.json
tests/qapi-schema/args-member-array-bad.err
tests/qapi-schema/args-member-array-bad.json
tests/qapi-schema/args-member-case.err
tests/qapi-schema/args-member-case.json
tests/qapi-schema/args-member-unknown.err
tests/qapi-schema/args-member-unknown.json
tests/qapi-schema/args-name-clash.err
tests/qapi-schema/args-name-clash.json
tests/qapi-schema/args-union.err
tests/qapi-schema/args-union.json
tests/qapi-schema/args-unknown.err
tests/qapi-schema/args-unknown.json
tests/qapi-schema/bad-base.err
tests/qapi-schema/bad-base.json
tests/qapi-schema/bad-data.err
tests/qapi-schema/bad-data.json
tests/qapi-schema/bad-ident.err
tests/qapi-schema/bad-ident.json
tests/qapi-schema/bad-type-bool.err
tests/qapi-schema/bad-type-bool.json
tests/qapi-schema/bad-type-dict.err
tests/qapi-schema/bad-type-dict.json
tests/qapi-schema/base-cycle-direct.err
tests/qapi-schema/base-cycle-direct.json
tests/qapi-schema/base-cycle-indirect.err
tests/qapi-schema/base-cycle-indirect.json
tests/qapi-schema/command-int.err
tests/qapi-schema/command-int.json
tests/qapi-schema/comments.json
tests/qapi-schema/comments.out
tests/qapi-schema/doc-bad-args.err [new file with mode: 0644]
tests/qapi-schema/doc-bad-args.exit [new file with mode: 0644]
tests/qapi-schema/doc-bad-args.json [new file with mode: 0644]
tests/qapi-schema/doc-bad-args.out [new file with mode: 0644]
tests/qapi-schema/doc-bad-symbol.err [new file with mode: 0644]
tests/qapi-schema/doc-bad-symbol.exit [new file with mode: 0644]
tests/qapi-schema/doc-bad-symbol.json [new file with mode: 0644]
tests/qapi-schema/doc-bad-symbol.out [new file with mode: 0644]
tests/qapi-schema/doc-duplicated-arg.err [new file with mode: 0644]
tests/qapi-schema/doc-duplicated-arg.exit [new file with mode: 0644]
tests/qapi-schema/doc-duplicated-arg.json [new file with mode: 0644]
tests/qapi-schema/doc-duplicated-arg.out [new file with mode: 0644]
tests/qapi-schema/doc-duplicated-return.err [new file with mode: 0644]
tests/qapi-schema/doc-duplicated-return.exit [new file with mode: 0644]
tests/qapi-schema/doc-duplicated-return.json [new file with mode: 0644]
tests/qapi-schema/doc-duplicated-return.out [new file with mode: 0644]
tests/qapi-schema/doc-duplicated-since.err [new file with mode: 0644]
tests/qapi-schema/doc-duplicated-since.exit [new file with mode: 0644]
tests/qapi-schema/doc-duplicated-since.json [new file with mode: 0644]
tests/qapi-schema/doc-duplicated-since.out [new file with mode: 0644]
tests/qapi-schema/doc-empty-arg.err [new file with mode: 0644]
tests/qapi-schema/doc-empty-arg.exit [new file with mode: 0644]
tests/qapi-schema/doc-empty-arg.json [new file with mode: 0644]
tests/qapi-schema/doc-empty-arg.out [new file with mode: 0644]
tests/qapi-schema/doc-empty-section.err [new file with mode: 0644]
tests/qapi-schema/doc-empty-section.exit [new file with mode: 0644]
tests/qapi-schema/doc-empty-section.json [new file with mode: 0644]
tests/qapi-schema/doc-empty-section.out [new file with mode: 0644]
tests/qapi-schema/doc-empty-symbol.err [new file with mode: 0644]
tests/qapi-schema/doc-empty-symbol.exit [new file with mode: 0644]
tests/qapi-schema/doc-empty-symbol.json [new file with mode: 0644]
tests/qapi-schema/doc-empty-symbol.out [new file with mode: 0644]
tests/qapi-schema/doc-interleaved-section.err [new file with mode: 0644]
tests/qapi-schema/doc-interleaved-section.exit [new file with mode: 0644]
tests/qapi-schema/doc-interleaved-section.json [new file with mode: 0644]
tests/qapi-schema/doc-interleaved-section.out [new file with mode: 0644]
tests/qapi-schema/doc-invalid-end.err [new file with mode: 0644]
tests/qapi-schema/doc-invalid-end.exit [new file with mode: 0644]
tests/qapi-schema/doc-invalid-end.json [new file with mode: 0644]
tests/qapi-schema/doc-invalid-end.out [new file with mode: 0644]
tests/qapi-schema/doc-invalid-end2.err [new file with mode: 0644]
tests/qapi-schema/doc-invalid-end2.exit [new file with mode: 0644]
tests/qapi-schema/doc-invalid-end2.json [new file with mode: 0644]
tests/qapi-schema/doc-invalid-end2.out [new file with mode: 0644]
tests/qapi-schema/doc-invalid-return.err [new file with mode: 0644]
tests/qapi-schema/doc-invalid-return.exit [new file with mode: 0644]
tests/qapi-schema/doc-invalid-return.json [new file with mode: 0644]
tests/qapi-schema/doc-invalid-return.out [new file with mode: 0644]
tests/qapi-schema/doc-invalid-section.err [new file with mode: 0644]
tests/qapi-schema/doc-invalid-section.exit [new file with mode: 0644]
tests/qapi-schema/doc-invalid-section.json [new file with mode: 0644]
tests/qapi-schema/doc-invalid-section.out [new file with mode: 0644]
tests/qapi-schema/doc-invalid-start.err [new file with mode: 0644]
tests/qapi-schema/doc-invalid-start.exit [new file with mode: 0644]
tests/qapi-schema/doc-invalid-start.json [new file with mode: 0644]
tests/qapi-schema/doc-invalid-start.out [new file with mode: 0644]
tests/qapi-schema/doc-missing-colon.err [new file with mode: 0644]
tests/qapi-schema/doc-missing-colon.exit [new file with mode: 0644]
tests/qapi-schema/doc-missing-colon.json [new file with mode: 0644]
tests/qapi-schema/doc-missing-colon.out [new file with mode: 0644]
tests/qapi-schema/doc-missing-expr.err [new file with mode: 0644]
tests/qapi-schema/doc-missing-expr.exit [new file with mode: 0644]
tests/qapi-schema/doc-missing-expr.json [new file with mode: 0644]
tests/qapi-schema/doc-missing-expr.out [new file with mode: 0644]
tests/qapi-schema/doc-missing-space.err [new file with mode: 0644]
tests/qapi-schema/doc-missing-space.exit [new file with mode: 0644]
tests/qapi-schema/doc-missing-space.json [new file with mode: 0644]
tests/qapi-schema/doc-missing-space.out [new file with mode: 0644]
tests/qapi-schema/doc-optional.err [new file with mode: 0644]
tests/qapi-schema/doc-optional.exit [new file with mode: 0644]
tests/qapi-schema/doc-optional.json [new file with mode: 0644]
tests/qapi-schema/doc-optional.out [new file with mode: 0644]
tests/qapi-schema/double-type.err
tests/qapi-schema/double-type.json
tests/qapi-schema/enum-bad-name.err
tests/qapi-schema/enum-bad-name.json
tests/qapi-schema/enum-bad-prefix.err
tests/qapi-schema/enum-bad-prefix.json
tests/qapi-schema/enum-clash-member.err
tests/qapi-schema/enum-clash-member.json
tests/qapi-schema/enum-dict-member.err
tests/qapi-schema/enum-dict-member.json
tests/qapi-schema/enum-member-case.err
tests/qapi-schema/enum-member-case.json
tests/qapi-schema/enum-missing-data.err
tests/qapi-schema/enum-missing-data.json
tests/qapi-schema/enum-wrong-data.err
tests/qapi-schema/enum-wrong-data.json
tests/qapi-schema/event-boxed-empty.err
tests/qapi-schema/event-boxed-empty.json
tests/qapi-schema/event-case.json
tests/qapi-schema/event-case.out
tests/qapi-schema/event-nest-struct.err
tests/qapi-schema/event-nest-struct.json
tests/qapi-schema/flat-union-array-branch.err
tests/qapi-schema/flat-union-array-branch.json
tests/qapi-schema/flat-union-bad-base.err
tests/qapi-schema/flat-union-bad-base.json
tests/qapi-schema/flat-union-bad-discriminator.err
tests/qapi-schema/flat-union-bad-discriminator.json
tests/qapi-schema/flat-union-base-any.err
tests/qapi-schema/flat-union-base-any.json
tests/qapi-schema/flat-union-base-union.err
tests/qapi-schema/flat-union-base-union.json
tests/qapi-schema/flat-union-clash-member.err
tests/qapi-schema/flat-union-clash-member.json
tests/qapi-schema/flat-union-empty.err
tests/qapi-schema/flat-union-empty.json
tests/qapi-schema/flat-union-incomplete-branch.err
tests/qapi-schema/flat-union-incomplete-branch.json
tests/qapi-schema/flat-union-inline.err
tests/qapi-schema/flat-union-inline.json
tests/qapi-schema/flat-union-int-branch.err
tests/qapi-schema/flat-union-int-branch.json
tests/qapi-schema/flat-union-invalid-branch-key.err
tests/qapi-schema/flat-union-invalid-branch-key.json
tests/qapi-schema/flat-union-invalid-discriminator.err
tests/qapi-schema/flat-union-invalid-discriminator.json
tests/qapi-schema/flat-union-no-base.err
tests/qapi-schema/flat-union-no-base.json
tests/qapi-schema/flat-union-optional-discriminator.err
tests/qapi-schema/flat-union-optional-discriminator.json
tests/qapi-schema/flat-union-string-discriminator.err
tests/qapi-schema/flat-union-string-discriminator.json
tests/qapi-schema/ident-with-escape.json
tests/qapi-schema/ident-with-escape.out
tests/qapi-schema/include-relpath-sub.json
tests/qapi-schema/include-relpath.out
tests/qapi-schema/include-repetition.out
tests/qapi-schema/include-simple-sub.json
tests/qapi-schema/include-simple.out
tests/qapi-schema/indented-expr.json
tests/qapi-schema/indented-expr.out
tests/qapi-schema/missing-type.err
tests/qapi-schema/missing-type.json
tests/qapi-schema/nested-struct-data.err
tests/qapi-schema/nested-struct-data.json
tests/qapi-schema/qapi-schema-test.json
tests/qapi-schema/qapi-schema-test.out
tests/qapi-schema/redefined-builtin.err
tests/qapi-schema/redefined-builtin.json
tests/qapi-schema/redefined-command.err
tests/qapi-schema/redefined-command.json
tests/qapi-schema/redefined-event.err
tests/qapi-schema/redefined-event.json
tests/qapi-schema/redefined-type.err
tests/qapi-schema/redefined-type.json
tests/qapi-schema/reserved-command-q.err
tests/qapi-schema/reserved-command-q.json
tests/qapi-schema/reserved-enum-q.err
tests/qapi-schema/reserved-enum-q.json
tests/qapi-schema/reserved-member-has.err
tests/qapi-schema/reserved-member-has.json
tests/qapi-schema/reserved-member-q.err
tests/qapi-schema/reserved-member-q.json
tests/qapi-schema/reserved-member-u.err
tests/qapi-schema/reserved-member-u.json
tests/qapi-schema/reserved-member-underscore.err
tests/qapi-schema/reserved-member-underscore.json
tests/qapi-schema/reserved-type-kind.err
tests/qapi-schema/reserved-type-kind.json
tests/qapi-schema/reserved-type-list.err
tests/qapi-schema/reserved-type-list.json
tests/qapi-schema/returns-alternate.err
tests/qapi-schema/returns-alternate.json
tests/qapi-schema/returns-array-bad.err
tests/qapi-schema/returns-array-bad.json
tests/qapi-schema/returns-dict.err
tests/qapi-schema/returns-dict.json
tests/qapi-schema/returns-unknown.err
tests/qapi-schema/returns-unknown.json
tests/qapi-schema/returns-whitelist.err
tests/qapi-schema/returns-whitelist.json
tests/qapi-schema/struct-base-clash-deep.err
tests/qapi-schema/struct-base-clash-deep.json
tests/qapi-schema/struct-base-clash.err
tests/qapi-schema/struct-base-clash.json
tests/qapi-schema/struct-data-invalid.err
tests/qapi-schema/struct-data-invalid.json
tests/qapi-schema/struct-member-invalid.err
tests/qapi-schema/struct-member-invalid.json
tests/qapi-schema/test-qapi.py
tests/qapi-schema/type-bypass-bad-gen.err
tests/qapi-schema/type-bypass-bad-gen.json
tests/qapi-schema/unicode-str.err
tests/qapi-schema/unicode-str.json
tests/qapi-schema/union-base-no-discriminator.err
tests/qapi-schema/union-base-no-discriminator.json
tests/qapi-schema/union-branch-case.err
tests/qapi-schema/union-branch-case.json
tests/qapi-schema/union-clash-branches.err
tests/qapi-schema/union-clash-branches.json
tests/qapi-schema/union-empty.err
tests/qapi-schema/union-empty.json
tests/qapi-schema/union-invalid-base.err
tests/qapi-schema/union-invalid-base.json
tests/qapi-schema/union-optional-branch.err
tests/qapi-schema/union-optional-branch.json
tests/qapi-schema/union-unknown.err
tests/qapi-schema/union-unknown.json
tests/qapi-schema/unknown-escape.err
tests/qapi-schema/unknown-escape.json
tests/qapi-schema/unknown-expr-key.err
tests/qapi-schema/unknown-expr-key.json

index 2841c5144a38ab5ccea8e25d656e1046b0c4ece7..7eb7be12abe66d85626005444928859a5ae55d21 100644 (file)
@@ -44,40 +44,154 @@ Input must be ASCII (although QMP supports full Unicode strings, the
 QAPI parser does not).  At present, there is no place where a QAPI
 schema requires the use of JSON numbers or null.
 
+
+=== Comments ===
+
 Comments are allowed; anything between an unquoted # and the following
-newline is ignored.  Although there is not yet a documentation
-generator, a form of stylized comments has developed for consistently
-documenting details about an expression and when it was added to the
-schema.  The documentation is delimited between two lines of ##, then
-the first line names the expression, an optional overview is provided,
-then individual documentation about each member of 'data' is provided,
-and finally, a 'Since: x.y.z' tag lists the release that introduced
-the expression.  Optional members are tagged with the phrase
-'#optional', often with their default value; and extensions added
-after the expression was first released are also given a '(since
-x.y.z)' comment.  For example:
-
-    ##
-    # @BlockStats:
-    #
-    # Statistics of a virtual block device or a block backing device.
-    #
-    # @device: #optional If the stats are for a virtual block device, the name
-    #          corresponding to the virtual block device.
-    #
-    # @stats:  A @BlockDeviceStats for the device.
-    #
-    # @parent: #optional This describes the file block device if it has one.
-    #
-    # @backing: #optional This describes the backing block device if it has one.
-    #           (Since 2.0)
-    #
-    # Since: 0.14.0
-    ##
-    { 'struct': 'BlockStats',
-      'data': {'*device': 'str', 'stats': 'BlockDeviceStats',
-               '*parent': 'BlockStats',
-               '*backing': 'BlockStats'} }
+newline is ignored.
+
+A multi-line comment that starts and ends with a '##' line is a
+documentation comment.  These are parsed by the documentation
+generator, which recognizes certain markup detailed below.
+
+
+==== Documentation markup ====
+
+Comment text starting with '=' is a section title:
+
+    # = Section title
+
+Double the '=' for a subsection title:
+
+    # == Subection title
+
+'|' denotes examples:
+
+    # | Text of the example, may span
+    # | multiple lines
+
+'*' starts an itemized list:
+
+    # * First item, may span
+    #   multiple lines
+    # * Second item
+
+You can also use '-' instead of '*'.
+
+A decimal number followed by '.' starts a numbered list:
+
+    # 1. First item, may span
+    #    multiple lines
+    # 2. Second item
+
+The actual number doesn't matter.  You could even use '*' instead of
+'2.' for the second item.
+
+Lists can't be nested.  Blank lines are currently not supported within
+lists.
+
+Additional whitespace between the initial '#' and the comment text is
+permitted.
+
+*foo* and _foo_ are for strong and emphasis styles respectively (they
+do not work over multiple lines). @foo is used to reference a name in
+the schema.
+
+Example:
+
+##
+# = Section
+# == Subsection
+#
+# Some text foo with *strong* and _emphasis_
+# 1. with a list
+# 2. like that
+#
+# And some code:
+# | $ echo foo
+# | -> do this
+# | <- get that
+#
+##
+
+
+==== Expression documentation ====
+
+Each expression that isn't an include directive must be preceded by a
+documentation block.  Such blocks are called expression documentation
+blocks.
+
+The documentation block consists of a first line naming the
+expression, an optional overview, a description of each argument (for
+commands and events) or member (for structs, unions and alternates),
+and optional tagged sections.
+
+FIXME: the parser accepts these things in almost any order.
+
+Optional arguments / members are tagged with the phrase '#optional',
+often with their default value; and extensions added after the
+expression was first released are also given a '(since x.y.z)'
+comment.
+
+A tagged section starts with one of the following words:
+"Note:"/"Notes:", "Since:", "Example"/"Examples", "Returns:", "TODO:".
+The section ends with the start of a new section.
+
+A 'Since: x.y.z' tagged section lists the release that introduced the
+expression.
+
+For example:
+
+##
+# @BlockStats:
+#
+# Statistics of a virtual block device or a block backing device.
+#
+# @device: #optional If the stats are for a virtual block device, the name
+#          corresponding to the virtual block device.
+#
+# @node-name: #optional The node name of the device. (since 2.3)
+#
+# ... more members ...
+#
+# Since: 0.14.0
+##
+{ 'struct': 'BlockStats',
+  'data': {'*device': 'str', '*node-name': 'str',
+           ... more members ... } }
+
+##
+# @query-blockstats:
+#
+# Query the @BlockStats for all virtual block devices.
+#
+# @query-nodes: #optional If true, the command will query all the
+#               block nodes ... explain, explain ...  (since 2.3)
+#
+# Returns: A list of @BlockStats for each virtual block devices.
+#
+# Since: 0.14.0
+#
+# Example:
+#
+# -> { "execute": "query-blockstats" }
+# <- {
+#      ... lots of output ...
+#    }
+#
+##
+{ 'command': 'query-blockstats',
+  'data': { '*query-nodes': 'bool' },
+  'returns': ['BlockStats'] }
+
+==== Free-form documentation ====
+
+A documentation block that isn't an expression documentation block is
+a free-form documentation block.  These may be used to provide
+additional text and structuring content.
+
+
+=== Schema overview ===
 
 The schema sets up a series of types, as well as commands and events
 that will use those types.  Forward references are allowed: the parser
index 1483ec09f5a8ef9a4289ed8a67b1adcc6e7088e3..53a44779d0b513c011916d044ba76fb88fe481ac 100644 (file)
@@ -125,6 +125,122 @@ class QAPISemError(QAPIError):
                            info['parent'], msg)
 
 
+class QAPIDoc(object):
+    class Section(object):
+        def __init__(self, name=None):
+            # optional section name (argument/member or section name)
+            self.name = name
+            # the list of lines for this section
+            self.content = []
+
+        def append(self, line):
+            self.content.append(line)
+
+        def __repr__(self):
+            return "\n".join(self.content).strip()
+
+    class ArgSection(Section):
+        pass
+
+    def __init__(self, parser, info):
+        # self.parser is used to report errors with QAPIParseError.  The
+        # resulting error position depends on the state of the parser.
+        # It happens to be the beginning of the comment.  More or less
+        # servicable, but action at a distance.
+        self.parser = parser
+        self.info = info
+        self.symbol = None
+        self.body = QAPIDoc.Section()
+        # dict mapping parameter name to ArgSection
+        self.args = OrderedDict()
+        # a list of Section
+        self.sections = []
+        # the current section
+        self.section = self.body
+        # associated expression (to be set by expression parser)
+        self.expr = None
+
+    def has_section(self, name):
+        """Return True if we have a section with this name."""
+        for i in self.sections:
+            if i.name == name:
+                return True
+        return False
+
+    def append(self, line):
+        """Parse a comment line and add it to the documentation."""
+        line = line[1:]
+        if not line:
+            self._append_freeform(line)
+            return
+
+        if line[0] != ' ':
+            raise QAPIParseError(self.parser, "Missing space after #")
+        line = line[1:]
+
+        # FIXME not nice: things like '#  @foo:' and '# @foo: ' aren't
+        # recognized, and get silently treated as ordinary text
+        if self.symbol:
+            self._append_symbol_line(line)
+        elif not self.body.content and line.startswith("@"):
+            if not line.endswith(":"):
+                raise QAPIParseError(self.parser, "Line should end with :")
+            self.symbol = line[1:-1]
+            # FIXME invalid names other than the empty string aren't flagged
+            if not self.symbol:
+                raise QAPIParseError(self.parser, "Invalid name")
+        else:
+            self._append_freeform(line)
+
+    def _append_symbol_line(self, line):
+        name = line.split(' ', 1)[0]
+
+        if name.startswith("@") and name.endswith(":"):
+            line = line[len(name)+1:]
+            self._start_args_section(name[1:-1])
+        elif name in ("Returns:", "Since:",
+                      # those are often singular or plural
+                      "Note:", "Notes:",
+                      "Example:", "Examples:",
+                      "TODO:"):
+            line = line[len(name)+1:]
+            self._start_section(name[:-1])
+
+        self._append_freeform(line)
+
+    def _start_args_section(self, name):
+        # FIXME invalid names other than the empty string aren't flagged
+        if not name:
+            raise QAPIParseError(self.parser, "Invalid parameter name")
+        if name in self.args:
+            raise QAPIParseError(self.parser,
+                                 "'%s' parameter name duplicated" % name)
+        if self.sections:
+            raise QAPIParseError(self.parser,
+                                 "'@%s:' can't follow '%s' section"
+                                 % (name, self.sections[0].name))
+        self.section = QAPIDoc.ArgSection(name)
+        self.args[name] = self.section
+
+    def _start_section(self, name=""):
+        if name in ("Returns", "Since") and self.has_section(name):
+            raise QAPIParseError(self.parser,
+                                 "Duplicated '%s' section" % name)
+        self.section = QAPIDoc.Section(name)
+        self.sections.append(self.section)
+
+    def _append_freeform(self, line):
+        in_arg = isinstance(self.section, QAPIDoc.ArgSection)
+        if (in_arg and self.section.content
+                and not self.section.content[-1]
+                and line and not line[0].isspace()):
+            self._start_section()
+        if (in_arg or not self.section.name
+                or not self.section.name.startswith("Example")):
+            line = line.strip()
+        self.section.append(line)
+
+
 class QAPISchemaParser(object):
 
     def __init__(self, fp, previously_included=[], incl_info=None):
@@ -140,11 +256,17 @@ class QAPISchemaParser(object):
         self.line = 1
         self.line_pos = 0
         self.exprs = []
+        self.docs = []
         self.accept()
 
         while self.tok is not None:
             info = {'file': fname, 'line': self.line,
                     'parent': self.incl_info}
+            if self.tok == '#':
+                doc = self.get_doc(info)
+                self.docs.append(doc)
+                continue
+
             expr = self.get_expr(False)
             if isinstance(expr, dict) and "include" in expr:
                 if len(expr) != 1:
@@ -162,6 +284,7 @@ class QAPISchemaParser(object):
                         raise QAPISemError(info, "Inclusion loop for %s"
                                            % include)
                     inf = inf['parent']
+
                 # skip multiple include of the same file
                 if incl_abs_fname in previously_included:
                     continue
@@ -172,12 +295,19 @@ class QAPISchemaParser(object):
                 exprs_include = QAPISchemaParser(fobj, previously_included,
                                                  info)
                 self.exprs.extend(exprs_include.exprs)
+                self.docs.extend(exprs_include.docs)
             else:
                 expr_elem = {'expr': expr,
                              'info': info}
+                if (self.docs
+                        and self.docs[-1].info['file'] == fname
+                        and not self.docs[-1].expr):
+                    self.docs[-1].expr = expr
+                    expr_elem['doc'] = self.docs[-1]
+
                 self.exprs.append(expr_elem)
 
-    def accept(self):
+    def accept(self, skip_comment=True):
         while True:
             self.tok = self.src[self.cursor]
             self.pos = self.cursor
@@ -185,7 +315,13 @@ class QAPISchemaParser(object):
             self.val = None
 
             if self.tok == '#':
+                if self.src[self.cursor] == '#':
+                    # Start of doc comment
+                    skip_comment = False
                 self.cursor = self.src.find('\n', self.cursor)
+                if not skip_comment:
+                    self.val = self.src[self.pos:self.cursor]
+                    return
             elif self.tok in "{}:,[]":
                 return
             elif self.tok == "'":
@@ -319,6 +455,28 @@ class QAPISchemaParser(object):
             raise QAPIParseError(self, 'Expected "{", "[" or string')
         return expr
 
+    def get_doc(self, info):
+        if self.val != '##':
+            raise QAPIParseError(self, "Junk after '##' at start of "
+                                 "documentation comment")
+
+        doc = QAPIDoc(self, info)
+        self.accept(False)
+        while self.tok == '#':
+            if self.val.startswith('##'):
+                # End of doc comment
+                if self.val != '##':
+                    raise QAPIParseError(self, "Junk after '##' at end of "
+                                         "documentation comment")
+                self.accept()
+                return doc
+            else:
+                doc.append(self.val)
+            self.accept(False)
+
+        raise QAPIParseError(self, "Documentation comment must end with '##'")
+
+
 #
 # Semantic analysis of schema expressions
 # TODO fold into QAPISchema
@@ -703,6 +861,11 @@ def check_exprs(exprs):
     for expr_elem in exprs:
         expr = expr_elem['expr']
         info = expr_elem['info']
+
+        if 'doc' not in expr_elem:
+            raise QAPISemError(info,
+                               "Expression missing documentation comment")
+
         if 'enum' in expr:
             check_keys(expr_elem, 'enum', ['data'], ['prefix'])
             add_enum(expr['enum'], info, expr['data'])
@@ -761,6 +924,88 @@ def check_exprs(exprs):
     return exprs
 
 
+def check_freeform_doc(doc):
+    if doc.symbol:
+        raise QAPISemError(doc.info,
+                           "Documention for '%s' is not followed"
+                           " by the definition" % doc.symbol)
+
+    body = str(doc.body)
+    if re.search(r'@\S+:', body, re.MULTILINE):
+        raise QAPISemError(doc.info,
+                           "Free-form documentation block must not contain"
+                           " @NAME: sections")
+
+
+def check_definition_doc(doc, expr, info):
+    for i in ('enum', 'union', 'alternate', 'struct', 'command', 'event'):
+        if i in expr:
+            meta = i
+            break
+
+    name = expr[meta]
+    if doc.symbol != name:
+        raise QAPISemError(info, "Definition of '%s' follows documentation"
+                           " for '%s'" % (name, doc.symbol))
+    if doc.has_section('Returns') and 'command' not in expr:
+        raise QAPISemError(info, "'Returns:' is only valid for commands")
+
+    if meta == 'union':
+        args = expr.get('base', [])
+    else:
+        args = expr.get('data', [])
+    if isinstance(args, str):
+        return
+    if isinstance(args, dict):
+        args = args.keys()
+    assert isinstance(args, list)
+
+    if (meta == 'alternate'
+            or (meta == 'union' and not expr.get('discriminator'))):
+        args.append('type')
+
+    for arg in args:
+        if arg[0] == '*':
+            opt = True
+            desc = doc.args.get(arg[1:])
+        else:
+            opt = False
+            desc = doc.args.get(arg)
+        if not desc:
+            continue
+        desc_opt = "#optional" in str(desc)
+        if desc_opt and not opt:
+            raise QAPISemError(info, "Description has #optional, "
+                               "but the declaration doesn't")
+        if not desc_opt and opt:
+            # silently fix the doc
+            # TODO either fix the schema and make this an error,
+            # or drop #optional entirely
+            desc.append("#optional")
+
+    doc_args = set(doc.args.keys())
+    args = set([name.strip('*') for name in args])
+    if not doc_args.issubset(args):
+        raise QAPISemError(info, "The following documented members are not in "
+                           "the declaration: %s" % ", ".join(doc_args - args))
+
+
+def check_docs(docs):
+    for doc in docs:
+        for section in doc.args.values() + doc.sections:
+            content = str(section)
+            if not content or content.isspace():
+                raise QAPISemError(doc.info,
+                                   "Empty doc section '%s'" % section.name)
+
+        if not doc.expr:
+            check_freeform_doc(doc)
+        else:
+            check_definition_doc(doc, doc.expr, doc.info)
+
+    return docs
+
+
 #
 # Schema compiler frontend
 #
@@ -1229,7 +1474,9 @@ class QAPISchemaEvent(QAPISchemaEntity):
 class QAPISchema(object):
     def __init__(self, fname):
         try:
-            self.exprs = check_exprs(QAPISchemaParser(open(fname, "r")).exprs)
+            parser = QAPISchemaParser(open(fname, "r"))
+            self.exprs = check_exprs(parser.exprs)
+            self.docs = check_docs(parser.docs)
             self._entity_dict = {}
             self._predefining = True
             self._def_predefineds()
diff --git a/scripts/qapi2texi.py b/scripts/qapi2texi.py
new file mode 100755 (executable)
index 0000000..83ded95
--- /dev/null
@@ -0,0 +1,271 @@
+#!/usr/bin/env python
+# QAPI texi generator
+#
+# This work is licensed under the terms of the GNU LGPL, version 2+.
+# See the COPYING file in the top-level directory.
+"""This script produces the documentation of a qapi schema in texinfo format"""
+import re
+import sys
+
+import qapi
+
+COMMAND_FMT = """
+@deftypefn {type} {{}} {name}
+
+{body}
+
+@end deftypefn
+
+""".format
+
+ENUM_FMT = """
+@deftp Enum {name}
+
+{body}
+
+@end deftp
+
+""".format
+
+STRUCT_FMT = """
+@deftp {{{type}}} {name}
+
+{body}
+
+@end deftp
+
+""".format
+
+EXAMPLE_FMT = """@example
+{code}
+@end example
+""".format
+
+
+def subst_strong(doc):
+    """Replaces *foo* by @strong{foo}"""
+    return re.sub(r'\*([^*\n]+)\*', r'@emph{\1}', doc)
+
+
+def subst_emph(doc):
+    """Replaces _foo_ by @emph{foo}"""
+    return re.sub(r'\b_([^_\n]+)_\b', r' @emph{\1} ', doc)
+
+
+def subst_vars(doc):
+    """Replaces @var by @code{var}"""
+    return re.sub(r'@([\w-]+)', r'@code{\1}', doc)
+
+
+def subst_braces(doc):
+    """Replaces {} with @{ @}"""
+    return doc.replace("{", "@{").replace("}", "@}")
+
+
+def texi_example(doc):
+    """Format @example"""
+    # TODO: Neglects to escape @ characters.
+    # We should probably escape them in subst_braces(), and rename the
+    # function to subst_special() or subs_texi_special().  If we do that, we
+    # need to delay it until after subst_vars() in texi_format().
+    doc = subst_braces(doc).strip('\n')
+    return EXAMPLE_FMT(code=doc)
+
+
+def texi_format(doc):
+    """
+    Format documentation
+
+    Lines starting with:
+    - |: generates an @example
+    - =: generates @section
+    - ==: generates @subsection
+    - 1. or 1): generates an @enumerate @item
+    - */-: generates an @itemize list
+    """
+    lines = []
+    doc = subst_braces(doc)
+    doc = subst_vars(doc)
+    doc = subst_emph(doc)
+    doc = subst_strong(doc)
+    inlist = ""
+    lastempty = False
+    for line in doc.split('\n'):
+        empty = line == ""
+
+        # FIXME: Doing this in a single if / elif chain is
+        # problematic.  For instance, a line without markup terminates
+        # a list if it follows a blank line (reaches the final elif),
+        # but a line with some *other* markup, such as a = title
+        # doesn't.
+        #
+        # Make sure to update section "Documentation markup" in
+        # docs/qapi-code-gen.txt when fixing this.
+        if line.startswith("| "):
+            line = EXAMPLE_FMT(code=line[2:])
+        elif line.startswith("= "):
+            line = "@section " + line[2:]
+        elif line.startswith("== "):
+            line = "@subsection " + line[3:]
+        elif re.match(r'^([0-9]*\.) ', line):
+            if not inlist:
+                lines.append("@enumerate")
+                inlist = "enumerate"
+            line = line[line.find(" ")+1:]
+            lines.append("@item")
+        elif re.match(r'^[*-] ', line):
+            if not inlist:
+                lines.append("@itemize %s" % {'*': "@bullet",
+                                              '-': "@minus"}[line[0]])
+                inlist = "itemize"
+            lines.append("@item")
+            line = line[2:]
+        elif lastempty and inlist:
+            lines.append("@end %s\n" % inlist)
+            inlist = ""
+
+        lastempty = empty
+        lines.append(line)
+
+    if inlist:
+        lines.append("@end %s\n" % inlist)
+    return "\n".join(lines)
+
+
+def texi_body(doc):
+    """
+    Format the body of a symbol documentation:
+    - main body
+    - table of arguments
+    - followed by "Returns/Notes/Since/Example" sections
+    """
+    body = texi_format(str(doc.body)) + "\n"
+    if doc.args:
+        body += "@table @asis\n"
+        for arg, section in doc.args.iteritems():
+            desc = str(section)
+            opt = ''
+            if "#optional" in desc:
+                desc = desc.replace("#optional", "")
+                opt = ' (optional)'
+            body += "@item @code{'%s'}%s\n%s\n" % (arg, opt,
+                                                   texi_format(desc))
+        body += "@end table\n"
+
+    for section in doc.sections:
+        name, doc = (section.name, str(section))
+        func = texi_format
+        if name.startswith("Example"):
+            func = texi_example
+
+        if name:
+            # FIXME the indentation produced by @quotation in .txt and
+            # .html output is confusing
+            body += "\n@quotation %s\n%s\n@end quotation" % \
+                    (name, func(doc))
+        else:
+            body += func(doc)
+
+    return body
+
+
+def texi_alternate(expr, doc):
+    """Format an alternate to texi"""
+    body = texi_body(doc)
+    return STRUCT_FMT(type="Alternate",
+                      name=doc.symbol,
+                      body=body)
+
+
+def texi_union(expr, doc):
+    """Format a union to texi"""
+    discriminator = expr.get("discriminator")
+    if discriminator:
+        union = "Flat Union"
+    else:
+        union = "Simple Union"
+
+    body = texi_body(doc)
+    return STRUCT_FMT(type=union,
+                      name=doc.symbol,
+                      body=body)
+
+
+def texi_enum(expr, doc):
+    """Format an enum to texi"""
+    for i in expr['data']:
+        if i not in doc.args:
+            doc.args[i] = ''
+    body = texi_body(doc)
+    return ENUM_FMT(name=doc.symbol,
+                    body=body)
+
+
+def texi_struct(expr, doc):
+    """Format a struct to texi"""
+    body = texi_body(doc)
+    return STRUCT_FMT(type="Struct",
+                      name=doc.symbol,
+                      body=body)
+
+
+def texi_command(expr, doc):
+    """Format a command to texi"""
+    body = texi_body(doc)
+    return COMMAND_FMT(type="Command",
+                       name=doc.symbol,
+                       body=body)
+
+
+def texi_event(expr, doc):
+    """Format an event to texi"""
+    body = texi_body(doc)
+    return COMMAND_FMT(type="Event",
+                       name=doc.symbol,
+                       body=body)
+
+
+def texi_expr(expr, doc):
+    """Format an expr to texi"""
+    (kind, _) = expr.items()[0]
+
+    fmt = {"command": texi_command,
+           "struct": texi_struct,
+           "enum": texi_enum,
+           "union": texi_union,
+           "alternate": texi_alternate,
+           "event": texi_event}[kind]
+
+    return fmt(expr, doc)
+
+
+def texi(docs):
+    """Convert QAPI schema expressions to texi documentation"""
+    res = []
+    for doc in docs:
+        expr = doc.expr
+        if not expr:
+            res.append(texi_body(doc))
+            continue
+        try:
+            doc = texi_expr(expr, doc)
+            res.append(doc)
+        except:
+            print >>sys.stderr, "error at @%s" % doc.info
+            raise
+
+    return '\n'.join(res)
+
+
+def main(argv):
+    """Takes schema argument, prints result to stdout"""
+    if len(argv) != 2:
+        print >>sys.stderr, "%s: need exactly 1 argument: SCHEMA" % argv[0]
+        sys.exit(1)
+
+    schema = qapi.QAPISchema(argv[1])
+    print texi(schema.docs)
+
+
+if __name__ == "__main__":
+    main(sys.argv)
index 202901374c8f12a9e7dbcbfa2e651a25fe093ecf..96f59703a191d4f5ed1e1e27f5da23ac90172db8 100644 (file)
@@ -352,6 +352,24 @@ qapi-schema += base-cycle-direct.json
 qapi-schema += base-cycle-indirect.json
 qapi-schema += command-int.json
 qapi-schema += comments.json
+qapi-schema += doc-bad-args.json
+qapi-schema += doc-bad-symbol.json
+qapi-schema += doc-duplicated-arg.json
+qapi-schema += doc-duplicated-return.json
+qapi-schema += doc-duplicated-since.json
+qapi-schema += doc-empty-arg.json
+qapi-schema += doc-empty-section.json
+qapi-schema += doc-empty-symbol.json
+qapi-schema += doc-interleaved-section.json
+qapi-schema += doc-invalid-end.json
+qapi-schema += doc-invalid-end2.json
+qapi-schema += doc-invalid-return.json
+qapi-schema += doc-invalid-section.json
+qapi-schema += doc-invalid-start.json
+qapi-schema += doc-missing-colon.json
+qapi-schema += doc-missing-expr.json
+qapi-schema += doc-missing-space.json
+qapi-schema += doc-optional.json
 qapi-schema += double-data.json
 qapi-schema += double-type.json
 qapi-schema += duplicate-key.json
@@ -445,6 +463,8 @@ qapi-schema += union-optional-branch.json
 qapi-schema += union-unknown.json
 qapi-schema += unknown-escape.json
 qapi-schema += unknown-expr-key.json
+
+
 check-qapi-schema-y := $(addprefix tests/qapi-schema/, $(qapi-schema))
 
 GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h \
index aaa0154731074d5c44dad6b8b6629ffb02b2597c..395c8ab5833f1e86df21d2b604b4a8af5eed9d59 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/alternate-any.json:2: Alternate 'Alt' member 'one' cannot use type 'any'
+tests/qapi-schema/alternate-any.json:6: Alternate 'Alt' member 'one' cannot use type 'any'
index e47a73a1161ba64bce8043c889a19bfb02e87602..c958776767dd30e674a571afab12152317f4c504 100644 (file)
@@ -1,4 +1,8 @@
 # we do not allow the 'any' type as an alternate branch
+
+##
+# @Alt:
+##
 { 'alternate': 'Alt',
   'data': { 'one': 'any',
             'two': 'int' } }
index 7b930c64abf6baebc4d283e3b2ab99f0ac01139b..09628e9755102cbd7e1bebb83fb25a72ed99cd9e 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/alternate-array.json:5: Member 'two' of alternate 'Alt' cannot be an array
+tests/qapi-schema/alternate-array.json:12: Member 'two' of alternate 'Alt' cannot be an array
index f241aac1220b8e542c1cb741efe355a9a2bf5a7c..c2f98ad608e8108611f40fef5bd2ffc243dc20f6 100644 (file)
@@ -1,7 +1,14 @@
 # we do not allow array branches in alternates
+
+##
+# @One:
+##
 # TODO: should we support this?
 { 'struct': 'One',
   'data': { 'name': 'str' } }
+##
+# @Alt:
+##
 { 'alternate': 'Alt',
   'data': { 'one': 'One',
             'two': [ 'int' ] } }
index 30d8a343734307ad77d421b5dfe03c4095b42974..3b679140e09bbcd79475067a240c759c24034669 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/alternate-base.json:4: Unknown key 'base' in alternate 'Alt'
+tests/qapi-schema/alternate-base.json:11: Unknown key 'base' in alternate 'Alt'
index 529430ecf2f26a010b4663dcb74a7c04eb4d9ff7..9612b7925d217ddda2383f6bb1dfbe2d278c8e36 100644 (file)
@@ -1,6 +1,13 @@
 # we reject alternate with base type
+
+##
+# @Base:
+##
 { 'struct': 'Base',
   'data': { 'string': 'str' } }
+##
+# @Alt:
+##
 { 'alternate': 'Alt',
   'base': 'Base',
   'data': { 'number': 'int' } }
index 604d8495eb055dfaaa1562502c7c418c4e691555..f07c3e8ad304f9b6dd5b2514fdce83a5286a80fb 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/alternate-clash.json:7: 'a_b' (branch of Alt1) collides with 'a-b' (branch of Alt1)
+tests/qapi-schema/alternate-clash.json:11: 'a_b' (branch of Alt1) collides with 'a-b' (branch of Alt1)
index 6d73bc527b9a7f4594e375e6a802f72cbf6bfb93..97ca7c80e70a33f814ec916f8fa44a467584f175 100644 (file)
@@ -4,5 +4,9 @@
 # TODO: In the future, if alternates are simplified to not generate
 # the implicit Alt1Kind enum, we would still have a collision with the
 # resulting C union trying to have two members named 'a_b'.
+
+##
+# @Alt1:
+##
 { 'alternate': 'Alt1',
   'data': { 'a-b': 'str', 'a_b': 'int' } }
index 0f411f4faf5c2c3c8b62c2c83a268b094265b829..7cb023fdd83061c881f44d8786bb92a7e7c95401 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/alternate-conflict-dict.json:6: Alternate 'Alt' member 'two' can't be distinguished from member 'one'
+tests/qapi-schema/alternate-conflict-dict.json:16: Alternate 'Alt' member 'two' can't be distinguished from member 'one'
index d566cca8169c265ab12ceb710791c59172d72bcf..9f9d97fa2ed0fc0331acbcabb2326bc081096dc1 100644 (file)
@@ -1,8 +1,18 @@
 # we reject alternates with multiple object branches
+
+##
+# @One:
+##
 { 'struct': 'One',
   'data': { 'name': 'str' } }
+##
+# @Two:
+##
 { 'struct': 'Two',
   'data': { 'value': 'int' } }
+##
+# @Alt:
+##
 { 'alternate': 'Alt',
   'data': { 'one': 'One',
             'two': 'Two' } }
index fc523b087903ec1cda14b3e43e8fcbac2b4fda0f..6dbbacd1d284103cb90c35a8e3fefa8cbdc885c7 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/alternate-conflict-string.json:4: Alternate 'Alt' member 'two' can't be distinguished from member 'one'
+tests/qapi-schema/alternate-conflict-string.json:11: Alternate 'Alt' member 'two' can't be distinguished from member 'one'
index 72f04a820a9d281b7c5ec25c6c3e7a00c9b0fd95..12aafab8082c2f3e4aa0e1bfe5b9d55e4996d6e8 100644 (file)
@@ -1,6 +1,13 @@
 # we reject alternates with multiple string-like branches
+
+##
+# @Enum:
+##
 { 'enum': 'Enum',
   'data': [ 'hello', 'world' ] }
+##
+# @Alt:
+##
 { 'alternate': 'Alt',
   'data': { 'one': 'str',
             'two': 'Enum' } }
index bb06c5bfec08a9b31324102c75790f2f05574319..8245ce3103fa4b6083a8e2579d9626cacb37cfbe 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/alternate-empty.json:2: Alternate 'Alt' should have at least two branches in 'data'
+tests/qapi-schema/alternate-empty.json:6: Alternate 'Alt' should have at least two branches in 'data'
index fff15baf16c2ef48efc4619d21d4a3ded29e2325..db54405240856fd2d80fbc4daf0e0be4624a0a76 100644 (file)
@@ -1,2 +1,6 @@
 # alternates must list at least two types to be useful
+
+##
+# @Alt:
+##
 { 'alternate': 'Alt', 'data': { 'i': 'int' } }
index 4d1187e60ec0df48eb2e04a943f241e7ca78d372..1804ffbf47b8b94940b17811b9b6c2bcab5ceb6f 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/alternate-nested.json:4: Member 'nested' of alternate 'Alt2' cannot use alternate type 'Alt1'
+tests/qapi-schema/alternate-nested.json:11: Member 'nested' of alternate 'Alt2' cannot use alternate type 'Alt1'
index 8e2218649158a5f322ee1c482c1d3a946d114527..9f83ebe2e0d54a28c4fa4845a473b8e4fd71ae6c 100644 (file)
@@ -1,5 +1,12 @@
 # we reject a nested alternate branch
+
+##
+# @Alt1:
+##
 { 'alternate': 'Alt1',
   'data': { 'name': 'str', 'value': 'int' } }
+##
+# @Alt2:
+##
 { 'alternate': 'Alt2',
   'data': { 'nested': 'Alt1', 'b': 'bool' } }
index dea45dc7302d4cc58701edf29290ad11c7cbbce8..cf5b9b68304aa2a4da65cade82e62bc0ca2af775 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/alternate-unknown.json:2: Member 'unknown' of alternate 'Alt' uses unknown type 'MissingType'
+tests/qapi-schema/alternate-unknown.json:6: Member 'unknown' of alternate 'Alt' uses unknown type 'MissingType'
index 08c80dced0e827d8ddd84c280faee9769e8bb614..941ba1fac4b4e3b1128a3bc433e15c80b9ac3aeb 100644 (file)
@@ -1,3 +1,7 @@
 # we reject an alternate with unknown type in branch
+
+##
+# @Alt:
+##
 { 'alternate': 'Alt',
   'data': { 'unknown': 'MissingType', 'i': 'int' } }
index 3086eae56b0895c5f8723a48a499aad6732e14a4..2e6bf54245a2e5a38f7efd7be538f21d96687909 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/args-alternate.json:3: 'data' for command 'oops' cannot use alternate type 'Alt'
+tests/qapi-schema/args-alternate.json:11: 'data' for command 'oops' cannot use alternate type 'Alt'
index 69e94d4819f8d5323bc8ff8587eab7555888119f..49d0211a03ac3c71d8d390e9406409b244a2c37c 100644 (file)
@@ -1,3 +1,11 @@
 # we do not allow alternate arguments
+
+##
+# @Alt:
+##
 { 'alternate': 'Alt', 'data': { 'case1': 'int', 'case2': 'str' } }
+
+##
+# @oops:
+##
 { 'command': 'oops', 'data': 'Alt' }
index bf9b5e0730bc4db8a051cf7c2d78ca8042279e01..955504b10f4a70d6cf605d5cb93e56ab16857f11 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/args-any.json:2: 'data' for command 'oops' cannot use built-in type 'any'
+tests/qapi-schema/args-any.json:6: 'data' for command 'oops' cannot use built-in type 'any'
index 58fe5e470e0d957c4d8fc05bebd6a899b27aba8c..f494479cc9ce107c4b6e9ed0091835fce32a5fa5 100644 (file)
@@ -1,2 +1,6 @@
 # we do not allow an 'any' argument
+
+##
+# @oops:
+##
 { 'command': 'oops', 'data': 'any' }
index cb7ed33b3fb5c7956c5991339b0b1699e9ea44f4..e85f7918abc31b92c5233059ba86415b4a6ebd11 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/args-array-empty.json:2: Member 'empty' of 'data' for command 'oops': array type must contain single type name
+tests/qapi-schema/args-array-empty.json:6: Member 'empty' of 'data' for command 'oops': array type must contain single type name
index 652dcfb24a9b8cbe44c7c8ec93b8f44236afe420..78a0b88221e310d20621a931be2a6bbc5c6f4172 100644 (file)
@@ -1,2 +1,6 @@
 # we reject an array for data if it does not contain a known type
+
+##
+# @oops:
+##
 { 'command': 'oops', 'data': { 'empty': [ ] } }
index cd7a0f98d799a55948dd8f1bc6aff4fe44ca9cb4..77788de0990ddffae5e3d1c40bff50b06e40376c 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/args-array-unknown.json:2: Member 'array' of 'data' for command 'oops' uses unknown type 'NoSuchType'
+tests/qapi-schema/args-array-unknown.json:6: Member 'array' of 'data' for command 'oops' uses unknown type 'NoSuchType'
index 6f3e883315c634c24bb4d2f77281647d016942c8..f680fc10d3c0b831fc20b6c50abc24665566679d 100644 (file)
@@ -1,2 +1,6 @@
 # we reject an array for data if it does not contain a known type
+
+##
+# @oops:
+##
 { 'command': 'oops', 'data': { 'array': [ 'NoSuchType' ] } }
index ad0d417321c8636ce2e6d42b73451e82819aa999..87a906137a054329809a91d3e34f17fcfe5e5f04 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/args-bad-boxed.json:2: 'boxed' of command 'foo' should only use true value
+tests/qapi-schema/args-bad-boxed.json:6: 'boxed' of command 'foo' should only use true value
index dea0cd0aa51a1376760508bed9e028e202f11783..4c0b28f29198fe1446d45ffb58d22661986f15f6 100644 (file)
@@ -1,2 +1,6 @@
 # 'boxed' should only appear with value true
+
+##
+# @foo:
+##
 { 'command': 'foo', 'boxed': false }
index f24f34521857acf1110b7e0d1ca25dccfc007eda..3cfac0b923fedb5916ea52261610fca7602595d6 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/args-boxed-anon.json:2: 'data' for command 'foo' should be a type name
+tests/qapi-schema/args-boxed-anon.json:6: 'data' for command 'foo' should be a type name
index 95f60da2edf98d248a6b609f8e9899c786a2a9d6..2358e6abb19f21c8eea14afcb8e95555319177d6 100644 (file)
@@ -1,2 +1,6 @@
 # 'boxed' can only be used with named types
+
+##
+# @foo:
+##
 { 'command': 'foo', 'boxed': true, 'data': { 'string': 'str' } }
index 039603e85cacd736d65419b873d4cd585bb5d429..963f495a9d4c064626704efc00d9588308519f75 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/args-boxed-empty.json:3: Cannot use 'boxed' with empty type
+tests/qapi-schema/args-boxed-empty.json:11: Cannot use 'boxed' with empty type
index 52717e065fff7ab6345e9e6b95ae6f91973a429f..8e8cc2652581aee4f1b75a73a33bb3796aa6e9d2 100644 (file)
@@ -1,3 +1,11 @@
 # 'boxed' requires a non-empty type
+
+##
+# @Empty:
+##
 { 'struct': 'Empty', 'data': {} }
+
+##
+# @foo:
+##
 { 'command': 'foo', 'boxed': true, 'data': 'Empty' }
index d326b48aefd24ae3802acab8063ec305880bf88f..762375520887df02dd95fc135845819b8925187e 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/args-boxed-string.json:2: 'data' for command 'foo' cannot use built-in type 'str'
+tests/qapi-schema/args-boxed-string.json:6: 'data' for command 'foo' cannot use built-in type 'str'
index f91a1502e726f0497a3223e9334d26850a897156..aecdf97ce949585b260a20c9c185d299a5e9012d 100644 (file)
@@ -1,2 +1,6 @@
 # 'boxed' requires a complex (not built-in) type
+
+##
+# @foo:
+##
 { 'command': 'foo', 'boxed': true, 'data': 'str' }
index dc1d2504ff8fcf4ea4b31d8cf11a623640fa1375..38b3202b091ac06f0c351c6c079a55fbfeb4f971 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/args-int.json:2: 'data' for command 'oops' cannot use built-in type 'int'
+tests/qapi-schema/args-int.json:6: 'data' for command 'oops' cannot use built-in type 'int'
index a334d92e8ccefd1999c37f00bbcc9943b9578bad..7f4e1b7aa64298e1f6cb839ba39b33673ab0aaed 100644 (file)
@@ -1,2 +1,6 @@
 # we reject commands where data is not an array or complex type
+
+##
+# @oops:
+##
 { 'command': 'oops', 'data': 'int' }
index fe1e94975b9401a16d734c8ead0bf4acb8803a77..5d3568d7c391957130d602a5bc77b6c1fd14e986 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/args-invalid.json:1: 'data' for command 'foo' should be a dictionary or type name
+tests/qapi-schema/args-invalid.json:4: 'data' for command 'foo' should be a dictionary or type name
index db0981341b8d643f7c064769c6a5f0ce72e58431..1a7e63bb2386f22097e1ecf37d1219104f8284e1 100644 (file)
@@ -1,2 +1,5 @@
+##
+# @foo:
+##
 { 'command': 'foo',
   'data': false }
index 881b4d954f2d317c118797071e0d922cb00251ce..825ffca9bf6298b820cc82325b6c7b199504dbc2 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/args-member-array-bad.json:2: Member 'member' of 'data' for command 'oops': array type must contain single type name
+tests/qapi-schema/args-member-array-bad.json:6: Member 'member' of 'data' for command 'oops': array type must contain single type name
index b2ff144ec632c5975c7e24847cc14a4898bdb452..e934f5c457fe207881d82629eb108d577047babd 100644 (file)
@@ -1,2 +1,6 @@
 # we reject data if it does not contain a valid array type
+
+##
+# @oops:
+##
 { 'command': 'oops', 'data': { 'member': [ { 'nested': 'str' } ] } }
index 19c44266015fe291ce2ca3fb98b49eef6e47b6ec..a3fb2bdd609fb5eeecd9d2e8e6e97f6604160d5b 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/args-member-case.json:2: 'Arg' (parameter of no-way-this-will-get-whitelisted) should not use uppercase
+tests/qapi-schema/args-member-case.json:6: 'Arg' (parameter of no-way-this-will-get-whitelisted) should not use uppercase
index 93439bee8b337349452b5dc7a2657aed48f8074c..811e658d6656d40391151f9b74803c0ece3879b3 100644 (file)
@@ -1,2 +1,6 @@
 # Member names should be 'lower-case' unless the struct/command is whitelisted
+
+##
+# @no-way-this-will-get-whitelisted:
+##
 { 'command': 'no-way-this-will-get-whitelisted', 'data': { 'Arg': 'int' } }
index f6f82828ce595d00328f63eeba33fdcaae42555f..3db452b95a65bcf03da00d94b1d5935076618aaf 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/args-member-unknown.json:2: Member 'member' of 'data' for command 'oops' uses unknown type 'NoSuchType'
+tests/qapi-schema/args-member-unknown.json:6: Member 'member' of 'data' for command 'oops' uses unknown type 'NoSuchType'
index 342a41ec90e70339f51e58f5f4ce083872238706..e2fef9c46f0092bc3c94ed627711a390b3a23783 100644 (file)
@@ -1,2 +1,6 @@
 # we reject data if it does not contain a known type
+
+##
+# @oops:
+##
 { 'command': 'oops', 'data': { 'member': 'NoSuchType' } }
index d953e8d241672c6d6ce378d95e53f9c60e3f655b..23988cb5ca8a1e2413534197288def4fa527f218 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/args-name-clash.json:4: 'a_b' (parameter of oops) collides with 'a-b' (parameter of oops)
+tests/qapi-schema/args-name-clash.json:8: 'a_b' (parameter of oops) collides with 'a-b' (parameter of oops)
index 61423cb89346c6a027c036e5804ba2206d883e53..991323b78dfa8d6e620c12045314a520ce8ed056 100644 (file)
@@ -1,4 +1,8 @@
 # C member name collision
 # Reject members that clash when mapped to C names (we would have two 'a_b'
 # members).
+
+##
+# @oops:
+##
 { 'command': 'oops', 'data': { 'a-b': 'str', 'a_b': 'str' } }
index f8ad223ddee62310aaa802b7b805dff3554c8654..ce0a34e16cb4e7e6f8bbcf55b46628c8ea2d08a0 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/args-union.json:3: 'data' for command 'oops' cannot use union type 'Uni'
+tests/qapi-schema/args-union.json:10: 'data' for command 'oops' cannot use union type 'Uni'
index 2fcaeaae16305ef167f23611daefa514719abf90..57284b43c523e9b25319f5a8be44785448a73240 100644 (file)
@@ -1,3 +1,10 @@
 # use of union arguments requires 'boxed':true
+
+##
+# @Uni:
+##
 { 'union': 'Uni', 'data': { 'case1': 'int', 'case2': 'str' } }
+##
+# oops:
+##
 { 'command': 'oops', 'data': 'Uni' }
index 4d91ec869f9d76e2a17ac025f1b00dcf16f5e4f5..ba6c6cf326cfcacfaf4b06c3059633aedbfba6f1 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/args-unknown.json:2: 'data' for command 'oops' uses unknown type 'NoSuchType'
+tests/qapi-schema/args-unknown.json:6: 'data' for command 'oops' uses unknown type 'NoSuchType'
index 32aba43b3f8c495e4453b0731c5571474a9e1a38..12666dc020c4f929c85b99a95751cc6f73cbcaa4 100644 (file)
@@ -1,2 +1,6 @@
 # we reject data if it does not contain a known type
+
+##
+# @oops:
+##
 { 'command': 'oops', 'data': 'NoSuchType' }
index 154274bdd3f2678da93b90328976892be6e10503..e668761c65303bea8efab770ad5d036f93a6e806 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/bad-base.json:3: 'base' for struct 'MyType' cannot use union type 'Union'
+tests/qapi-schema/bad-base.json:10: 'base' for struct 'MyType' cannot use union type 'Union'
index a634331cdd81a453ef17839c24101a6e1dfc28b0..c3faa8242b02888358705e82272d3c8fded9336b 100644 (file)
@@ -1,3 +1,10 @@
 # we reject a base that is not a struct
+
+##
+# @Union:
+##
 { 'union': 'Union', 'data': { 'a': 'int', 'b': 'str' } }
+##
+# @MyType:
+##
 { 'struct': 'MyType', 'base': 'Union', 'data': { 'c': 'int' } }
index 8523ac4f46d6bb1984c78ca275edb8b42c179622..c1b9e353137136203170ec3b59e87a82a34f1617 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/bad-data.json:2: 'data' for command 'oops' cannot be an array
+tests/qapi-schema/bad-data.json:6: 'data' for command 'oops' cannot be an array
index 832eeb76f4cc088e4da5c08941994d125c45aca0..51c444f4f8defa752023ba03624dc9e9e810fc12 100644 (file)
@@ -1,2 +1,6 @@
 # we ensure 'data' is a dictionary for all but enums
+
+##
+# @oops:
+##
 { 'command': 'oops', 'data': [ ] }
index c4190602b5c4f552b4fea0fe3d6f9e186134cd11..b757aa21e70506afc088c97a6a94128d2c0876f2 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/bad-ident.json:2: 'struct' does not allow optional name '*oops'
+tests/qapi-schema/bad-ident.json:6: 'struct' does not allow optional name '*oops'
index 763627ad23807c32c08c5d3fd1b8005225f7d74b..b43df7a3e04bb102ffe468f0840fbc6dcecfd165 100644 (file)
@@ -1,2 +1,6 @@
 # we reject creating a type name with bad name
+
+##
+# @*oops:
+##
 { 'struct': '*oops', 'data': { 'i': 'int' } }
index 62fd70baafd63ec5e37ecd445663959bcb9023df..72e026b46c992854de17f01f0e6e56db0999b577 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/bad-type-bool.json:2: 'struct' key must have a string value
+tests/qapi-schema/bad-type-bool.json:6: 'struct' key must have a string value
index bde17b56c4cd35bca588225574555bed49a11ffb..1f9eddf938e3310ec941dec091fcf36d73714a81 100644 (file)
@@ -1,2 +1,6 @@
 # we reject an expression with a metatype that is not a string
+
+##
+# @true:
+##
 { 'struct': true, 'data': { } }
index 0b2a2aeac429ab61cb9397b9d82b9828bfdcea0a..d0d1f607e53bbb4c6869bc0a1420b2070cd48a05 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/bad-type-dict.json:2: 'command' key must have a string value
+tests/qapi-schema/bad-type-dict.json:6: 'command' key must have a string value
index 2a91b241f8e4c50ec5193a5565e78c5812d0a0ce..5952caab28ee670ce5c7d495c77024be883c9c6b 100644 (file)
@@ -1,2 +1,6 @@
 # we reject an expression with a metatype that is not a string
+
+##
+# @foo:
+##
 { 'command': { } }
index 9c68f6543d3414c5e02b208b5bcaf2094c8acb64..dd7f5aace67c5bd19d4b2a3796efd5ed8fa10b1e 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/base-cycle-direct.json:2: Object Loopy contains itself
+tests/qapi-schema/base-cycle-direct.json:6: Object Loopy contains itself
index 4fc66d05161920a198523ce32bce872764797f21..9780f7e2ca3ce5336815bcef8604f157882f4607 100644 (file)
@@ -1,2 +1,6 @@
 # we reject a loop in base classes
+
+##
+# @Loopy:
+##
 { 'struct': 'Loopy', 'base': 'Loopy', 'data': {} }
index fc92fe47f82a36df8b0f79253903bc4f5757d205..f4198e4a402b2dbd3a1fc2ae48b3dc3af37369ab 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/base-cycle-indirect.json:2: Object Base1 contains itself
+tests/qapi-schema/base-cycle-indirect.json:6: Object Base1 contains itself
index 28667721a31ab30c6c488d7ad0b4cc865cffa8e9..99926c46098b5df533afbfbc80dff141cc54bf5b 100644 (file)
@@ -1,3 +1,10 @@
 # we reject a loop in base classes
+
+##
+# @Base1:
+##
 { 'struct': 'Base1', 'base': 'Base2', 'data': {} }
+##
+# @Base2:
+##
 { 'struct': 'Base2', 'base': 'Base1', 'data': {} }
index 0f9300679b304398cf6ec9cd1fdbe51ede29d3d4..3c834a97ab2b38251525de74990e8620751bdccf 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/command-int.json:2: built-in 'int' is already defined
+tests/qapi-schema/command-int.json:6: built-in 'int' is already defined
index 9a62554fc6dd7b3073b3993fa4a7c7e88b7384c4..5b51bf148b863d7d8cc2181f5053fb8a3ee54491 100644 (file)
@@ -1,2 +1,6 @@
 # we reject collisions between commands and types
+
+##
+# @int:
+##
 { 'command': 'int', 'data': { 'character': 'str' } }
index e643f3a74c332057f9f621adb7e83d3174f43604..d31ef0d90aba6ae92869fda0aaaa62b6a29b2101 100644 (file)
@@ -1,4 +1,8 @@
 # Unindented comment
+
+##
+# @Status:
+##
 { 'enum': 'Status',             # Comment to the right of code
   # Indented comment
   'data': [ 'good', 'bad', 'ugly' ] }
index 5d7c13cad167fc05b67b5cd3c8c4a48aa2b75803..a962fb2d2efb11242b33f93d80e8d6d1a1af6201 100644 (file)
@@ -2,3 +2,4 @@ enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbo
     prefix QTYPE
 enum Status ['good', 'bad', 'ugly']
 object q_empty
+doc symbol=Status expr=('enum', 'Status')
diff --git a/tests/qapi-schema/doc-bad-args.err b/tests/qapi-schema/doc-bad-args.err
new file mode 100644 (file)
index 0000000..5d44d9b
--- /dev/null
@@ -0,0 +1 @@
+tests/qapi-schema/doc-bad-args.json:3: The following documented members are not in the declaration: b
diff --git a/tests/qapi-schema/doc-bad-args.exit b/tests/qapi-schema/doc-bad-args.exit
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/doc-bad-args.json b/tests/qapi-schema/doc-bad-args.json
new file mode 100644 (file)
index 0000000..048e0fc
--- /dev/null
@@ -0,0 +1,8 @@
+# Arguments listed in the doc comment must exist in the actual schema
+
+##
+# @foo:
+# @a: a
+# @b: b
+##
+{ 'command': 'foo', 'data': {'a': 'int'} }
diff --git a/tests/qapi-schema/doc-bad-args.out b/tests/qapi-schema/doc-bad-args.out
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/qapi-schema/doc-bad-symbol.err b/tests/qapi-schema/doc-bad-symbol.err
new file mode 100644 (file)
index 0000000..ac4e566
--- /dev/null
@@ -0,0 +1 @@
+tests/qapi-schema/doc-bad-symbol.json:3: Definition of 'foo' follows documentation for 'food'
diff --git a/tests/qapi-schema/doc-bad-symbol.exit b/tests/qapi-schema/doc-bad-symbol.exit
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/doc-bad-symbol.json b/tests/qapi-schema/doc-bad-symbol.json
new file mode 100644 (file)
index 0000000..a7c15b3
--- /dev/null
@@ -0,0 +1,6 @@
+# Documentation symbol mismatch with expression
+
+##
+# @food:
+##
+{ 'command': 'foo', 'data': {'a': 'int'} }
diff --git a/tests/qapi-schema/doc-bad-symbol.out b/tests/qapi-schema/doc-bad-symbol.out
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/qapi-schema/doc-duplicated-arg.err b/tests/qapi-schema/doc-duplicated-arg.err
new file mode 100644 (file)
index 0000000..1c3f8e0
--- /dev/null
@@ -0,0 +1 @@
+tests/qapi-schema/doc-duplicated-arg.json:6:1: 'a' parameter name duplicated
diff --git a/tests/qapi-schema/doc-duplicated-arg.exit b/tests/qapi-schema/doc-duplicated-arg.exit
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/doc-duplicated-arg.json b/tests/qapi-schema/doc-duplicated-arg.json
new file mode 100644 (file)
index 0000000..035cae9
--- /dev/null
@@ -0,0 +1,7 @@
+# Do not allow duplicated argument
+
+##
+# @foo:
+# @a:
+# @a:
+##
diff --git a/tests/qapi-schema/doc-duplicated-arg.out b/tests/qapi-schema/doc-duplicated-arg.out
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/qapi-schema/doc-duplicated-return.err b/tests/qapi-schema/doc-duplicated-return.err
new file mode 100644 (file)
index 0000000..e48039f
--- /dev/null
@@ -0,0 +1 @@
+tests/qapi-schema/doc-duplicated-return.json:7:1: Duplicated 'Returns' section
diff --git a/tests/qapi-schema/doc-duplicated-return.exit b/tests/qapi-schema/doc-duplicated-return.exit
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/doc-duplicated-return.json b/tests/qapi-schema/doc-duplicated-return.json
new file mode 100644 (file)
index 0000000..b44b5ae
--- /dev/null
@@ -0,0 +1,8 @@
+# Do not allow duplicated Returns section
+
+##
+# @foo:
+#
+# Returns: 0
+# Returns: 1
+##
diff --git a/tests/qapi-schema/doc-duplicated-return.out b/tests/qapi-schema/doc-duplicated-return.out
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/qapi-schema/doc-duplicated-since.err b/tests/qapi-schema/doc-duplicated-since.err
new file mode 100644 (file)
index 0000000..3fb8907
--- /dev/null
@@ -0,0 +1 @@
+tests/qapi-schema/doc-duplicated-since.json:7:1: Duplicated 'Since' section
diff --git a/tests/qapi-schema/doc-duplicated-since.exit b/tests/qapi-schema/doc-duplicated-since.exit
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/doc-duplicated-since.json b/tests/qapi-schema/doc-duplicated-since.json
new file mode 100644 (file)
index 0000000..343cd87
--- /dev/null
@@ -0,0 +1,8 @@
+# Do not allow duplicated Since section
+
+##
+# @foo:
+#
+# Since: 0
+# Since: 1
+##
diff --git a/tests/qapi-schema/doc-duplicated-since.out b/tests/qapi-schema/doc-duplicated-since.out
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/qapi-schema/doc-empty-arg.err b/tests/qapi-schema/doc-empty-arg.err
new file mode 100644 (file)
index 0000000..2895518
--- /dev/null
@@ -0,0 +1 @@
+tests/qapi-schema/doc-empty-arg.json:5:1: Invalid parameter name
diff --git a/tests/qapi-schema/doc-empty-arg.exit b/tests/qapi-schema/doc-empty-arg.exit
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/doc-empty-arg.json b/tests/qapi-schema/doc-empty-arg.json
new file mode 100644 (file)
index 0000000..8f76ede
--- /dev/null
@@ -0,0 +1,6 @@
+# An invalid empty argument name
+
+##
+# @foo:
+# @:
+##
diff --git a/tests/qapi-schema/doc-empty-arg.out b/tests/qapi-schema/doc-empty-arg.out
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/qapi-schema/doc-empty-section.err b/tests/qapi-schema/doc-empty-section.err
new file mode 100644 (file)
index 0000000..00ad625
--- /dev/null
@@ -0,0 +1 @@
+tests/qapi-schema/doc-empty-section.json:3: Empty doc section 'Note'
diff --git a/tests/qapi-schema/doc-empty-section.exit b/tests/qapi-schema/doc-empty-section.exit
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/doc-empty-section.json b/tests/qapi-schema/doc-empty-section.json
new file mode 100644 (file)
index 0000000..f3384e9
--- /dev/null
@@ -0,0 +1,8 @@
+# Tagged-section must not be empty
+
+##
+# @foo:
+#
+# Note:
+##
+{ 'command': 'foo', 'data': {'a': 'int'} }
diff --git a/tests/qapi-schema/doc-empty-section.out b/tests/qapi-schema/doc-empty-section.out
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/qapi-schema/doc-empty-symbol.err b/tests/qapi-schema/doc-empty-symbol.err
new file mode 100644 (file)
index 0000000..1936ad0
--- /dev/null
@@ -0,0 +1 @@
+tests/qapi-schema/doc-empty-symbol.json:4:1: Invalid name
diff --git a/tests/qapi-schema/doc-empty-symbol.exit b/tests/qapi-schema/doc-empty-symbol.exit
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/doc-empty-symbol.json b/tests/qapi-schema/doc-empty-symbol.json
new file mode 100644 (file)
index 0000000..fb8fddc
--- /dev/null
@@ -0,0 +1,5 @@
+# Invalid documentation symbol
+
+##
+# @:
+##
diff --git a/tests/qapi-schema/doc-empty-symbol.out b/tests/qapi-schema/doc-empty-symbol.out
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/qapi-schema/doc-interleaved-section.err b/tests/qapi-schema/doc-interleaved-section.err
new file mode 100644 (file)
index 0000000..d373eab
--- /dev/null
@@ -0,0 +1 @@
+tests/qapi-schema/doc-interleaved-section.json:15:1: '@foobar:' can't follow 'Note' section
diff --git a/tests/qapi-schema/doc-interleaved-section.exit b/tests/qapi-schema/doc-interleaved-section.exit
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/doc-interleaved-section.json b/tests/qapi-schema/doc-interleaved-section.json
new file mode 100644 (file)
index 0000000..adb29e9
--- /dev/null
@@ -0,0 +1,21 @@
+# Arguments and sections must not be interleaved
+
+##
+# @TestStruct:
+#
+# body
+#
+# @integer: foo
+#           blah
+#
+#           bao
+#
+# Note: a section.
+#
+# @foobar: catch this
+#
+# Since: 2.3
+#
+##
+{ 'struct': 'TestStruct',
+  'data': { 'integer': 'int', 'foobar': 'int' } }
diff --git a/tests/qapi-schema/doc-interleaved-section.out b/tests/qapi-schema/doc-interleaved-section.out
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/qapi-schema/doc-invalid-end.err b/tests/qapi-schema/doc-invalid-end.err
new file mode 100644 (file)
index 0000000..2bda28c
--- /dev/null
@@ -0,0 +1 @@
+tests/qapi-schema/doc-invalid-end.json:5:2: Documentation comment must end with '##'
diff --git a/tests/qapi-schema/doc-invalid-end.exit b/tests/qapi-schema/doc-invalid-end.exit
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/doc-invalid-end.json b/tests/qapi-schema/doc-invalid-end.json
new file mode 100644 (file)
index 0000000..3583b23
--- /dev/null
@@ -0,0 +1,5 @@
+# Documentation must end with '##'
+
+##
+# An invalid comment
+#
diff --git a/tests/qapi-schema/doc-invalid-end.out b/tests/qapi-schema/doc-invalid-end.out
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/qapi-schema/doc-invalid-end2.err b/tests/qapi-schema/doc-invalid-end2.err
new file mode 100644 (file)
index 0000000..6fad9c7
--- /dev/null
@@ -0,0 +1 @@
+tests/qapi-schema/doc-invalid-end2.json:5:1: Junk after '##' at end of documentation comment
diff --git a/tests/qapi-schema/doc-invalid-end2.exit b/tests/qapi-schema/doc-invalid-end2.exit
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/doc-invalid-end2.json b/tests/qapi-schema/doc-invalid-end2.json
new file mode 100644 (file)
index 0000000..fa2d39d
--- /dev/null
@@ -0,0 +1,5 @@
+# Documentation must end with '##'
+
+##
+#
+## invalid
diff --git a/tests/qapi-schema/doc-invalid-end2.out b/tests/qapi-schema/doc-invalid-end2.out
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/qapi-schema/doc-invalid-return.err b/tests/qapi-schema/doc-invalid-return.err
new file mode 100644 (file)
index 0000000..5aaba33
--- /dev/null
@@ -0,0 +1 @@
+tests/qapi-schema/doc-invalid-return.json:3: 'Returns:' is only valid for commands
diff --git a/tests/qapi-schema/doc-invalid-return.exit b/tests/qapi-schema/doc-invalid-return.exit
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/doc-invalid-return.json b/tests/qapi-schema/doc-invalid-return.json
new file mode 100644 (file)
index 0000000..1ba45de
--- /dev/null
@@ -0,0 +1,7 @@
+# Events can't have 'Returns' section
+
+##
+# @foo:
+# Returns: blah
+##
+{ 'event': 'foo' }
diff --git a/tests/qapi-schema/doc-invalid-return.out b/tests/qapi-schema/doc-invalid-return.out
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/qapi-schema/doc-invalid-section.err b/tests/qapi-schema/doc-invalid-section.err
new file mode 100644 (file)
index 0000000..85bb67b
--- /dev/null
@@ -0,0 +1 @@
+tests/qapi-schema/doc-invalid-section.json:3: Free-form documentation block must not contain @NAME: sections
diff --git a/tests/qapi-schema/doc-invalid-section.exit b/tests/qapi-schema/doc-invalid-section.exit
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/doc-invalid-section.json b/tests/qapi-schema/doc-invalid-section.json
new file mode 100644 (file)
index 0000000..0578b8a
--- /dev/null
@@ -0,0 +1,6 @@
+# Free-form documentation doesn't have tagged-sections
+
+##
+# freeform
+# @note: foo
+##
diff --git a/tests/qapi-schema/doc-invalid-section.out b/tests/qapi-schema/doc-invalid-section.out
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/qapi-schema/doc-invalid-start.err b/tests/qapi-schema/doc-invalid-start.err
new file mode 100644 (file)
index 0000000..149af2b
--- /dev/null
@@ -0,0 +1 @@
+tests/qapi-schema/doc-invalid-start.json:3:1: Junk after '##' at start of documentation comment
diff --git a/tests/qapi-schema/doc-invalid-start.exit b/tests/qapi-schema/doc-invalid-start.exit
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/doc-invalid-start.json b/tests/qapi-schema/doc-invalid-start.json
new file mode 100644 (file)
index 0000000..4f6c15a
--- /dev/null
@@ -0,0 +1,5 @@
+# Documentation must start with '##'
+
+## invalid
+#
+##
diff --git a/tests/qapi-schema/doc-invalid-start.out b/tests/qapi-schema/doc-invalid-start.out
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/qapi-schema/doc-missing-colon.err b/tests/qapi-schema/doc-missing-colon.err
new file mode 100644 (file)
index 0000000..817398b
--- /dev/null
@@ -0,0 +1 @@
+tests/qapi-schema/doc-missing-colon.json:4:1: Line should end with :
diff --git a/tests/qapi-schema/doc-missing-colon.exit b/tests/qapi-schema/doc-missing-colon.exit
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/doc-missing-colon.json b/tests/qapi-schema/doc-missing-colon.json
new file mode 100644 (file)
index 0000000..d88c06c
--- /dev/null
@@ -0,0 +1,5 @@
+# The symbol section must end with ':'
+
+##
+# @missing-colon
+##
diff --git a/tests/qapi-schema/doc-missing-colon.out b/tests/qapi-schema/doc-missing-colon.out
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/qapi-schema/doc-missing-expr.err b/tests/qapi-schema/doc-missing-expr.err
new file mode 100644 (file)
index 0000000..c0e687c
--- /dev/null
@@ -0,0 +1 @@
+tests/qapi-schema/doc-missing-expr.json:3: Documention for 'bar' is not followed by the definition
diff --git a/tests/qapi-schema/doc-missing-expr.exit b/tests/qapi-schema/doc-missing-expr.exit
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/doc-missing-expr.json b/tests/qapi-schema/doc-missing-expr.json
new file mode 100644 (file)
index 0000000..06ad7df
--- /dev/null
@@ -0,0 +1,5 @@
+# Expression documentation must be followed by the actual expression
+
+##
+# @bar:
+##
diff --git a/tests/qapi-schema/doc-missing-expr.out b/tests/qapi-schema/doc-missing-expr.out
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/qapi-schema/doc-missing-space.err b/tests/qapi-schema/doc-missing-space.err
new file mode 100644 (file)
index 0000000..d6b46ff
--- /dev/null
@@ -0,0 +1 @@
+tests/qapi-schema/doc-missing-space.json:5:1: Missing space after #
diff --git a/tests/qapi-schema/doc-missing-space.exit b/tests/qapi-schema/doc-missing-space.exit
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/doc-missing-space.json b/tests/qapi-schema/doc-missing-space.json
new file mode 100644 (file)
index 0000000..beb276b
--- /dev/null
@@ -0,0 +1,6 @@
+# Documentation line must have a leading space
+
+##
+# missing space:
+#wef
+##
diff --git a/tests/qapi-schema/doc-missing-space.out b/tests/qapi-schema/doc-missing-space.out
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/qapi-schema/doc-optional.err b/tests/qapi-schema/doc-optional.err
new file mode 100644 (file)
index 0000000..20d405a
--- /dev/null
@@ -0,0 +1 @@
+tests/qapi-schema/doc-optional.json:3: Description has #optional, but the declaration doesn't
diff --git a/tests/qapi-schema/doc-optional.exit b/tests/qapi-schema/doc-optional.exit
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/doc-optional.json b/tests/qapi-schema/doc-optional.json
new file mode 100644 (file)
index 0000000..06c855e
--- /dev/null
@@ -0,0 +1,7 @@
+# Description #optional should match declaration
+
+##
+# @foo:
+# @a: a #optional
+##
+{ 'command': 'foo', 'data': {'a': 'int'} }
diff --git a/tests/qapi-schema/doc-optional.out b/tests/qapi-schema/doc-optional.out
new file mode 100644 (file)
index 0000000..e69de29
index f9613c6d6b53b4c07d40b280979a846745157970..424df9bedd28bfa1d1721796c9f0e496388f6e0a 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/double-type.json:2: Unknown key 'command' in struct 'bar'
+tests/qapi-schema/double-type.json:6: Unknown key 'command' in struct 'bar'
index 911fa7af502bf758bea5ba94cc04858ebd0eb2ca..ab59523ff76fa20ced27f12f0c6a12a95c8acf36 100644 (file)
@@ -1,2 +1,6 @@
 # we reject an expression with ambiguous metatype
+
+##
+# @foo:
+##
 { 'command': 'foo', 'struct': 'bar', 'data': { } }
index 9c3c1002b782c8f5d7ac6252cfe3b76bafdb3174..157d1b0d69ad33612e9e402095d9b8fc97859123 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/enum-bad-name.json:2: Member of enum 'MyEnum' uses invalid name 'not^possible'
+tests/qapi-schema/enum-bad-name.json:6: Member of enum 'MyEnum' uses invalid name 'not^possible'
index 8506562b313fdc9d2e9e5651230b3f81555e3788..978cb889941ac68f6df911ec00416822b2282e9f 100644 (file)
@@ -1,2 +1,6 @@
 # we ensure all enum names can map to C
+
+##
+# @MyEnum:
+##
 { 'enum': 'MyEnum', 'data': [ 'not^possible' ] }
index 399f5f7af5c3d3464d0f1ac756f51dccd2de0b42..918915f7ab18c9d0134727b61e91985f2db6cabd 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/enum-bad-prefix.json:2: Enum 'MyEnum' requires a string for 'prefix'
+tests/qapi-schema/enum-bad-prefix.json:6: Enum 'MyEnum' requires a string for 'prefix'
index 996f628f6d0e19c4c445dd1477af3582b9e9b878..25f17a7b08585b606bf3c0af74a3a87a8fb80d18 100644 (file)
@@ -1,2 +1,6 @@
 # The prefix must be a string type
+
+##
+# @MyEnum:
+##
 { 'enum': 'MyEnum', 'data': [ 'one' ], 'prefix': [ 'fish' ] }
index 5403c785079479c95ce75180acab0a72702e31cc..25249b63c4d1374ffd526ac22379eee3a3ae1c18 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/enum-clash-member.json:2: 'one_two' (member of MyEnum) collides with 'one-two' (member of MyEnum)
+tests/qapi-schema/enum-clash-member.json:6: 'one_two' (member of MyEnum) collides with 'one-two' (member of MyEnum)
index b6928b8bfdc9c221d1ff7dfbc345bc9259e2b4ae..fd52751941f348a3f2dc922b51c7fc4ff00eab3f 100644 (file)
@@ -1,2 +1,6 @@
 # we reject enums where members will clash when mapped to C enum
+
+##
+# @MyEnum:
+##
 { 'enum': 'MyEnum', 'data': [ 'one-two', 'one_two' ] }
index 8ca146ea592102b063e1fc6cbd75b222f4db06a9..9b7d2f111d260a1b99361cf88dfb5fafb08d18f9 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/enum-dict-member.json:2: Member of enum 'MyEnum' requires a string name
+tests/qapi-schema/enum-dict-member.json:6: Member of enum 'MyEnum' requires a string name
index 79672e0f091ab56989b189cf8eb5fa7a08bf1e9d..69d30f0c1e0c02aef89866a7f465f3e8c8faa947 100644 (file)
@@ -1,2 +1,6 @@
 # we reject any enum member that is not a string
+
+##
+# @MyEnum:
+##
 { 'enum': 'MyEnum', 'data': [ { 'value': 'str' } ] }
index b652e9aacc10c9dd261404919138468934510223..df96e2205a6d5f72129c6c9fc92a44a883707465 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/enum-member-case.json:3: 'Value' (member of NoWayThisWillGetWhitelisted) should not use uppercase
+tests/qapi-schema/enum-member-case.json:10: 'Value' (member of NoWayThisWillGetWhitelisted) should not use uppercase
index 2096b350cae9f9ccad1a64aa462a604d017c7a4e..d2e4aba39d1b61df7089da9f08723916b30f6181 100644 (file)
@@ -1,3 +1,10 @@
 # Member names should be 'lower-case' unless the enum is whitelisted
+
+##
+# @UuidInfo:
+##
 { 'enum': 'UuidInfo', 'data': [ 'Value' ] } # UuidInfo is whitelisted
+##
+# @NoWayThisWillGetWhitelisted:
+##
 { 'enum': 'NoWayThisWillGetWhitelisted', 'data': [ 'Value' ] }
index ba4873ae69ada3459aa27e3d4acceca1a958fe1c..de4b9e8281e52786e3972bd040c97a3d31eeb334 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/enum-missing-data.json:2: Key 'data' is missing from enum 'MyEnum'
+tests/qapi-schema/enum-missing-data.json:6: Key 'data' is missing from enum 'MyEnum'
index 558fd35e93f8a68725ca915c89fa3997f46a51ab..d7601f91fb95a36431595162c5efc92a3e235258 100644 (file)
@@ -1,2 +1,6 @@
 # we require that all QAPI enums have a data array
+
+##
+# @MyEnum:
+##
 { 'enum': 'MyEnum' }
index 11b43471cf34a1f028e75b3756fa6eda93d1a36b..c44e9b59dc69954cf9bfd84d68b8156891bc3e05 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/enum-wrong-data.json:2: Enum 'MyEnum' requires an array for 'data'
+tests/qapi-schema/enum-wrong-data.json:6: Enum 'MyEnum' requires an array for 'data'
index 7b3e255c146e1a5d6d5572afb5e5e7a3b9cb13d4..4b9e97878b4e9691adfaad68ec8e31ae98ef570e 100644 (file)
@@ -1,2 +1,6 @@
 # we require that all qapi enums have an array for data
+
+##
+# @MyEnum:
+##
 { 'enum': 'MyEnum', 'data': { 'value': 'str' } }
index 68ec6f2d2bdbd90adfa0c0e2c637faa3792c6485..defe656e32e2ad5a9a576f964632a3e54b7154d6 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/event-boxed-empty.json:2: Use of 'boxed' requires 'data'
+tests/qapi-schema/event-boxed-empty.json:6: Use of 'boxed' requires 'data'
index cb145f1433f00169c72253f7fedadf62f5231c41..63b870b31b7fdd4806b25ec9265932e6da15004f 100644 (file)
@@ -1,2 +1,6 @@
 # 'boxed' requires a non-empty type
+
+##
+# @FOO:
+##
 { 'event': 'FOO', 'boxed': true }
index 3a92d8b6105ddf3942a9d79dcbfc787ff891cc71..6b05c5d24748a3a5c720d1d47afc81ace3cc797a 100644 (file)
@@ -1,3 +1,7 @@
 # TODO: might be nice to enforce naming conventions; but until then this works
 # even though events should usually be ALL_CAPS
+
+##
+# @oops:
+##
 { 'event': 'oops' }
index 5a0f2bf8055a6aec4076678418e164ba9f5fc1a8..2865714ad5d4e4310c72c10d8591a8604b9f5aab 100644 (file)
@@ -3,3 +3,4 @@ enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbo
 event oops None
    boxed=False
 object q_empty
+doc symbol=oops expr=('event', 'oops')
index 5a42701b8f2f4164909ced660e891147a1426c28..17a6c3c7b92a246825fc60cb5fc7c3042705d9f2 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/event-nest-struct.json:1: Member 'a' of 'data' for event 'EVENT_A' should be a type name
+tests/qapi-schema/event-nest-struct.json:5: Member 'a' of 'data' for event 'EVENT_A' should be a type name
index ee6f3ecb6f2017a9d7e9b21fc38c9ec05b662167..328e0a64d36e50df7aa133a6988045f89b53ab42 100644 (file)
@@ -1,2 +1,6 @@
+##
+# @EVENT_A:
+# event-nest-struct
+##
 { 'event': 'EVENT_A',
   'data': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' } }
index 8ea91eadb2dfff257adb802d4e09183528943555..e456094993e255dbbb6a6862e84c57b33425b812 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/flat-union-array-branch.json:8: Member 'value1' of union 'TestUnion' cannot be an array
+tests/qapi-schema/flat-union-array-branch.json:20: Member 'value1' of union 'TestUnion' cannot be an array
index 0b98820a8febf619e5b9b9ad5edcf572c1fa4d2d..51dde10392739c70c4de7b2985b8f6b709a4c741 100644 (file)
@@ -1,10 +1,22 @@
+##
+# @TestEnum:
+##
 # we require flat union branches to be a struct
 { 'enum': 'TestEnum',
   'data': [ 'value1', 'value2' ] }
+##
+# @Base:
+##
 { 'struct': 'Base',
   'data': { 'enum1': 'TestEnum' } }
+##
+# @TestTypeB:
+##
 { 'struct': 'TestTypeB',
   'data': { 'integer': 'int' } }
+##
+# @TestUnion:
+##
 { 'union': 'TestUnion',
   'base': 'Base',
   'discriminator': 'enum1',
index bee24a217ae52fbe711630d59e53bcaf0d98be14..072ffbaadd1b39a79415a8d6e6828133d0e9a90a 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/flat-union-bad-base.json:8: 'string' (member of TestTypeA) collides with 'string' (base of TestUnion)
+tests/qapi-schema/flat-union-bad-base.json:21: 'string' (member of TestTypeA) collides with 'string' (base of TestUnion)
index 74dd421708d78a1b78fed8aaa920b2ed6fb90cb1..7713e7f0adc65c502597c834acf405d4c218d737 100644 (file)
@@ -1,10 +1,23 @@
 # we allow anonymous base, but enforce no duplicate keys
+
+##
+# @TestEnum:
+##
 { 'enum': 'TestEnum',
   'data': [ 'value1', 'value2' ] }
+##
+# @TestTypeA:
+##
 { 'struct': 'TestTypeA',
   'data': { 'string': 'str' } }
+##
+# @TestTypeB:
+##
 { 'struct': 'TestTypeB',
   'data': { 'integer': 'int' } }
+##
+# @TestUnion:
+##
 { 'union': 'TestUnion',
   'base': { 'enum1': 'TestEnum', 'string': 'str' },
   'discriminator': 'enum1',
index c38cc8e4dfd6ced3d9a73ed40f86782b01e813b2..1be4e7b23a2c4fde2f999dd1cccb964954bbcd4d 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/flat-union-bad-discriminator.json:11: Discriminator of flat union 'TestUnion' requires a string name
+tests/qapi-schema/flat-union-bad-discriminator.json:27: Discriminator of flat union 'TestUnion' requires a string name
index cd10b9d901823cc53e28d40864508ccaafb9606a..ef92f9b583c62da5b6a8e334c489b55de11a7f74 100644 (file)
@@ -1,13 +1,29 @@
 # we require the discriminator to be a string naming a base-type member
 # this tests the old syntax for anonymous unions before we added alternates
+
+##
+# @TestEnum:
+##
 { 'enum': 'TestEnum',
   'data': [ 'value1', 'value2' ] }
+##
+# @TestBase:
+##
 { 'struct': 'TestBase',
   'data': { 'enum1': 'TestEnum', 'kind': 'str' } }
+##
+# @TestTypeA:
+##
 { 'struct': 'TestTypeA',
   'data': { 'string': 'str' } }
+##
+# @TestTypeB:
+##
 { 'struct': 'TestTypeB',
   'data': { 'integer': 'int' } }
+##
+# @TestUnion:
+##
 { 'union': 'TestUnion',
   'base': 'TestBase',
   'discriminator': {},
index 646f1c9cd1168ce8beead3e49bee7448699f6178..c1ea2d76b30bbb20173bc3bb00ec645b417eec8c 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/flat-union-base-any.json:8: 'base' for union 'TestUnion' cannot use built-in type 'any'
+tests/qapi-schema/flat-union-base-any.json:21: 'base' for union 'TestUnion' cannot use built-in type 'any'
index fe66b713ef4746ea375199e346d3c29662b1747f..3dfb02fa30ab26ebadd40af76bf7a2d3f5983049 100644 (file)
@@ -1,10 +1,23 @@
 # we require the base to be an existing struct
+
+##
+# @TestEnum:
+##
 { 'enum': 'TestEnum',
   'data': [ 'value1', 'value2' ] }
+##
+# @TestTypeA:
+##
 { 'struct': 'TestTypeA',
   'data': { 'string': 'str' } }
+##
+# @TestTypeB:
+##
 { 'struct': 'TestTypeB',
   'data': { 'integer': 'int' } }
+##
+# @TestUnion:
+##
 { 'union': 'TestUnion',
   'base': 'any',
   'discriminator': 'enum1',
index f138395e456a6f30b6ca9dffd1cd9ea1685dbac0..ccc5e85876861462b1e61203e5876c33a1e42bfe 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/flat-union-base-union.json:14: 'base' for union 'TestUnion' cannot use union type 'UnionBase'
+tests/qapi-schema/flat-union-base-union.json:30: 'base' for union 'TestUnion' cannot use union type 'UnionBase'
index 98b4eba181e4426b32728b0916d2175184d89db3..c63c6130b8f2b0f3f5dc62837a13008b8429b0a6 100644 (file)
@@ -2,15 +2,31 @@
 # TODO: It would be possible to allow a union as a base, as long as all
 # permutations of QMP names exposed by base do not clash with any QMP
 # member names added by local variants.
+
+##
+# @TestEnum:
+##
 { 'enum': 'TestEnum',
   'data': [ 'value1', 'value2' ] }
+##
+# @TestTypeA:
+##
 { 'struct': 'TestTypeA',
   'data': { 'string': 'str' } }
+##
+# @TestTypeB:
+##
 { 'struct': 'TestTypeB',
   'data': { 'integer': 'int' } }
+##
+# @UnionBase:
+##
 { 'union': 'UnionBase',
   'data': { 'kind1': 'TestTypeA',
             'kind2': 'TestTypeB' } }
+##
+# @TestUnion:
+##
 { 'union': 'TestUnion',
   'base': 'UnionBase',
   'discriminator': 'type',
index 2adf69755ab82a8efe666320be0ed785ed562e81..fe12a07e2d74f0d9ed04cb3bf82140d5c5fe6a7f 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/flat-union-clash-member.json:11: 'name' (member of Branch1) collides with 'name' (member of Base)
+tests/qapi-schema/flat-union-clash-member.json:27: 'name' (member of Branch1) collides with 'name' (member of Base)
index 9efc7719b859053078223ed4e8555fbb2a209b5e..9000b94f1692283a6c31e1040ddc9200c11b8888 100644 (file)
@@ -1,13 +1,29 @@
 # We check for no duplicate keys between branch members and base
 # base's member 'name' clashes with Branch1's
+
+##
+# @TestEnum:
+##
 { 'enum': 'TestEnum',
   'data': [ 'value1', 'value2' ] }
+##
+# @Base:
+##
 { 'struct': 'Base',
   'data': { 'enum1': 'TestEnum', '*name': 'str' } }
+##
+# @Branch1:
+##
 { 'struct': 'Branch1',
   'data': { 'name': 'str' } }
+##
+# @Branch2:
+##
 { 'struct': 'Branch2',
   'data': { 'value': 'int' } }
+##
+# @TestUnion:
+##
 { 'union': 'TestUnion',
   'base': 'Base',
   'discriminator': 'enum1',
index 15754f54eb9251724eaddb1dc98085fc941b781c..ead7bd4fcbfdf14875011072e1e2e21c8b6f0077 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/flat-union-empty.json:4: Union 'Union' cannot have empty 'data'
+tests/qapi-schema/flat-union-empty.json:14: Union 'Union' cannot have empty 'data'
index 77f1d9abfb8f9b39a0a9dd9a03be3cb4e4343c15..afa89882054e4177bd0578391afa0ad98f2c5a46 100644 (file)
@@ -1,4 +1,14 @@
 # flat unions cannot be empty
+
+##
+# @Empty:
+##
 { 'enum': 'Empty', 'data': [ ] }
+##
+# @Base:
+##
 { 'struct': 'Base', 'data': { 'type': 'Empty' } }
+##
+# @Union:
+##
 { 'union': 'Union', 'base': 'Base', 'discriminator': 'type', 'data': { } }
index e826bf07893a1c298a32f042f1e7a055ef937ed7..c655bbfb4a77e78d4856fd0582fcaf5ab5276283 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/flat-union-incomplete-branch.json:6: Union 'TestUnion' data missing 'value2' branch
+tests/qapi-schema/flat-union-incomplete-branch.json:16: Union 'TestUnion' data missing 'value2' branch
index 25a411bc83e6adbc5ea9374b0f92391c8156ce99..dea03775c7df81f288ff51dc1cde7236e381d8d3 100644 (file)
@@ -1,8 +1,18 @@
 # we require all branches of the union to be covered
+
+##
+# @TestEnum:
+##
 { 'enum': 'TestEnum',
   'data': [ 'value1', 'value2' ] }
+##
+# @TestTypeA:
+##
 { 'struct': 'TestTypeA',
   'data': { 'string': 'str' } }
+##
+# @TestUnion:
+##
 { 'union': 'TestUnion',
   'base': { 'type': 'TestEnum' },
   'discriminator': 'type',
index 2333358d28b7850ddeb1d604d4871e75cecbe2db..c2c3f7604ba8a1d5d0fc44ab417c5f3036654d44 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/flat-union-inline.json:7: Member 'value1' of union 'TestUnion' should be a type name
+tests/qapi-schema/flat-union-inline.json:17: Member 'value1' of union 'TestUnion' should be a type name
index 62c7cda61750fa72701a3d6b2f626a65d9064d5a..400f0817a18a5e38aadd3beb306e4d47be74d33c 100644 (file)
@@ -1,9 +1,19 @@
 # we require branches to be a struct name
 # TODO: should we allow anonymous inline branch types?
+
+##
+# @TestEnum:
+##
 { 'enum': 'TestEnum',
   'data': [ 'value1', 'value2' ] }
+##
+# @Base:
+##
 { 'struct': 'Base',
   'data': { 'enum1': 'TestEnum', 'kind': 'str' } }
+##
+# @TestUnion:
+##
 { 'union': 'TestUnion',
   'base': 'Base',
   'discriminator': 'enum1',
index faf01573b79db7826661c290f3d273848292ef51..299cbb24b2b78a763ad4aef6364061ac1b6e09b6 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/flat-union-int-branch.json:8: Member 'value1' of union 'TestUnion' cannot use built-in type 'int'
+tests/qapi-schema/flat-union-int-branch.json:21: Member 'value1' of union 'TestUnion' cannot use built-in type 'int'
index 9370c349e8b3b2fd0248d6b5de13c065c3413e65..9603e172f8d61bcf17b126ea25010698dcfac8ac 100644 (file)
@@ -1,10 +1,23 @@
 # we require flat union branches to be a struct
+
+##
+# @TestEnum:
+##
 { 'enum': 'TestEnum',
   'data': [ 'value1', 'value2' ] }
+##
+# @Base:
+##
 { 'struct': 'Base',
   'data': { 'enum1': 'TestEnum' } }
+##
+# @TestTypeB:
+##
 { 'struct': 'TestTypeB',
   'data': { 'integer': 'int' } }
+##
+# @TestUnion:
+##
 { 'union': 'TestUnion',
   'base': 'Base',
   'discriminator': 'enum1',
index ccf72d2dfe2f9c500811cf8bf2723f93467d6f6e..455f2dc083e01c8d62b0ca54beb3dd9ef916d485 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/flat-union-invalid-branch-key.json:13: Discriminator value 'value_wrong' is not found in enum 'TestEnum'
+tests/qapi-schema/flat-union-invalid-branch-key.json:28: Discriminator value 'value_wrong' is not found in enum 'TestEnum'
index 95ff7746bfd1e20e915a2bc39b912f1aebdad56c..00f28966ff6aa7120160247a7e1e5804a853056d 100644 (file)
@@ -1,15 +1,30 @@
+##
+# @TestEnum:
+##
 { 'enum': 'TestEnum',
   'data': [ 'value1', 'value2' ] }
 
+##
+# @TestBase:
+##
 { 'struct': 'TestBase',
   'data': { 'enum1': 'TestEnum' } }
 
+##
+# @TestTypeA:
+##
 { 'struct': 'TestTypeA',
   'data': { 'string': 'str' } }
 
+##
+# @TestTypeB:
+##
 { 'struct': 'TestTypeB',
   'data': { 'integer': 'int' } }
 
+##
+# @TestUnion:
+##
 { 'union': 'TestUnion',
   'base': 'TestBase',
   'discriminator': 'enum1',
index 5f4055614eab17214adf2e8da4b543bac3083bd9..f0e427b0a71956216619eb64c7c02df2b79a856b 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/flat-union-invalid-discriminator.json:13: Discriminator 'enum_wrong' is not a member of base struct 'TestBase'
+tests/qapi-schema/flat-union-invalid-discriminator.json:28: Discriminator 'enum_wrong' is not a member of base struct 'TestBase'
index 48b94c3a4d3e668fcca710c461ba63417d3a90ce..c8700c7d71c17fa34d55102493d1bc5d70c327bf 100644 (file)
@@ -1,15 +1,30 @@
+##
+# @TestEnum:
+##
 { 'enum': 'TestEnum',
   'data': [ 'value1', 'value2' ] }
 
+##
+# @TestBase:
+##
 { 'struct': 'TestBase',
   'data': { 'enum1': 'TestEnum' } }
 
+##
+# @TestTypeA:
+##
 { 'struct': 'TestTypeA',
   'data': { 'string': 'str' } }
 
+##
+# @TestTypeB:
+##
 { 'struct': 'TestTypeB',
   'data': { 'integer': 'int' } }
 
+##
+# @TestUnion:
+##
 { 'union': 'TestUnion',
   'base': 'TestBase',
   'discriminator': 'enum_wrong',
index 841c93b55443eef661e4fb4e1980a7ee12f4d2b1..a2d0a81aa01451d0a75e1f83d27223ac1c05a86e 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/flat-union-no-base.json:9: Flat union 'TestUnion' must have a base
+tests/qapi-schema/flat-union-no-base.json:22: Flat union 'TestUnion' must have a base
index ffc4c6f0e6445552c2fc60dd7ddcf9b94234e4aa..641f68aea46b5277f13dacbdfb921c4654426af6 100644 (file)
@@ -1,11 +1,24 @@
 # flat unions require a base
 # TODO: simple unions should be able to use an enum discriminator
+
+##
+# @TestTypeA:
+##
 { 'struct': 'TestTypeA',
   'data': { 'string': 'str' } }
+##
+# @TestTypeB:
+##
 { 'struct': 'TestTypeB',
   'data': { 'integer': 'int' } }
+##
+# @Enum:
+##
 { 'enum': 'Enum',
   'data': [ 'value1', 'value2' ] }
+##
+# @TestUnion:
+##
 { 'union': 'TestUnion',
   'discriminator': 'Enum',
   'data': { 'value1': 'TestTypeA',
index aaabedb3bd628078b64e8ad72c744c8b24e65ca9..e15f8564dd0b5334893b65e990374d95c11e4e77 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/flat-union-optional-discriminator.json:6: Discriminator of flat union 'MyUnion' does not allow optional name '*switch'
+tests/qapi-schema/flat-union-optional-discriminator.json:19: Discriminator of flat union 'MyUnion' does not allow optional name '*switch'
index 08a8f7ef8bcbe76e566387ab0a0cab5136d4efed..9f19af5789a5ac831f6e520df76e30960dda6832 100644 (file)
@@ -1,8 +1,21 @@
 # we require the discriminator to be non-optional
+
+##
+# @Enum:
+##
 { 'enum': 'Enum', 'data': [ 'one', 'two' ] }
+##
+# @Base:
+##
 { 'struct': 'Base',
   'data': { '*switch': 'Enum' } }
+##
+# @Branch:
+##
 { 'struct': 'Branch', 'data': { 'name': 'str' } }
+##
+# @MyUnion:
+##
 { 'union': 'MyUnion',
   'base': 'Base',
   'discriminator': '*switch',
index 200016bd5c5099cbfffea604d75ee9839835b475..bc0c133aa953cc3828a6f99c13f1be07df7d7384 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/flat-union-string-discriminator.json:13: Discriminator 'kind' must be of enumeration type
+tests/qapi-schema/flat-union-string-discriminator.json:28: Discriminator 'kind' must be of enumeration type
index 8af60333b60f4f9ed89e8a5c3bab9542e0e9fb12..47a17d2e4acec5509ea112f32c90ef69aee07bee 100644 (file)
@@ -1,15 +1,30 @@
+##
+# @TestEnum:
+##
 { 'enum': 'TestEnum',
   'data': [ 'value1', 'value2' ] }
 
+##
+# @TestBase:
+##
 { 'struct': 'TestBase',
   'data': { 'enum1': 'TestEnum', 'kind': 'str' } }
 
+##
+# @TestTypeA:
+##
 { 'struct': 'TestTypeA',
   'data': { 'string': 'str' } }
 
+##
+# @TestTypeB:
+##
 { 'struct': 'TestTypeB',
   'data': { 'integer': 'int' } }
 
+##
+# @TestUnion:
+##
 { 'union': 'TestUnion',
   'base': 'TestBase',
   'discriminator': 'kind',
index 56617501e7b0d79bd005feed6008c4a7905711bc..c03404bee36d3df8e99a7638f79f055fab0b66ef 100644 (file)
@@ -1,4 +1,8 @@
 # we allow escape sequences in strings, if they map back to ASCII
 # { 'command': 'fooA', 'data': { 'bar1': 'str' } }
+
+##
+# @fooA:
+##
 { 'c\u006fmmand': '\u0066\u006f\u006FA',
   'd\u0061ta': { '\u0062\u0061\u00721': '\u0073\u0074\u0072' } }
index 1d2722c02ea7f448ad268395bfca3f7786eaead3..69fc908e68307cd7cbc7b5dab5c860df3c50656e 100644 (file)
@@ -5,3 +5,4 @@ command fooA q_obj_fooA-arg -> None
 object q_empty
 object q_obj_fooA-arg
     member bar1: str optional=False
+doc symbol=fooA expr=('command', 'fooA')
index 4bd4af4162432a8ac0402ff3746dd06013545b1c..b4bd8a23d7773a3e7bd09848d66c180cfc479d5a 100644 (file)
@@ -1,2 +1,5 @@
+##
+# @Status:
+##
 { 'enum': 'Status',
   'data': [ 'good', 'bad', 'ugly' ] }
index 5d7c13cad167fc05b67b5cd3c8c4a48aa2b75803..a962fb2d2efb11242b33f93d80e8d6d1a1af6201 100644 (file)
@@ -2,3 +2,4 @@ enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbo
     prefix QTYPE
 enum Status ['good', 'bad', 'ugly']
 object q_empty
+doc symbol=Status expr=('enum', 'Status')
index 5d7c13cad167fc05b67b5cd3c8c4a48aa2b75803..a962fb2d2efb11242b33f93d80e8d6d1a1af6201 100644 (file)
@@ -2,3 +2,4 @@ enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbo
     prefix QTYPE
 enum Status ['good', 'bad', 'ugly']
 object q_empty
+doc symbol=Status expr=('enum', 'Status')
index 4bd4af4162432a8ac0402ff3746dd06013545b1c..b4bd8a23d7773a3e7bd09848d66c180cfc479d5a 100644 (file)
@@ -1,2 +1,5 @@
+##
+# @Status:
+##
 { 'enum': 'Status',
   'data': [ 'good', 'bad', 'ugly' ] }
index 5d7c13cad167fc05b67b5cd3c8c4a48aa2b75803..a962fb2d2efb11242b33f93d80e8d6d1a1af6201 100644 (file)
@@ -2,3 +2,4 @@ enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbo
     prefix QTYPE
 enum Status ['good', 'bad', 'ugly']
 object q_empty
+doc symbol=Status expr=('enum', 'Status')
index 7115d3131e81bc59603d41c47f46345fc077b709..d759be187765eaf19c756297714d445e939fe6b2 100644 (file)
@@ -1,2 +1,8 @@
+##
+# @eins:
+##
 { 'command' : 'eins' }
+##
+# @zwei:
+##
  { 'command' : 'zwei' }
index e8171c935f96b5ee0cd2788da34146538baf6fa1..285d0522577dff5815efb64e3ee0768d279a2b55 100644 (file)
@@ -5,3 +5,5 @@ command eins None -> None
 object q_empty
 command zwei None -> None
    gen=True success_response=True boxed=False
+doc symbol=eins expr=('command', 'eins')
+doc symbol=zwei expr=('command', 'zwei')
index b3e7b14e420a27d2e86fc79dfca03c2ac609505a..74c4ef73241e056dd4a1269b00ecd965f9698393 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/missing-type.json:2: Expression is missing metatype
+tests/qapi-schema/missing-type.json:6: Expression is missing metatype
index ff5349d3febe4778e78a50a786d49a2449673947..c2fc62d0af76e9c3fc31d251ed83164dd26a3814 100644 (file)
@@ -1,2 +1,6 @@
 # we reject an expression with missing metatype
+
+##
+# @foo:
+##
 { 'data': { } }
index da767bade2317b4f41924ffe13ceb2a42ead6c2b..379bd1d3f422b437528ad46961dbf02e8088951e 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/nested-struct-data.json:2: Member 'a' of 'data' for command 'foo' should be a type name
+tests/qapi-schema/nested-struct-data.json:6: Member 'a' of 'data' for command 'foo' should be a type name
index efbe773dedf8195c2d44e977601fdaad0eccfd91..6106e15e864b8da2f789c62f1e59ca8998f45032 100644 (file)
@@ -1,3 +1,7 @@
 # inline subtypes collide with our desired future use of defaults
+
+##
+# @foo:
+##
 { 'command': 'foo',
   'data': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' } }
index 17194637baeedef2a13a7c8d691016abe99ad9e2..f4d8cc4230ba7b7442026d9fa44e7772280766e7 100644 (file)
 # This file is a stress test of supported qapi constructs that must
 # parse and compile correctly.
 
+##
+# = Section
+# == subsection
+#
+# Some text foo with *strong* and _emphasis_
+# 1. with a list
+# 2. like that @foo
+#
+# And some code:
+# | $ echo foo
+# | -> do this
+# | <- get that
+#
+# Note: is not a meta
+##
+
+##
+# @TestStruct:
+#
+# body with @var
+#
+# @integer: foo
+#           blah
+#
+#           bao
+#
+# @boolean: bar
+# @string: baz
+#
+# Example:
+#
+# -> { "execute": ... }
+# <- { "return": ... }
+#
+# Since: 2.3
+# Note: a note
+#
+##
 { 'struct': 'TestStruct',
   'data': { 'integer': 'int', 'boolean': 'bool', 'string': 'str' } }
 
+##
+# @NestedEnumsOne:
 # for testing enums
+##
 { 'struct': 'NestedEnumsOne',
   'data': { 'enum1': 'EnumOne',   # Intentional forward reference
             '*enum2': 'EnumOne', 'enum3': 'EnumOne', '*enum4': 'EnumOne' } }
 
+##
+# @MyEnum:
 # An empty enum, although unusual, is currently acceptable
+##
 { 'enum': 'MyEnum', 'data': [ ] }
 
+##
+# @Empty1:
 # Likewise for an empty struct, including an empty base
+##
 { 'struct': 'Empty1', 'data': { } }
+##
+# @Empty2:
+##
 { 'struct': 'Empty2', 'base': 'Empty1', 'data': { } }
 
+##
+# @user_def_cmd0:
+##
 { 'command': 'user_def_cmd0', 'data': 'Empty2', 'returns': 'Empty2' }
 
+##
+# @QEnumTwo:
 # for testing override of default naming heuristic
+##
 { 'enum': 'QEnumTwo',
   'prefix': 'QENUM_TWO',
   'data': [ 'value1', 'value2' ] }
 
+##
+# @UserDefOne:
 # for testing nested structs
+##
 { 'struct': 'UserDefOne',
   'base': 'UserDefZero',        # intentional forward reference
   'data': { 'string': 'str',
             '*enum1': 'EnumOne' } }   # intentional forward reference
 
+##
+# @EnumOne:
+##
 { 'enum': 'EnumOne',
   'data': [ 'value1', 'value2', 'value3' ] }
 
+##
+# @UserDefZero:
+##
 { 'struct': 'UserDefZero',
   'data': { 'integer': 'int' } }
 
+##
+# @UserDefTwoDictDict:
+##
 { 'struct': 'UserDefTwoDictDict',
   'data': { 'userdef': 'UserDefOne', 'string': 'str' } }
 
+##
+# @UserDefTwoDict:
+##
 { 'struct': 'UserDefTwoDict',
   'data': { 'string1': 'str',
             'dict2': 'UserDefTwoDictDict',
             '*dict3': 'UserDefTwoDictDict' } }
 
+##
+# @UserDefTwo:
+##
 { 'struct': 'UserDefTwo',
   'data': { 'string0': 'str',
             'dict1': 'UserDefTwoDict' } }
 
+##
+# @ForceArrays:
 # dummy struct to force generation of array types not otherwise mentioned
+##
 { 'struct': 'ForceArrays',
   'data': { 'unused1':['UserDefOne'], 'unused2':['UserDefTwo'],
             'unused3':['TestStruct'] } }
 
+##
+# @UserDefA:
 # for testing unions
 # Among other things, test that a name collision between branches does
 # not cause any problems (since only one branch can be in use at a time),
 # by intentionally using two branches that both have a C member 'a_b'
+##
 { 'struct': 'UserDefA',
   'data': { 'boolean': 'bool', '*a_b': 'int' } }
 
+##
+# @UserDefB:
+##
 { 'struct': 'UserDefB',
   'data': { 'intb': 'int', '*a-b': 'bool' } }
 
+##
+# @UserDefFlatUnion:
+##
 { 'union': 'UserDefFlatUnion',
   'base': 'UserDefUnionBase',   # intentional forward reference
   'discriminator': 'enum1',
             'value2' : 'UserDefB',
             'value3' : 'UserDefB' } }
 
+##
+# @UserDefUnionBase:
+##
 { 'struct': 'UserDefUnionBase',
   'base': 'UserDefZero',
   'data': { 'string': 'str', 'enum1': 'EnumOne' } }
 
+##
+# @UserDefFlatUnion2:
 # this variant of UserDefFlatUnion defaults to a union that uses members with
 # allocated types to test corner cases in the cleanup/dealloc visitor
+##
 { 'union': 'UserDefFlatUnion2',
   'base': { '*integer': 'int', 'string': 'str', 'enum1': 'QEnumTwo' },
   'discriminator': 'enum1',
   'data': { 'value1' : 'UserDefC', # intentional forward reference
             'value2' : 'UserDefB' } }
 
+##
+# @WrapAlternate:
+##
 { 'struct': 'WrapAlternate',
   'data': { 'alt': 'UserDefAlternate' } }
+##
+# @UserDefAlternate:
+##
 { 'alternate': 'UserDefAlternate',
   'data': { 'udfu': 'UserDefFlatUnion', 's': 'str', 'i': 'int' } }
 
+##
+# @UserDefC:
+##
 { 'struct': 'UserDefC',
   'data': { 'string1': 'str', 'string2': 'str' } }
 
 # for testing use of 'number' within alternates
+##
+# @AltStrBool:
+##
 { 'alternate': 'AltStrBool', 'data': { 's': 'str', 'b': 'bool' } }
+##
+# @AltStrNum:
+##
 { 'alternate': 'AltStrNum', 'data': { 's': 'str', 'n': 'number' } }
+##
+# @AltNumStr:
+##
 { 'alternate': 'AltNumStr', 'data': { 'n': 'number', 's': 'str' } }
+##
+# @AltStrInt:
+##
 { 'alternate': 'AltStrInt', 'data': { 's': 'str', 'i': 'int' } }
+##
+# @AltIntNum:
+##
 { 'alternate': 'AltIntNum', 'data': { 'i': 'int', 'n': 'number' } }
+##
+# @AltNumInt:
+##
 { 'alternate': 'AltNumInt', 'data': { 'n': 'number', 'i': 'int' } }
 
+##
+# @UserDefNativeListUnion:
 # for testing native lists
+##
 { 'union': 'UserDefNativeListUnion',
   'data': { 'integer': ['int'],
             's8': ['int8'],
             'any': ['any'] } }
 
 # testing commands
+##
+# @user_def_cmd:
+##
 { 'command': 'user_def_cmd', 'data': {} }
+##
+# @user_def_cmd1:
+##
 { 'command': 'user_def_cmd1', 'data': {'ud1a': 'UserDefOne'} }
+##
+# @user_def_cmd2:
+##
 { 'command': 'user_def_cmd2',
   'data': {'ud1a': 'UserDefOne', '*ud1b': 'UserDefOne'},
   'returns': 'UserDefTwo' }
 
+##
+# Another comment
+##
+
+##
+# @guest-get-time:
+#
+# @guest-get-time body
+#
+# @a: an integer
+# @b: #optional integer
+#
+# Returns: returns something
+#
+# Example:
+#
+# -> { "execute": "guest-get-time", ... }
+# <- { "return": "42" }
+#
+##
+
 # Returning a non-dictionary requires a name from the whitelist
 { 'command': 'guest-get-time', 'data': {'a': 'int', '*b': 'int' },
   'returns': 'int' }
+##
+# @guest-sync:
+##
 { 'command': 'guest-sync', 'data': { 'arg': 'any' }, 'returns': 'any' }
+##
+# @boxed-struct:
+##
 { 'command': 'boxed-struct', 'boxed': true, 'data': 'UserDefZero' }
+##
+# @boxed-union:
+##
 { 'command': 'boxed-union', 'data': 'UserDefNativeListUnion', 'boxed': true }
 
+##
+# @UserDefOptions:
+#
 # For testing integer range flattening in opts-visitor. The following schema
 # corresponds to the option format:
 #
 #
 # For simplicity, this example doesn't use [type=]discriminator nor optargs
 # specific to discriminator values.
+##
 { 'struct': 'UserDefOptions',
   'data': {
     '*i64' : [ 'int'    ],
     '*u64x':   'uint64'  } }
 
 # testing event
+##
+# @EventStructOne:
+##
 { 'struct': 'EventStructOne',
   'data': { 'struct1': 'UserDefOne', 'string': 'str', '*enum2': 'EnumOne' } }
 
+##
+# @EVENT_A:
+##
 { 'event': 'EVENT_A' }
+##
+# @EVENT_B:
+##
 { 'event': 'EVENT_B',
   'data': { } }
+##
+# @EVENT_C:
+##
 { 'event': 'EVENT_C',
   'data': { '*a': 'int', '*b': 'UserDefOne', 'c': 'str' } }
+##
+# @EVENT_D:
+##
 { 'event': 'EVENT_D',
   'data': { 'a' : 'EventStructOne', 'b' : 'str', '*c': 'str', '*enum3': 'EnumOne' } }
+##
+# @EVENT_E:
+##
 { 'event': 'EVENT_E', 'boxed': true, 'data': 'UserDefZero' }
+##
+# @EVENT_F:
+##
 { 'event': 'EVENT_F', 'boxed': true, 'data': 'UserDefAlternate' }
 
 # test that we correctly compile downstream extensions, as well as munge
 # ticklish names
+##
+# @__org.qemu_x-Enum:
+##
 { 'enum': '__org.qemu_x-Enum', 'data': [ '__org.qemu_x-value' ] }
+##
+# @__org.qemu_x-Base:
+##
 { 'struct': '__org.qemu_x-Base',
   'data': { '__org.qemu_x-member1': '__org.qemu_x-Enum' } }
+##
+# @__org.qemu_x-Struct:
+##
 { 'struct': '__org.qemu_x-Struct', 'base': '__org.qemu_x-Base',
   'data': { '__org.qemu_x-member2': 'str', '*wchar-t': 'int' } }
+##
+# @__org.qemu_x-Union1:
+##
 { 'union': '__org.qemu_x-Union1', 'data': { '__org.qemu_x-branch': 'str' } }
+##
+# @__org.qemu_x-Struct2:
+##
 { 'struct': '__org.qemu_x-Struct2',
   'data': { 'array': ['__org.qemu_x-Union1'] } }
+##
+# @__org.qemu_x-Union2:
+##
 { 'union': '__org.qemu_x-Union2', 'base': '__org.qemu_x-Base',
   'discriminator': '__org.qemu_x-member1',
   'data': { '__org.qemu_x-value': '__org.qemu_x-Struct2' } }
+##
+# @__org.qemu_x-Alt:
+##
 { 'alternate': '__org.qemu_x-Alt',
   'data': { '__org.qemu_x-branch': 'str', 'b': '__org.qemu_x-Base' } }
+##
+# @__ORG.QEMU_X-EVENT:
+##
 { 'event': '__ORG.QEMU_X-EVENT', 'data': '__org.qemu_x-Struct' }
+##
+# @__org.qemu_x-command:
+##
 { 'command': '__org.qemu_x-command',
   'data': { 'a': ['__org.qemu_x-Enum'], 'b': ['__org.qemu_x-Struct'],
             'c': '__org.qemu_x-Union2', 'd': '__org.qemu_x-Alt' },
index 9d99c4eebbeb132ba5064eea330937b882a5fdb6..bc8d496ff46733686946944eb4d3d950e0292321 100644 (file)
@@ -232,3 +232,133 @@ command user_def_cmd1 q_obj_user_def_cmd1-arg -> None
    gen=True success_response=True boxed=False
 command user_def_cmd2 q_obj_user_def_cmd2-arg -> UserDefTwo
    gen=True success_response=True boxed=False
+doc freeform
+    body=
+= Section
+== subsection
+
+Some text foo with *strong* and _emphasis_
+1. with a list
+2. like that @foo
+
+And some code:
+| $ echo foo
+| -> do this
+| <- get that
+
+Note: is not a meta
+doc symbol=TestStruct expr=('struct', 'TestStruct')
+    arg=integer
+foo
+blah
+
+bao
+    arg=boolean
+bar
+    arg=string
+baz
+    section=Example
+-> { "execute": ... }
+<- { "return": ... }
+    section=Since
+2.3
+    section=Note
+a note
+    body=
+body with @var
+doc symbol=NestedEnumsOne expr=('struct', 'NestedEnumsOne')
+    body=
+for testing enums
+doc symbol=MyEnum expr=('enum', 'MyEnum')
+    body=
+An empty enum, although unusual, is currently acceptable
+doc symbol=Empty1 expr=('struct', 'Empty1')
+    body=
+Likewise for an empty struct, including an empty base
+doc symbol=Empty2 expr=('struct', 'Empty2')
+doc symbol=user_def_cmd0 expr=('command', 'user_def_cmd0')
+doc symbol=QEnumTwo expr=('enum', 'QEnumTwo')
+    body=
+for testing override of default naming heuristic
+doc symbol=UserDefOne expr=('struct', 'UserDefOne')
+    body=
+for testing nested structs
+doc symbol=EnumOne expr=('enum', 'EnumOne')
+doc symbol=UserDefZero expr=('struct', 'UserDefZero')
+doc symbol=UserDefTwoDictDict expr=('struct', 'UserDefTwoDictDict')
+doc symbol=UserDefTwoDict expr=('struct', 'UserDefTwoDict')
+doc symbol=UserDefTwo expr=('struct', 'UserDefTwo')
+doc symbol=ForceArrays expr=('struct', 'ForceArrays')
+    body=
+dummy struct to force generation of array types not otherwise mentioned
+doc symbol=UserDefA expr=('struct', 'UserDefA')
+    body=
+for testing unions
+Among other things, test that a name collision between branches does
+not cause any problems (since only one branch can be in use at a time),
+by intentionally using two branches that both have a C member 'a_b'
+doc symbol=UserDefB expr=('struct', 'UserDefB')
+doc symbol=UserDefFlatUnion expr=('union', 'UserDefFlatUnion')
+doc symbol=UserDefUnionBase expr=('struct', 'UserDefUnionBase')
+doc symbol=UserDefFlatUnion2 expr=('union', 'UserDefFlatUnion2')
+    body=
+this variant of UserDefFlatUnion defaults to a union that uses members with
+allocated types to test corner cases in the cleanup/dealloc visitor
+doc symbol=WrapAlternate expr=('struct', 'WrapAlternate')
+doc symbol=UserDefAlternate expr=('alternate', 'UserDefAlternate')
+doc symbol=UserDefC expr=('struct', 'UserDefC')
+doc symbol=AltStrBool expr=('alternate', 'AltStrBool')
+doc symbol=AltStrNum expr=('alternate', 'AltStrNum')
+doc symbol=AltNumStr expr=('alternate', 'AltNumStr')
+doc symbol=AltStrInt expr=('alternate', 'AltStrInt')
+doc symbol=AltIntNum expr=('alternate', 'AltIntNum')
+doc symbol=AltNumInt expr=('alternate', 'AltNumInt')
+doc symbol=UserDefNativeListUnion expr=('union', 'UserDefNativeListUnion')
+    body=
+for testing native lists
+doc symbol=user_def_cmd expr=('command', 'user_def_cmd')
+doc symbol=user_def_cmd1 expr=('command', 'user_def_cmd1')
+doc symbol=user_def_cmd2 expr=('command', 'user_def_cmd2')
+doc freeform
+    body=
+Another comment
+doc symbol=guest-get-time expr=('command', 'guest-get-time')
+    arg=a
+an integer
+    arg=b
+#optional integer
+    section=Returns
+returns something
+    section=Example
+-> { "execute": "guest-get-time", ... }
+<- { "return": "42" }
+    body=
+@guest-get-time body
+doc symbol=guest-sync expr=('command', 'guest-sync')
+doc symbol=boxed-struct expr=('command', 'boxed-struct')
+doc symbol=boxed-union expr=('command', 'boxed-union')
+doc symbol=UserDefOptions expr=('struct', 'UserDefOptions')
+    body=
+For testing integer range flattening in opts-visitor. The following schema
+corresponds to the option format:
+
+-userdef i64=3-6,i64=-5--1,u64=2,u16=1,u16=7-12
+
+For simplicity, this example doesn't use [type=]discriminator nor optargs
+specific to discriminator values.
+doc symbol=EventStructOne expr=('struct', 'EventStructOne')
+doc symbol=EVENT_A expr=('event', 'EVENT_A')
+doc symbol=EVENT_B expr=('event', 'EVENT_B')
+doc symbol=EVENT_C expr=('event', 'EVENT_C')
+doc symbol=EVENT_D expr=('event', 'EVENT_D')
+doc symbol=EVENT_E expr=('event', 'EVENT_E')
+doc symbol=EVENT_F expr=('event', 'EVENT_F')
+doc symbol=__org.qemu_x-Enum expr=('enum', '__org.qemu_x-Enum')
+doc symbol=__org.qemu_x-Base expr=('struct', '__org.qemu_x-Base')
+doc symbol=__org.qemu_x-Struct expr=('struct', '__org.qemu_x-Struct')
+doc symbol=__org.qemu_x-Union1 expr=('union', '__org.qemu_x-Union1')
+doc symbol=__org.qemu_x-Struct2 expr=('struct', '__org.qemu_x-Struct2')
+doc symbol=__org.qemu_x-Union2 expr=('union', '__org.qemu_x-Union2')
+doc symbol=__org.qemu_x-Alt expr=('alternate', '__org.qemu_x-Alt')
+doc symbol=__ORG.QEMU_X-EVENT expr=('event', '__ORG.QEMU_X-EVENT')
+doc symbol=__org.qemu_x-command expr=('command', '__org.qemu_x-command')
index b2757225c4ea5aecaa2ecc02ae2acca023e57659..ee0a2adf0b24811d9b9a3bf84985ecca2a94dcd0 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/redefined-builtin.json:2: built-in 'size' is already defined
+tests/qapi-schema/redefined-builtin.json:6: built-in 'size' is already defined
index 45b8a550ad8d133fb7dea8516357aeb22df1a0ba..6d3a940d5e03a7cac64f52fc90226b7bfc87b0db 100644 (file)
@@ -1,2 +1,6 @@
 # we reject types that duplicate builtin names
+
+##
+# @size:
+##
 { 'struct': 'size', 'data': { 'myint': 'size' } }
index 82ae256e639e8866926572ac7be6329c2a8738b6..1e297c43ba16039a9eb879cac9374618eb47c52c 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/redefined-command.json:3: command 'foo' is already defined
+tests/qapi-schema/redefined-command.json:10: command 'foo' is already defined
index 247e4019483eca0e15aa0576fe6300fa71deca4a..3a8cb9024c217bbfa6ba1073ea1d3a4103fed6f8 100644 (file)
@@ -1,3 +1,10 @@
 # we reject commands defined more than once
+
+##
+# @foo:
+##
 { 'command': 'foo', 'data': { 'one': 'str' } }
+##
+# @foo:
+##
 { 'command': 'foo', 'data': { '*two': 'str' } }
index 35429cb48186a0bd2d3798c458c06c1f71e1bf45..912c7851192df0a346d8be6f6073b853c203a685 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/redefined-event.json:3: event 'EVENT_A' is already defined
+tests/qapi-schema/redefined-event.json:10: event 'EVENT_A' is already defined
index 7717e91c18c4b44f33211e2a2696da2d921e1b1e..ec7aeea0f05f8d2262bee7202673b61c8224198e 100644 (file)
@@ -1,3 +1,10 @@
 # we reject duplicate events
+
+##
+# @EVENT_A:
+##
 { 'event': 'EVENT_A', 'data': { 'myint': 'int' } }
+##
+# @EVENT_A:
+##
 { 'event': 'EVENT_A', 'data': { 'myint': 'int' } }
index 06ea78c4781e479cea71a15b34609adc36ba36cd..28d87c098cfd9dad6b2a85a55a4460fc228d1c76 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/redefined-type.json:3: struct 'foo' is already defined
+tests/qapi-schema/redefined-type.json:10: struct 'foo' is already defined
index a09e768baeb5e6c3ec73b8f35e6d6009159c7615..7a8f3e1ec8426eb9e769d773bd78479133d74976 100644 (file)
@@ -1,3 +1,10 @@
 # we reject types defined more than once
+
+##
+# @foo:
+##
 { 'struct': 'foo', 'data': { 'one': 'str' } }
+##
+# @foo:
+##
 { 'enum': 'foo', 'data': [ 'two' ] }
index f939e044eba01713c57ca727882753af8adaf1dd..5e17f3169b0a7165de273ef9a9edb13361a3c3d6 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/reserved-command-q.json:5: 'command' uses invalid name 'q-unix'
+tests/qapi-schema/reserved-command-q.json:12: 'command' uses invalid name 'q-unix'
index 99f8aae314bc1295a354d89546c3fdbebbc02358..bba0860c99f2aca85b77df9642932f8a4fb822e9 100644 (file)
@@ -1,5 +1,12 @@
 # C entity name collision
 # We reject names like 'q-unix', because they can collide with the mangled
 # name for 'unix' in generated C.
+
+##
+# @unix:
+##
 { 'command': 'unix' }
+##
+# @q-unix:
+##
 { 'command': 'q-unix' }
index e1c3480ee227adc41f3a66fb535d3b48a804ee08..acb2df811d2ecc59e4c06ef3476ca7987d894592 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/reserved-enum-q.json:4: Member of enum 'Foo' uses invalid name 'q-Unix'
+tests/qapi-schema/reserved-enum-q.json:8: Member of enum 'Foo' uses invalid name 'q-Unix'
index 3593a765eae720c8ba142940b04951cdb45446f8..6c7e7177c3d47eec8bdb024adf49e014a04bbb8b 100644 (file)
@@ -1,4 +1,8 @@
 # C entity name collision
 # We reject names like 'q-unix', because they can collide with the mangled
 # name for 'unix' in generated C.
+
+##
+# @Foo:
+##
 { 'enum': 'Foo', 'data': [ 'unix', 'q-Unix' ] }
index e75577144613cccc031fd515b51e3cb584963d70..9ace79605540c57c959242fe94ec68e25e949ce0 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/reserved-member-has.json:5: Member of 'data' for command 'oops' uses reserved name 'has-a'
+tests/qapi-schema/reserved-member-has.json:9: Member of 'data' for command 'oops' uses reserved name 'has-a'
index 45b9109bdc5e5edd67a8c6b385a11c0d22db4bf9..f0d8905ca22fb5dd3c183f5120d578238e2ed38d 100644 (file)
@@ -2,4 +2,8 @@
 # We reject names like 'has-a', because they can collide with the flag
 # for an optional 'a' in generated C.
 # TODO we could munge the optional flag name to avoid the collision.
+
+##
+# @oops:
+##
 { 'command': 'oops', 'data': { '*a': 'str', 'has-a': 'str' } }
index f3d5dd78187759a3b515333e26688760e505ee09..1709a884626419aacdd442463e86ff6882ac5479 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/reserved-member-q.json:4: Member of 'data' for struct 'Foo' uses invalid name 'q-unix'
+tests/qapi-schema/reserved-member-q.json:8: Member of 'data' for struct 'Foo' uses invalid name 'q-unix'
index 62fed8fddfe8a4d584fa7b99120cfcb943c7354b..f51e3129176863d5bb12adb0cf627a9087e72ecb 100644 (file)
@@ -1,4 +1,8 @@
 # C member name collision
 # We reject names like 'q-unix', because they can collide with the mangled
 # name for 'unix' in generated C.
+
+##
+# @Foo:
+##
 { 'struct': 'Foo', 'data': { 'unix':'int', 'q-unix':'bool' } }
index 87d42296cc1e3d3f4818c34742ba425b9b9aa84e..6ec69a712ae6d045f8430a876a1db1f9955d5410 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/reserved-member-u.json:7: Member of 'data' for struct 'Oops' uses reserved name 'u'
+tests/qapi-schema/reserved-member-u.json:11: Member of 'data' for struct 'Oops' uses reserved name 'u'
index 1eaf0f301c68e906d29f82331600dd53c30ee8f4..3a578e5b567acd3a6010491b44250499a006da59 100644 (file)
@@ -4,4 +4,8 @@
 # This is true even for non-unions, because it is possible to convert a
 # struct to flat union while remaining backwards compatible in QMP.
 # TODO - we could munge the member name to 'q_u' to avoid the collision
+
+##
+# @Oops:
+##
 { 'struct': 'Oops', 'data': { 'u': 'str' } }
index 65ff0da8ce95ba1e561ebc9d44a01be15b12a9db..c9aefee3a85cf4864cb40ee2542617d659e4e232 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/reserved-member-underscore.json:4: Member of 'data' for struct 'Oops' uses invalid name '_oops'
+tests/qapi-schema/reserved-member-underscore.json:8: Member of 'data' for struct 'Oops' uses invalid name '_oops'
index 4a3a0176381b6af918067f9d9ced5ed56aead43f..cc34b54b02adc64e793ff625c80ac767b2da38c3 100644 (file)
@@ -1,4 +1,8 @@
 # C member name collision
 # We reject use of a single leading underscore in all names (names must
 # begin with a letter or a downstream extension double-underscore prefix).
+
+##
+# @Oops:
+##
 { 'struct': 'Oops', 'data': { '_oops': 'str' } }
index 0a38efaad8d8df6331a8d9431c5613bd18986068..869807306215e3b7e29ce68dd0a0ee78ba0016b9 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/reserved-type-kind.json:2: enum 'UnionKind' should not end in 'Kind'
+tests/qapi-schema/reserved-type-kind.json:6: enum 'UnionKind' should not end in 'Kind'
index 9ecaba12bc1bad7a30916a614451190a01ae8c9e..a09494156136e2461133568bacde4cfffea8c3b9 100644 (file)
@@ -1,2 +1,6 @@
 # we reject types that would conflict with implicit union enum
+
+##
+# @UnionKind:
+##
 { 'enum': 'UnionKind', 'data': [ 'oops' ] }
index 4510fa6d903c1907e3f8cd268c419f1bcff374f3..ec0531c4b913b23dd08b5394a3f7b2c6978a2c52 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/reserved-type-list.json:5: struct 'FooList' should not end in 'List'
+tests/qapi-schema/reserved-type-list.json:9: struct 'FooList' should not end in 'List'
index 98d53bf808f6c7e07ec75e14722b21a9187ae8b6..6effb78e7fa3ff18f55a6baba457182ac0113ec1 100644 (file)
@@ -2,4 +2,8 @@
 # We reserve names ending in 'List' for use by array types.
 # TODO - we could choose array names to avoid collision with user types,
 # in order to let this compile
+
+##
+# @FooList:
+##
 { 'struct': 'FooList', 'data': { 's': 'str' } }
index dfbb419cac287e386e90fef2addec8f57ce35858..2b81623ca3450881a785270f981b6e594d4215db 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/returns-alternate.json:3: 'returns' for command 'oops' cannot use alternate type 'Alt'
+tests/qapi-schema/returns-alternate.json:10: 'returns' for command 'oops' cannot use alternate type 'Alt'
index 972390c06b0825a8fe57cfc7d49a3df13ed4dcc9..005bf2d1485f5ad36c0db962227ed43061b008cf 100644 (file)
@@ -1,3 +1,10 @@
 # we reject returns if it is an alternate type
+
+##
+# @Alt:
+##
 { 'alternate': 'Alt', 'data': { 'a': 'int', 'b': 'str' } }
+##
+# @oops:
+##
 { 'command': 'oops', 'returns': 'Alt' }
index 138095ccde1cfb056cb088bd6be2b56b43165cab..b53bdb0adee96a4dc2cac04e001cbc3e36b634f9 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/returns-array-bad.json:2: 'returns' for command 'oops': array type must contain single type name
+tests/qapi-schema/returns-array-bad.json:6: 'returns' for command 'oops': array type must contain single type name
index 09b0b1f1827a182abb6eb0f8187a30afcc5d2f61..30528fed2995e0df9d28c3523b7ee7f2d7028afb 100644 (file)
@@ -1,2 +1,6 @@
 # we reject an array return that is not a single type
+
+##
+# @oops:
+##
 { 'command': 'oops', 'returns': [ 'str', 'str' ] }
index eb2d0c466158e225da714558d416307eab582bc2..1570a35d49e67d32d4f73066af8899aebb89c3eb 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/returns-dict.json:2: 'returns' for command 'oops' should be a type name
+tests/qapi-schema/returns-dict.json:6: 'returns' for command 'oops' should be a type name
index 1cfef3ede7d26c28c800519e4627a8fcabdb4e39..6a3ed0f34d8a2830b563f38ebc3b3dba0701645b 100644 (file)
@@ -1,2 +1,6 @@
 # we reject inline struct return type
+
+##
+# @oops:
+##
 { 'command': 'oops', 'returns': { 'a': 'str' } }
index 1f43e3ac9f6116ab2a78b99e468fa7f2fdd432bf..d76bcfe45541a0be26bb3009c512da79d90b1687 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/returns-unknown.json:2: 'returns' for command 'oops' uses unknown type 'NoSuchType'
+tests/qapi-schema/returns-unknown.json:6: 'returns' for command 'oops' uses unknown type 'NoSuchType'
index 25bd498bffb7dd35b28abba85e3c459006b90161..3837f0e60715ac3517625acd11759b177e0356cb 100644 (file)
@@ -1,2 +1,6 @@
 # we reject returns if it does not contain a known type
+
+##
+# @oops:
+##
 { 'command': 'oops', 'returns': 'NoSuchType' }
index f47c1ee7ca4a0ed05fb3bc651df094d1137dc5e9..e77ea2da3f609dc16c9f953132f3ecf40d22bb5e 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/returns-whitelist.json:10: 'returns' for command 'no-way-this-will-get-whitelisted' cannot use built-in type 'int'
+tests/qapi-schema/returns-whitelist.json:26: 'returns' for command 'no-way-this-will-get-whitelisted' cannot use built-in type 'int'
index e8b3cea39613c0f5cea18e72384f22ed17d6fd27..0bc952db8751f3d2529bcccd157af46f4d5afc64 100644 (file)
@@ -1,11 +1,27 @@
 # we enforce that 'returns' be a dict or array of dict unless whitelisted
+
+##
+# @human-monitor-command:
+##
 { 'command': 'human-monitor-command',
   'data': {'command-line': 'str', '*cpu-index': 'int'},
   'returns': 'str' }
+##
+# @TpmModel:
+##
 { 'enum': 'TpmModel', 'data': [ 'tpm-tis' ] }
+##
+# @query-tpm-models:
+##
 { 'command': 'query-tpm-models', 'returns': ['TpmModel'] }
+##
+# @guest-get-time:
+##
 { 'command': 'guest-get-time',
   'returns': 'int' }
 
+##
+# @no-way-this-will-get-whitelisted:
+##
 { 'command': 'no-way-this-will-get-whitelisted',
   'returns': [ 'int' ] }
index e2d7943f2174e82f7588a0f4bb33ff05d0ba6abb..1b7c0e9d123b4a80d9c419bff05eff123a5d2ec0 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/struct-base-clash-deep.json:10: 'name' (member of Sub) collides with 'name' (member of Base)
+tests/qapi-schema/struct-base-clash-deep.json:20: 'name' (member of Sub) collides with 'name' (member of Base)
index fa873ab5d4e8a75cac7049f391b8185c1f61afde..646d680ad6213b038e8f31da4752f09fb9cecc86 100644 (file)
@@ -2,11 +2,21 @@
 # Here, 'name' would have to appear twice on the wire, locally and
 # indirectly for the grandparent base; the collision doesn't care that
 # one instance is optional.
+
+##
+# @Base:
+##
 { 'struct': 'Base',
   'data': { 'name': 'str' } }
+##
+# @Mid:
+##
 { 'struct': 'Mid',
   'base': 'Base',
   'data': { 'value': 'int' } }
+##
+# @Sub:
+##
 { 'struct': 'Sub',
   'base': 'Mid',
   'data': { '*name': 'str' } }
index c52f33d27bf316b3a3ac71234149bca6096dbd3e..5fe6393efab7204a0a8c16b1cc84270b50e33b3e 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/struct-base-clash.json:5: 'name' (member of Sub) collides with 'name' (member of Base)
+tests/qapi-schema/struct-base-clash.json:12: 'name' (member of Sub) collides with 'name' (member of Base)
index 11aec80fe53191cf53b793199c3e353356042cde..a8539958b59bf857c8925712bdc68ebfc8180250 100644 (file)
@@ -1,7 +1,14 @@
 # Reject attempts to duplicate QMP members
 # Here, 'name' would have to appear twice on the wire, locally and for base.
+
+##
+# @Base:
+##
 { 'struct': 'Base',
   'data': { 'name': 'str' } }
+##
+# @Sub:
+##
 { 'struct': 'Sub',
   'base': 'Base',
   'data': { 'name': 'str' } }
index 6644f4c2ad2f88738a57664fd3b5c90dfe1566c3..27163355bdc0de0c3a06cf1c20de4bede9a70e74 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/struct-data-invalid.json:1: 'data' for struct 'foo' should be a dictionary or type name
+tests/qapi-schema/struct-data-invalid.json:4: 'data' for struct 'foo' should be a dictionary or type name
index 9adbc3bb6b63912e3d684c017be0f59950f8a765..aa817bda34fdeb6a703c0d48c9a68d5b1bc95b26 100644 (file)
@@ -1,2 +1,5 @@
+##
+# @foo:
+##
 { 'struct': 'foo',
   'data': false }
index 69a326d450e9fbf9c6fb7e5464e99f59a441e485..f2b105ba8866b028869c75fe819da8cb17e4c6dc 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/struct-member-invalid.json:1: Member 'a' of 'data' for struct 'foo' should be a type name
+tests/qapi-schema/struct-member-invalid.json:4: Member 'a' of 'data' for struct 'foo' should be a type name
index 8f172f7a87e7bc63b0f8bb32a0ae2676b4b8a175..10c74262d34ac43270e5fb48fdf3e92bb93d7f11 100644 (file)
@@ -1,2 +1,5 @@
+##
+# @foo:
+##
 { 'struct': 'foo',
   'data': { 'a': false } }
index ef74e2c4c82b0ad835d2d94cf446e00a4e772d23..b4cde4ff4fc5fc6ad1da10f1dddd98cf13c70238 100644 (file)
@@ -55,3 +55,17 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
 
 schema = QAPISchema(sys.argv[1])
 schema.visit(QAPISchemaTestVisitor())
+
+for doc in schema.docs:
+    if doc.symbol:
+        print 'doc symbol=%s expr=%s' % \
+            (doc.symbol, doc.expr.items()[0])
+    else:
+        print 'doc freeform'
+    for arg, section in doc.args.iteritems():
+        print '    arg=%s\n%s' % (arg, section)
+    for section in doc.sections:
+        print '    section=%s\n%s' % (section.name, section)
+    body = str(doc.body)
+    if body:
+        print '    body=\n%s' % body
index a83c3c655de2d0b8d6255f0f0d78ec647cafb5d5..bd5431f60b99a3ad3cae5e4dc3a1f5f88884b7b6 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/type-bypass-bad-gen.json:2: 'gen' of command 'foo' should only use false value
+tests/qapi-schema/type-bypass-bad-gen.json:6: 'gen' of command 'foo' should only use false value
index e8dec342492d01efb0eebc31b84a60454cb6f30b..7162c1a0cac0b26ddd15aa1ba26a8d93b9b15424 100644 (file)
@@ -1,2 +1,6 @@
 # 'gen' should only appear with value false
+
+##
+# @foo:
+##
 { 'command': 'foo', 'gen': 'whatever' }
index f621cd6448b0d83516a4e68f4080300087358713..92ee277370e32fe552daa90f02d17cafd45df6d0 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/unicode-str.json:2: 'command' uses invalid name 'é'
+tests/qapi-schema/unicode-str.json:6: 'command' uses invalid name 'é'
index 5253a1b9f3989b7e0b9c3bd1b66ae8116407ea09..75a08b3d93a3fe73ed9f2de540235d260bd10987 100644 (file)
@@ -1,2 +1,6 @@
 # we don't support full Unicode strings, yet
+
+##
+# @e:
+##
 { 'command': 'é' }
index 8b7a24260ffa8d900f3ffa73d3af8f5616803be6..ca6ee92357d21e76ce262640e26aded51046c37d 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/union-base-no-discriminator.json:11: Simple union 'TestUnion' must not have a base
+tests/qapi-schema/union-base-no-discriminator.json:23: Simple union 'TestUnion' must not have a base
index 1409cf5c9e0c2ce851f2b917d8ce62bc292ae330..cc6bac14242f6b907463a1136e098c9b2a1cab30 100644 (file)
@@ -1,13 +1,25 @@
+##
+# @TestTypeA:
+##
 # we reject simple unions with a base (or flat unions without discriminator)
 { 'struct': 'TestTypeA',
   'data': { 'string': 'str' } }
 
+##
+# @TestTypeB:
+##
 { 'struct': 'TestTypeB',
   'data': { 'integer': 'int' } }
 
+##
+# @Base:
+##
 { 'struct': 'Base',
   'data': { 'string': 'str' } }
 
+##
+# @TestUnion:
+##
 { 'union': 'TestUnion',
   'base': 'Base',
   'data': { 'value1': 'TestTypeA',
index 11521901d88ef1fe4689d588b5e3e103020ddc29..9095bae5658dd0cd1f1f75d5c57791f2694220da 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/union-branch-case.json:2: 'Branch' (branch of NoWayThisWillGetWhitelisted) should not use uppercase
+tests/qapi-schema/union-branch-case.json:6: 'Branch' (branch of NoWayThisWillGetWhitelisted) should not use uppercase
index e6565dc3b35d9e60feb09f202bc87edd0b49aa2f..6de131548cd9d2d94a16364337d23170bfd8dad8 100644 (file)
@@ -1,2 +1,6 @@
 # Branch names should be 'lower-case' unless the union is whitelisted
+
+##
+# @NoWayThisWillGetWhitelisted:
+##
 { 'union': 'NoWayThisWillGetWhitelisted', 'data': { 'Branch': 'int' } }
index e5b21135bb8538042e6337e7d0df3124c0298ff9..640caeab8c070b8238c6b1f17cd4ba500133b1eb 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/union-clash-branches.json:4: 'a_b' (branch of TestUnion) collides with 'a-b' (branch of TestUnion)
+tests/qapi-schema/union-clash-branches.json:8: 'a_b' (branch of TestUnion) collides with 'a-b' (branch of TestUnion)
index 3bece8c948b90ed57655d1dc37d941def6a5ebeb..6615665dfede60877d0ef4ea56edf58c4fc6dc57 100644 (file)
@@ -1,5 +1,9 @@
 # Union branch name collision
 # Reject a union that would result in a collision in generated C names (this
 # would try to generate two members 'a_b').
+
+##
+# @TestUnion:
+##
 { 'union': 'TestUnion',
   'data': { 'a-b': 'int', 'a_b': 'str' } }
index 12c20221bdfb6f7b046a7deaaf1074aa6126b651..749bc76fc5c280b8f37e0ed88aea0156b2756957 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/union-empty.json:2: Union 'Union' cannot have empty 'data'
+tests/qapi-schema/union-empty.json:6: Union 'Union' cannot have empty 'data'
index 1f0b13ca21ae25d34b9d67da84df14b88f35e959..c9b0a1ef330e309257cc6c77805bdb839f825074 100644 (file)
@@ -1,2 +1,6 @@
 # unions cannot be empty
+
+##
+# @Union:
+##
 { 'union': 'Union', 'data': { } }
index 03d7b97a93ba94853c8e2a78824f69c8bb25305e..41e238f4530211ea22f6fc354207ed07debd9c9e 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/union-invalid-base.json:8: 'base' for union 'TestUnion' cannot use built-in type 'int'
+tests/qapi-schema/union-invalid-base.json:18: 'base' for union 'TestUnion' cannot use built-in type 'int'
index 92be39df69ae7fce191f5113fb82f2be3fd80157..fd837cb80bebed63a6122535bdaf4a04ea33b34a 100644 (file)
@@ -1,10 +1,20 @@
 # a union base type must be a struct
+
+##
+# @TestTypeA:
+##
 { 'struct': 'TestTypeA',
   'data': { 'string': 'str' } }
 
+##
+# @TestTypeB:
+##
 { 'struct': 'TestTypeB',
   'data': { 'integer': 'int' } }
 
+##
+# @TestUnion:
+##
 { 'union': 'TestUnion',
   'base': 'int',
   'discriminator': 'int',
index 3ada1334dc6317df276ad28648941c29f3826563..60523c07e48942d0b50b8a38df5ace0c99a3b57b 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/union-optional-branch.json:2: Member of union 'Union' does not allow optional name '*a'
+tests/qapi-schema/union-optional-branch.json:6: Member of union 'Union' does not allow optional name '*a'
index 591615fc689bb8757c8e99f1cf76f6ad8c95063b..7d2ee4c730e025b708052592b22f3e3de2f304a1 100644 (file)
@@ -1,2 +1,6 @@
 # union branches cannot be optional
+
+##
+# @Union:
+##
 { 'union': 'Union', 'data': { '*a': 'int', 'b': 'str' } }
index 54fe456f9cd850132c752aaaefd81f27183b7826..5568302205179b0ae4bbb1ef1391cf0bc280cae9 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/union-unknown.json:2: Member 'unknown' of union 'Union' uses unknown type 'MissingType'
+tests/qapi-schema/union-unknown.json:6: Member 'unknown' of union 'Union' uses unknown type 'MissingType'
index aa7e8143d81b5142a976a2f3add1a4ab79eca2b4..5042b23197283e911a3ee1f69346e3d656b3d94b 100644 (file)
@@ -1,3 +1,7 @@
 # we reject a union with unknown type in branch
+
+##
+# @Union:
+##
 { 'union': 'Union',
   'data': { 'unknown': 'MissingType' } }
index 000e30ddf3335608d2aaeb8f9050dc89a0fd1fe1..1a4ead632b424c192e7e1be1f073349b71bfd05a 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/unknown-escape.json:3:21: Unknown escape \x
+tests/qapi-schema/unknown-escape.json:7:21: Unknown escape \x
index 8e6891e52a29b43015ffc56fae0d23f953e2bd34..e3ae6793f21a60a6f7d69117c86f23c15c717da3 100644 (file)
@@ -1,3 +1,7 @@
 # we only recognize JSON escape sequences, plus our \' extension (no \x)
+
+##
+# @foo:
+##
 # { 'command': 'foo', 'data': {} }
 { 'command': 'foo', 'dat\x61':{} }
index 12f5ed5b435bab9a6aaf487b9a8a9ca3a5e73408..b19a668bd6e5b7d07c610fc641dc3509294ed195 100644 (file)
@@ -1 +1 @@
-tests/qapi-schema/unknown-expr-key.json:2: Unknown key 'bogus' in struct 'bar'
+tests/qapi-schema/unknown-expr-key.json:6: Unknown key 'bogus' in struct 'bar'
index 3b2be00cc49794c9d3ffea8d3020e7399efe5a6a..1b764c7b9d44cdf19f7b14094b897861b983af3f 100644 (file)
@@ -1,2 +1,6 @@
 # we reject an expression with unknown top-level keys
+
+##
+# @bar:
+##
 { 'struct': 'bar', 'data': { 'string': 'str'}, 'bogus': { } }