qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 00/25] qapi: Pay back some frontend technical debt
@ 2019-09-24 13:28 Markus Armbruster
  2019-09-24 13:28 ` [PATCH 01/25] qapi: Tighten QAPISchemaFOO.check() assertions Markus Armbruster
                   ` (24 more replies)
  0 siblings, 25 replies; 63+ messages in thread
From: Markus Armbruster @ 2019-09-24 13:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, mdroth

When we introduced the QAPISchema intermediate representation (commit
ac88219a6c7), we took a shortcut: we left check_exprs() & friends
alone instead of moving the semantic checks to their proper place.
Time to finish this job.  Topped off with error message gardening.

Based-on: <20190924123334.30645-1-armbru@redhat.com>

Markus Armbruster (25):
  qapi: Tighten QAPISchemaFOO.check() assertions
  qapi: Rename .owner to .defined_in
  qapi: New QAPISourceInfo, replacing dict
  qapi: Prefix frontend errors with an "in definition" line
  qapi: Clean up member name case checking
  qapi: Change frontend error messages to start with lower case
  qapi: Improve reporting of member name clashes
  qapi: Reorder check_FOO() parameters for consistency
  qapi: Improve reporting of invalid name errors
  qapi: Use check_name_str() where it suffices
  qapi: Report invalid '*' prefix like any other invalid name
  qapi: Move check for reserved names out of add_name()
  qapi: Make check_type()'s array case a bit more obvious
  qapi: Plumb info to the QAPISchemaMember
  qapi: Inline check_name() into check_union()
  qapi: Move context-sensitive checking to the proper place
  qapi: Move context-free checking to the proper place
  qapi: Improve reporting of invalid 'if' errors
  qapi: Improve reporting of invalid flags
  qapi: Improve reporting of missing / unknown definition keys
  qapi: Avoid redundant definition references in error messages
  qapi: Eliminate check_keys(), rename check_known_keys()
  qapi: Improve reporting of missing documentation comment
  qapi: Improve reporting of redefinition
  qapi: Improve source file read error handling

 qapi/qapi-schema.json                         |    2 +-
 scripts/qapi/common.py                        | 1017 +++++++++--------
 scripts/qapi/events.py                        |    2 +-
 tests/qapi-schema/allow-preconfig-test.err    |    3 +-
 tests/qapi-schema/alternate-any.err           |    3 +-
 tests/qapi-schema/alternate-array.err         |    3 +-
 tests/qapi-schema/alternate-base.err          |    3 +-
 .../alternate-branch-if-invalid.err           |    1 +
 tests/qapi-schema/alternate-clash.err         |    3 +-
 .../alternate-conflict-bool-string.err        |    3 +-
 tests/qapi-schema/alternate-conflict-dict.err |    3 +-
 .../alternate-conflict-enum-bool.err          |    3 +-
 .../alternate-conflict-enum-int.err           |    3 +-
 .../alternate-conflict-num-string.err         |    3 +-
 .../qapi-schema/alternate-conflict-string.err |    3 +-
 tests/qapi-schema/alternate-empty.err         |    3 +-
 tests/qapi-schema/alternate-invalid-dict.err  |    3 +-
 tests/qapi-schema/alternate-nested.err        |    3 +-
 tests/qapi-schema/alternate-unknown.err       |    3 +-
 tests/qapi-schema/args-alternate.err          |    3 +-
 tests/qapi-schema/args-any.err                |    3 +-
 tests/qapi-schema/args-array-empty.err        |    3 +-
 tests/qapi-schema/args-array-unknown.err      |    3 +-
 tests/qapi-schema/args-bad-boxed.err          |    3 +-
 tests/qapi-schema/args-boxed-anon.err         |    3 +-
 tests/qapi-schema/args-boxed-string.err       |    3 +-
 tests/qapi-schema/args-int.err                |    3 +-
 tests/qapi-schema/args-invalid.err            |    3 +-
 tests/qapi-schema/args-member-array-bad.err   |    3 +-
 tests/qapi-schema/args-member-case.err        |    3 +-
 tests/qapi-schema/args-member-case.json       |    2 +-
 tests/qapi-schema/args-member-unknown.err     |    3 +-
 tests/qapi-schema/args-name-clash.err         |    3 +-
 tests/qapi-schema/args-union.err              |    3 +-
 tests/qapi-schema/args-unknown.err            |    3 +-
 tests/qapi-schema/bad-base.err                |    3 +-
 tests/qapi-schema/bad-data.err                |    3 +-
 tests/qapi-schema/bad-ident.err               |    3 +-
 tests/qapi-schema/bad-if-empty-list.err       |    1 +
 tests/qapi-schema/bad-if-empty.err            |    1 +
 tests/qapi-schema/bad-if-list.err             |    1 +
 tests/qapi-schema/bad-if.err                  |    1 +
 tests/qapi-schema/bad-type-bool.err           |    2 +-
 tests/qapi-schema/bad-type-dict.err           |    2 +-
 tests/qapi-schema/bad-type-int.err            |    2 +-
 tests/qapi-schema/base-cycle-direct.err       |    3 +-
 tests/qapi-schema/base-cycle-indirect.err     |    3 +-
 tests/qapi-schema/command-int.err             |    3 +-
 .../qapi-schema/doc-bad-alternate-member.err  |    2 +-
 tests/qapi-schema/doc-bad-command-arg.err     |    2 +-
 tests/qapi-schema/doc-bad-symbol.err          |    3 +-
 tests/qapi-schema/doc-bad-union-member.err    |    2 +-
 tests/qapi-schema/doc-before-include.err      |    2 +-
 tests/qapi-schema/doc-before-pragma.err       |    2 +-
 tests/qapi-schema/doc-duplicated-return.err   |    2 +-
 tests/qapi-schema/doc-duplicated-since.err    |    2 +-
 tests/qapi-schema/doc-empty-arg.err           |    2 +-
 tests/qapi-schema/doc-empty-section.err       |    2 +-
 tests/qapi-schema/doc-empty-symbol.err        |    2 +-
 tests/qapi-schema/doc-invalid-end.err         |    2 +-
 tests/qapi-schema/doc-invalid-end2.err        |    2 +-
 tests/qapi-schema/doc-invalid-start.err       |    2 +-
 tests/qapi-schema/doc-missing-colon.err       |    2 +-
 tests/qapi-schema/doc-missing-expr.err        |    2 +-
 tests/qapi-schema/doc-missing-space.err       |    2 +-
 tests/qapi-schema/doc-missing.err             |    3 +-
 tests/qapi-schema/doc-no-symbol.err           |    2 +-
 tests/qapi-schema/double-type.err             |    3 +-
 tests/qapi-schema/duplicate-key.err           |    2 +-
 tests/qapi-schema/enum-bad-member.err         |    3 +-
 tests/qapi-schema/enum-bad-name.err           |    3 +-
 tests/qapi-schema/enum-bad-prefix.err         |    3 +-
 tests/qapi-schema/enum-clash-member.err       |    3 +-
 .../qapi-schema/enum-dict-member-unknown.err  |    3 +-
 tests/qapi-schema/enum-if-invalid.err         |    1 +
 tests/qapi-schema/enum-int-member.err         |    2 +-
 tests/qapi-schema/enum-member-case.err        |    3 +-
 tests/qapi-schema/enum-missing-data.err       |    3 +-
 tests/qapi-schema/enum-wrong-data.err         |    3 +-
 tests/qapi-schema/escape-outside-string.err   |    2 +-
 tests/qapi-schema/escape-too-big.err          |    1 +
 tests/qapi-schema/event-boxed-empty.err       |    3 +-
 .../qapi-schema/event-member-invalid-dict.err |    3 +-
 tests/qapi-schema/event-nest-struct.err       |    3 +-
 tests/qapi-schema/features-bad-type.err       |    3 +-
 tests/qapi-schema/features-duplicate-name.err |    3 +-
 tests/qapi-schema/features-if-invalid.err     |    1 +
 tests/qapi-schema/features-missing-name.err   |    3 +-
 tests/qapi-schema/features-name-bad-type.err  |    3 +-
 tests/qapi-schema/features-no-list.err        |    3 +-
 tests/qapi-schema/features-unknown-key.err    |    3 +-
 tests/qapi-schema/flat-union-array-branch.err |    3 +-
 tests/qapi-schema/flat-union-bad-base.err     |    3 +-
 .../flat-union-bad-discriminator.err          |    3 +-
 tests/qapi-schema/flat-union-base-any.err     |    3 +-
 tests/qapi-schema/flat-union-base-union.err   |    3 +-
 tests/qapi-schema/flat-union-clash-member.err |    3 +-
 .../flat-union-discriminator-bad-name.err     |    3 +-
 .../flat-union-discriminator-bad-name.json    |    1 -
 tests/qapi-schema/flat-union-empty.err        |    3 +-
 .../flat-union-inline-invalid-dict.err        |    3 +-
 tests/qapi-schema/flat-union-inline.err       |    3 +-
 tests/qapi-schema/flat-union-int-branch.err   |    3 +-
 .../flat-union-invalid-branch-key.err         |    3 +-
 .../flat-union-invalid-discriminator.err      |    3 +-
 .../flat-union-invalid-if-discriminator.err   |    3 +-
 tests/qapi-schema/flat-union-no-base.err      |    3 +-
 .../flat-union-optional-discriminator.err     |    3 +-
 .../flat-union-optional-discriminator.json    |    1 -
 .../flat-union-string-discriminator.err       |    3 +-
 tests/qapi-schema/funny-char.err              |    2 +-
 tests/qapi-schema/funny-word.err              |    2 +-
 tests/qapi-schema/ident-with-escape.err       |    2 +-
 tests/qapi-schema/include-before-err.err      |    2 +-
 tests/qapi-schema/include-cycle.err           |    2 +-
 tests/qapi-schema/include-extra-junk.err      |    2 +-
 tests/qapi-schema/include-nested-err.err      |    2 +-
 tests/qapi-schema/include-no-file.err         |    2 +-
 tests/qapi-schema/include-non-file.err        |    2 +-
 tests/qapi-schema/include-self-cycle.err      |    2 +-
 tests/qapi-schema/leading-comma-list.err      |    2 +-
 tests/qapi-schema/leading-comma-object.err    |    2 +-
 tests/qapi-schema/missing-colon.err           |    2 +-
 tests/qapi-schema/missing-comma-list.err      |    2 +-
 tests/qapi-schema/missing-comma-object.err    |    2 +-
 tests/qapi-schema/missing-type.err            |    2 +-
 .../nested-struct-data-invalid-dict.err       |    3 +-
 tests/qapi-schema/nested-struct-data.err      |    3 +-
 tests/qapi-schema/non-objects.err             |    2 +-
 tests/qapi-schema/oob-test.err                |    3 +-
 .../qapi-schema/pragma-doc-required-crap.err  |    2 +-
 tests/qapi-schema/pragma-extra-junk.err       |    2 +-
 .../pragma-name-case-whitelist-crap.err       |    2 +-
 tests/qapi-schema/pragma-non-dict.err         |    2 +-
 .../pragma-returns-whitelist-crap.err         |    2 +-
 tests/qapi-schema/pragma-unknown.err          |    2 +-
 tests/qapi-schema/quoted-structural-chars.err |    2 +-
 tests/qapi-schema/redefined-builtin.err       |    3 +-
 tests/qapi-schema/redefined-command.err       |    5 +-
 tests/qapi-schema/redefined-event.err         |    5 +-
 tests/qapi-schema/redefined-type.err          |    5 +-
 tests/qapi-schema/reserved-command-q.err      |    3 +-
 tests/qapi-schema/reserved-enum-q.err         |    3 +-
 tests/qapi-schema/reserved-member-has.err     |    3 +-
 tests/qapi-schema/reserved-member-q.err       |    3 +-
 tests/qapi-schema/reserved-member-u.err       |    3 +-
 .../reserved-member-underscore.err            |    3 +-
 tests/qapi-schema/reserved-type-kind.err      |    3 +-
 tests/qapi-schema/reserved-type-list.err      |    3 +-
 tests/qapi-schema/returns-alternate.err       |    3 +-
 tests/qapi-schema/returns-array-bad.err       |    3 +-
 tests/qapi-schema/returns-dict.err            |    3 +-
 tests/qapi-schema/returns-unknown.err         |    3 +-
 tests/qapi-schema/returns-whitelist.err       |    3 +-
 tests/qapi-schema/string-code-point-127.err   |    2 +-
 tests/qapi-schema/string-code-point-31.err    |    2 +-
 tests/qapi-schema/string-control.err          |    1 +
 tests/qapi-schema/string-unclosed.err         |    1 +
 tests/qapi-schema/string-unicode.err          |    1 +
 tests/qapi-schema/struct-base-clash-deep.err  |    3 +-
 tests/qapi-schema/struct-base-clash.err       |    3 +-
 tests/qapi-schema/struct-data-invalid.err     |    3 +-
 .../qapi-schema/struct-member-if-invalid.err  |    1 +
 .../struct-member-invalid-dict.err            |    3 +-
 tests/qapi-schema/struct-member-invalid.err   |    3 +-
 tests/qapi-schema/trailing-comma-list.err     |    2 +-
 tests/qapi-schema/trailing-comma-object.err   |    2 +-
 tests/qapi-schema/type-bypass-bad-gen.err     |    3 +-
 tests/qapi-schema/unclosed-list.err           |    2 +-
 tests/qapi-schema/unclosed-object.err         |    2 +-
 tests/qapi-schema/unclosed-string.err         |    2 +-
 tests/qapi-schema/union-base-empty.err        |    3 +-
 .../union-base-no-discriminator.err           |    3 +-
 tests/qapi-schema/union-branch-case.err       |    3 +-
 tests/qapi-schema/union-branch-case.json      |    4 +-
 tests/qapi-schema/union-branch-if-invalid.err |    1 +
 .../qapi-schema/union-branch-invalid-dict.err |    3 +-
 tests/qapi-schema/union-clash-branches.err    |    3 +-
 tests/qapi-schema/union-empty.err             |    3 +-
 tests/qapi-schema/union-invalid-base.err      |    3 +-
 tests/qapi-schema/union-optional-branch.err   |    3 +-
 tests/qapi-schema/union-unknown.err           |    3 +-
 tests/qapi-schema/union-unknown.json          |    2 +-
 tests/qapi-schema/unknown-escape.err          |    2 +-
 tests/qapi-schema/unknown-expr-key.err        |    3 +-
 185 files changed, 814 insertions(+), 673 deletions(-)
 create mode 100644 tests/qapi-schema/escape-too-big.err
 create mode 100644 tests/qapi-schema/string-control.err
 create mode 100644 tests/qapi-schema/string-unclosed.err
 create mode 100644 tests/qapi-schema/string-unicode.err

-- 
2.21.0



^ permalink raw reply	[flat|nested] 63+ messages in thread

* [PATCH 01/25] qapi: Tighten QAPISchemaFOO.check() assertions
  2019-09-24 13:28 [PATCH 00/25] qapi: Pay back some frontend technical debt Markus Armbruster
@ 2019-09-24 13:28 ` Markus Armbruster
  2019-09-24 14:39   ` Eric Blake
  2019-09-24 13:28 ` [PATCH 02/25] qapi: Rename .owner to .defined_in Markus Armbruster
                   ` (23 subsequent siblings)
  24 siblings, 1 reply; 63+ messages in thread
From: Markus Armbruster @ 2019-09-24 13:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, mdroth

When we introduced the QAPISchema intermediate representation (commit
ac88219a6c7), we took a shortcut: we left check_exprs() & friends
alone instead of moving semantic checks into the
QAPISchemaFOO.check().  check_exprs() still checks and reports errors,
and the .check() assert check_exprs() did the job.  There are a few
gaps, though.

QAPISchemaArrayType.check() neglects to assert the element type is not
an array.  Add the assertion.

QAPISchemaObjectTypeVariants.check() neglects to assert the tag member
is not optional.  Add the assertion.

It neglects to assert the tag member is not conditional.  Add the
assertion.

It neglects to assert we actually have variants.  Add the assertion.

It asserts the variants are object types, but neglects to assert they
don't have variants.  Tighten the assertion.

QAPISchemaObjectTypeVariants.check_clash() has the same issue.
However, it can run only after .check().  Delete the assertion instead
of tightening it.

QAPISchemaAlternateType.check() neglects to assert the branch types
don't conflict.  Fixing that isn't trivial, so add just a TODO comment
for now.  It'll be resolved later in this series.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 scripts/qapi/common.py | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index b00caacca3..155b87b825 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -1362,6 +1362,7 @@ class QAPISchemaArrayType(QAPISchemaType):
         QAPISchemaType.check(self, schema)
         self.element_type = schema.lookup_type(self._element_type_name)
         assert self.element_type
+        assert not isinstance(self.element_type, QAPISchemaArrayType)
 
     @property
     def ifcond(self):
@@ -1606,6 +1607,8 @@ class QAPISchemaObjectTypeVariants(object):
             self.tag_member = seen[c_name(self._tag_name)]
             assert self._tag_name == self.tag_member.name
         assert isinstance(self.tag_member.type, QAPISchemaEnumType)
+        assert not self.tag_member.optional
+        assert self.tag_member.ifcond == []
         if self._tag_name:    # flat union
             # branches that are not explicitly covered get an empty type
             cases = set([v.name for v in self.variants])
@@ -1615,20 +1618,21 @@ class QAPISchemaObjectTypeVariants(object):
                                                     m.ifcond)
                     v.set_owner(self.tag_member.owner)
                     self.variants.append(v)
+        assert self.variants
         for v in self.variants:
             v.check(schema)
             # Union names must match enum values; alternate names are
             # checked separately. Use 'seen' to tell the two apart.
             if seen:
                 assert v.name in self.tag_member.type.member_names()
-                assert isinstance(v.type, QAPISchemaObjectType)
+                assert (isinstance(v.type, QAPISchemaObjectType)
+                        and not v.type.variants)
                 v.type.check(schema)
 
     def check_clash(self, info, seen):
         for v in self.variants:
             # Reset seen map for each variant, since qapi names from one
             # branch do not affect another branch
-            assert isinstance(v.type, QAPISchemaObjectType)
             v.type.check_clash(info, dict(seen))
 
 
@@ -1659,6 +1663,7 @@ class QAPISchemaAlternateType(QAPISchemaType):
         seen = {}
         for v in self.variants.variants:
             v.check_clash(self.info, seen)
+            # TODO check conflicting qtypes
             if self.doc:
                 self.doc.connect_member(v)
         if self.doc:
-- 
2.21.0



^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH 02/25] qapi: Rename .owner to .defined_in
  2019-09-24 13:28 [PATCH 00/25] qapi: Pay back some frontend technical debt Markus Armbruster
  2019-09-24 13:28 ` [PATCH 01/25] qapi: Tighten QAPISchemaFOO.check() assertions Markus Armbruster
@ 2019-09-24 13:28 ` Markus Armbruster
  2019-09-24 14:41   ` Eric Blake
  2019-09-24 13:28 ` [PATCH 03/25] qapi: New QAPISourceInfo, replacing dict Markus Armbruster
                   ` (22 subsequent siblings)
  24 siblings, 1 reply; 63+ messages in thread
From: Markus Armbruster @ 2019-09-24 13:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, mdroth

QAPISchemaMember.owner is the name of the defining entity.  That's a
confusing name when an object type inherits members from a base type.
Rename it to .defined_in.  Rename .set_owner() and ._pretty_owner() to
match.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 scripts/qapi/common.py | 61 +++++++++++++++++++++---------------------
 1 file changed, 31 insertions(+), 30 deletions(-)

diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index 155b87b825..bfb3e8a493 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -1319,7 +1319,7 @@ class QAPISchemaEnumType(QAPISchemaType):
         QAPISchemaType.__init__(self, name, info, doc, ifcond)
         for m in members:
             assert isinstance(m, QAPISchemaEnumMember)
-            m.set_owner(name)
+            m.set_defined_in(name)
         assert prefix is None or isinstance(prefix, str)
         self.members = members
         self.prefix = prefix
@@ -1405,13 +1405,13 @@ class QAPISchemaObjectType(QAPISchemaType):
         assert base is None or isinstance(base, str)
         for m in local_members:
             assert isinstance(m, QAPISchemaObjectTypeMember)
-            m.set_owner(name)
+            m.set_defined_in(name)
         if variants is not None:
             assert isinstance(variants, QAPISchemaObjectTypeVariants)
-            variants.set_owner(name)
+            variants.set_defined_in(name)
         for f in features:
             assert isinstance(f, QAPISchemaFeature)
-            f.set_owner(name)
+            f.set_defined_in(name)
         self._base_name = base
         self.base = None
         self.local_members = local_members
@@ -1521,15 +1521,16 @@ class QAPISchemaMember(object):
         assert isinstance(name, str)
         self.name = name
         self.ifcond = ifcond or []
-        self.owner = None
+        self.defined_in = None
 
-    def set_owner(self, name):
-        assert not self.owner
-        self.owner = name
+    def set_defined_in(self, name):
+        assert not self.defined_in
+        self.defined_in = name
 
     def check_clash(self, info, seen):
         cname = c_name(self.name)
-        if cname.lower() != cname and self.owner not in name_case_whitelist:
+        if (cname.lower() != cname
+                and self.defined_in not in name_case_whitelist):
             raise QAPISemError(info,
                                "%s should not use uppercase" % self.describe())
         if cname in seen:
@@ -1537,27 +1538,27 @@ class QAPISchemaMember(object):
                                (self.describe(), seen[cname].describe()))
         seen[cname] = self
 
-    def _pretty_owner(self):
-        owner = self.owner
-        if owner.startswith('q_obj_'):
+    def _pretty_defined_in(self):
+        defined_in = self.defined_in
+        if defined_in.startswith('q_obj_'):
             # See QAPISchema._make_implicit_object_type() - reverse the
             # mapping there to create a nice human-readable description
-            owner = owner[6:]
-            if owner.endswith('-arg'):
-                return '(parameter of %s)' % owner[:-4]
-            elif owner.endswith('-base'):
-                return '(base of %s)' % owner[:-5]
+            defined_in = defined_in[6:]
+            if defined_in.endswith('-arg'):
+                return '(parameter of %s)' % defined_in[:-4]
+            elif defined_in.endswith('-base'):
+                return '(base of %s)' % defined_in[:-5]
             else:
-                assert owner.endswith('-wrapper')
+                assert defined_in.endswith('-wrapper')
                 # Unreachable and not implemented
                 assert False
-        if owner.endswith('Kind'):
+        if defined_in.endswith('Kind'):
             # See QAPISchema._make_implicit_enum_type()
-            return '(branch of %s)' % owner[:-4]
-        return '(%s of %s)' % (self.role, owner)
+            return '(branch of %s)' % defined_in[:-4]
+        return '(%s of %s)' % (self.role, defined_in)
 
     def describe(self):
-        return "'%s' %s" % (self.name, self._pretty_owner())
+        return "'%s' %s" % (self.name, self._pretty_defined_in())
 
 
 class QAPISchemaEnumMember(QAPISchemaMember):
@@ -1578,7 +1579,7 @@ class QAPISchemaObjectTypeMember(QAPISchemaMember):
         self.optional = optional
 
     def check(self, schema):
-        assert self.owner
+        assert self.defined_in
         self.type = schema.lookup_type(self._type_name)
         assert self.type
 
@@ -1598,9 +1599,9 @@ class QAPISchemaObjectTypeVariants(object):
         self.tag_member = tag_member
         self.variants = variants
 
-    def set_owner(self, name):
+    def set_defined_in(self, name):
         for v in self.variants:
-            v.set_owner(name)
+            v.set_defined_in(name)
 
     def check(self, schema, seen):
         if not self.tag_member:    # flat union
@@ -1616,7 +1617,7 @@ class QAPISchemaObjectTypeVariants(object):
                 if m.name not in cases:
                     v = QAPISchemaObjectTypeVariant(m.name, 'q_empty',
                                                     m.ifcond)
-                    v.set_owner(self.tag_member.owner)
+                    v.set_defined_in(self.tag_member.defined_in)
                     self.variants.append(v)
         assert self.variants
         for v in self.variants:
@@ -1648,8 +1649,8 @@ class QAPISchemaAlternateType(QAPISchemaType):
         QAPISchemaType.__init__(self, name, info, doc, ifcond)
         assert isinstance(variants, QAPISchemaObjectTypeVariants)
         assert variants.tag_member
-        variants.set_owner(name)
-        variants.tag_member.set_owner(self.name)
+        variants.set_defined_in(name)
+        variants.tag_member.set_defined_in(self.name)
         self.variants = variants
 
     def check(self, schema):
@@ -1829,7 +1830,7 @@ class QAPISchema(object):
                 for v in values]
 
     def _make_implicit_enum_type(self, name, info, ifcond, values):
-        # See also QAPISchemaObjectTypeMember._pretty_owner()
+        # See also QAPISchemaObjectTypeMember._pretty_defined_in()
         name = name + 'Kind'   # Use namespace reserved by add_name()
         self._def_entity(QAPISchemaEnumType(
             name, info, None, ifcond, self._make_enum_members(values), None))
@@ -1845,7 +1846,7 @@ class QAPISchema(object):
                                    role, members):
         if not members:
             return None
-        # See also QAPISchemaObjectTypeMember._pretty_owner()
+        # See also QAPISchemaObjectTypeMember._pretty_defined_in()
         name = 'q_obj_%s-%s' % (name, role)
         typ = self.lookup_entity(name, QAPISchemaObjectType)
         if typ:
-- 
2.21.0



^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH 03/25] qapi: New QAPISourceInfo, replacing dict
  2019-09-24 13:28 [PATCH 00/25] qapi: Pay back some frontend technical debt Markus Armbruster
  2019-09-24 13:28 ` [PATCH 01/25] qapi: Tighten QAPISchemaFOO.check() assertions Markus Armbruster
  2019-09-24 13:28 ` [PATCH 02/25] qapi: Rename .owner to .defined_in Markus Armbruster
@ 2019-09-24 13:28 ` Markus Armbruster
  2019-09-24 14:51   ` Eric Blake
  2019-09-24 19:12   ` Eric Blake
  2019-09-24 13:28 ` [PATCH 04/25] qapi: Prefix frontend errors with an "in definition" line Markus Armbruster
                   ` (21 subsequent siblings)
  24 siblings, 2 replies; 63+ messages in thread
From: Markus Armbruster @ 2019-09-24 13:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, mdroth

We track source locations with a dict of the form

    {'file': FNAME, 'line': LINENO, parent': PARENT}

where PARENT is None for the main file, and the include directive's
source location for included files.

This is servicable enough, but the next commit will add information,
and that's going to come out cleaner if we turn this into a class.  So
do that.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 scripts/qapi/common.py | 69 +++++++++++++++++++++++++-----------------
 1 file changed, 41 insertions(+), 28 deletions(-)

diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index bfb3e8a493..5843f3eeb2 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -13,6 +13,7 @@
 
 from __future__ import print_function
 from contextlib import contextmanager
+import copy
 import errno
 import os
 import re
@@ -53,34 +54,50 @@ struct_types = {}
 union_types = {}
 all_names = {}
 
+
 #
 # Parsing the schema into expressions
 #
 
+class QAPISourceInfo(object):
+    def __init__(self, fname, line, parent):
+        self.fname = fname
+        self.line = line
+        self.parent = parent
 
-def error_path(parent):
-    res = ''
-    while parent:
-        res = ('In file included from %s:%d:\n' % (parent['file'],
-                                                   parent['line'])) + res
-        parent = parent['parent']
-    return res
+    def next_line(self):
+        info = copy.copy(self)
+        info.line += 1
+        return info
+
+    def loc(self):
+        return '%s:%d' % (self.fname, self.line)
+
+    def include_path(self):
+        ret = ''
+        parent = self.parent
+        while parent:
+            ret = 'In file included from %s:\n' % parent.loc() + ret
+            parent = parent.parent
+        return ret
+
+    def __str__(self):
+        return self.include_path() + self.loc()
 
 
 class QAPIError(Exception):
-    def __init__(self, fname, line, col, incl_info, msg):
+    def __init__(self, info, col, msg):
         Exception.__init__(self)
-        self.fname = fname
-        self.line = line
+        self.info = info
         self.col = col
-        self.info = incl_info
         self.msg = msg
 
     def __str__(self):
-        loc = '%s:%d' % (self.fname, self.line)
+        loc = str(self.info)
         if self.col is not None:
+            assert self.info.line is not None
             loc += ':%s' % self.col
-        return error_path(self.info) + '%s: %s' % (loc, self.msg)
+        return loc + ': ' + self.msg
 
 
 class QAPIParseError(QAPIError):
@@ -91,14 +108,12 @@ class QAPIParseError(QAPIError):
                 col = (col + 7) % 8 + 1
             else:
                 col += 1
-        QAPIError.__init__(self, parser.fname, parser.line, col,
-                           parser.incl_info, msg)
+        QAPIError.__init__(self, parser.info, col, msg)
 
 
 class QAPISemError(QAPIError):
     def __init__(self, info, msg):
-        QAPIError.__init__(self, info['file'], info['line'], None,
-                           info['parent'], msg)
+        QAPIError.__init__(self, info, None, msg)
 
 
 class QAPIDoc(object):
@@ -382,12 +397,11 @@ class QAPISchemaParser(object):
     def __init__(self, fp, previously_included=[], incl_info=None):
         self.fname = fp.name
         previously_included.append(os.path.abspath(fp.name))
-        self.incl_info = incl_info
         self.src = fp.read()
         if self.src == '' or self.src[-1] != '\n':
             self.src += '\n'
         self.cursor = 0
-        self.line = 1
+        self.info = QAPISourceInfo(self.fname, 1, incl_info)
         self.line_pos = 0
         self.exprs = []
         self.docs = []
@@ -395,8 +409,7 @@ class QAPISchemaParser(object):
         cur_doc = None
 
         while self.tok is not None:
-            info = {'file': self.fname, 'line': self.line,
-                    'parent': self.incl_info}
+            info = self.info
             if self.tok == '#':
                 self.reject_expr_doc(cur_doc)
                 cur_doc = self.get_doc(info)
@@ -456,9 +469,9 @@ class QAPISchemaParser(object):
         # catch inclusion cycle
         inf = info
         while inf:
-            if incl_abs_fname == os.path.abspath(inf['file']):
+            if incl_abs_fname == os.path.abspath(inf.fname):
                 raise QAPISemError(info, "Inclusion loop for %s" % include)
-            inf = inf['parent']
+            inf = inf.parent
 
         # skip multiple include of the same file
         if incl_abs_fname in previously_included:
@@ -552,7 +565,7 @@ class QAPISchemaParser(object):
                 if self.cursor == len(self.src):
                     self.tok = None
                     return
-                self.line += 1
+                self.info = self.info.next_line()
                 self.line_pos = self.cursor
             elif not self.tok.isspace():
                 # Show up to next structural, whitespace or quote
@@ -1172,7 +1185,7 @@ class QAPISchemaEntity(object):
     def check(self, schema):
         assert not self._checked
         if self.info:
-            self._module = os.path.relpath(self.info['file'],
+            self._module = os.path.relpath(self.info.fname,
                                            os.path.dirname(schema.fname))
         self._checked = True
 
@@ -1781,9 +1794,9 @@ class QAPISchema(object):
         include = expr['include']
         assert doc is None
         main_info = info
-        while main_info['parent']:
-            main_info = main_info['parent']
-        fname = os.path.relpath(include, os.path.dirname(main_info['file']))
+        while main_info.parent:
+            main_info = main_info.parent
+        fname = os.path.relpath(include, os.path.dirname(main_info.fname))
         self._def_entity(QAPISchemaInclude(fname, info))
 
     def _def_builtin_type(self, name, json_type, c_type):
-- 
2.21.0



^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH 04/25] qapi: Prefix frontend errors with an "in definition" line
  2019-09-24 13:28 [PATCH 00/25] qapi: Pay back some frontend technical debt Markus Armbruster
                   ` (2 preceding siblings ...)
  2019-09-24 13:28 ` [PATCH 03/25] qapi: New QAPISourceInfo, replacing dict Markus Armbruster
@ 2019-09-24 13:28 ` Markus Armbruster
  2019-09-24 14:58   ` Eric Blake
  2019-09-24 13:28 ` [PATCH 05/25] qapi: Clean up member name case checking Markus Armbruster
                   ` (20 subsequent siblings)
  24 siblings, 1 reply; 63+ messages in thread
From: Markus Armbruster @ 2019-09-24 13:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, mdroth

We take pains to include the offending expression in error messages,
e.g.

    tests/qapi-schema/alternate-any.json:2: alternate 'Alt' member 'one' cannot use type 'any'

But not always:

    tests/qapi-schema/enum-if-invalid.json:2: 'if' condition must be a string or a list of strings

Instead of improving them one by one, report the offending expression
whenever it is known, like this:

    tests/qapi-schema/enum-if-invalid.json: In enum 'TestIfEnum':
    tests/qapi-schema/enum-if-invalid.json:2: 'if' condition must be a string or a list of strings

Error messages that mention the offending expression become a bit
redundant, e.g.

    tests/qapi-schema/alternate-any.json: In alternate 'Alt':
    tests/qapi-schema/alternate-any.json:2: alternate 'Alt' member 'one' cannot use type 'any'

I'll take care of that later in this series.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 scripts/qapi/common.py                            | 15 ++++++++++++++-
 tests/qapi-schema/alternate-any.err               |  1 +
 tests/qapi-schema/alternate-array.err             |  1 +
 tests/qapi-schema/alternate-branch-if-invalid.err |  1 +
 tests/qapi-schema/alternate-clash.err             |  1 +
 .../alternate-conflict-bool-string.err            |  1 +
 tests/qapi-schema/alternate-conflict-dict.err     |  1 +
 .../qapi-schema/alternate-conflict-enum-bool.err  |  1 +
 tests/qapi-schema/alternate-conflict-enum-int.err |  1 +
 .../qapi-schema/alternate-conflict-num-string.err |  1 +
 tests/qapi-schema/alternate-conflict-string.err   |  1 +
 tests/qapi-schema/alternate-empty.err             |  1 +
 tests/qapi-schema/alternate-invalid-dict.err      |  1 +
 tests/qapi-schema/alternate-nested.err            |  1 +
 tests/qapi-schema/alternate-unknown.err           |  1 +
 tests/qapi-schema/args-alternate.err              |  1 +
 tests/qapi-schema/args-any.err                    |  1 +
 tests/qapi-schema/args-array-empty.err            |  1 +
 tests/qapi-schema/args-array-unknown.err          |  1 +
 tests/qapi-schema/args-boxed-anon.err             |  1 +
 tests/qapi-schema/args-boxed-string.err           |  1 +
 tests/qapi-schema/args-int.err                    |  1 +
 tests/qapi-schema/args-invalid.err                |  1 +
 tests/qapi-schema/args-member-array-bad.err       |  1 +
 tests/qapi-schema/args-member-case.err            |  1 +
 tests/qapi-schema/args-member-unknown.err         |  1 +
 tests/qapi-schema/args-name-clash.err             |  1 +
 tests/qapi-schema/args-union.err                  |  1 +
 tests/qapi-schema/args-unknown.err                |  1 +
 tests/qapi-schema/bad-base.err                    |  1 +
 tests/qapi-schema/bad-data.err                    |  1 +
 tests/qapi-schema/base-cycle-direct.err           |  1 +
 tests/qapi-schema/base-cycle-indirect.err         |  1 +
 tests/qapi-schema/doc-bad-symbol.err              |  1 +
 tests/qapi-schema/enum-bad-member.err             |  1 +
 tests/qapi-schema/enum-bad-name.err               |  1 +
 tests/qapi-schema/enum-bad-prefix.err             |  1 +
 tests/qapi-schema/enum-clash-member.err           |  1 +
 tests/qapi-schema/enum-dict-member-unknown.err    |  1 +
 tests/qapi-schema/enum-if-invalid.err             |  1 +
 tests/qapi-schema/enum-member-case.err            |  1 +
 tests/qapi-schema/enum-wrong-data.err             |  1 +
 tests/qapi-schema/event-boxed-empty.err           |  1 +
 tests/qapi-schema/event-member-invalid-dict.err   |  1 +
 tests/qapi-schema/event-nest-struct.err           |  1 +
 tests/qapi-schema/features-bad-type.err           |  1 +
 tests/qapi-schema/features-duplicate-name.err     |  1 +
 tests/qapi-schema/features-if-invalid.err         |  1 +
 tests/qapi-schema/features-missing-name.err       |  1 +
 tests/qapi-schema/features-name-bad-type.err      |  1 +
 tests/qapi-schema/features-no-list.err            |  1 +
 tests/qapi-schema/features-unknown-key.err        |  1 +
 tests/qapi-schema/flat-union-array-branch.err     |  1 +
 tests/qapi-schema/flat-union-bad-base.err         |  1 +
 .../qapi-schema/flat-union-bad-discriminator.err  |  1 +
 tests/qapi-schema/flat-union-base-any.err         |  1 +
 tests/qapi-schema/flat-union-base-union.err       |  1 +
 tests/qapi-schema/flat-union-clash-member.err     |  1 +
 .../flat-union-discriminator-bad-name.err         |  1 +
 tests/qapi-schema/flat-union-empty.err            |  1 +
 .../flat-union-inline-invalid-dict.err            |  1 +
 tests/qapi-schema/flat-union-inline.err           |  1 +
 tests/qapi-schema/flat-union-int-branch.err       |  1 +
 .../qapi-schema/flat-union-invalid-branch-key.err |  1 +
 .../flat-union-invalid-discriminator.err          |  1 +
 .../flat-union-invalid-if-discriminator.err       |  1 +
 tests/qapi-schema/flat-union-no-base.err          |  1 +
 .../flat-union-optional-discriminator.err         |  1 +
 .../flat-union-string-discriminator.err           |  1 +
 .../nested-struct-data-invalid-dict.err           |  1 +
 tests/qapi-schema/nested-struct-data.err          |  1 +
 tests/qapi-schema/reserved-enum-q.err             |  1 +
 tests/qapi-schema/reserved-member-has.err         |  1 +
 tests/qapi-schema/reserved-member-q.err           |  1 +
 tests/qapi-schema/reserved-member-u.err           |  1 +
 tests/qapi-schema/reserved-member-underscore.err  |  1 +
 tests/qapi-schema/returns-alternate.err           |  1 +
 tests/qapi-schema/returns-array-bad.err           |  1 +
 tests/qapi-schema/returns-dict.err                |  1 +
 tests/qapi-schema/returns-unknown.err             |  1 +
 tests/qapi-schema/returns-whitelist.err           |  1 +
 tests/qapi-schema/struct-base-clash-deep.err      |  1 +
 tests/qapi-schema/struct-base-clash.err           |  1 +
 tests/qapi-schema/struct-data-invalid.err         |  1 +
 tests/qapi-schema/struct-member-if-invalid.err    |  1 +
 tests/qapi-schema/struct-member-invalid-dict.err  |  1 +
 tests/qapi-schema/struct-member-invalid.err       |  1 +
 tests/qapi-schema/union-base-empty.err            |  1 +
 tests/qapi-schema/union-base-no-discriminator.err |  1 +
 tests/qapi-schema/union-branch-case.err           |  1 +
 tests/qapi-schema/union-branch-if-invalid.err     |  1 +
 tests/qapi-schema/union-branch-invalid-dict.err   |  1 +
 tests/qapi-schema/union-clash-branches.err        |  1 +
 tests/qapi-schema/union-empty.err                 |  1 +
 tests/qapi-schema/union-invalid-base.err          |  1 +
 tests/qapi-schema/union-optional-branch.err       |  1 +
 tests/qapi-schema/union-unknown.err               |  1 +
 97 files changed, 110 insertions(+), 1 deletion(-)

diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index 5843f3eeb2..f0e7d5ad34 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -64,6 +64,12 @@ class QAPISourceInfo(object):
         self.fname = fname
         self.line = line
         self.parent = parent
+        self.defn_meta = None
+        self.defn_name = None
+
+    def set_defn(self, meta, name):
+        self.defn_meta = meta
+        self.defn_name = name
 
     def next_line(self):
         info = copy.copy(self)
@@ -73,6 +79,12 @@ class QAPISourceInfo(object):
     def loc(self):
         return '%s:%d' % (self.fname, self.line)
 
+    def in_defn(self):
+        if self.defn_name:
+            return "%s: In %s '%s':\n" % (self.fname,
+                                          self.defn_meta, self.defn_name)
+        return ''
+
     def include_path(self):
         ret = ''
         parent = self.parent
@@ -82,7 +94,7 @@ class QAPISourceInfo(object):
         return ret
 
     def __str__(self):
-        return self.include_path() + self.loc()
+        return self.include_path() + self.in_defn() + self.loc()
 
 
 class QAPIError(Exception):
@@ -1127,6 +1139,7 @@ def check_exprs(exprs):
         normalize_if(expr)
         name = expr[meta]
         add_name(name, info, meta)
+        info.set_defn(meta, name)
         if doc and doc.symbol != name:
             raise QAPISemError(info, "Definition of '%s' follows documentation"
                                " for '%s'" % (name, doc.symbol))
diff --git a/tests/qapi-schema/alternate-any.err b/tests/qapi-schema/alternate-any.err
index aaa0154731..177a11cc12 100644
--- a/tests/qapi-schema/alternate-any.err
+++ b/tests/qapi-schema/alternate-any.err
@@ -1 +1,2 @@
+tests/qapi-schema/alternate-any.json: In alternate 'Alt':
 tests/qapi-schema/alternate-any.json:2: Alternate 'Alt' member 'one' cannot use type 'any'
diff --git a/tests/qapi-schema/alternate-array.err b/tests/qapi-schema/alternate-array.err
index 7b930c64ab..f16f266c76 100644
--- a/tests/qapi-schema/alternate-array.err
+++ b/tests/qapi-schema/alternate-array.err
@@ -1 +1,2 @@
+tests/qapi-schema/alternate-array.json: In alternate 'Alt':
 tests/qapi-schema/alternate-array.json:5: Member 'two' of alternate 'Alt' cannot be an array
diff --git a/tests/qapi-schema/alternate-branch-if-invalid.err b/tests/qapi-schema/alternate-branch-if-invalid.err
index f1d6c10e00..8684829aca 100644
--- a/tests/qapi-schema/alternate-branch-if-invalid.err
+++ b/tests/qapi-schema/alternate-branch-if-invalid.err
@@ -1 +1,2 @@
+tests/qapi-schema/alternate-branch-if-invalid.json: In alternate 'Alt':
 tests/qapi-schema/alternate-branch-if-invalid.json:2: 'if' condition ' ' makes no sense
diff --git a/tests/qapi-schema/alternate-clash.err b/tests/qapi-schema/alternate-clash.err
index 604d8495eb..426ff6a7c4 100644
--- a/tests/qapi-schema/alternate-clash.err
+++ b/tests/qapi-schema/alternate-clash.err
@@ -1 +1,2 @@
+tests/qapi-schema/alternate-clash.json: In alternate 'Alt1':
 tests/qapi-schema/alternate-clash.json:7: 'a_b' (branch of Alt1) collides with 'a-b' (branch of Alt1)
diff --git a/tests/qapi-schema/alternate-conflict-bool-string.err b/tests/qapi-schema/alternate-conflict-bool-string.err
index e52fee7620..edfd36b7ad 100644
--- a/tests/qapi-schema/alternate-conflict-bool-string.err
+++ b/tests/qapi-schema/alternate-conflict-bool-string.err
@@ -1 +1,2 @@
+tests/qapi-schema/alternate-conflict-bool-string.json: In alternate 'Alt':
 tests/qapi-schema/alternate-conflict-bool-string.json:2: Alternate 'Alt' member 'two' can't be distinguished from member 'one'
diff --git a/tests/qapi-schema/alternate-conflict-dict.err b/tests/qapi-schema/alternate-conflict-dict.err
index 0f411f4faf..61ae93273a 100644
--- a/tests/qapi-schema/alternate-conflict-dict.err
+++ b/tests/qapi-schema/alternate-conflict-dict.err
@@ -1 +1,2 @@
+tests/qapi-schema/alternate-conflict-dict.json: In alternate 'Alt':
 tests/qapi-schema/alternate-conflict-dict.json:6: Alternate 'Alt' member 'two' can't be distinguished from member 'one'
diff --git a/tests/qapi-schema/alternate-conflict-enum-bool.err b/tests/qapi-schema/alternate-conflict-enum-bool.err
index 0dfc00242d..b006d1efbc 100644
--- a/tests/qapi-schema/alternate-conflict-enum-bool.err
+++ b/tests/qapi-schema/alternate-conflict-enum-bool.err
@@ -1 +1,2 @@
+tests/qapi-schema/alternate-conflict-enum-bool.json: In alternate 'Alt':
 tests/qapi-schema/alternate-conflict-enum-bool.json:4: Alternate 'Alt' member 'two' can't be distinguished from member 'one'
diff --git a/tests/qapi-schema/alternate-conflict-enum-int.err b/tests/qapi-schema/alternate-conflict-enum-int.err
index 2cc8e7b9aa..817b5c0191 100644
--- a/tests/qapi-schema/alternate-conflict-enum-int.err
+++ b/tests/qapi-schema/alternate-conflict-enum-int.err
@@ -1 +1,2 @@
+tests/qapi-schema/alternate-conflict-enum-int.json: In alternate 'Alt':
 tests/qapi-schema/alternate-conflict-enum-int.json:4: Alternate 'Alt' member 'two' can't be distinguished from member 'one'
diff --git a/tests/qapi-schema/alternate-conflict-num-string.err b/tests/qapi-schema/alternate-conflict-num-string.err
index 5ba3827dd1..abc188e0a6 100644
--- a/tests/qapi-schema/alternate-conflict-num-string.err
+++ b/tests/qapi-schema/alternate-conflict-num-string.err
@@ -1 +1,2 @@
+tests/qapi-schema/alternate-conflict-num-string.json: In alternate 'Alt':
 tests/qapi-schema/alternate-conflict-num-string.json:2: Alternate 'Alt' member 'two' can't be distinguished from member 'one'
diff --git a/tests/qapi-schema/alternate-conflict-string.err b/tests/qapi-schema/alternate-conflict-string.err
index fe2f188295..77ccd80cd7 100644
--- a/tests/qapi-schema/alternate-conflict-string.err
+++ b/tests/qapi-schema/alternate-conflict-string.err
@@ -1 +1,2 @@
+tests/qapi-schema/alternate-conflict-string.json: In alternate 'Alt':
 tests/qapi-schema/alternate-conflict-string.json:2: Alternate 'Alt' member 'two' can't be distinguished from member 'one'
diff --git a/tests/qapi-schema/alternate-empty.err b/tests/qapi-schema/alternate-empty.err
index 86dbc666eb..9daac031e4 100644
--- a/tests/qapi-schema/alternate-empty.err
+++ b/tests/qapi-schema/alternate-empty.err
@@ -1 +1,2 @@
+tests/qapi-schema/alternate-empty.json: In alternate 'Alt':
 tests/qapi-schema/alternate-empty.json:2: Alternate 'Alt' cannot have empty 'data'
diff --git a/tests/qapi-schema/alternate-invalid-dict.err b/tests/qapi-schema/alternate-invalid-dict.err
index 631d46628e..701db8cdce 100644
--- a/tests/qapi-schema/alternate-invalid-dict.err
+++ b/tests/qapi-schema/alternate-invalid-dict.err
@@ -1 +1,2 @@
+tests/qapi-schema/alternate-invalid-dict.json: In alternate 'Alt':
 tests/qapi-schema/alternate-invalid-dict.json:2: Key 'type' is missing from member 'two' of alternate 'Alt'
diff --git a/tests/qapi-schema/alternate-nested.err b/tests/qapi-schema/alternate-nested.err
index 4d1187e60e..4ab5328025 100644
--- a/tests/qapi-schema/alternate-nested.err
+++ b/tests/qapi-schema/alternate-nested.err
@@ -1 +1,2 @@
+tests/qapi-schema/alternate-nested.json: In alternate 'Alt2':
 tests/qapi-schema/alternate-nested.json:4: Member 'nested' of alternate 'Alt2' cannot use alternate type 'Alt1'
diff --git a/tests/qapi-schema/alternate-unknown.err b/tests/qapi-schema/alternate-unknown.err
index dea45dc730..5bd473325e 100644
--- a/tests/qapi-schema/alternate-unknown.err
+++ b/tests/qapi-schema/alternate-unknown.err
@@ -1 +1,2 @@
+tests/qapi-schema/alternate-unknown.json: In alternate 'Alt':
 tests/qapi-schema/alternate-unknown.json:2: Member 'unknown' of alternate 'Alt' uses unknown type 'MissingType'
diff --git a/tests/qapi-schema/args-alternate.err b/tests/qapi-schema/args-alternate.err
index 3086eae56b..915f5d463b 100644
--- a/tests/qapi-schema/args-alternate.err
+++ b/tests/qapi-schema/args-alternate.err
@@ -1 +1,2 @@
+tests/qapi-schema/args-alternate.json: In command 'oops':
 tests/qapi-schema/args-alternate.json:3: 'data' for command 'oops' cannot use alternate type 'Alt'
diff --git a/tests/qapi-schema/args-any.err b/tests/qapi-schema/args-any.err
index bf9b5e0730..8c4f9aeef4 100644
--- a/tests/qapi-schema/args-any.err
+++ b/tests/qapi-schema/args-any.err
@@ -1 +1,2 @@
+tests/qapi-schema/args-any.json: In command 'oops':
 tests/qapi-schema/args-any.json:2: 'data' for command 'oops' cannot use built-in type 'any'
diff --git a/tests/qapi-schema/args-array-empty.err b/tests/qapi-schema/args-array-empty.err
index cb7ed33b3f..fb95f0c4de 100644
--- a/tests/qapi-schema/args-array-empty.err
+++ b/tests/qapi-schema/args-array-empty.err
@@ -1 +1,2 @@
+tests/qapi-schema/args-array-empty.json: In command 'oops':
 tests/qapi-schema/args-array-empty.json:2: Member 'empty' of 'data' for command 'oops': array type must contain single type name
diff --git a/tests/qapi-schema/args-array-unknown.err b/tests/qapi-schema/args-array-unknown.err
index cd7a0f98d7..c88a5612b2 100644
--- a/tests/qapi-schema/args-array-unknown.err
+++ b/tests/qapi-schema/args-array-unknown.err
@@ -1 +1,2 @@
+tests/qapi-schema/args-array-unknown.json: In command 'oops':
 tests/qapi-schema/args-array-unknown.json:2: Member 'array' of 'data' for command 'oops' uses unknown type 'NoSuchType'
diff --git a/tests/qapi-schema/args-boxed-anon.err b/tests/qapi-schema/args-boxed-anon.err
index f24f345218..27460e6c07 100644
--- a/tests/qapi-schema/args-boxed-anon.err
+++ b/tests/qapi-schema/args-boxed-anon.err
@@ -1 +1,2 @@
+tests/qapi-schema/args-boxed-anon.json: In command 'foo':
 tests/qapi-schema/args-boxed-anon.json:2: 'data' for command 'foo' should be a type name
diff --git a/tests/qapi-schema/args-boxed-string.err b/tests/qapi-schema/args-boxed-string.err
index d326b48aef..f284406f0f 100644
--- a/tests/qapi-schema/args-boxed-string.err
+++ b/tests/qapi-schema/args-boxed-string.err
@@ -1 +1,2 @@
+tests/qapi-schema/args-boxed-string.json: In command 'foo':
 tests/qapi-schema/args-boxed-string.json:2: 'data' for command 'foo' cannot use built-in type 'str'
diff --git a/tests/qapi-schema/args-int.err b/tests/qapi-schema/args-int.err
index dc1d2504ff..419268186e 100644
--- a/tests/qapi-schema/args-int.err
+++ b/tests/qapi-schema/args-int.err
@@ -1 +1,2 @@
+tests/qapi-schema/args-int.json: In command 'oops':
 tests/qapi-schema/args-int.json:2: 'data' for command 'oops' cannot use built-in type 'int'
diff --git a/tests/qapi-schema/args-invalid.err b/tests/qapi-schema/args-invalid.err
index bfb2e4133e..212c2076fc 100644
--- a/tests/qapi-schema/args-invalid.err
+++ b/tests/qapi-schema/args-invalid.err
@@ -1 +1,2 @@
+tests/qapi-schema/args-invalid.json: In command 'foo':
 tests/qapi-schema/args-invalid.json:1: 'data' for command 'foo' should be an object or type name
diff --git a/tests/qapi-schema/args-member-array-bad.err b/tests/qapi-schema/args-member-array-bad.err
index 881b4d954f..81e1e95523 100644
--- a/tests/qapi-schema/args-member-array-bad.err
+++ b/tests/qapi-schema/args-member-array-bad.err
@@ -1 +1,2 @@
+tests/qapi-schema/args-member-array-bad.json: In command 'oops':
 tests/qapi-schema/args-member-array-bad.json:2: Member 'member' of 'data' for command 'oops': array type must contain single type name
diff --git a/tests/qapi-schema/args-member-case.err b/tests/qapi-schema/args-member-case.err
index 19c4426601..725ba16192 100644
--- a/tests/qapi-schema/args-member-case.err
+++ b/tests/qapi-schema/args-member-case.err
@@ -1 +1,2 @@
+tests/qapi-schema/args-member-case.json: In command 'no-way-this-will-get-whitelisted':
 tests/qapi-schema/args-member-case.json:2: 'Arg' (parameter of no-way-this-will-get-whitelisted) should not use uppercase
diff --git a/tests/qapi-schema/args-member-unknown.err b/tests/qapi-schema/args-member-unknown.err
index f6f82828ce..9d43e54ca9 100644
--- a/tests/qapi-schema/args-member-unknown.err
+++ b/tests/qapi-schema/args-member-unknown.err
@@ -1 +1,2 @@
+tests/qapi-schema/args-member-unknown.json: In command 'oops':
 tests/qapi-schema/args-member-unknown.json:2: Member 'member' of 'data' for command 'oops' uses unknown type 'NoSuchType'
diff --git a/tests/qapi-schema/args-name-clash.err b/tests/qapi-schema/args-name-clash.err
index d953e8d241..eeb4e1b4dd 100644
--- a/tests/qapi-schema/args-name-clash.err
+++ b/tests/qapi-schema/args-name-clash.err
@@ -1 +1,2 @@
+tests/qapi-schema/args-name-clash.json: In command 'oops':
 tests/qapi-schema/args-name-clash.json:4: 'a_b' (parameter of oops) collides with 'a-b' (parameter of oops)
diff --git a/tests/qapi-schema/args-union.err b/tests/qapi-schema/args-union.err
index f8ad223dde..30a1534b4c 100644
--- a/tests/qapi-schema/args-union.err
+++ b/tests/qapi-schema/args-union.err
@@ -1 +1,2 @@
+tests/qapi-schema/args-union.json: In command 'oops':
 tests/qapi-schema/args-union.json:3: 'data' for command 'oops' cannot use union type 'Uni'
diff --git a/tests/qapi-schema/args-unknown.err b/tests/qapi-schema/args-unknown.err
index 4d91ec869f..cb835d7489 100644
--- a/tests/qapi-schema/args-unknown.err
+++ b/tests/qapi-schema/args-unknown.err
@@ -1 +1,2 @@
+tests/qapi-schema/args-unknown.json: In command 'oops':
 tests/qapi-schema/args-unknown.json:2: 'data' for command 'oops' uses unknown type 'NoSuchType'
diff --git a/tests/qapi-schema/bad-base.err b/tests/qapi-schema/bad-base.err
index 154274bdd3..fdfd288f5c 100644
--- a/tests/qapi-schema/bad-base.err
+++ b/tests/qapi-schema/bad-base.err
@@ -1 +1,2 @@
+tests/qapi-schema/bad-base.json: In struct 'MyType':
 tests/qapi-schema/bad-base.json:3: 'base' for struct 'MyType' cannot use union type 'Union'
diff --git a/tests/qapi-schema/bad-data.err b/tests/qapi-schema/bad-data.err
index 8523ac4f46..8ef6bbd2b5 100644
--- a/tests/qapi-schema/bad-data.err
+++ b/tests/qapi-schema/bad-data.err
@@ -1 +1,2 @@
+tests/qapi-schema/bad-data.json: In command 'oops':
 tests/qapi-schema/bad-data.json:2: 'data' for command 'oops' cannot be an array
diff --git a/tests/qapi-schema/base-cycle-direct.err b/tests/qapi-schema/base-cycle-direct.err
index 9c68f6543d..52c21bc2b8 100644
--- a/tests/qapi-schema/base-cycle-direct.err
+++ b/tests/qapi-schema/base-cycle-direct.err
@@ -1 +1,2 @@
+tests/qapi-schema/base-cycle-direct.json: In struct 'Loopy':
 tests/qapi-schema/base-cycle-direct.json:2: Object Loopy contains itself
diff --git a/tests/qapi-schema/base-cycle-indirect.err b/tests/qapi-schema/base-cycle-indirect.err
index fc92fe47f8..1f60cd78f6 100644
--- a/tests/qapi-schema/base-cycle-indirect.err
+++ b/tests/qapi-schema/base-cycle-indirect.err
@@ -1 +1,2 @@
+tests/qapi-schema/base-cycle-indirect.json: In struct 'Base1':
 tests/qapi-schema/base-cycle-indirect.json:2: Object Base1 contains itself
diff --git a/tests/qapi-schema/doc-bad-symbol.err b/tests/qapi-schema/doc-bad-symbol.err
index 8472030c79..375cdff939 100644
--- a/tests/qapi-schema/doc-bad-symbol.err
+++ b/tests/qapi-schema/doc-bad-symbol.err
@@ -1 +1,2 @@
+tests/qapi-schema/doc-bad-symbol.json: In command 'foo':
 tests/qapi-schema/doc-bad-symbol.json:6: Definition of 'foo' follows documentation for 'food'
diff --git a/tests/qapi-schema/enum-bad-member.err b/tests/qapi-schema/enum-bad-member.err
index 211db9e6fc..49e4160dc4 100644
--- a/tests/qapi-schema/enum-bad-member.err
+++ b/tests/qapi-schema/enum-bad-member.err
@@ -1 +1,2 @@
+tests/qapi-schema/enum-bad-member.json: In enum 'MyEnum':
 tests/qapi-schema/enum-bad-member.json:2: Member of enum 'MyEnum' requires a string name
diff --git a/tests/qapi-schema/enum-bad-name.err b/tests/qapi-schema/enum-bad-name.err
index 26a09f84ad..3af7345792 100644
--- a/tests/qapi-schema/enum-bad-name.err
+++ b/tests/qapi-schema/enum-bad-name.err
@@ -1 +1,2 @@
+tests/qapi-schema/enum-bad-name.json: In enum 'MyEnum':
 tests/qapi-schema/enum-bad-name.json:3: Member of enum 'MyEnum' uses invalid name 'not\possible'
diff --git a/tests/qapi-schema/enum-bad-prefix.err b/tests/qapi-schema/enum-bad-prefix.err
index 399f5f7af5..383ebaea7e 100644
--- a/tests/qapi-schema/enum-bad-prefix.err
+++ b/tests/qapi-schema/enum-bad-prefix.err
@@ -1 +1,2 @@
+tests/qapi-schema/enum-bad-prefix.json: In enum 'MyEnum':
 tests/qapi-schema/enum-bad-prefix.json:2: Enum 'MyEnum' requires a string for 'prefix'
diff --git a/tests/qapi-schema/enum-clash-member.err b/tests/qapi-schema/enum-clash-member.err
index 8238d2e807..26944f5e06 100644
--- a/tests/qapi-schema/enum-clash-member.err
+++ b/tests/qapi-schema/enum-clash-member.err
@@ -1 +1,2 @@
+tests/qapi-schema/enum-clash-member.json: In enum 'MyEnum':
 tests/qapi-schema/enum-clash-member.json:2: 'one_two' (value of MyEnum) collides with 'one-two' (value of MyEnum)
diff --git a/tests/qapi-schema/enum-dict-member-unknown.err b/tests/qapi-schema/enum-dict-member-unknown.err
index 7fd9c032bf..02952f472a 100644
--- a/tests/qapi-schema/enum-dict-member-unknown.err
+++ b/tests/qapi-schema/enum-dict-member-unknown.err
@@ -1,2 +1,3 @@
+tests/qapi-schema/enum-dict-member-unknown.json: In enum 'MyEnum':
 tests/qapi-schema/enum-dict-member-unknown.json:2: Unknown key 'bad-key' in member of enum 'MyEnum'
 Valid keys are 'if', 'name'.
diff --git a/tests/qapi-schema/enum-if-invalid.err b/tests/qapi-schema/enum-if-invalid.err
index 54c3cf887b..db9eb45b25 100644
--- a/tests/qapi-schema/enum-if-invalid.err
+++ b/tests/qapi-schema/enum-if-invalid.err
@@ -1 +1,2 @@
+tests/qapi-schema/enum-if-invalid.json: In enum 'TestIfEnum':
 tests/qapi-schema/enum-if-invalid.json:2: 'if' condition must be a string or a list of strings
diff --git a/tests/qapi-schema/enum-member-case.err b/tests/qapi-schema/enum-member-case.err
index 5d689e92d5..f6c872d3bf 100644
--- a/tests/qapi-schema/enum-member-case.err
+++ b/tests/qapi-schema/enum-member-case.err
@@ -1 +1,2 @@
+tests/qapi-schema/enum-member-case.json: In enum 'NoWayThisWillGetWhitelisted':
 tests/qapi-schema/enum-member-case.json:4: 'Value' (value of NoWayThisWillGetWhitelisted) should not use uppercase
diff --git a/tests/qapi-schema/enum-wrong-data.err b/tests/qapi-schema/enum-wrong-data.err
index 11b43471cf..4ab0c44e48 100644
--- a/tests/qapi-schema/enum-wrong-data.err
+++ b/tests/qapi-schema/enum-wrong-data.err
@@ -1 +1,2 @@
+tests/qapi-schema/enum-wrong-data.json: In enum 'MyEnum':
 tests/qapi-schema/enum-wrong-data.json:2: Enum 'MyEnum' requires an array for 'data'
diff --git a/tests/qapi-schema/event-boxed-empty.err b/tests/qapi-schema/event-boxed-empty.err
index 68ec6f2d2b..6ccbdf7b22 100644
--- a/tests/qapi-schema/event-boxed-empty.err
+++ b/tests/qapi-schema/event-boxed-empty.err
@@ -1 +1,2 @@
+tests/qapi-schema/event-boxed-empty.json: In event 'FOO':
 tests/qapi-schema/event-boxed-empty.json:2: Use of 'boxed' requires 'data'
diff --git a/tests/qapi-schema/event-member-invalid-dict.err b/tests/qapi-schema/event-member-invalid-dict.err
index 1a57fa29b0..9981a48b81 100644
--- a/tests/qapi-schema/event-member-invalid-dict.err
+++ b/tests/qapi-schema/event-member-invalid-dict.err
@@ -1 +1,2 @@
+tests/qapi-schema/event-member-invalid-dict.json: In event 'EVENT_A':
 tests/qapi-schema/event-member-invalid-dict.json:1: Key 'type' is missing from member 'a' of 'data' for event 'EVENT_A'
diff --git a/tests/qapi-schema/event-nest-struct.err b/tests/qapi-schema/event-nest-struct.err
index 5a42701b8f..2dd57f1784 100644
--- a/tests/qapi-schema/event-nest-struct.err
+++ b/tests/qapi-schema/event-nest-struct.err
@@ -1 +1,2 @@
+tests/qapi-schema/event-nest-struct.json: In event 'EVENT_A':
 tests/qapi-schema/event-nest-struct.json:1: Member 'a' of 'data' for event 'EVENT_A' should be a type name
diff --git a/tests/qapi-schema/features-bad-type.err b/tests/qapi-schema/features-bad-type.err
index 5fb95c2f90..a08485387a 100644
--- a/tests/qapi-schema/features-bad-type.err
+++ b/tests/qapi-schema/features-bad-type.err
@@ -1 +1,2 @@
+tests/qapi-schema/features-bad-type.json: In struct 'FeatureStruct0':
 tests/qapi-schema/features-bad-type.json:1: Feature of struct FeatureStruct0 requires a string name
diff --git a/tests/qapi-schema/features-duplicate-name.err b/tests/qapi-schema/features-duplicate-name.err
index c0a4cccae6..0ebec8e4b0 100644
--- a/tests/qapi-schema/features-duplicate-name.err
+++ b/tests/qapi-schema/features-duplicate-name.err
@@ -1 +1,2 @@
+tests/qapi-schema/features-duplicate-name.json: In struct 'FeatureStruct0':
 tests/qapi-schema/features-duplicate-name.json:1: 'foo' (feature of FeatureStruct0) collides with 'foo' (feature of FeatureStruct0)
diff --git a/tests/qapi-schema/features-if-invalid.err b/tests/qapi-schema/features-if-invalid.err
index 295800b4fc..90f4119ae7 100644
--- a/tests/qapi-schema/features-if-invalid.err
+++ b/tests/qapi-schema/features-if-invalid.err
@@ -1 +1,2 @@
+tests/qapi-schema/features-if-invalid.json: In struct 'Stru':
 tests/qapi-schema/features-if-invalid.json:2: 'if' condition must be a string or a list of strings
diff --git a/tests/qapi-schema/features-missing-name.err b/tests/qapi-schema/features-missing-name.err
index 4f1d2715aa..d445936b0c 100644
--- a/tests/qapi-schema/features-missing-name.err
+++ b/tests/qapi-schema/features-missing-name.err
@@ -1 +1,2 @@
+tests/qapi-schema/features-missing-name.json: In struct 'FeatureStruct0':
 tests/qapi-schema/features-missing-name.json:1: Key 'name' is missing from feature of struct FeatureStruct0
diff --git a/tests/qapi-schema/features-name-bad-type.err b/tests/qapi-schema/features-name-bad-type.err
index 8a3eecb972..cc4bd33e7f 100644
--- a/tests/qapi-schema/features-name-bad-type.err
+++ b/tests/qapi-schema/features-name-bad-type.err
@@ -1 +1,2 @@
+tests/qapi-schema/features-name-bad-type.json: In struct 'FeatureStruct0':
 tests/qapi-schema/features-name-bad-type.json:1: Feature of struct FeatureStruct0 requires a string name
diff --git a/tests/qapi-schema/features-no-list.err b/tests/qapi-schema/features-no-list.err
index 61ed68612b..663d9cd158 100644
--- a/tests/qapi-schema/features-no-list.err
+++ b/tests/qapi-schema/features-no-list.err
@@ -1 +1,2 @@
+tests/qapi-schema/features-no-list.json: In struct 'FeatureStruct0':
 tests/qapi-schema/features-no-list.json:1: Struct 'FeatureStruct0' requires an array for 'features'
diff --git a/tests/qapi-schema/features-unknown-key.err b/tests/qapi-schema/features-unknown-key.err
index a1d693030d..2aeca4c1a6 100644
--- a/tests/qapi-schema/features-unknown-key.err
+++ b/tests/qapi-schema/features-unknown-key.err
@@ -1,2 +1,3 @@
+tests/qapi-schema/features-unknown-key.json: In struct 'FeatureStruct0':
 tests/qapi-schema/features-unknown-key.json:1: Unknown key 'colour' in feature of struct FeatureStruct0
 Valid keys are 'if', 'name'.
diff --git a/tests/qapi-schema/flat-union-array-branch.err b/tests/qapi-schema/flat-union-array-branch.err
index 8ea91eadb2..13b4e60658 100644
--- a/tests/qapi-schema/flat-union-array-branch.err
+++ b/tests/qapi-schema/flat-union-array-branch.err
@@ -1 +1,2 @@
+tests/qapi-schema/flat-union-array-branch.json: In union 'TestUnion':
 tests/qapi-schema/flat-union-array-branch.json:8: Member 'value1' of union 'TestUnion' cannot be an array
diff --git a/tests/qapi-schema/flat-union-bad-base.err b/tests/qapi-schema/flat-union-bad-base.err
index bee24a217a..ae8adc3947 100644
--- a/tests/qapi-schema/flat-union-bad-base.err
+++ b/tests/qapi-schema/flat-union-bad-base.err
@@ -1 +1,2 @@
+tests/qapi-schema/flat-union-bad-base.json: In union 'TestUnion':
 tests/qapi-schema/flat-union-bad-base.json:8: 'string' (member of TestTypeA) collides with 'string' (base of TestUnion)
diff --git a/tests/qapi-schema/flat-union-bad-discriminator.err b/tests/qapi-schema/flat-union-bad-discriminator.err
index c38cc8e4df..9b3746343f 100644
--- a/tests/qapi-schema/flat-union-bad-discriminator.err
+++ b/tests/qapi-schema/flat-union-bad-discriminator.err
@@ -1 +1,2 @@
+tests/qapi-schema/flat-union-bad-discriminator.json: In union 'TestUnion':
 tests/qapi-schema/flat-union-bad-discriminator.json:11: Discriminator of flat union 'TestUnion' requires a string name
diff --git a/tests/qapi-schema/flat-union-base-any.err b/tests/qapi-schema/flat-union-base-any.err
index 646f1c9cd1..039b9a68b9 100644
--- a/tests/qapi-schema/flat-union-base-any.err
+++ b/tests/qapi-schema/flat-union-base-any.err
@@ -1 +1,2 @@
+tests/qapi-schema/flat-union-base-any.json: In union 'TestUnion':
 tests/qapi-schema/flat-union-base-any.json:8: 'base' for union 'TestUnion' cannot use built-in type 'any'
diff --git a/tests/qapi-schema/flat-union-base-union.err b/tests/qapi-schema/flat-union-base-union.err
index f138395e45..93ab91378f 100644
--- a/tests/qapi-schema/flat-union-base-union.err
+++ b/tests/qapi-schema/flat-union-base-union.err
@@ -1 +1,2 @@
+tests/qapi-schema/flat-union-base-union.json: In union 'TestUnion':
 tests/qapi-schema/flat-union-base-union.json:14: 'base' for union 'TestUnion' cannot use union type 'UnionBase'
diff --git a/tests/qapi-schema/flat-union-clash-member.err b/tests/qapi-schema/flat-union-clash-member.err
index 2adf69755a..48e939db19 100644
--- a/tests/qapi-schema/flat-union-clash-member.err
+++ b/tests/qapi-schema/flat-union-clash-member.err
@@ -1 +1,2 @@
+tests/qapi-schema/flat-union-clash-member.json: In union 'TestUnion':
 tests/qapi-schema/flat-union-clash-member.json:11: 'name' (member of Branch1) collides with 'name' (member of Base)
diff --git a/tests/qapi-schema/flat-union-discriminator-bad-name.err b/tests/qapi-schema/flat-union-discriminator-bad-name.err
index 7238d126ca..72dc328bad 100644
--- a/tests/qapi-schema/flat-union-discriminator-bad-name.err
+++ b/tests/qapi-schema/flat-union-discriminator-bad-name.err
@@ -1 +1,2 @@
+tests/qapi-schema/flat-union-discriminator-bad-name.json: In union 'MyUnion':
 tests/qapi-schema/flat-union-discriminator-bad-name.json:7: Discriminator of flat union 'MyUnion' does not allow optional name '*switch'
diff --git a/tests/qapi-schema/flat-union-empty.err b/tests/qapi-schema/flat-union-empty.err
index fedbc0d1cf..3fa79a6a47 100644
--- a/tests/qapi-schema/flat-union-empty.err
+++ b/tests/qapi-schema/flat-union-empty.err
@@ -1 +1,2 @@
+tests/qapi-schema/flat-union-empty.json: In union 'Union':
 tests/qapi-schema/flat-union-empty.json:4: Union 'Union' has no branches
diff --git a/tests/qapi-schema/flat-union-inline-invalid-dict.err b/tests/qapi-schema/flat-union-inline-invalid-dict.err
index 9c4c45b7f0..3716c02a9b 100644
--- a/tests/qapi-schema/flat-union-inline-invalid-dict.err
+++ b/tests/qapi-schema/flat-union-inline-invalid-dict.err
@@ -1 +1,2 @@
+tests/qapi-schema/flat-union-inline-invalid-dict.json: In union 'TestUnion':
 tests/qapi-schema/flat-union-inline-invalid-dict.json:7: Key 'type' is missing from member 'value1' of union 'TestUnion'
diff --git a/tests/qapi-schema/flat-union-inline.err b/tests/qapi-schema/flat-union-inline.err
index 2333358d28..0a7a94b03f 100644
--- a/tests/qapi-schema/flat-union-inline.err
+++ b/tests/qapi-schema/flat-union-inline.err
@@ -1 +1,2 @@
+tests/qapi-schema/flat-union-inline.json: In union 'TestUnion':
 tests/qapi-schema/flat-union-inline.json:7: Member 'value1' of union 'TestUnion' should be a type name
diff --git a/tests/qapi-schema/flat-union-int-branch.err b/tests/qapi-schema/flat-union-int-branch.err
index faf01573b7..075751bcf6 100644
--- a/tests/qapi-schema/flat-union-int-branch.err
+++ b/tests/qapi-schema/flat-union-int-branch.err
@@ -1 +1,2 @@
+tests/qapi-schema/flat-union-int-branch.json: In union 'TestUnion':
 tests/qapi-schema/flat-union-int-branch.json:8: Member 'value1' of union 'TestUnion' cannot use built-in type 'int'
diff --git a/tests/qapi-schema/flat-union-invalid-branch-key.err b/tests/qapi-schema/flat-union-invalid-branch-key.err
index ccf72d2dfe..a4d0e3ee66 100644
--- a/tests/qapi-schema/flat-union-invalid-branch-key.err
+++ b/tests/qapi-schema/flat-union-invalid-branch-key.err
@@ -1 +1,2 @@
+tests/qapi-schema/flat-union-invalid-branch-key.json: In union 'TestUnion':
 tests/qapi-schema/flat-union-invalid-branch-key.json:13: Discriminator value 'value_wrong' is not found in enum 'TestEnum'
diff --git a/tests/qapi-schema/flat-union-invalid-discriminator.err b/tests/qapi-schema/flat-union-invalid-discriminator.err
index 495d5a520e..ca9a413dae 100644
--- a/tests/qapi-schema/flat-union-invalid-discriminator.err
+++ b/tests/qapi-schema/flat-union-invalid-discriminator.err
@@ -1 +1,2 @@
+tests/qapi-schema/flat-union-invalid-discriminator.json: In union 'TestUnion':
 tests/qapi-schema/flat-union-invalid-discriminator.json:10: Discriminator 'enum_wrong' is not a member of 'base'
diff --git a/tests/qapi-schema/flat-union-invalid-if-discriminator.err b/tests/qapi-schema/flat-union-invalid-if-discriminator.err
index cc5c3fb80b..c06307db98 100644
--- a/tests/qapi-schema/flat-union-invalid-if-discriminator.err
+++ b/tests/qapi-schema/flat-union-invalid-if-discriminator.err
@@ -1 +1,2 @@
+tests/qapi-schema/flat-union-invalid-if-discriminator.json: In union 'TestUnion':
 tests/qapi-schema/flat-union-invalid-if-discriminator.json:10: The discriminator 'enum1' for union TestUnion must not be conditional
diff --git a/tests/qapi-schema/flat-union-no-base.err b/tests/qapi-schema/flat-union-no-base.err
index 841c93b554..6e2035495c 100644
--- a/tests/qapi-schema/flat-union-no-base.err
+++ b/tests/qapi-schema/flat-union-no-base.err
@@ -1 +1,2 @@
+tests/qapi-schema/flat-union-no-base.json: In union 'TestUnion':
 tests/qapi-schema/flat-union-no-base.json:9: Flat union 'TestUnion' must have a base
diff --git a/tests/qapi-schema/flat-union-optional-discriminator.err b/tests/qapi-schema/flat-union-optional-discriminator.err
index 45f5407c34..db268fffaa 100644
--- a/tests/qapi-schema/flat-union-optional-discriminator.err
+++ b/tests/qapi-schema/flat-union-optional-discriminator.err
@@ -1 +1,2 @@
+tests/qapi-schema/flat-union-optional-discriminator.json: In union 'MyUnion':
 tests/qapi-schema/flat-union-optional-discriminator.json:7: Discriminator 'switch' is not a member of 'base'
diff --git a/tests/qapi-schema/flat-union-string-discriminator.err b/tests/qapi-schema/flat-union-string-discriminator.err
index 200016bd5c..9bca7082bb 100644
--- a/tests/qapi-schema/flat-union-string-discriminator.err
+++ b/tests/qapi-schema/flat-union-string-discriminator.err
@@ -1 +1,2 @@
+tests/qapi-schema/flat-union-string-discriminator.json: In union 'TestUnion':
 tests/qapi-schema/flat-union-string-discriminator.json:13: Discriminator 'kind' must be of enumeration type
diff --git a/tests/qapi-schema/nested-struct-data-invalid-dict.err b/tests/qapi-schema/nested-struct-data-invalid-dict.err
index 5bd364e8d9..4d9c9b491a 100644
--- a/tests/qapi-schema/nested-struct-data-invalid-dict.err
+++ b/tests/qapi-schema/nested-struct-data-invalid-dict.err
@@ -1 +1,2 @@
+tests/qapi-schema/nested-struct-data-invalid-dict.json: In command 'foo':
 tests/qapi-schema/nested-struct-data-invalid-dict.json:2: Key 'type' is missing from member 'a' of 'data' for command 'foo'
diff --git a/tests/qapi-schema/nested-struct-data.err b/tests/qapi-schema/nested-struct-data.err
index da767bade2..74d44ab111 100644
--- a/tests/qapi-schema/nested-struct-data.err
+++ b/tests/qapi-schema/nested-struct-data.err
@@ -1 +1,2 @@
+tests/qapi-schema/nested-struct-data.json: In command 'foo':
 tests/qapi-schema/nested-struct-data.json:2: Member 'a' of 'data' for command 'foo' should be a type name
diff --git a/tests/qapi-schema/reserved-enum-q.err b/tests/qapi-schema/reserved-enum-q.err
index e1c3480ee2..e3eecd29dc 100644
--- a/tests/qapi-schema/reserved-enum-q.err
+++ b/tests/qapi-schema/reserved-enum-q.err
@@ -1 +1,2 @@
+tests/qapi-schema/reserved-enum-q.json: In enum 'Foo':
 tests/qapi-schema/reserved-enum-q.json:4: Member of enum 'Foo' uses invalid name 'q-Unix'
diff --git a/tests/qapi-schema/reserved-member-has.err b/tests/qapi-schema/reserved-member-has.err
index e755771446..2c6d0418db 100644
--- a/tests/qapi-schema/reserved-member-has.err
+++ b/tests/qapi-schema/reserved-member-has.err
@@ -1 +1,2 @@
+tests/qapi-schema/reserved-member-has.json: In command 'oops':
 tests/qapi-schema/reserved-member-has.json:5: Member of 'data' for command 'oops' uses reserved name 'has-a'
diff --git a/tests/qapi-schema/reserved-member-q.err b/tests/qapi-schema/reserved-member-q.err
index f3d5dd7818..eaabe579bc 100644
--- a/tests/qapi-schema/reserved-member-q.err
+++ b/tests/qapi-schema/reserved-member-q.err
@@ -1 +1,2 @@
+tests/qapi-schema/reserved-member-q.json: In struct 'Foo':
 tests/qapi-schema/reserved-member-q.json:4: Member of 'data' for struct 'Foo' uses invalid name 'q-unix'
diff --git a/tests/qapi-schema/reserved-member-u.err b/tests/qapi-schema/reserved-member-u.err
index 87d42296cc..b01629da29 100644
--- a/tests/qapi-schema/reserved-member-u.err
+++ b/tests/qapi-schema/reserved-member-u.err
@@ -1 +1,2 @@
+tests/qapi-schema/reserved-member-u.json: In struct 'Oops':
 tests/qapi-schema/reserved-member-u.json:7: Member of 'data' for struct 'Oops' uses reserved name 'u'
diff --git a/tests/qapi-schema/reserved-member-underscore.err b/tests/qapi-schema/reserved-member-underscore.err
index 65ff0da8ce..6089cc1d3b 100644
--- a/tests/qapi-schema/reserved-member-underscore.err
+++ b/tests/qapi-schema/reserved-member-underscore.err
@@ -1 +1,2 @@
+tests/qapi-schema/reserved-member-underscore.json: In struct 'Oops':
 tests/qapi-schema/reserved-member-underscore.json:4: Member of 'data' for struct 'Oops' uses invalid name '_oops'
diff --git a/tests/qapi-schema/returns-alternate.err b/tests/qapi-schema/returns-alternate.err
index dfbb419cac..b98cf84cef 100644
--- a/tests/qapi-schema/returns-alternate.err
+++ b/tests/qapi-schema/returns-alternate.err
@@ -1 +1,2 @@
+tests/qapi-schema/returns-alternate.json: In command 'oops':
 tests/qapi-schema/returns-alternate.json:3: 'returns' for command 'oops' cannot use alternate type 'Alt'
diff --git a/tests/qapi-schema/returns-array-bad.err b/tests/qapi-schema/returns-array-bad.err
index 138095ccde..6295ba89c0 100644
--- a/tests/qapi-schema/returns-array-bad.err
+++ b/tests/qapi-schema/returns-array-bad.err
@@ -1 +1,2 @@
+tests/qapi-schema/returns-array-bad.json: In command 'oops':
 tests/qapi-schema/returns-array-bad.json:2: 'returns' for command 'oops': array type must contain single type name
diff --git a/tests/qapi-schema/returns-dict.err b/tests/qapi-schema/returns-dict.err
index eb2d0c4661..7329b9526f 100644
--- a/tests/qapi-schema/returns-dict.err
+++ b/tests/qapi-schema/returns-dict.err
@@ -1 +1,2 @@
+tests/qapi-schema/returns-dict.json: In command 'oops':
 tests/qapi-schema/returns-dict.json:2: 'returns' for command 'oops' should be a type name
diff --git a/tests/qapi-schema/returns-unknown.err b/tests/qapi-schema/returns-unknown.err
index 1f43e3ac9f..cbece00bd2 100644
--- a/tests/qapi-schema/returns-unknown.err
+++ b/tests/qapi-schema/returns-unknown.err
@@ -1 +1,2 @@
+tests/qapi-schema/returns-unknown.json: In command 'oops':
 tests/qapi-schema/returns-unknown.json:2: 'returns' for command 'oops' uses unknown type 'NoSuchType'
diff --git a/tests/qapi-schema/returns-whitelist.err b/tests/qapi-schema/returns-whitelist.err
index b2ba7a9deb..1ccd7d8de2 100644
--- a/tests/qapi-schema/returns-whitelist.err
+++ b/tests/qapi-schema/returns-whitelist.err
@@ -1 +1,2 @@
+tests/qapi-schema/returns-whitelist.json: In command 'no-way-this-will-get-whitelisted':
 tests/qapi-schema/returns-whitelist.json:14: 'returns' for command 'no-way-this-will-get-whitelisted' cannot use built-in type 'int'
diff --git a/tests/qapi-schema/struct-base-clash-deep.err b/tests/qapi-schema/struct-base-clash-deep.err
index e2d7943f21..53e9bb108e 100644
--- a/tests/qapi-schema/struct-base-clash-deep.err
+++ b/tests/qapi-schema/struct-base-clash-deep.err
@@ -1 +1,2 @@
+tests/qapi-schema/struct-base-clash-deep.json: In struct 'Sub':
 tests/qapi-schema/struct-base-clash-deep.json:10: 'name' (member of Sub) collides with 'name' (member of Base)
diff --git a/tests/qapi-schema/struct-base-clash.err b/tests/qapi-schema/struct-base-clash.err
index c52f33d27b..bf94eee8b3 100644
--- a/tests/qapi-schema/struct-base-clash.err
+++ b/tests/qapi-schema/struct-base-clash.err
@@ -1 +1,2 @@
+tests/qapi-schema/struct-base-clash.json: In struct 'Sub':
 tests/qapi-schema/struct-base-clash.json:5: 'name' (member of Sub) collides with 'name' (member of Base)
diff --git a/tests/qapi-schema/struct-data-invalid.err b/tests/qapi-schema/struct-data-invalid.err
index 4bf5bcc255..a88754869f 100644
--- a/tests/qapi-schema/struct-data-invalid.err
+++ b/tests/qapi-schema/struct-data-invalid.err
@@ -1 +1,2 @@
+tests/qapi-schema/struct-data-invalid.json: In struct 'foo':
 tests/qapi-schema/struct-data-invalid.json:1: 'data' for struct 'foo' should be an object or type name
diff --git a/tests/qapi-schema/struct-member-if-invalid.err b/tests/qapi-schema/struct-member-if-invalid.err
index bfd65db97b..e8ad02b9fc 100644
--- a/tests/qapi-schema/struct-member-if-invalid.err
+++ b/tests/qapi-schema/struct-member-if-invalid.err
@@ -1 +1,2 @@
+tests/qapi-schema/struct-member-if-invalid.json: In struct 'Stru':
 tests/qapi-schema/struct-member-if-invalid.json:2: 'if' condition must be a string or a list of strings
diff --git a/tests/qapi-schema/struct-member-invalid-dict.err b/tests/qapi-schema/struct-member-invalid-dict.err
index 6a765bc668..e8b0ee72e0 100644
--- a/tests/qapi-schema/struct-member-invalid-dict.err
+++ b/tests/qapi-schema/struct-member-invalid-dict.err
@@ -1 +1,2 @@
+tests/qapi-schema/struct-member-invalid-dict.json: In struct 'foo':
 tests/qapi-schema/struct-member-invalid-dict.json:2: Key 'type' is missing from member '*a' of 'data' for struct 'foo'
diff --git a/tests/qapi-schema/struct-member-invalid.err b/tests/qapi-schema/struct-member-invalid.err
index 69a326d450..466c1af1d0 100644
--- a/tests/qapi-schema/struct-member-invalid.err
+++ b/tests/qapi-schema/struct-member-invalid.err
@@ -1 +1,2 @@
+tests/qapi-schema/struct-member-invalid.json: In struct 'foo':
 tests/qapi-schema/struct-member-invalid.json:1: Member 'a' of 'data' for struct 'foo' should be a type name
diff --git a/tests/qapi-schema/union-base-empty.err b/tests/qapi-schema/union-base-empty.err
index 9453720ede..1e24341819 100644
--- a/tests/qapi-schema/union-base-empty.err
+++ b/tests/qapi-schema/union-base-empty.err
@@ -1 +1,2 @@
+tests/qapi-schema/union-base-empty.json: In union 'TestUnion':
 tests/qapi-schema/union-base-empty.json:5: Discriminator 'type' is not a member of 'base'
diff --git a/tests/qapi-schema/union-base-no-discriminator.err b/tests/qapi-schema/union-base-no-discriminator.err
index 8b7a24260f..fa9343fb8e 100644
--- a/tests/qapi-schema/union-base-no-discriminator.err
+++ b/tests/qapi-schema/union-base-no-discriminator.err
@@ -1 +1,2 @@
+tests/qapi-schema/union-base-no-discriminator.json: In union 'TestUnion':
 tests/qapi-schema/union-base-no-discriminator.json:11: Simple union 'TestUnion' must not have a base
diff --git a/tests/qapi-schema/union-branch-case.err b/tests/qapi-schema/union-branch-case.err
index 11521901d8..8e81a2d0b6 100644
--- a/tests/qapi-schema/union-branch-case.err
+++ b/tests/qapi-schema/union-branch-case.err
@@ -1 +1,2 @@
+tests/qapi-schema/union-branch-case.json: In union 'NoWayThisWillGetWhitelisted':
 tests/qapi-schema/union-branch-case.json:2: 'Branch' (branch of NoWayThisWillGetWhitelisted) should not use uppercase
diff --git a/tests/qapi-schema/union-branch-if-invalid.err b/tests/qapi-schema/union-branch-if-invalid.err
index 607edee382..b49cf9b664 100644
--- a/tests/qapi-schema/union-branch-if-invalid.err
+++ b/tests/qapi-schema/union-branch-if-invalid.err
@@ -1 +1,2 @@
+tests/qapi-schema/union-branch-if-invalid.json: In union 'Uni':
 tests/qapi-schema/union-branch-if-invalid.json:4: 'if' condition '' makes no sense
diff --git a/tests/qapi-schema/union-branch-invalid-dict.err b/tests/qapi-schema/union-branch-invalid-dict.err
index 89f9b36791..5b8b68432c 100644
--- a/tests/qapi-schema/union-branch-invalid-dict.err
+++ b/tests/qapi-schema/union-branch-invalid-dict.err
@@ -1 +1,2 @@
+tests/qapi-schema/union-branch-invalid-dict.json: In union 'UnionInvalidBranch':
 tests/qapi-schema/union-branch-invalid-dict.json:2: Key 'type' is missing from member 'integer' of union 'UnionInvalidBranch'
diff --git a/tests/qapi-schema/union-clash-branches.err b/tests/qapi-schema/union-clash-branches.err
index e5b21135bb..145efebd9f 100644
--- a/tests/qapi-schema/union-clash-branches.err
+++ b/tests/qapi-schema/union-clash-branches.err
@@ -1 +1,2 @@
+tests/qapi-schema/union-clash-branches.json: In union 'TestUnion':
 tests/qapi-schema/union-clash-branches.json:4: 'a_b' (branch of TestUnion) collides with 'a-b' (branch of TestUnion)
diff --git a/tests/qapi-schema/union-empty.err b/tests/qapi-schema/union-empty.err
index d4241a38a2..75f2fa9f21 100644
--- a/tests/qapi-schema/union-empty.err
+++ b/tests/qapi-schema/union-empty.err
@@ -1 +1,2 @@
+tests/qapi-schema/union-empty.json: In union 'Union':
 tests/qapi-schema/union-empty.json:2: Union 'Union' has no branches
diff --git a/tests/qapi-schema/union-invalid-base.err b/tests/qapi-schema/union-invalid-base.err
index 03d7b97a93..be908709d2 100644
--- a/tests/qapi-schema/union-invalid-base.err
+++ b/tests/qapi-schema/union-invalid-base.err
@@ -1 +1,2 @@
+tests/qapi-schema/union-invalid-base.json: In union 'TestUnion':
 tests/qapi-schema/union-invalid-base.json:8: 'base' for union 'TestUnion' cannot use built-in type 'int'
diff --git a/tests/qapi-schema/union-optional-branch.err b/tests/qapi-schema/union-optional-branch.err
index 3ada1334dc..09320416ad 100644
--- a/tests/qapi-schema/union-optional-branch.err
+++ b/tests/qapi-schema/union-optional-branch.err
@@ -1 +1,2 @@
+tests/qapi-schema/union-optional-branch.json: In union 'Union':
 tests/qapi-schema/union-optional-branch.json:2: Member of union 'Union' does not allow optional name '*a'
diff --git a/tests/qapi-schema/union-unknown.err b/tests/qapi-schema/union-unknown.err
index 54fe456f9c..7c6cabb8cc 100644
--- a/tests/qapi-schema/union-unknown.err
+++ b/tests/qapi-schema/union-unknown.err
@@ -1 +1,2 @@
+tests/qapi-schema/union-unknown.json: In union 'Union':
 tests/qapi-schema/union-unknown.json:2: Member 'unknown' of union 'Union' uses unknown type 'MissingType'
-- 
2.21.0



^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH 05/25] qapi: Clean up member name case checking
  2019-09-24 13:28 [PATCH 00/25] qapi: Pay back some frontend technical debt Markus Armbruster
                   ` (3 preceding siblings ...)
  2019-09-24 13:28 ` [PATCH 04/25] qapi: Prefix frontend errors with an "in definition" line Markus Armbruster
@ 2019-09-24 13:28 ` Markus Armbruster
  2019-09-24 15:07   ` Eric Blake
  2019-09-24 13:28 ` [PATCH 06/25] qapi: Change frontend error messages to start with lower case Markus Armbruster
                   ` (19 subsequent siblings)
  24 siblings, 1 reply; 63+ messages in thread
From: Markus Armbruster @ 2019-09-24 13:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, mdroth

QAPISchemaMember.check_clash() checks for member names that map to the
same c_name().  Takes care of rejecting duplicate names.

It also checks a naming rule: no uppercase in member names.  That's a
rather odd place to do it.  Enforcing naming rules is
check_name_str()'s job.

qapi-code-gen.txt specifies the name case rule applies to the name as
it appears in the schema.  check_clash() checks c_name(name) instead.
No difference, as c_name() leaves alone case, but unclean.

Move the name case check into check_name_str(), less the c_name().
New argument @permit_upper suppresses it.  Pass permit_upper=True for
definitions (which are not members), and when the member's owner is
whitelisted with pragma name-case-whitelist.

Bonus: name-case-whitelist now applies to a union's inline base, too.
Update qapi/qapi-schema.json pragma to whitelist union CpuInfo instead
of CpuInfo's implicit base type's name q_obj_CpuInfo-base.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 qapi/qapi-schema.json                    |  2 +-
 scripts/qapi/common.py                   | 25 +++++++++++++-----------
 tests/qapi-schema/args-member-case.err   |  2 +-
 tests/qapi-schema/args-member-case.json  |  2 +-
 tests/qapi-schema/enum-member-case.err   |  2 +-
 tests/qapi-schema/union-branch-case.err  |  4 ++--
 tests/qapi-schema/union-branch-case.json |  4 ++--
 7 files changed, 22 insertions(+), 19 deletions(-)

diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json
index 920b03b0aa..9751b11f8f 100644
--- a/qapi/qapi-schema.json
+++ b/qapi/qapi-schema.json
@@ -71,7 +71,7 @@
         'QapiErrorClass',           # all members, visible through errors
         'UuidInfo',                 # UUID, visible through query-uuid
         'X86CPURegister32',         # all members, visible indirectly through qom-get
-        'q_obj_CpuInfo-base'        # CPU, visible through query-cpu
+        'CpuInfo'                   # CPU, visible through query-cpu
     ] } }
 
 # Documentation generated with qapi-gen.py is in source order, with
diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index f0e7d5ad34..ed4bff4479 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -704,8 +704,8 @@ valid_name = re.compile(r'^(__[a-zA-Z0-9.-]+_)?'
                         '[a-zA-Z][a-zA-Z0-9_-]*$')
 
 
-def check_name(info, source, name, allow_optional=False,
-               enum_member=False):
+def check_name(info, source, name,
+               allow_optional=False, enum_member=False, permit_upper=False):
     global valid_name
     membername = name
 
@@ -725,11 +725,14 @@ def check_name(info, source, name, allow_optional=False,
     if not valid_name.match(membername) or \
        c_name(membername, False).startswith('q_'):
         raise QAPISemError(info, "%s uses invalid name '%s'" % (source, name))
+    if not permit_upper and name.lower() != name:
+        raise QAPISemError(
+            info, "%s uses uppercase in name '%s'" % (source, name))
 
 
 def add_name(name, info, meta):
     global all_names
-    check_name(info, "'%s'" % meta, name)
+    check_name(info, "'%s'" % meta, name, permit_upper=True)
     # FIXME should reject names that differ only in '_' vs. '.'
     # vs. '-', because they're liable to clash in generated C.
     if name in all_names:
@@ -797,10 +800,12 @@ def check_type(info, source, value,
         raise QAPISemError(info,
                            "%s should be an object or type name" % source)
 
+    permit_upper = allow_dict in name_case_whitelist
+
     # value is a dictionary, check that each member is okay
     for (key, arg) in value.items():
         check_name(info, "Member of %s" % source, key,
-                   allow_optional=True)
+                   allow_optional=True, permit_upper=permit_upper)
         if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
             raise QAPISemError(info, "Member of %s uses reserved name '%s'"
                                % (source, key))
@@ -870,7 +875,7 @@ def check_union(expr, info):
     else:
         # The object must have a string or dictionary 'base'.
         check_type(info, "'base' for union '%s'" % name,
-                   base, allow_dict=True,
+                   base, allow_dict=name,
                    allow_metas=['struct'])
         if not base:
             raise QAPISemError(info, "Flat union '%s' must have a base"
@@ -982,13 +987,15 @@ def check_enum(expr, info):
         raise QAPISemError(info,
                            "Enum '%s' requires a string for 'prefix'" % name)
 
+    permit_upper = name in name_case_whitelist
+
     for member in members:
         check_known_keys(info, "member of enum '%s'" % name, member,
                          ['name'], ['if'])
         check_if(member, info)
         normalize_if(member)
         check_name(info, "Member of enum '%s'" % name, member['name'],
-                   enum_member=True)
+                   enum_member=True, permit_upper=permit_upper)
 
 
 def check_struct(expr, info):
@@ -997,7 +1004,7 @@ def check_struct(expr, info):
     features = expr.get('features')
 
     check_type(info, "'data' for struct '%s'" % name, members,
-               allow_dict=True)
+               allow_dict=name)
     check_type(info, "'base' for struct '%s'" % name, expr.get('base'),
                allow_metas=['struct'])
 
@@ -1555,10 +1562,6 @@ class QAPISchemaMember(object):
 
     def check_clash(self, info, seen):
         cname = c_name(self.name)
-        if (cname.lower() != cname
-                and self.defined_in not in name_case_whitelist):
-            raise QAPISemError(info,
-                               "%s should not use uppercase" % self.describe())
         if cname in seen:
             raise QAPISemError(info, "%s collides with %s" %
                                (self.describe(), seen[cname].describe()))
diff --git a/tests/qapi-schema/args-member-case.err b/tests/qapi-schema/args-member-case.err
index 725ba16192..da183957b2 100644
--- a/tests/qapi-schema/args-member-case.err
+++ b/tests/qapi-schema/args-member-case.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/args-member-case.json: In command 'no-way-this-will-get-whitelisted':
-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:2: Member of 'data' for command 'no-way-this-will-get-whitelisted' uses uppercase in name 'Arg'
diff --git a/tests/qapi-schema/args-member-case.json b/tests/qapi-schema/args-member-case.json
index 93439bee8b..e27c603548 100644
--- a/tests/qapi-schema/args-member-case.json
+++ b/tests/qapi-schema/args-member-case.json
@@ -1,2 +1,2 @@
-# Member names should be 'lower-case' unless the struct/command is whitelisted
+# Member names should be 'lower-case' unless the struct is whitelisted
 { 'command': 'no-way-this-will-get-whitelisted', 'data': { 'Arg': 'int' } }
diff --git a/tests/qapi-schema/enum-member-case.err b/tests/qapi-schema/enum-member-case.err
index f6c872d3bf..8f2007c86f 100644
--- a/tests/qapi-schema/enum-member-case.err
+++ b/tests/qapi-schema/enum-member-case.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/enum-member-case.json: In enum 'NoWayThisWillGetWhitelisted':
-tests/qapi-schema/enum-member-case.json:4: 'Value' (value of NoWayThisWillGetWhitelisted) should not use uppercase
+tests/qapi-schema/enum-member-case.json:4: Member of enum 'NoWayThisWillGetWhitelisted' uses uppercase in name 'Value'
diff --git a/tests/qapi-schema/union-branch-case.err b/tests/qapi-schema/union-branch-case.err
index 8e81a2d0b6..09313d7f83 100644
--- a/tests/qapi-schema/union-branch-case.err
+++ b/tests/qapi-schema/union-branch-case.err
@@ -1,2 +1,2 @@
-tests/qapi-schema/union-branch-case.json: In union 'NoWayThisWillGetWhitelisted':
-tests/qapi-schema/union-branch-case.json:2: 'Branch' (branch of NoWayThisWillGetWhitelisted) should not use uppercase
+tests/qapi-schema/union-branch-case.json: In union 'Uni':
+tests/qapi-schema/union-branch-case.json:2: Member of union 'Uni' uses uppercase in name 'Branch'
diff --git a/tests/qapi-schema/union-branch-case.json b/tests/qapi-schema/union-branch-case.json
index e6565dc3b3..b7894b75d6 100644
--- a/tests/qapi-schema/union-branch-case.json
+++ b/tests/qapi-schema/union-branch-case.json
@@ -1,2 +1,2 @@
-# Branch names should be 'lower-case' unless the union is whitelisted
-{ 'union': 'NoWayThisWillGetWhitelisted', 'data': { 'Branch': 'int' } }
+# Branch names should be 'lower-case'
+{ 'union': 'Uni', 'data': { 'Branch': 'int' } }
-- 
2.21.0



^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH 06/25] qapi: Change frontend error messages to start with lower case
  2019-09-24 13:28 [PATCH 00/25] qapi: Pay back some frontend technical debt Markus Armbruster
                   ` (4 preceding siblings ...)
  2019-09-24 13:28 ` [PATCH 05/25] qapi: Clean up member name case checking Markus Armbruster
@ 2019-09-24 13:28 ` Markus Armbruster
  2019-09-24 15:17   ` Eric Blake
  2019-09-24 13:28 ` [PATCH 07/25] qapi: Improve reporting of member name clashes Markus Armbruster
                   ` (18 subsequent siblings)
  24 siblings, 1 reply; 63+ messages in thread
From: Markus Armbruster @ 2019-09-24 13:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, mdroth

Starting error messages with a capital letter complicates things when
text can get interpolated both at the beginning and in the middle of
an error message.  The next patch will do that.  Switch to lower case
to keep it simpler.

For what it's worth, the GNU Coding Standards advise the message
"should not begin with a capital letter when it follows a program name
and/or file name, because that isn’t the beginning of a sentence. (The
sentence conceptually starts at the beginning of the line.)"

Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 scripts/qapi/common.py                        | 175 +++++++++---------
 tests/qapi-schema/alternate-any.err           |   2 +-
 tests/qapi-schema/alternate-array.err         |   2 +-
 tests/qapi-schema/alternate-base.err          |   2 +-
 .../alternate-conflict-bool-string.err        |   2 +-
 tests/qapi-schema/alternate-conflict-dict.err |   2 +-
 .../alternate-conflict-enum-bool.err          |   2 +-
 .../alternate-conflict-enum-int.err           |   2 +-
 .../alternate-conflict-num-string.err         |   2 +-
 .../qapi-schema/alternate-conflict-string.err |   2 +-
 tests/qapi-schema/alternate-empty.err         |   2 +-
 tests/qapi-schema/alternate-invalid-dict.err  |   2 +-
 tests/qapi-schema/alternate-nested.err        |   2 +-
 tests/qapi-schema/alternate-unknown.err       |   2 +-
 tests/qapi-schema/args-array-empty.err        |   2 +-
 tests/qapi-schema/args-array-unknown.err      |   2 +-
 tests/qapi-schema/args-member-array-bad.err   |   2 +-
 tests/qapi-schema/args-member-case.err        |   2 +-
 tests/qapi-schema/args-member-unknown.err     |   2 +-
 tests/qapi-schema/bad-type-int.err            |   2 +-
 tests/qapi-schema/base-cycle-direct.err       |   2 +-
 tests/qapi-schema/base-cycle-indirect.err     |   2 +-
 .../qapi-schema/doc-bad-alternate-member.err  |   2 +-
 tests/qapi-schema/doc-bad-command-arg.err     |   2 +-
 tests/qapi-schema/doc-bad-symbol.err          |   2 +-
 tests/qapi-schema/doc-bad-union-member.err    |   2 +-
 tests/qapi-schema/doc-before-include.err      |   2 +-
 tests/qapi-schema/doc-before-pragma.err       |   2 +-
 tests/qapi-schema/doc-duplicated-return.err   |   2 +-
 tests/qapi-schema/doc-duplicated-since.err    |   2 +-
 tests/qapi-schema/doc-empty-arg.err           |   2 +-
 tests/qapi-schema/doc-empty-section.err       |   2 +-
 tests/qapi-schema/doc-empty-symbol.err        |   2 +-
 tests/qapi-schema/doc-invalid-end.err         |   2 +-
 tests/qapi-schema/doc-invalid-end2.err        |   2 +-
 tests/qapi-schema/doc-invalid-start.err       |   2 +-
 tests/qapi-schema/doc-missing-colon.err       |   2 +-
 tests/qapi-schema/doc-missing-expr.err        |   2 +-
 tests/qapi-schema/doc-missing-space.err       |   2 +-
 tests/qapi-schema/doc-missing.err             |   2 +-
 tests/qapi-schema/doc-no-symbol.err           |   2 +-
 tests/qapi-schema/double-type.err             |   2 +-
 tests/qapi-schema/duplicate-key.err           |   2 +-
 tests/qapi-schema/enum-bad-member.err         |   2 +-
 tests/qapi-schema/enum-bad-name.err           |   2 +-
 tests/qapi-schema/enum-bad-prefix.err         |   2 +-
 .../qapi-schema/enum-dict-member-unknown.err  |   2 +-
 tests/qapi-schema/enum-int-member.err         |   2 +-
 tests/qapi-schema/enum-member-case.err        |   2 +-
 tests/qapi-schema/enum-missing-data.err       |   2 +-
 tests/qapi-schema/enum-wrong-data.err         |   2 +-
 tests/qapi-schema/escape-outside-string.err   |   2 +-
 tests/qapi-schema/escape-too-big.err          |   1 +
 tests/qapi-schema/event-boxed-empty.err       |   2 +-
 .../qapi-schema/event-member-invalid-dict.err |   2 +-
 tests/qapi-schema/event-nest-struct.err       |   2 +-
 tests/qapi-schema/features-bad-type.err       |   2 +-
 tests/qapi-schema/features-missing-name.err   |   2 +-
 tests/qapi-schema/features-name-bad-type.err  |   2 +-
 tests/qapi-schema/features-no-list.err        |   2 +-
 tests/qapi-schema/features-unknown-key.err    |   2 +-
 tests/qapi-schema/flat-union-array-branch.err |   2 +-
 .../flat-union-bad-discriminator.err          |   2 +-
 .../flat-union-discriminator-bad-name.err     |   2 +-
 tests/qapi-schema/flat-union-empty.err        |   2 +-
 .../flat-union-inline-invalid-dict.err        |   2 +-
 tests/qapi-schema/flat-union-inline.err       |   2 +-
 tests/qapi-schema/flat-union-int-branch.err   |   2 +-
 .../flat-union-invalid-branch-key.err         |   2 +-
 .../flat-union-invalid-discriminator.err      |   2 +-
 .../flat-union-invalid-if-discriminator.err   |   2 +-
 tests/qapi-schema/flat-union-no-base.err      |   2 +-
 .../flat-union-optional-discriminator.err     |   2 +-
 .../flat-union-string-discriminator.err       |   2 +-
 tests/qapi-schema/funny-char.err              |   2 +-
 tests/qapi-schema/funny-word.err              |   2 +-
 tests/qapi-schema/ident-with-escape.err       |   2 +-
 tests/qapi-schema/include-before-err.err      |   2 +-
 tests/qapi-schema/include-cycle.err           |   2 +-
 tests/qapi-schema/include-extra-junk.err      |   2 +-
 tests/qapi-schema/include-nested-err.err      |   2 +-
 tests/qapi-schema/include-non-file.err        |   2 +-
 tests/qapi-schema/include-self-cycle.err      |   2 +-
 tests/qapi-schema/leading-comma-list.err      |   2 +-
 tests/qapi-schema/leading-comma-object.err    |   2 +-
 tests/qapi-schema/missing-colon.err           |   2 +-
 tests/qapi-schema/missing-comma-list.err      |   2 +-
 tests/qapi-schema/missing-comma-object.err    |   2 +-
 tests/qapi-schema/missing-type.err            |   2 +-
 .../nested-struct-data-invalid-dict.err       |   2 +-
 tests/qapi-schema/nested-struct-data.err      |   2 +-
 tests/qapi-schema/non-objects.err             |   2 +-
 .../qapi-schema/pragma-doc-required-crap.err  |   2 +-
 tests/qapi-schema/pragma-extra-junk.err       |   2 +-
 .../pragma-name-case-whitelist-crap.err       |   2 +-
 tests/qapi-schema/pragma-non-dict.err         |   2 +-
 .../pragma-returns-whitelist-crap.err         |   2 +-
 tests/qapi-schema/pragma-unknown.err          |   2 +-
 tests/qapi-schema/quoted-structural-chars.err |   2 +-
 tests/qapi-schema/reserved-enum-q.err         |   2 +-
 tests/qapi-schema/reserved-member-has.err     |   2 +-
 tests/qapi-schema/reserved-member-q.err       |   2 +-
 tests/qapi-schema/reserved-member-u.err       |   2 +-
 .../reserved-member-underscore.err            |   2 +-
 tests/qapi-schema/string-code-point-127.err   |   2 +-
 tests/qapi-schema/string-code-point-31.err    |   2 +-
 tests/qapi-schema/string-control.err          |   1 +
 tests/qapi-schema/string-unclosed.err         |   1 +
 tests/qapi-schema/string-unicode.err          |   1 +
 .../struct-member-invalid-dict.err            |   2 +-
 tests/qapi-schema/struct-member-invalid.err   |   2 +-
 tests/qapi-schema/trailing-comma-list.err     |   2 +-
 tests/qapi-schema/trailing-comma-object.err   |   2 +-
 tests/qapi-schema/unclosed-list.err           |   2 +-
 tests/qapi-schema/unclosed-object.err         |   2 +-
 tests/qapi-schema/unclosed-string.err         |   2 +-
 tests/qapi-schema/union-base-empty.err        |   2 +-
 .../union-base-no-discriminator.err           |   2 +-
 tests/qapi-schema/union-branch-case.err       |   2 +-
 .../qapi-schema/union-branch-invalid-dict.err |   2 +-
 tests/qapi-schema/union-empty.err             |   2 +-
 tests/qapi-schema/union-optional-branch.err   |   2 +-
 tests/qapi-schema/union-unknown.err           |   2 +-
 tests/qapi-schema/unknown-escape.err          |   2 +-
 tests/qapi-schema/unknown-expr-key.err        |   2 +-
 125 files changed, 215 insertions(+), 204 deletions(-)
 create mode 100644 tests/qapi-schema/escape-too-big.err
 create mode 100644 tests/qapi-schema/string-control.err
 create mode 100644 tests/qapi-schema/string-unclosed.err
 create mode 100644 tests/qapi-schema/string-unicode.err

diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index ed4bff4479..23f1c8eece 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -207,7 +207,7 @@ class QAPIDoc(object):
             return
 
         if line[0] != ' ':
-            raise QAPIParseError(self._parser, "Missing space after #")
+            raise QAPIParseError(self._parser, "missing space after #")
         line = line[1:]
         self._append_line(line)
 
@@ -241,11 +241,11 @@ class QAPIDoc(object):
         # recognized, and get silently treated as ordinary text
         if not self.symbol and not self.body.text and line.startswith('@'):
             if not line.endswith(':'):
-                raise QAPIParseError(self._parser, "Line should end with ':'")
+                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")
+                raise QAPIParseError(self._parser, "invalid name")
         elif self.symbol:
             # This is a definition documentation block
             if name.startswith('@') and name.endswith(':'):
@@ -344,7 +344,7 @@ class QAPIDoc(object):
     def _start_symbol_section(self, symbols_dict, name):
         # FIXME invalid names other than the empty string aren't flagged
         if not name:
-            raise QAPIParseError(self._parser, "Invalid parameter name")
+            raise QAPIParseError(self._parser, "invalid parameter name")
         if name in symbols_dict:
             raise QAPIParseError(self._parser,
                                  "'%s' parameter name duplicated" % name)
@@ -362,7 +362,7 @@ class QAPIDoc(object):
     def _start_section(self, name=None):
         if name in ('Returns', 'Since') and self.has_section(name):
             raise QAPIParseError(self._parser,
-                                 "Duplicated '%s' section" % name)
+                                 "duplicated '%s' section" % name)
         self._end_section()
         self._section = QAPIDoc.Section(name)
         self.sections.append(self._section)
@@ -371,7 +371,7 @@ class QAPIDoc(object):
         if self._section:
             text = self._section.text = self._section.text.strip()
             if self._section.name and (not text or text.isspace()):
-                raise QAPIParseError(self._parser, "Empty doc section '%s'"
+                raise QAPIParseError(self._parser, "empty doc section '%s'"
                                      % self._section.name)
             self._section = None
 
@@ -400,7 +400,7 @@ class QAPIDoc(object):
         if bogus:
             raise QAPISemError(
                 self.info,
-                "The following documented members are not in "
+                "the following documented members are not in "
                 "the declaration: %s" % ", ".join(bogus))
 
 
@@ -432,11 +432,11 @@ class QAPISchemaParser(object):
             if 'include' in expr:
                 self.reject_expr_doc(cur_doc)
                 if len(expr) != 1:
-                    raise QAPISemError(info, "Invalid 'include' directive")
+                    raise QAPISemError(info, "invalid 'include' directive")
                 include = expr['include']
                 if not isinstance(include, str):
                     raise QAPISemError(info,
-                                       "Value of 'include' must be a string")
+                                       "value of 'include' must be a string")
                 incl_fname = os.path.join(os.path.dirname(self.fname),
                                           include)
                 self.exprs.append({'expr': {'include': incl_fname},
@@ -449,11 +449,11 @@ class QAPISchemaParser(object):
             elif "pragma" in expr:
                 self.reject_expr_doc(cur_doc)
                 if len(expr) != 1:
-                    raise QAPISemError(info, "Invalid 'pragma' directive")
+                    raise QAPISemError(info, "invalid 'pragma' directive")
                 pragma = expr['pragma']
                 if not isinstance(pragma, dict):
                     raise QAPISemError(
-                        info, "Value of 'pragma' must be an object")
+                        info, "value of 'pragma' must be an object")
                 for name, value in pragma.items():
                     self._pragma(name, value, info)
             else:
@@ -462,7 +462,7 @@ class QAPISchemaParser(object):
                 if cur_doc:
                     if not cur_doc.symbol:
                         raise QAPISemError(
-                            cur_doc.info, "Definition documentation required")
+                            cur_doc.info, "definition documentation required")
                     expr_elem['doc'] = cur_doc
                 self.exprs.append(expr_elem)
             cur_doc = None
@@ -473,7 +473,7 @@ class QAPISchemaParser(object):
         if doc and doc.symbol:
             raise QAPISemError(
                 doc.info,
-                "Documentation for '%s' is not followed by the definition"
+                "documentation for '%s' is not followed by the definition"
                 % doc.symbol)
 
     def _include(self, include, info, incl_fname, previously_included):
@@ -482,7 +482,7 @@ class QAPISchemaParser(object):
         inf = info
         while inf:
             if incl_abs_fname == os.path.abspath(inf.fname):
-                raise QAPISemError(info, "Inclusion loop for %s" % include)
+                raise QAPISemError(info, "inclusion loop for %s" % include)
             inf = inf.parent
 
         # skip multiple include of the same file
@@ -503,24 +503,24 @@ class QAPISchemaParser(object):
         if name == 'doc-required':
             if not isinstance(value, bool):
                 raise QAPISemError(info,
-                                   "Pragma 'doc-required' must be boolean")
+                                   "pragma 'doc-required' must be boolean")
             doc_required = value
         elif name == 'returns-whitelist':
             if (not isinstance(value, list)
                     or any([not isinstance(elt, str) for elt in value])):
-                raise QAPISemError(info,
-                                   "Pragma returns-whitelist must be"
-                                   " a list of strings")
+                raise QAPISemError(
+                    info,
+                    "pragma returns-whitelist must be a list of strings")
             returns_whitelist = value
         elif name == 'name-case-whitelist':
             if (not isinstance(value, list)
                     or any([not isinstance(elt, str) for elt in value])):
-                raise QAPISemError(info,
-                                   "Pragma name-case-whitelist must be"
-                                   " a list of strings")
+                raise QAPISemError(
+                    info,
+                    "pragma name-case-whitelist must be a list of strings")
             name_case_whitelist = value
         else:
-            raise QAPISemError(info, "Unknown pragma '%s'" % name)
+            raise QAPISemError(info, "unknown pragma '%s'" % name)
 
     def accept(self, skip_comment=True):
         while True:
@@ -547,13 +547,13 @@ class QAPISchemaParser(object):
                     ch = self.src[self.cursor]
                     self.cursor += 1
                     if ch == '\n':
-                        raise QAPIParseError(self, "Missing terminating \"'\"")
+                        raise QAPIParseError(self, "missing terminating \"'\"")
                     if esc:
                         # Note: we recognize only \\ because we have
                         # no use for funny characters in strings
                         if ch != '\\':
                             raise QAPIParseError(self,
-                                                 "Unknown escape \\%s" % ch)
+                                                 "unknown escape \\%s" % ch)
                         esc = False
                     elif ch == '\\':
                         esc = True
@@ -563,7 +563,7 @@ class QAPISchemaParser(object):
                         return
                     if ord(ch) < 32 or ord(ch) >= 127:
                         raise QAPIParseError(
-                            self, "Funny character in string")
+                            self, "funny character in string")
                     string += ch
             elif self.src.startswith('true', self.pos):
                 self.val = True
@@ -584,7 +584,7 @@ class QAPISchemaParser(object):
                 # character
                 match = re.match('[^[\\]{}:,\\s\'"]+',
                                  self.src[self.cursor-1:])
-                raise QAPIParseError(self, "Stray '%s'" % match.group(0))
+                raise QAPIParseError(self, "stray '%s'" % match.group(0))
 
     def get_members(self):
         expr = OrderedDict()
@@ -592,24 +592,24 @@ class QAPISchemaParser(object):
             self.accept()
             return expr
         if self.tok != "'":
-            raise QAPIParseError(self, "Expected string or '}'")
+            raise QAPIParseError(self, "expected string or '}'")
         while True:
             key = self.val
             self.accept()
             if self.tok != ':':
-                raise QAPIParseError(self, "Expected ':'")
+                raise QAPIParseError(self, "expected ':'")
             self.accept()
             if key in expr:
-                raise QAPIParseError(self, "Duplicate key '%s'" % key)
+                raise QAPIParseError(self, "duplicate key '%s'" % key)
             expr[key] = self.get_expr(True)
             if self.tok == '}':
                 self.accept()
                 return expr
             if self.tok != ',':
-                raise QAPIParseError(self, "Expected ',' or '}'")
+                raise QAPIParseError(self, "expected ',' or '}'")
             self.accept()
             if self.tok != "'":
-                raise QAPIParseError(self, "Expected string")
+                raise QAPIParseError(self, "expected string")
 
     def get_values(self):
         expr = []
@@ -618,19 +618,19 @@ class QAPISchemaParser(object):
             return expr
         if self.tok not in "{['tfn":
             raise QAPIParseError(
-                self, "Expected '{', '[', ']', string, boolean or 'null'")
+                self, "expected '{', '[', ']', string, boolean or 'null'")
         while True:
             expr.append(self.get_expr(True))
             if self.tok == ']':
                 self.accept()
                 return expr
             if self.tok != ',':
-                raise QAPIParseError(self, "Expected ',' or ']'")
+                raise QAPIParseError(self, "expected ',' or ']'")
             self.accept()
 
     def get_expr(self, nested):
         if self.tok != '{' and not nested:
-            raise QAPIParseError(self, "Expected '{'")
+            raise QAPIParseError(self, "expected '{'")
         if self.tok == '{':
             self.accept()
             expr = self.get_members()
@@ -642,13 +642,13 @@ class QAPISchemaParser(object):
             self.accept()
         else:
             raise QAPIParseError(
-                self, "Expected '{', '[', string, boolean or 'null'")
+                self, "expected '{', '[', string, boolean or 'null'")
         return expr
 
     def get_doc(self, info):
         if self.val != '##':
-            raise QAPIParseError(self, "Junk after '##' at start of "
-                                 "documentation comment")
+            raise QAPIParseError(
+                self, "junk after '##' at start of documentation comment")
 
         doc = QAPIDoc(self, info)
         self.accept(False)
@@ -656,8 +656,9 @@ class QAPISchemaParser(object):
             if self.val.startswith('##'):
                 # End of doc comment
                 if self.val != '##':
-                    raise QAPIParseError(self, "Junk after '##' at end of "
-                                         "documentation comment")
+                    raise QAPIParseError(
+                        self,
+                        "junk after '##' at end of documentation comment")
                 doc.end_comment()
                 self.accept()
                 return doc
@@ -665,7 +666,7 @@ class QAPISchemaParser(object):
                 doc.append(self.val)
             self.accept(False)
 
-        raise QAPIParseError(self, "Documentation comment must end with '##'")
+        raise QAPIParseError(self, "documentation comment must end with '##'")
 
 
 #
@@ -804,18 +805,18 @@ def check_type(info, source, value,
 
     # value is a dictionary, check that each member is okay
     for (key, arg) in value.items():
-        check_name(info, "Member of %s" % source, key,
+        check_name(info, "member of %s" % source, key,
                    allow_optional=True, permit_upper=permit_upper)
         if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
-            raise QAPISemError(info, "Member of %s uses reserved name '%s'"
-                               % (source, key))
+            raise QAPISemError(
+                info, "member of %s uses reserved name '%s'" % (source, key))
         # Todo: allow dictionaries to represent default values of
         # an optional argument.
         check_known_keys(info, "member '%s' of %s" % (key, source),
                          arg, ['type'], ['if'])
         check_if(arg, info)
         normalize_if(arg)
-        check_type(info, "Member '%s' of %s" % (key, source),
+        check_type(info, "member '%s' of %s" % (key, source),
                    arg['type'], allow_array=True,
                    allow_metas=['built-in', 'union', 'alternate', 'struct',
                                 'enum'])
@@ -868,8 +869,8 @@ def check_union(expr, info):
         enum_values = members.keys()
         allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
         if base is not None:
-            raise QAPISemError(info, "Simple union '%s' must not have a base" %
-                               name)
+            raise QAPISemError(
+                info, "simple union '%s' must not have a base" % name)
 
     # Else, it's a flat union.
     else:
@@ -878,46 +879,47 @@ def check_union(expr, info):
                    base, allow_dict=name,
                    allow_metas=['struct'])
         if not base:
-            raise QAPISemError(info, "Flat union '%s' must have a base"
+            raise QAPISemError(info, "flat union '%s' must have a base"
                                % name)
         base_members = find_base_members(base)
         assert base_members is not None
 
         # The value of member 'discriminator' must name a non-optional
         # member of the base struct.
-        check_name(info, "Discriminator of flat union '%s'" % name,
+        check_name(info, "discriminator of flat union '%s'" % name,
                    discriminator)
         discriminator_value = base_members.get(discriminator)
         if not discriminator_value:
             raise QAPISemError(info,
-                               "Discriminator '%s' is not a member of 'base'"
+                               "discriminator '%s' is not a member of 'base'"
                                % discriminator)
         if discriminator_value.get('if'):
             raise QAPISemError(
                 info,
-                "The discriminator '%s' for union %s must not be conditional"
+                "the discriminator '%s' for union %s must not be conditional"
                 % (discriminator, name))
         enum_define = enum_types.get(discriminator_value['type'])
         # Do not allow string discriminator
         if not enum_define:
-            raise QAPISemError(info,
-                               "Discriminator '%s' must be of enumeration "
-                               "type" % discriminator)
+            raise QAPISemError(
+                info,
+                "discriminator '%s' must be of enumeration type"
+                % discriminator)
         enum_values = enum_get_names(enum_define)
         allow_metas = ['struct']
 
     if (len(enum_values) == 0):
-        raise QAPISemError(info, "Union '%s' has no branches" % name)
+        raise QAPISemError(info, "union '%s' has no branches" % name)
 
     for (key, value) in members.items():
-        check_name(info, "Member of union '%s'" % name, key)
+        check_name(info, "member of union '%s'" % name, key)
 
         check_known_keys(info, "member '%s' of union '%s'" % (key, name),
                          value, ['type'], ['if'])
         check_if(value, info)
         normalize_if(value)
         # Each value must name a known type
-        check_type(info, "Member '%s' of union '%s'" % (key, name),
+        check_type(info, "member '%s' of union '%s'" % (key, name),
                    value['type'],
                    allow_array=not base, allow_metas=allow_metas)
 
@@ -925,10 +927,10 @@ def check_union(expr, info):
         # of 'data' must also be members of the enum type.
         if discriminator is not None:
             if key not in enum_values:
-                raise QAPISemError(info,
-                                   "Discriminator value '%s' is not found in "
-                                   "enum '%s'"
-                                   % (key, enum_define['enum']))
+                raise QAPISemError(
+                    info,
+                    "discriminator value '%s' is not found in enum '%s'"
+                    % (key, enum_define['enum']))
 
 
 def check_alternate(expr, info):
@@ -938,9 +940,9 @@ def check_alternate(expr, info):
 
     if len(members) == 0:
         raise QAPISemError(info,
-                           "Alternate '%s' cannot have empty 'data'" % name)
+                           "alternate '%s' cannot have empty 'data'" % name)
     for (key, value) in members.items():
-        check_name(info, "Member of alternate '%s'" % name, key)
+        check_name(info, "member of alternate '%s'" % name, key)
         check_known_keys(info,
                          "member '%s' of alternate '%s'" % (key, name),
                          value, ['type'], ['if'])
@@ -949,12 +951,14 @@ def check_alternate(expr, info):
         typ = value['type']
 
         # Ensure alternates have no type conflicts.
-        check_type(info, "Member '%s' of alternate '%s'" % (key, name), typ,
+        check_type(info, "member '%s' of alternate '%s'" % (key, name), typ,
                    allow_metas=['built-in', 'union', 'struct', 'enum'])
         qtype = find_alternate_member_qtype(typ)
         if not qtype:
-            raise QAPISemError(info, "Alternate '%s' member '%s' cannot use "
-                               "type '%s'" % (name, key, typ))
+            raise QAPISemError(
+                info,
+                "alternate '%s' member '%s' cannot use type '%s'"
+                % (name, key, typ))
         conflicting = set([qtype])
         if qtype == 'QTYPE_QSTRING':
             enum_expr = enum_types.get(typ)
@@ -969,9 +973,11 @@ def check_alternate(expr, info):
                 conflicting.add('QTYPE_QBOOL')
         for qt in conflicting:
             if qt in types_seen:
-                raise QAPISemError(info, "Alternate '%s' member '%s' can't "
-                                   "be distinguished from member '%s'"
-                                   % (name, key, types_seen[qt]))
+                raise QAPISemError(
+                    info,
+                    "alternate '%s' member '%s' can't be distinguished "
+                    "from member '%s'"
+                    % (name, key, types_seen[qt]))
             types_seen[qt] = key
 
 
@@ -982,10 +988,10 @@ def check_enum(expr, info):
 
     if not isinstance(members, list):
         raise QAPISemError(info,
-                           "Enum '%s' requires an array for 'data'" % name)
+                           "enum '%s' requires an array for 'data'" % name)
     if prefix is not None and not isinstance(prefix, str):
         raise QAPISemError(info,
-                           "Enum '%s' requires a string for 'prefix'" % name)
+                           "enum '%s' requires a string for 'prefix'" % name)
 
     permit_upper = name in name_case_whitelist
 
@@ -994,7 +1000,7 @@ def check_enum(expr, info):
                          ['name'], ['if'])
         check_if(member, info)
         normalize_if(member)
-        check_name(info, "Member of enum '%s'" % name, member['name'],
+        check_name(info, "member of enum '%s'" % name, member['name'],
                    enum_member=True, permit_upper=permit_upper)
 
 
@@ -1010,9 +1016,8 @@ def check_struct(expr, info):
 
     if features:
         if not isinstance(features, list):
-            raise QAPISemError(info,
-                               "Struct '%s' requires an array for 'features'" %
-                               name)
+            raise QAPISemError(
+                info, "struct '%s' requires an array for 'features'" % name)
         for f in features:
             assert isinstance(f, dict)
             check_known_keys(info, "feature of struct %s" % name, f,
@@ -1020,7 +1025,7 @@ def check_struct(expr, info):
 
             check_if(f, info)
             normalize_if(f)
-            check_name(info, "Feature of struct %s" % name, f['name'])
+            check_name(info, "feature of struct %s" % name, f['name'])
 
 
 def check_known_keys(info, source, value, required, optional):
@@ -1030,13 +1035,13 @@ def check_known_keys(info, source, value, required, optional):
 
     missing = set(required) - set(value)
     if missing:
-        raise QAPISemError(info, "Key%s %s %s missing from %s"
+        raise QAPISemError(info, "key%s %s %s missing from %s"
                            % ('s' if len(missing) > 1 else '', pprint(missing),
                               'are' if len(missing) > 1 else 'is', source))
     allowed = set(required + optional)
     unknown = set(value) - allowed
     if unknown:
-        raise QAPISemError(info, "Unknown key%s %s in %s\nValid keys are %s."
+        raise QAPISemError(info, "unknown key%s %s in %s\nValid keys are %s."
                            % ('s' if len(unknown) > 1 else '', pprint(unknown),
                               source, pprint(allowed)))
 
@@ -1106,7 +1111,7 @@ def check_exprs(exprs):
 
         if not doc and doc_required:
             raise QAPISemError(info,
-                               "Definition missing documentation comment")
+                               "definition missing documentation comment")
 
         if 'enum' in expr:
             meta = 'enum'
@@ -1142,14 +1147,16 @@ def check_exprs(exprs):
             check_keys(expr, info, 'event', [], ['data', 'boxed', 'if'])
             normalize_members(expr.get('data'))
         else:
-            raise QAPISemError(info, "Expression is missing metatype")
+            raise QAPISemError(info, "expression is missing metatype")
         normalize_if(expr)
         name = expr[meta]
         add_name(name, info, meta)
         info.set_defn(meta, name)
         if doc and doc.symbol != name:
-            raise QAPISemError(info, "Definition of '%s' follows documentation"
-                               " for '%s'" % (name, doc.symbol))
+            raise QAPISemError(
+                info,
+                "definition of '%s' follows documentation for '%s'"
+                % (name, doc.symbol))
 
     # Validate that exprs make sense
     for expr_elem in exprs:
@@ -1462,7 +1469,7 @@ class QAPISchemaObjectType(QAPISchemaType):
         if self._checked:
             # Recursed: C struct contains itself
             raise QAPISemError(self.info,
-                               "Object %s contains itself" % self.name)
+                               "object %s contains itself" % self.name)
 
         QAPISchemaType.check(self, schema)
         assert self._checked and self.members is None
@@ -1734,7 +1741,7 @@ class QAPISchemaCommand(QAPISchemaEntity):
             assert isinstance(self.arg_type, QAPISchemaObjectType)
             assert not self.arg_type.variants or self.boxed
         elif self.boxed:
-            raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
+            raise QAPISemError(self.info, "use of 'boxed' requires 'data'")
         if self._ret_type_name:
             self.ret_type = schema.lookup_type(self._ret_type_name)
             assert isinstance(self.ret_type, QAPISchemaType)
@@ -1763,7 +1770,7 @@ class QAPISchemaEvent(QAPISchemaEntity):
             assert isinstance(self.arg_type, QAPISchemaObjectType)
             assert not self.arg_type.variants or self.boxed
         elif self.boxed:
-            raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
+            raise QAPISemError(self.info, "use of 'boxed' requires 'data'")
 
     def visit(self, visitor):
         QAPISchemaEntity.visit(self, visitor)
diff --git a/tests/qapi-schema/alternate-any.err b/tests/qapi-schema/alternate-any.err
index 177a11cc12..5314760775 100644
--- a/tests/qapi-schema/alternate-any.err
+++ b/tests/qapi-schema/alternate-any.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/alternate-any.json: In alternate 'Alt':
-tests/qapi-schema/alternate-any.json:2: Alternate 'Alt' member 'one' cannot use type 'any'
+tests/qapi-schema/alternate-any.json:2: alternate 'Alt' member 'one' cannot use type 'any'
diff --git a/tests/qapi-schema/alternate-array.err b/tests/qapi-schema/alternate-array.err
index f16f266c76..a72e4b274d 100644
--- a/tests/qapi-schema/alternate-array.err
+++ b/tests/qapi-schema/alternate-array.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/alternate-array.json: In alternate 'Alt':
-tests/qapi-schema/alternate-array.json:5: Member 'two' of alternate 'Alt' cannot be an array
+tests/qapi-schema/alternate-array.json:5: member 'two' of alternate 'Alt' cannot be an array
diff --git a/tests/qapi-schema/alternate-base.err b/tests/qapi-schema/alternate-base.err
index ebe05bc898..4c9158db02 100644
--- a/tests/qapi-schema/alternate-base.err
+++ b/tests/qapi-schema/alternate-base.err
@@ -1,2 +1,2 @@
-tests/qapi-schema/alternate-base.json:4: Unknown key 'base' in alternate 'Alt'
+tests/qapi-schema/alternate-base.json:4: unknown key 'base' in alternate 'Alt'
 Valid keys are 'alternate', 'data', 'if'.
diff --git a/tests/qapi-schema/alternate-conflict-bool-string.err b/tests/qapi-schema/alternate-conflict-bool-string.err
index edfd36b7ad..e2b89f3cac 100644
--- a/tests/qapi-schema/alternate-conflict-bool-string.err
+++ b/tests/qapi-schema/alternate-conflict-bool-string.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/alternate-conflict-bool-string.json: In alternate 'Alt':
-tests/qapi-schema/alternate-conflict-bool-string.json:2: Alternate 'Alt' member 'two' can't be distinguished from member 'one'
+tests/qapi-schema/alternate-conflict-bool-string.json:2: alternate 'Alt' member 'two' can't be distinguished from member 'one'
diff --git a/tests/qapi-schema/alternate-conflict-dict.err b/tests/qapi-schema/alternate-conflict-dict.err
index 61ae93273a..e3f7d9072d 100644
--- a/tests/qapi-schema/alternate-conflict-dict.err
+++ b/tests/qapi-schema/alternate-conflict-dict.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/alternate-conflict-dict.json: In alternate 'Alt':
-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:6: alternate 'Alt' member 'two' can't be distinguished from member 'one'
diff --git a/tests/qapi-schema/alternate-conflict-enum-bool.err b/tests/qapi-schema/alternate-conflict-enum-bool.err
index b006d1efbc..d0fe1433fc 100644
--- a/tests/qapi-schema/alternate-conflict-enum-bool.err
+++ b/tests/qapi-schema/alternate-conflict-enum-bool.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/alternate-conflict-enum-bool.json: In alternate 'Alt':
-tests/qapi-schema/alternate-conflict-enum-bool.json:4: Alternate 'Alt' member 'two' can't be distinguished from member 'one'
+tests/qapi-schema/alternate-conflict-enum-bool.json:4: alternate 'Alt' member 'two' can't be distinguished from member 'one'
diff --git a/tests/qapi-schema/alternate-conflict-enum-int.err b/tests/qapi-schema/alternate-conflict-enum-int.err
index 817b5c0191..866428b1d1 100644
--- a/tests/qapi-schema/alternate-conflict-enum-int.err
+++ b/tests/qapi-schema/alternate-conflict-enum-int.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/alternate-conflict-enum-int.json: In alternate 'Alt':
-tests/qapi-schema/alternate-conflict-enum-int.json:4: Alternate 'Alt' member 'two' can't be distinguished from member 'one'
+tests/qapi-schema/alternate-conflict-enum-int.json:4: alternate 'Alt' member 'two' can't be distinguished from member 'one'
diff --git a/tests/qapi-schema/alternate-conflict-num-string.err b/tests/qapi-schema/alternate-conflict-num-string.err
index abc188e0a6..d00975453e 100644
--- a/tests/qapi-schema/alternate-conflict-num-string.err
+++ b/tests/qapi-schema/alternate-conflict-num-string.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/alternate-conflict-num-string.json: In alternate 'Alt':
-tests/qapi-schema/alternate-conflict-num-string.json:2: Alternate 'Alt' member 'two' can't be distinguished from member 'one'
+tests/qapi-schema/alternate-conflict-num-string.json:2: alternate 'Alt' member 'two' can't be distinguished from member 'one'
diff --git a/tests/qapi-schema/alternate-conflict-string.err b/tests/qapi-schema/alternate-conflict-string.err
index 77ccd80cd7..0231f4fed1 100644
--- a/tests/qapi-schema/alternate-conflict-string.err
+++ b/tests/qapi-schema/alternate-conflict-string.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/alternate-conflict-string.json: In alternate 'Alt':
-tests/qapi-schema/alternate-conflict-string.json:2: Alternate 'Alt' member 'two' can't be distinguished from member 'one'
+tests/qapi-schema/alternate-conflict-string.json:2: alternate 'Alt' member 'two' can't be distinguished from member 'one'
diff --git a/tests/qapi-schema/alternate-empty.err b/tests/qapi-schema/alternate-empty.err
index 9daac031e4..db92d70f10 100644
--- a/tests/qapi-schema/alternate-empty.err
+++ b/tests/qapi-schema/alternate-empty.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/alternate-empty.json: In alternate 'Alt':
-tests/qapi-schema/alternate-empty.json:2: Alternate 'Alt' cannot have empty 'data'
+tests/qapi-schema/alternate-empty.json:2: alternate 'Alt' cannot have empty 'data'
diff --git a/tests/qapi-schema/alternate-invalid-dict.err b/tests/qapi-schema/alternate-invalid-dict.err
index 701db8cdce..f85b941750 100644
--- a/tests/qapi-schema/alternate-invalid-dict.err
+++ b/tests/qapi-schema/alternate-invalid-dict.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/alternate-invalid-dict.json: In alternate 'Alt':
-tests/qapi-schema/alternate-invalid-dict.json:2: Key 'type' is missing from member 'two' of alternate 'Alt'
+tests/qapi-schema/alternate-invalid-dict.json:2: key 'type' is missing from member 'two' of alternate 'Alt'
diff --git a/tests/qapi-schema/alternate-nested.err b/tests/qapi-schema/alternate-nested.err
index 4ab5328025..03fb9fb212 100644
--- a/tests/qapi-schema/alternate-nested.err
+++ b/tests/qapi-schema/alternate-nested.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/alternate-nested.json: In alternate 'Alt2':
-tests/qapi-schema/alternate-nested.json:4: Member 'nested' of alternate 'Alt2' cannot use alternate type 'Alt1'
+tests/qapi-schema/alternate-nested.json:4: member 'nested' of alternate 'Alt2' cannot use alternate type 'Alt1'
diff --git a/tests/qapi-schema/alternate-unknown.err b/tests/qapi-schema/alternate-unknown.err
index 5bd473325e..7a533b2b74 100644
--- a/tests/qapi-schema/alternate-unknown.err
+++ b/tests/qapi-schema/alternate-unknown.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/alternate-unknown.json: In alternate 'Alt':
-tests/qapi-schema/alternate-unknown.json:2: Member 'unknown' of alternate 'Alt' uses unknown type 'MissingType'
+tests/qapi-schema/alternate-unknown.json:2: member 'unknown' of alternate 'Alt' uses unknown type 'MissingType'
diff --git a/tests/qapi-schema/args-array-empty.err b/tests/qapi-schema/args-array-empty.err
index fb95f0c4de..fe1480671b 100644
--- a/tests/qapi-schema/args-array-empty.err
+++ b/tests/qapi-schema/args-array-empty.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/args-array-empty.json: In command 'oops':
-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:2: member 'empty' of 'data' for command 'oops': array type must contain single type name
diff --git a/tests/qapi-schema/args-array-unknown.err b/tests/qapi-schema/args-array-unknown.err
index c88a5612b2..57b0d8972e 100644
--- a/tests/qapi-schema/args-array-unknown.err
+++ b/tests/qapi-schema/args-array-unknown.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/args-array-unknown.json: In command 'oops':
-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:2: member 'array' of 'data' for command 'oops' uses unknown type 'NoSuchType'
diff --git a/tests/qapi-schema/args-member-array-bad.err b/tests/qapi-schema/args-member-array-bad.err
index 81e1e95523..89b8b1ce01 100644
--- a/tests/qapi-schema/args-member-array-bad.err
+++ b/tests/qapi-schema/args-member-array-bad.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/args-member-array-bad.json: In command 'oops':
-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:2: member 'member' of 'data' for command 'oops': array type must contain single type name
diff --git a/tests/qapi-schema/args-member-case.err b/tests/qapi-schema/args-member-case.err
index da183957b2..faa8168d36 100644
--- a/tests/qapi-schema/args-member-case.err
+++ b/tests/qapi-schema/args-member-case.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/args-member-case.json: In command 'no-way-this-will-get-whitelisted':
-tests/qapi-schema/args-member-case.json:2: Member of 'data' for command 'no-way-this-will-get-whitelisted' uses uppercase in name 'Arg'
+tests/qapi-schema/args-member-case.json:2: member of 'data' for command 'no-way-this-will-get-whitelisted' uses uppercase in name 'Arg'
diff --git a/tests/qapi-schema/args-member-unknown.err b/tests/qapi-schema/args-member-unknown.err
index 9d43e54ca9..168e24a4b8 100644
--- a/tests/qapi-schema/args-member-unknown.err
+++ b/tests/qapi-schema/args-member-unknown.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/args-member-unknown.json: In command 'oops':
-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:2: member 'member' of 'data' for command 'oops' uses unknown type 'NoSuchType'
diff --git a/tests/qapi-schema/bad-type-int.err b/tests/qapi-schema/bad-type-int.err
index 9b2c12c1eb..7f5916ea29 100644
--- a/tests/qapi-schema/bad-type-int.err
+++ b/tests/qapi-schema/bad-type-int.err
@@ -1 +1 @@
-tests/qapi-schema/bad-type-int.json:3:13: Stray '123'
+tests/qapi-schema/bad-type-int.json:3:13: stray '123'
diff --git a/tests/qapi-schema/base-cycle-direct.err b/tests/qapi-schema/base-cycle-direct.err
index 52c21bc2b8..233e4b8952 100644
--- a/tests/qapi-schema/base-cycle-direct.err
+++ b/tests/qapi-schema/base-cycle-direct.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/base-cycle-direct.json: In struct 'Loopy':
-tests/qapi-schema/base-cycle-direct.json:2: Object Loopy contains itself
+tests/qapi-schema/base-cycle-direct.json:2: object Loopy contains itself
diff --git a/tests/qapi-schema/base-cycle-indirect.err b/tests/qapi-schema/base-cycle-indirect.err
index 1f60cd78f6..4472f30ba1 100644
--- a/tests/qapi-schema/base-cycle-indirect.err
+++ b/tests/qapi-schema/base-cycle-indirect.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/base-cycle-indirect.json: In struct 'Base1':
-tests/qapi-schema/base-cycle-indirect.json:2: Object Base1 contains itself
+tests/qapi-schema/base-cycle-indirect.json:2: object Base1 contains itself
diff --git a/tests/qapi-schema/doc-bad-alternate-member.err b/tests/qapi-schema/doc-bad-alternate-member.err
index 387f7824da..19a1ffd76e 100644
--- a/tests/qapi-schema/doc-bad-alternate-member.err
+++ b/tests/qapi-schema/doc-bad-alternate-member.err
@@ -1 +1 @@
-tests/qapi-schema/doc-bad-alternate-member.json:3: The following documented members are not in the declaration: aa, bb
+tests/qapi-schema/doc-bad-alternate-member.json:3: the following documented members are not in the declaration: aa, bb
diff --git a/tests/qapi-schema/doc-bad-command-arg.err b/tests/qapi-schema/doc-bad-command-arg.err
index 8075b146ae..6962ebed69 100644
--- a/tests/qapi-schema/doc-bad-command-arg.err
+++ b/tests/qapi-schema/doc-bad-command-arg.err
@@ -1 +1 @@
-tests/qapi-schema/doc-bad-command-arg.json:3: The following documented members are not in the declaration: b
+tests/qapi-schema/doc-bad-command-arg.json:3: the following documented members are not in the declaration: b
diff --git a/tests/qapi-schema/doc-bad-symbol.err b/tests/qapi-schema/doc-bad-symbol.err
index 375cdff939..205d0ed619 100644
--- a/tests/qapi-schema/doc-bad-symbol.err
+++ b/tests/qapi-schema/doc-bad-symbol.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/doc-bad-symbol.json: In command 'foo':
-tests/qapi-schema/doc-bad-symbol.json:6: Definition of 'foo' follows documentation for 'food'
+tests/qapi-schema/doc-bad-symbol.json:6: definition of 'foo' follows documentation for 'food'
diff --git a/tests/qapi-schema/doc-bad-union-member.err b/tests/qapi-schema/doc-bad-union-member.err
index 4b016df7ff..da3cd806e3 100644
--- a/tests/qapi-schema/doc-bad-union-member.err
+++ b/tests/qapi-schema/doc-bad-union-member.err
@@ -1 +1 @@
-tests/qapi-schema/doc-bad-union-member.json:3: The following documented members are not in the declaration: a, b
+tests/qapi-schema/doc-bad-union-member.json:3: the following documented members are not in the declaration: a, b
diff --git a/tests/qapi-schema/doc-before-include.err b/tests/qapi-schema/doc-before-include.err
index a649d38a63..e5566f11e9 100644
--- a/tests/qapi-schema/doc-before-include.err
+++ b/tests/qapi-schema/doc-before-include.err
@@ -1 +1 @@
-tests/qapi-schema/doc-before-include.json:3: Documentation for 'foo' is not followed by the definition
+tests/qapi-schema/doc-before-include.json:3: documentation for 'foo' is not followed by the definition
diff --git a/tests/qapi-schema/doc-before-pragma.err b/tests/qapi-schema/doc-before-pragma.err
index c0fb0660d1..8a97ebb578 100644
--- a/tests/qapi-schema/doc-before-pragma.err
+++ b/tests/qapi-schema/doc-before-pragma.err
@@ -1 +1 @@
-tests/qapi-schema/doc-before-pragma.json:3: Documentation for 'foo' is not followed by the definition
+tests/qapi-schema/doc-before-pragma.json:3: documentation for 'foo' is not followed by the definition
diff --git a/tests/qapi-schema/doc-duplicated-return.err b/tests/qapi-schema/doc-duplicated-return.err
index e48039f8e5..7631933093 100644
--- a/tests/qapi-schema/doc-duplicated-return.err
+++ b/tests/qapi-schema/doc-duplicated-return.err
@@ -1 +1 @@
-tests/qapi-schema/doc-duplicated-return.json:7:1: Duplicated 'Returns' section
+tests/qapi-schema/doc-duplicated-return.json:7:1: duplicated 'Returns' section
diff --git a/tests/qapi-schema/doc-duplicated-since.err b/tests/qapi-schema/doc-duplicated-since.err
index 3fb890744a..5ee15ae2a1 100644
--- a/tests/qapi-schema/doc-duplicated-since.err
+++ b/tests/qapi-schema/doc-duplicated-since.err
@@ -1 +1 @@
-tests/qapi-schema/doc-duplicated-since.json:7:1: Duplicated 'Since' section
+tests/qapi-schema/doc-duplicated-since.json:7:1: duplicated 'Since' section
diff --git a/tests/qapi-schema/doc-empty-arg.err b/tests/qapi-schema/doc-empty-arg.err
index 2895518fa7..3c78a37ae1 100644
--- a/tests/qapi-schema/doc-empty-arg.err
+++ b/tests/qapi-schema/doc-empty-arg.err
@@ -1 +1 @@
-tests/qapi-schema/doc-empty-arg.json:5:1: Invalid parameter name
+tests/qapi-schema/doc-empty-arg.json:5:1: invalid parameter name
diff --git a/tests/qapi-schema/doc-empty-section.err b/tests/qapi-schema/doc-empty-section.err
index b61e4a7886..f6586c566f 100644
--- a/tests/qapi-schema/doc-empty-section.err
+++ b/tests/qapi-schema/doc-empty-section.err
@@ -1 +1 @@
-tests/qapi-schema/doc-empty-section.json:7:1: Empty doc section 'Note'
+tests/qapi-schema/doc-empty-section.json:7:1: empty doc section 'Note'
diff --git a/tests/qapi-schema/doc-empty-symbol.err b/tests/qapi-schema/doc-empty-symbol.err
index 1936ad094f..2dcddca7f6 100644
--- a/tests/qapi-schema/doc-empty-symbol.err
+++ b/tests/qapi-schema/doc-empty-symbol.err
@@ -1 +1 @@
-tests/qapi-schema/doc-empty-symbol.json:4:1: Invalid name
+tests/qapi-schema/doc-empty-symbol.json:4:1: invalid name
diff --git a/tests/qapi-schema/doc-invalid-end.err b/tests/qapi-schema/doc-invalid-end.err
index 2bda28cb54..6345aa6a0f 100644
--- a/tests/qapi-schema/doc-invalid-end.err
+++ b/tests/qapi-schema/doc-invalid-end.err
@@ -1 +1 @@
-tests/qapi-schema/doc-invalid-end.json:5:2: Documentation comment must end with '##'
+tests/qapi-schema/doc-invalid-end.json:5:2: documentation comment must end with '##'
diff --git a/tests/qapi-schema/doc-invalid-end2.err b/tests/qapi-schema/doc-invalid-end2.err
index 6fad9c789e..13ead36fd2 100644
--- a/tests/qapi-schema/doc-invalid-end2.err
+++ b/tests/qapi-schema/doc-invalid-end2.err
@@ -1 +1 @@
-tests/qapi-schema/doc-invalid-end2.json:5:1: Junk after '##' at end of documentation comment
+tests/qapi-schema/doc-invalid-end2.json:5:1: junk after '##' at end of documentation comment
diff --git a/tests/qapi-schema/doc-invalid-start.err b/tests/qapi-schema/doc-invalid-start.err
index 149af2bfac..dcaa9699d6 100644
--- a/tests/qapi-schema/doc-invalid-start.err
+++ b/tests/qapi-schema/doc-invalid-start.err
@@ -1 +1 @@
-tests/qapi-schema/doc-invalid-start.json:3:1: Junk after '##' at start of documentation comment
+tests/qapi-schema/doc-invalid-start.json:3:1: junk after '##' at start of documentation comment
diff --git a/tests/qapi-schema/doc-missing-colon.err b/tests/qapi-schema/doc-missing-colon.err
index b41d5078b0..6fb5a380bd 100644
--- a/tests/qapi-schema/doc-missing-colon.err
+++ b/tests/qapi-schema/doc-missing-colon.err
@@ -1 +1 @@
-tests/qapi-schema/doc-missing-colon.json:4:1: Line should end with ':'
+tests/qapi-schema/doc-missing-colon.json:4:1: line should end with ':'
diff --git a/tests/qapi-schema/doc-missing-expr.err b/tests/qapi-schema/doc-missing-expr.err
index c909e26eca..622a37cc6c 100644
--- a/tests/qapi-schema/doc-missing-expr.err
+++ b/tests/qapi-schema/doc-missing-expr.err
@@ -1 +1 @@
-tests/qapi-schema/doc-missing-expr.json:3: Documentation for 'bar' is not followed by the definition
+tests/qapi-schema/doc-missing-expr.json:3: documentation for 'bar' is not followed by the definition
diff --git a/tests/qapi-schema/doc-missing-space.err b/tests/qapi-schema/doc-missing-space.err
index d6b46ffd77..1187ba12c4 100644
--- a/tests/qapi-schema/doc-missing-space.err
+++ b/tests/qapi-schema/doc-missing-space.err
@@ -1 +1 @@
-tests/qapi-schema/doc-missing-space.json:5:1: Missing space after #
+tests/qapi-schema/doc-missing-space.json:5:1: missing space after #
diff --git a/tests/qapi-schema/doc-missing.err b/tests/qapi-schema/doc-missing.err
index 3a377ddc57..08c827931a 100644
--- a/tests/qapi-schema/doc-missing.err
+++ b/tests/qapi-schema/doc-missing.err
@@ -1 +1 @@
-tests/qapi-schema/doc-missing.json:5: Definition missing documentation comment
+tests/qapi-schema/doc-missing.json:5: definition missing documentation comment
diff --git a/tests/qapi-schema/doc-no-symbol.err b/tests/qapi-schema/doc-no-symbol.err
index 212984ff20..9a3057730c 100644
--- a/tests/qapi-schema/doc-no-symbol.err
+++ b/tests/qapi-schema/doc-no-symbol.err
@@ -1 +1 @@
-tests/qapi-schema/doc-no-symbol.json:3: Definition documentation required
+tests/qapi-schema/doc-no-symbol.json:3: definition documentation required
diff --git a/tests/qapi-schema/double-type.err b/tests/qapi-schema/double-type.err
index 69457173a7..44a9dfdd55 100644
--- a/tests/qapi-schema/double-type.err
+++ b/tests/qapi-schema/double-type.err
@@ -1,2 +1,2 @@
-tests/qapi-schema/double-type.json:2: Unknown key 'command' in struct 'bar'
+tests/qapi-schema/double-type.json:2: unknown key 'command' in struct 'bar'
 Valid keys are 'base', 'data', 'features', 'if', 'struct'.
diff --git a/tests/qapi-schema/duplicate-key.err b/tests/qapi-schema/duplicate-key.err
index 3af2f55174..7f34a34eb6 100644
--- a/tests/qapi-schema/duplicate-key.err
+++ b/tests/qapi-schema/duplicate-key.err
@@ -1 +1 @@
-tests/qapi-schema/duplicate-key.json:3:10: Duplicate key 'key'
+tests/qapi-schema/duplicate-key.json:3:10: duplicate key 'key'
diff --git a/tests/qapi-schema/enum-bad-member.err b/tests/qapi-schema/enum-bad-member.err
index 49e4160dc4..1e59d42fca 100644
--- a/tests/qapi-schema/enum-bad-member.err
+++ b/tests/qapi-schema/enum-bad-member.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/enum-bad-member.json: In enum 'MyEnum':
-tests/qapi-schema/enum-bad-member.json:2: Member of enum 'MyEnum' requires a string name
+tests/qapi-schema/enum-bad-member.json:2: member of enum 'MyEnum' requires a string name
diff --git a/tests/qapi-schema/enum-bad-name.err b/tests/qapi-schema/enum-bad-name.err
index 3af7345792..ed2d608098 100644
--- a/tests/qapi-schema/enum-bad-name.err
+++ b/tests/qapi-schema/enum-bad-name.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/enum-bad-name.json: In enum 'MyEnum':
-tests/qapi-schema/enum-bad-name.json:3: Member of enum 'MyEnum' uses invalid name 'not\possible'
+tests/qapi-schema/enum-bad-name.json:3: member of enum 'MyEnum' uses invalid name 'not\possible'
diff --git a/tests/qapi-schema/enum-bad-prefix.err b/tests/qapi-schema/enum-bad-prefix.err
index 383ebaea7e..4f92736e52 100644
--- a/tests/qapi-schema/enum-bad-prefix.err
+++ b/tests/qapi-schema/enum-bad-prefix.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/enum-bad-prefix.json: In enum 'MyEnum':
-tests/qapi-schema/enum-bad-prefix.json:2: Enum 'MyEnum' requires a string for 'prefix'
+tests/qapi-schema/enum-bad-prefix.json:2: enum 'MyEnum' requires a string for 'prefix'
diff --git a/tests/qapi-schema/enum-dict-member-unknown.err b/tests/qapi-schema/enum-dict-member-unknown.err
index 02952f472a..79062729a1 100644
--- a/tests/qapi-schema/enum-dict-member-unknown.err
+++ b/tests/qapi-schema/enum-dict-member-unknown.err
@@ -1,3 +1,3 @@
 tests/qapi-schema/enum-dict-member-unknown.json: In enum 'MyEnum':
-tests/qapi-schema/enum-dict-member-unknown.json:2: Unknown key 'bad-key' in member of enum 'MyEnum'
+tests/qapi-schema/enum-dict-member-unknown.json:2: unknown key 'bad-key' in member of enum 'MyEnum'
 Valid keys are 'if', 'name'.
diff --git a/tests/qapi-schema/enum-int-member.err b/tests/qapi-schema/enum-int-member.err
index 3f8d7b5b71..27f06e334c 100644
--- a/tests/qapi-schema/enum-int-member.err
+++ b/tests/qapi-schema/enum-int-member.err
@@ -1 +1 @@
-tests/qapi-schema/enum-int-member.json:3:31: Stray '1'
+tests/qapi-schema/enum-int-member.json:3:31: stray '1'
diff --git a/tests/qapi-schema/enum-member-case.err b/tests/qapi-schema/enum-member-case.err
index 8f2007c86f..c3c6f8d709 100644
--- a/tests/qapi-schema/enum-member-case.err
+++ b/tests/qapi-schema/enum-member-case.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/enum-member-case.json: In enum 'NoWayThisWillGetWhitelisted':
-tests/qapi-schema/enum-member-case.json:4: Member of enum 'NoWayThisWillGetWhitelisted' uses uppercase in name 'Value'
+tests/qapi-schema/enum-member-case.json:4: member of enum 'NoWayThisWillGetWhitelisted' uses uppercase in name 'Value'
diff --git a/tests/qapi-schema/enum-missing-data.err b/tests/qapi-schema/enum-missing-data.err
index ba4873ae69..3c3c52d037 100644
--- a/tests/qapi-schema/enum-missing-data.err
+++ b/tests/qapi-schema/enum-missing-data.err
@@ -1 +1 @@
-tests/qapi-schema/enum-missing-data.json:2: Key 'data' is missing from enum 'MyEnum'
+tests/qapi-schema/enum-missing-data.json:2: key 'data' is missing from enum 'MyEnum'
diff --git a/tests/qapi-schema/enum-wrong-data.err b/tests/qapi-schema/enum-wrong-data.err
index 4ab0c44e48..ab9af5e995 100644
--- a/tests/qapi-schema/enum-wrong-data.err
+++ b/tests/qapi-schema/enum-wrong-data.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/enum-wrong-data.json: In enum 'MyEnum':
-tests/qapi-schema/enum-wrong-data.json:2: Enum 'MyEnum' requires an array for 'data'
+tests/qapi-schema/enum-wrong-data.json:2: enum 'MyEnum' requires an array for 'data'
diff --git a/tests/qapi-schema/escape-outside-string.err b/tests/qapi-schema/escape-outside-string.err
index efee335ba0..06f5f2ed55 100644
--- a/tests/qapi-schema/escape-outside-string.err
+++ b/tests/qapi-schema/escape-outside-string.err
@@ -1 +1 @@
-tests/qapi-schema/escape-outside-string.json:3:27: Stray '\'
+tests/qapi-schema/escape-outside-string.json:3:27: stray '\'
diff --git a/tests/qapi-schema/escape-too-big.err b/tests/qapi-schema/escape-too-big.err
new file mode 100644
index 0000000000..c34002b1e5
--- /dev/null
+++ b/tests/qapi-schema/escape-too-big.err
@@ -0,0 +1 @@
+tests/qapi-schema/escape-too-big.json:3:14: for now, \u escape only supports non-zero values up to \u007f
diff --git a/tests/qapi-schema/event-boxed-empty.err b/tests/qapi-schema/event-boxed-empty.err
index 6ccbdf7b22..9c691b7d97 100644
--- a/tests/qapi-schema/event-boxed-empty.err
+++ b/tests/qapi-schema/event-boxed-empty.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/event-boxed-empty.json: In event 'FOO':
-tests/qapi-schema/event-boxed-empty.json:2: Use of 'boxed' requires 'data'
+tests/qapi-schema/event-boxed-empty.json:2: use of 'boxed' requires 'data'
diff --git a/tests/qapi-schema/event-member-invalid-dict.err b/tests/qapi-schema/event-member-invalid-dict.err
index 9981a48b81..8bf89b7a3a 100644
--- a/tests/qapi-schema/event-member-invalid-dict.err
+++ b/tests/qapi-schema/event-member-invalid-dict.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/event-member-invalid-dict.json: In event 'EVENT_A':
-tests/qapi-schema/event-member-invalid-dict.json:1: Key 'type' is missing from member 'a' of 'data' for event 'EVENT_A'
+tests/qapi-schema/event-member-invalid-dict.json:1: key 'type' is missing from member 'a' of 'data' for event 'EVENT_A'
diff --git a/tests/qapi-schema/event-nest-struct.err b/tests/qapi-schema/event-nest-struct.err
index 2dd57f1784..8900052e83 100644
--- a/tests/qapi-schema/event-nest-struct.err
+++ b/tests/qapi-schema/event-nest-struct.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/event-nest-struct.json: In event 'EVENT_A':
-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:1: member 'a' of 'data' for event 'EVENT_A' should be a type name
diff --git a/tests/qapi-schema/features-bad-type.err b/tests/qapi-schema/features-bad-type.err
index a08485387a..2182c3ec75 100644
--- a/tests/qapi-schema/features-bad-type.err
+++ b/tests/qapi-schema/features-bad-type.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/features-bad-type.json: In struct 'FeatureStruct0':
-tests/qapi-schema/features-bad-type.json:1: Feature of struct FeatureStruct0 requires a string name
+tests/qapi-schema/features-bad-type.json:1: feature of struct FeatureStruct0 requires a string name
diff --git a/tests/qapi-schema/features-missing-name.err b/tests/qapi-schema/features-missing-name.err
index d445936b0c..8cbf1ef3f0 100644
--- a/tests/qapi-schema/features-missing-name.err
+++ b/tests/qapi-schema/features-missing-name.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/features-missing-name.json: In struct 'FeatureStruct0':
-tests/qapi-schema/features-missing-name.json:1: Key 'name' is missing from feature of struct FeatureStruct0
+tests/qapi-schema/features-missing-name.json:1: key 'name' is missing from feature of struct FeatureStruct0
diff --git a/tests/qapi-schema/features-name-bad-type.err b/tests/qapi-schema/features-name-bad-type.err
index cc4bd33e7f..19a7b61214 100644
--- a/tests/qapi-schema/features-name-bad-type.err
+++ b/tests/qapi-schema/features-name-bad-type.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/features-name-bad-type.json: In struct 'FeatureStruct0':
-tests/qapi-schema/features-name-bad-type.json:1: Feature of struct FeatureStruct0 requires a string name
+tests/qapi-schema/features-name-bad-type.json:1: feature of struct FeatureStruct0 requires a string name
diff --git a/tests/qapi-schema/features-no-list.err b/tests/qapi-schema/features-no-list.err
index 663d9cd158..28f91824bd 100644
--- a/tests/qapi-schema/features-no-list.err
+++ b/tests/qapi-schema/features-no-list.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/features-no-list.json: In struct 'FeatureStruct0':
-tests/qapi-schema/features-no-list.json:1: Struct 'FeatureStruct0' requires an array for 'features'
+tests/qapi-schema/features-no-list.json:1: struct 'FeatureStruct0' requires an array for 'features'
diff --git a/tests/qapi-schema/features-unknown-key.err b/tests/qapi-schema/features-unknown-key.err
index 2aeca4c1a6..78e63c8cf7 100644
--- a/tests/qapi-schema/features-unknown-key.err
+++ b/tests/qapi-schema/features-unknown-key.err
@@ -1,3 +1,3 @@
 tests/qapi-schema/features-unknown-key.json: In struct 'FeatureStruct0':
-tests/qapi-schema/features-unknown-key.json:1: Unknown key 'colour' in feature of struct FeatureStruct0
+tests/qapi-schema/features-unknown-key.json:1: unknown key 'colour' in feature of struct FeatureStruct0
 Valid keys are 'if', 'name'.
diff --git a/tests/qapi-schema/flat-union-array-branch.err b/tests/qapi-schema/flat-union-array-branch.err
index 13b4e60658..323d79737c 100644
--- a/tests/qapi-schema/flat-union-array-branch.err
+++ b/tests/qapi-schema/flat-union-array-branch.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/flat-union-array-branch.json: In union 'TestUnion':
-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:8: member 'value1' of union 'TestUnion' cannot be an array
diff --git a/tests/qapi-schema/flat-union-bad-discriminator.err b/tests/qapi-schema/flat-union-bad-discriminator.err
index 9b3746343f..27a6c9f3fb 100644
--- a/tests/qapi-schema/flat-union-bad-discriminator.err
+++ b/tests/qapi-schema/flat-union-bad-discriminator.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/flat-union-bad-discriminator.json: In union 'TestUnion':
-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:11: discriminator of flat union 'TestUnion' requires a string name
diff --git a/tests/qapi-schema/flat-union-discriminator-bad-name.err b/tests/qapi-schema/flat-union-discriminator-bad-name.err
index 72dc328bad..f7f64c5c1a 100644
--- a/tests/qapi-schema/flat-union-discriminator-bad-name.err
+++ b/tests/qapi-schema/flat-union-discriminator-bad-name.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/flat-union-discriminator-bad-name.json: In union 'MyUnion':
-tests/qapi-schema/flat-union-discriminator-bad-name.json:7: Discriminator of flat union 'MyUnion' does not allow optional name '*switch'
+tests/qapi-schema/flat-union-discriminator-bad-name.json:7: discriminator of flat union 'MyUnion' does not allow optional name '*switch'
diff --git a/tests/qapi-schema/flat-union-empty.err b/tests/qapi-schema/flat-union-empty.err
index 3fa79a6a47..96fe46b918 100644
--- a/tests/qapi-schema/flat-union-empty.err
+++ b/tests/qapi-schema/flat-union-empty.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/flat-union-empty.json: In union 'Union':
-tests/qapi-schema/flat-union-empty.json:4: Union 'Union' has no branches
+tests/qapi-schema/flat-union-empty.json:4: union 'Union' has no branches
diff --git a/tests/qapi-schema/flat-union-inline-invalid-dict.err b/tests/qapi-schema/flat-union-inline-invalid-dict.err
index 3716c02a9b..85739c2733 100644
--- a/tests/qapi-schema/flat-union-inline-invalid-dict.err
+++ b/tests/qapi-schema/flat-union-inline-invalid-dict.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/flat-union-inline-invalid-dict.json: In union 'TestUnion':
-tests/qapi-schema/flat-union-inline-invalid-dict.json:7: Key 'type' is missing from member 'value1' of union 'TestUnion'
+tests/qapi-schema/flat-union-inline-invalid-dict.json:7: key 'type' is missing from member 'value1' of union 'TestUnion'
diff --git a/tests/qapi-schema/flat-union-inline.err b/tests/qapi-schema/flat-union-inline.err
index 0a7a94b03f..33a8d6e3bd 100644
--- a/tests/qapi-schema/flat-union-inline.err
+++ b/tests/qapi-schema/flat-union-inline.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/flat-union-inline.json: In union 'TestUnion':
-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:7: member 'value1' of union 'TestUnion' should be a type name
diff --git a/tests/qapi-schema/flat-union-int-branch.err b/tests/qapi-schema/flat-union-int-branch.err
index 075751bcf6..2bbb8bf22e 100644
--- a/tests/qapi-schema/flat-union-int-branch.err
+++ b/tests/qapi-schema/flat-union-int-branch.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/flat-union-int-branch.json: In union 'TestUnion':
-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:8: member 'value1' of union 'TestUnion' cannot use built-in type 'int'
diff --git a/tests/qapi-schema/flat-union-invalid-branch-key.err b/tests/qapi-schema/flat-union-invalid-branch-key.err
index a4d0e3ee66..da4377be69 100644
--- a/tests/qapi-schema/flat-union-invalid-branch-key.err
+++ b/tests/qapi-schema/flat-union-invalid-branch-key.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/flat-union-invalid-branch-key.json: In union 'TestUnion':
-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:13: discriminator value 'value_wrong' is not found in enum 'TestEnum'
diff --git a/tests/qapi-schema/flat-union-invalid-discriminator.err b/tests/qapi-schema/flat-union-invalid-discriminator.err
index ca9a413dae..3f80de3044 100644
--- a/tests/qapi-schema/flat-union-invalid-discriminator.err
+++ b/tests/qapi-schema/flat-union-invalid-discriminator.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/flat-union-invalid-discriminator.json: In union 'TestUnion':
-tests/qapi-schema/flat-union-invalid-discriminator.json:10: Discriminator 'enum_wrong' is not a member of 'base'
+tests/qapi-schema/flat-union-invalid-discriminator.json:10: discriminator 'enum_wrong' is not a member of 'base'
diff --git a/tests/qapi-schema/flat-union-invalid-if-discriminator.err b/tests/qapi-schema/flat-union-invalid-if-discriminator.err
index c06307db98..be770d6baa 100644
--- a/tests/qapi-schema/flat-union-invalid-if-discriminator.err
+++ b/tests/qapi-schema/flat-union-invalid-if-discriminator.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/flat-union-invalid-if-discriminator.json: In union 'TestUnion':
-tests/qapi-schema/flat-union-invalid-if-discriminator.json:10: The discriminator 'enum1' for union TestUnion must not be conditional
+tests/qapi-schema/flat-union-invalid-if-discriminator.json:10: the discriminator 'enum1' for union TestUnion must not be conditional
diff --git a/tests/qapi-schema/flat-union-no-base.err b/tests/qapi-schema/flat-union-no-base.err
index 6e2035495c..c845259824 100644
--- a/tests/qapi-schema/flat-union-no-base.err
+++ b/tests/qapi-schema/flat-union-no-base.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/flat-union-no-base.json: In union 'TestUnion':
-tests/qapi-schema/flat-union-no-base.json:9: Flat union 'TestUnion' must have a base
+tests/qapi-schema/flat-union-no-base.json:9: flat union 'TestUnion' must have a base
diff --git a/tests/qapi-schema/flat-union-optional-discriminator.err b/tests/qapi-schema/flat-union-optional-discriminator.err
index db268fffaa..536ac775e2 100644
--- a/tests/qapi-schema/flat-union-optional-discriminator.err
+++ b/tests/qapi-schema/flat-union-optional-discriminator.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/flat-union-optional-discriminator.json: In union 'MyUnion':
-tests/qapi-schema/flat-union-optional-discriminator.json:7: Discriminator 'switch' is not a member of 'base'
+tests/qapi-schema/flat-union-optional-discriminator.json:7: discriminator 'switch' is not a member of 'base'
diff --git a/tests/qapi-schema/flat-union-string-discriminator.err b/tests/qapi-schema/flat-union-string-discriminator.err
index 9bca7082bb..f5635a5774 100644
--- a/tests/qapi-schema/flat-union-string-discriminator.err
+++ b/tests/qapi-schema/flat-union-string-discriminator.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/flat-union-string-discriminator.json: In union 'TestUnion':
-tests/qapi-schema/flat-union-string-discriminator.json:13: Discriminator 'kind' must be of enumeration type
+tests/qapi-schema/flat-union-string-discriminator.json:13: discriminator 'kind' must be of enumeration type
diff --git a/tests/qapi-schema/funny-char.err b/tests/qapi-schema/funny-char.err
index 139ecf4eb8..132fac93ea 100644
--- a/tests/qapi-schema/funny-char.err
+++ b/tests/qapi-schema/funny-char.err
@@ -1 +1 @@
-tests/qapi-schema/funny-char.json:2:36: Stray ';'
+tests/qapi-schema/funny-char.json:2:36: stray ';'
diff --git a/tests/qapi-schema/funny-word.err b/tests/qapi-schema/funny-word.err
index af92fe2551..d9286c8962 100644
--- a/tests/qapi-schema/funny-word.err
+++ b/tests/qapi-schema/funny-word.err
@@ -1 +1 @@
-tests/qapi-schema/funny-word.json:1:3: Stray 'command'
+tests/qapi-schema/funny-word.json:1:3: stray 'command'
diff --git a/tests/qapi-schema/ident-with-escape.err b/tests/qapi-schema/ident-with-escape.err
index 5517dcb4b1..1117283c81 100644
--- a/tests/qapi-schema/ident-with-escape.err
+++ b/tests/qapi-schema/ident-with-escape.err
@@ -1 +1 @@
-tests/qapi-schema/ident-with-escape.json:3:3: Unknown escape \u
+tests/qapi-schema/ident-with-escape.json:3:3: unknown escape \u
diff --git a/tests/qapi-schema/include-before-err.err b/tests/qapi-schema/include-before-err.err
index 2b26322170..098314bc49 100644
--- a/tests/qapi-schema/include-before-err.err
+++ b/tests/qapi-schema/include-before-err.err
@@ -1 +1 @@
-tests/qapi-schema/include-before-err.json:2:13: Expected ':'
+tests/qapi-schema/include-before-err.json:2:13: expected ':'
diff --git a/tests/qapi-schema/include-cycle.err b/tests/qapi-schema/include-cycle.err
index bdcd07dce2..52028669b5 100644
--- a/tests/qapi-schema/include-cycle.err
+++ b/tests/qapi-schema/include-cycle.err
@@ -1,3 +1,3 @@
 In file included from tests/qapi-schema/include-cycle.json:1:
 In file included from tests/qapi-schema/include-cycle-b.json:1:
-tests/qapi-schema/include-cycle-c.json:1: Inclusion loop for include-cycle.json
+tests/qapi-schema/include-cycle-c.json:1: inclusion loop for include-cycle.json
diff --git a/tests/qapi-schema/include-extra-junk.err b/tests/qapi-schema/include-extra-junk.err
index e6ef2a3720..854cec3ce3 100644
--- a/tests/qapi-schema/include-extra-junk.err
+++ b/tests/qapi-schema/include-extra-junk.err
@@ -1 +1 @@
-tests/qapi-schema/include-extra-junk.json:3: Invalid 'include' directive
+tests/qapi-schema/include-extra-junk.json:3: invalid 'include' directive
diff --git a/tests/qapi-schema/include-nested-err.err b/tests/qapi-schema/include-nested-err.err
index aec6207eb0..11928b4f7f 100644
--- a/tests/qapi-schema/include-nested-err.err
+++ b/tests/qapi-schema/include-nested-err.err
@@ -1,2 +1,2 @@
 In file included from tests/qapi-schema/include-nested-err.json:1:
-tests/qapi-schema/missing-colon.json:1:10: Expected ':'
+tests/qapi-schema/missing-colon.json:1:10: expected ':'
diff --git a/tests/qapi-schema/include-non-file.err b/tests/qapi-schema/include-non-file.err
index faae1eacf1..65dd3c7c2c 100644
--- a/tests/qapi-schema/include-non-file.err
+++ b/tests/qapi-schema/include-non-file.err
@@ -1 +1 @@
-tests/qapi-schema/include-non-file.json:1: Value of 'include' must be a string
+tests/qapi-schema/include-non-file.json:1: value of 'include' must be a string
diff --git a/tests/qapi-schema/include-self-cycle.err b/tests/qapi-schema/include-self-cycle.err
index 981742ae5f..c84795d1dc 100644
--- a/tests/qapi-schema/include-self-cycle.err
+++ b/tests/qapi-schema/include-self-cycle.err
@@ -1 +1 @@
-tests/qapi-schema/include-self-cycle.json:1: Inclusion loop for include-self-cycle.json
+tests/qapi-schema/include-self-cycle.json:1: inclusion loop for include-self-cycle.json
diff --git a/tests/qapi-schema/leading-comma-list.err b/tests/qapi-schema/leading-comma-list.err
index e021e42ad9..cddf471f71 100644
--- a/tests/qapi-schema/leading-comma-list.err
+++ b/tests/qapi-schema/leading-comma-list.err
@@ -1 +1 @@
-tests/qapi-schema/leading-comma-list.json:2:13: Expected '{', '[', ']', string, boolean or 'null'
+tests/qapi-schema/leading-comma-list.json:2:13: expected '{', '[', ']', string, boolean or 'null'
diff --git a/tests/qapi-schema/leading-comma-object.err b/tests/qapi-schema/leading-comma-object.err
index 3852f123d2..2f3b193274 100644
--- a/tests/qapi-schema/leading-comma-object.err
+++ b/tests/qapi-schema/leading-comma-object.err
@@ -1 +1 @@
-tests/qapi-schema/leading-comma-object.json:1:3: Expected string or '}'
+tests/qapi-schema/leading-comma-object.json:1:3: expected string or '}'
diff --git a/tests/qapi-schema/missing-colon.err b/tests/qapi-schema/missing-colon.err
index a255e51918..e642c7c549 100644
--- a/tests/qapi-schema/missing-colon.err
+++ b/tests/qapi-schema/missing-colon.err
@@ -1 +1 @@
-tests/qapi-schema/missing-colon.json:1:10: Expected ':'
+tests/qapi-schema/missing-colon.json:1:10: expected ':'
diff --git a/tests/qapi-schema/missing-comma-list.err b/tests/qapi-schema/missing-comma-list.err
index df3f553f39..5359499430 100644
--- a/tests/qapi-schema/missing-comma-list.err
+++ b/tests/qapi-schema/missing-comma-list.err
@@ -1 +1 @@
-tests/qapi-schema/missing-comma-list.json:2:20: Expected ',' or ']'
+tests/qapi-schema/missing-comma-list.json:2:20: expected ',' or ']'
diff --git a/tests/qapi-schema/missing-comma-object.err b/tests/qapi-schema/missing-comma-object.err
index 0f691b8ddd..c9b02b0760 100644
--- a/tests/qapi-schema/missing-comma-object.err
+++ b/tests/qapi-schema/missing-comma-object.err
@@ -1 +1 @@
-tests/qapi-schema/missing-comma-object.json:2:3: Expected ',' or '}'
+tests/qapi-schema/missing-comma-object.json:2:3: expected ',' or '}'
diff --git a/tests/qapi-schema/missing-type.err b/tests/qapi-schema/missing-type.err
index b3e7b14e42..19b7c495e7 100644
--- a/tests/qapi-schema/missing-type.err
+++ b/tests/qapi-schema/missing-type.err
@@ -1 +1 @@
-tests/qapi-schema/missing-type.json:2: Expression is missing metatype
+tests/qapi-schema/missing-type.json:2: expression is missing metatype
diff --git a/tests/qapi-schema/nested-struct-data-invalid-dict.err b/tests/qapi-schema/nested-struct-data-invalid-dict.err
index 4d9c9b491a..f2c7a8096c 100644
--- a/tests/qapi-schema/nested-struct-data-invalid-dict.err
+++ b/tests/qapi-schema/nested-struct-data-invalid-dict.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/nested-struct-data-invalid-dict.json: In command 'foo':
-tests/qapi-schema/nested-struct-data-invalid-dict.json:2: Key 'type' is missing from member 'a' of 'data' for command 'foo'
+tests/qapi-schema/nested-struct-data-invalid-dict.json:2: key 'type' is missing from member 'a' of 'data' for command 'foo'
diff --git a/tests/qapi-schema/nested-struct-data.err b/tests/qapi-schema/nested-struct-data.err
index 74d44ab111..b5e136674c 100644
--- a/tests/qapi-schema/nested-struct-data.err
+++ b/tests/qapi-schema/nested-struct-data.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/nested-struct-data.json: In command 'foo':
-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:2: member 'a' of 'data' for command 'foo' should be a type name
diff --git a/tests/qapi-schema/non-objects.err b/tests/qapi-schema/non-objects.err
index a972abd937..9237af6939 100644
--- a/tests/qapi-schema/non-objects.err
+++ b/tests/qapi-schema/non-objects.err
@@ -1 +1 @@
-tests/qapi-schema/non-objects.json:1:1: Expected '{'
+tests/qapi-schema/non-objects.json:1:1: expected '{'
diff --git a/tests/qapi-schema/pragma-doc-required-crap.err b/tests/qapi-schema/pragma-doc-required-crap.err
index 39cd56cd48..bcd981ada8 100644
--- a/tests/qapi-schema/pragma-doc-required-crap.err
+++ b/tests/qapi-schema/pragma-doc-required-crap.err
@@ -1 +1 @@
-tests/qapi-schema/pragma-doc-required-crap.json:3: Pragma 'doc-required' must be boolean
+tests/qapi-schema/pragma-doc-required-crap.json:3: pragma 'doc-required' must be boolean
diff --git a/tests/qapi-schema/pragma-extra-junk.err b/tests/qapi-schema/pragma-extra-junk.err
index 4481688dbf..3ae48d3668 100644
--- a/tests/qapi-schema/pragma-extra-junk.err
+++ b/tests/qapi-schema/pragma-extra-junk.err
@@ -1 +1 @@
-tests/qapi-schema/pragma-extra-junk.json:3: Invalid 'pragma' directive
+tests/qapi-schema/pragma-extra-junk.json:3: invalid 'pragma' directive
diff --git a/tests/qapi-schema/pragma-name-case-whitelist-crap.err b/tests/qapi-schema/pragma-name-case-whitelist-crap.err
index f83b97e075..81f829ff36 100644
--- a/tests/qapi-schema/pragma-name-case-whitelist-crap.err
+++ b/tests/qapi-schema/pragma-name-case-whitelist-crap.err
@@ -1 +1 @@
-tests/qapi-schema/pragma-name-case-whitelist-crap.json:3: Pragma name-case-whitelist must be a list of strings
+tests/qapi-schema/pragma-name-case-whitelist-crap.json:3: pragma name-case-whitelist must be a list of strings
diff --git a/tests/qapi-schema/pragma-non-dict.err b/tests/qapi-schema/pragma-non-dict.err
index b358261050..8221724b0a 100644
--- a/tests/qapi-schema/pragma-non-dict.err
+++ b/tests/qapi-schema/pragma-non-dict.err
@@ -1 +1 @@
-tests/qapi-schema/pragma-non-dict.json:3: Value of 'pragma' must be an object
+tests/qapi-schema/pragma-non-dict.json:3: value of 'pragma' must be an object
diff --git a/tests/qapi-schema/pragma-returns-whitelist-crap.err b/tests/qapi-schema/pragma-returns-whitelist-crap.err
index 5d77021674..c0cae5de18 100644
--- a/tests/qapi-schema/pragma-returns-whitelist-crap.err
+++ b/tests/qapi-schema/pragma-returns-whitelist-crap.err
@@ -1 +1 @@
-tests/qapi-schema/pragma-returns-whitelist-crap.json:3: Pragma returns-whitelist must be a list of strings
+tests/qapi-schema/pragma-returns-whitelist-crap.json:3: pragma returns-whitelist must be a list of strings
diff --git a/tests/qapi-schema/pragma-unknown.err b/tests/qapi-schema/pragma-unknown.err
index 6ef2058316..f1335f0a0a 100644
--- a/tests/qapi-schema/pragma-unknown.err
+++ b/tests/qapi-schema/pragma-unknown.err
@@ -1 +1 @@
-tests/qapi-schema/pragma-unknown.json:1: Unknown pragma 'no-such-pragma'
+tests/qapi-schema/pragma-unknown.json:1: unknown pragma 'no-such-pragma'
diff --git a/tests/qapi-schema/quoted-structural-chars.err b/tests/qapi-schema/quoted-structural-chars.err
index 6e036c8044..d8460a63a7 100644
--- a/tests/qapi-schema/quoted-structural-chars.err
+++ b/tests/qapi-schema/quoted-structural-chars.err
@@ -1 +1 @@
-tests/qapi-schema/quoted-structural-chars.json:1:1: Expected '{'
+tests/qapi-schema/quoted-structural-chars.json:1:1: expected '{'
diff --git a/tests/qapi-schema/reserved-enum-q.err b/tests/qapi-schema/reserved-enum-q.err
index e3eecd29dc..d9c0af5a05 100644
--- a/tests/qapi-schema/reserved-enum-q.err
+++ b/tests/qapi-schema/reserved-enum-q.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/reserved-enum-q.json: In enum 'Foo':
-tests/qapi-schema/reserved-enum-q.json:4: Member of enum 'Foo' uses invalid name 'q-Unix'
+tests/qapi-schema/reserved-enum-q.json:4: member of enum 'Foo' uses invalid name 'q-Unix'
diff --git a/tests/qapi-schema/reserved-member-has.err b/tests/qapi-schema/reserved-member-has.err
index 2c6d0418db..6f405ec2a9 100644
--- a/tests/qapi-schema/reserved-member-has.err
+++ b/tests/qapi-schema/reserved-member-has.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/reserved-member-has.json: In command 'oops':
-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:5: member of 'data' for command 'oops' uses reserved name 'has-a'
diff --git a/tests/qapi-schema/reserved-member-q.err b/tests/qapi-schema/reserved-member-q.err
index eaabe579bc..ece2664005 100644
--- a/tests/qapi-schema/reserved-member-q.err
+++ b/tests/qapi-schema/reserved-member-q.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/reserved-member-q.json: In struct 'Foo':
-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:4: member of 'data' for struct 'Foo' uses invalid name 'q-unix'
diff --git a/tests/qapi-schema/reserved-member-u.err b/tests/qapi-schema/reserved-member-u.err
index b01629da29..e812a1e404 100644
--- a/tests/qapi-schema/reserved-member-u.err
+++ b/tests/qapi-schema/reserved-member-u.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/reserved-member-u.json: In struct 'Oops':
-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:7: member of 'data' for struct 'Oops' uses reserved name 'u'
diff --git a/tests/qapi-schema/reserved-member-underscore.err b/tests/qapi-schema/reserved-member-underscore.err
index 6089cc1d3b..e1d54f0a27 100644
--- a/tests/qapi-schema/reserved-member-underscore.err
+++ b/tests/qapi-schema/reserved-member-underscore.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/reserved-member-underscore.json: In struct 'Oops':
-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:4: member of 'data' for struct 'Oops' uses invalid name '_oops'
diff --git a/tests/qapi-schema/string-code-point-127.err b/tests/qapi-schema/string-code-point-127.err
index c310910c23..b4fa2610a9 100644
--- a/tests/qapi-schema/string-code-point-127.err
+++ b/tests/qapi-schema/string-code-point-127.err
@@ -1 +1 @@
-tests/qapi-schema/string-code-point-127.json:2:14: Funny character in string
+tests/qapi-schema/string-code-point-127.json:2:14: funny character in string
diff --git a/tests/qapi-schema/string-code-point-31.err b/tests/qapi-schema/string-code-point-31.err
index 45797928d9..0bb5ce37b8 100644
--- a/tests/qapi-schema/string-code-point-31.err
+++ b/tests/qapi-schema/string-code-point-31.err
@@ -1 +1 @@
-tests/qapi-schema/string-code-point-31.json:2:14: Funny character in string
+tests/qapi-schema/string-code-point-31.json:2:14: funny character in string
diff --git a/tests/qapi-schema/string-control.err b/tests/qapi-schema/string-control.err
new file mode 100644
index 0000000000..5459acf526
--- /dev/null
+++ b/tests/qapi-schema/string-control.err
@@ -0,0 +1 @@
+tests/qapi-schema/string-control.json:2:14: control character \u007f in string
diff --git a/tests/qapi-schema/string-unclosed.err b/tests/qapi-schema/string-unclosed.err
new file mode 100644
index 0000000000..e41de99283
--- /dev/null
+++ b/tests/qapi-schema/string-unclosed.err
@@ -0,0 +1 @@
+tests/qapi-schema/string-unclosed.json:1:11: missing terminating "'"
diff --git a/tests/qapi-schema/string-unicode.err b/tests/qapi-schema/string-unicode.err
new file mode 100644
index 0000000000..646468f815
--- /dev/null
+++ b/tests/qapi-schema/string-unicode.err
@@ -0,0 +1 @@
+tests/qapi-schema/string-unicode.json:2:14: non-ASCII character in string
diff --git a/tests/qapi-schema/struct-member-invalid-dict.err b/tests/qapi-schema/struct-member-invalid-dict.err
index e8b0ee72e0..0c770bb1e8 100644
--- a/tests/qapi-schema/struct-member-invalid-dict.err
+++ b/tests/qapi-schema/struct-member-invalid-dict.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/struct-member-invalid-dict.json: In struct 'foo':
-tests/qapi-schema/struct-member-invalid-dict.json:2: Key 'type' is missing from member '*a' of 'data' for struct 'foo'
+tests/qapi-schema/struct-member-invalid-dict.json:2: key 'type' is missing from member '*a' of 'data' for struct 'foo'
diff --git a/tests/qapi-schema/struct-member-invalid.err b/tests/qapi-schema/struct-member-invalid.err
index 466c1af1d0..e5a19fc8af 100644
--- a/tests/qapi-schema/struct-member-invalid.err
+++ b/tests/qapi-schema/struct-member-invalid.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/struct-member-invalid.json: In struct 'foo':
-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:1: member 'a' of 'data' for struct 'foo' should be a type name
diff --git a/tests/qapi-schema/trailing-comma-list.err b/tests/qapi-schema/trailing-comma-list.err
index 601c4537c3..167d688beb 100644
--- a/tests/qapi-schema/trailing-comma-list.err
+++ b/tests/qapi-schema/trailing-comma-list.err
@@ -1 +1 @@
-tests/qapi-schema/trailing-comma-list.json:2:36: Expected '{', '[', string, boolean or 'null'
+tests/qapi-schema/trailing-comma-list.json:2:36: expected '{', '[', string, boolean or 'null'
diff --git a/tests/qapi-schema/trailing-comma-object.err b/tests/qapi-schema/trailing-comma-object.err
index 30bce5e194..186df0fa45 100644
--- a/tests/qapi-schema/trailing-comma-object.err
+++ b/tests/qapi-schema/trailing-comma-object.err
@@ -1 +1 @@
-tests/qapi-schema/trailing-comma-object.json:2:38: Expected string
+tests/qapi-schema/trailing-comma-object.json:2:38: expected string
diff --git a/tests/qapi-schema/unclosed-list.err b/tests/qapi-schema/unclosed-list.err
index 1cc3a094fe..6648a8e30b 100644
--- a/tests/qapi-schema/unclosed-list.err
+++ b/tests/qapi-schema/unclosed-list.err
@@ -1 +1 @@
-tests/qapi-schema/unclosed-list.json:1:20: Expected ',' or ']'
+tests/qapi-schema/unclosed-list.json:1:20: expected ',' or ']'
diff --git a/tests/qapi-schema/unclosed-object.err b/tests/qapi-schema/unclosed-object.err
index fd1a86b704..54d221e3a9 100644
--- a/tests/qapi-schema/unclosed-object.err
+++ b/tests/qapi-schema/unclosed-object.err
@@ -1 +1 @@
-tests/qapi-schema/unclosed-object.json:1:21: Expected ',' or '}'
+tests/qapi-schema/unclosed-object.json:1:21: expected ',' or '}'
diff --git a/tests/qapi-schema/unclosed-string.err b/tests/qapi-schema/unclosed-string.err
index 12b187074e..9439698fe0 100644
--- a/tests/qapi-schema/unclosed-string.err
+++ b/tests/qapi-schema/unclosed-string.err
@@ -1 +1 @@
-tests/qapi-schema/unclosed-string.json:1:11: Missing terminating "'"
+tests/qapi-schema/unclosed-string.json:1:11: missing terminating "'"
diff --git a/tests/qapi-schema/union-base-empty.err b/tests/qapi-schema/union-base-empty.err
index 1e24341819..b76542d47a 100644
--- a/tests/qapi-schema/union-base-empty.err
+++ b/tests/qapi-schema/union-base-empty.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/union-base-empty.json: In union 'TestUnion':
-tests/qapi-schema/union-base-empty.json:5: Discriminator 'type' is not a member of 'base'
+tests/qapi-schema/union-base-empty.json:5: discriminator 'type' is not a member of 'base'
diff --git a/tests/qapi-schema/union-base-no-discriminator.err b/tests/qapi-schema/union-base-no-discriminator.err
index fa9343fb8e..883a98866b 100644
--- a/tests/qapi-schema/union-base-no-discriminator.err
+++ b/tests/qapi-schema/union-base-no-discriminator.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/union-base-no-discriminator.json: In union 'TestUnion':
-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:11: simple union 'TestUnion' must not have a base
diff --git a/tests/qapi-schema/union-branch-case.err b/tests/qapi-schema/union-branch-case.err
index 09313d7f83..f111210281 100644
--- a/tests/qapi-schema/union-branch-case.err
+++ b/tests/qapi-schema/union-branch-case.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/union-branch-case.json: In union 'Uni':
-tests/qapi-schema/union-branch-case.json:2: Member of union 'Uni' uses uppercase in name 'Branch'
+tests/qapi-schema/union-branch-case.json:2: member of union 'Uni' uses uppercase in name 'Branch'
diff --git a/tests/qapi-schema/union-branch-invalid-dict.err b/tests/qapi-schema/union-branch-invalid-dict.err
index 5b8b68432c..d11a739674 100644
--- a/tests/qapi-schema/union-branch-invalid-dict.err
+++ b/tests/qapi-schema/union-branch-invalid-dict.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/union-branch-invalid-dict.json: In union 'UnionInvalidBranch':
-tests/qapi-schema/union-branch-invalid-dict.json:2: Key 'type' is missing from member 'integer' of union 'UnionInvalidBranch'
+tests/qapi-schema/union-branch-invalid-dict.json:2: key 'type' is missing from member 'integer' of union 'UnionInvalidBranch'
diff --git a/tests/qapi-schema/union-empty.err b/tests/qapi-schema/union-empty.err
index 75f2fa9f21..522b19e7ae 100644
--- a/tests/qapi-schema/union-empty.err
+++ b/tests/qapi-schema/union-empty.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/union-empty.json: In union 'Union':
-tests/qapi-schema/union-empty.json:2: Union 'Union' has no branches
+tests/qapi-schema/union-empty.json:2: union 'Union' has no branches
diff --git a/tests/qapi-schema/union-optional-branch.err b/tests/qapi-schema/union-optional-branch.err
index 09320416ad..a5677f74bc 100644
--- a/tests/qapi-schema/union-optional-branch.err
+++ b/tests/qapi-schema/union-optional-branch.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/union-optional-branch.json: In union 'Union':
-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:2: member of union 'Union' does not allow optional name '*a'
diff --git a/tests/qapi-schema/union-unknown.err b/tests/qapi-schema/union-unknown.err
index 7c6cabb8cc..5d4fdd2857 100644
--- a/tests/qapi-schema/union-unknown.err
+++ b/tests/qapi-schema/union-unknown.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/union-unknown.json: In union 'Union':
-tests/qapi-schema/union-unknown.json:2: Member 'unknown' of union 'Union' uses unknown type 'MissingType'
+tests/qapi-schema/union-unknown.json:2: member 'unknown' of union 'Union' uses unknown type 'MissingType'
diff --git a/tests/qapi-schema/unknown-escape.err b/tests/qapi-schema/unknown-escape.err
index 000e30ddf3..e24bbaf046 100644
--- a/tests/qapi-schema/unknown-escape.err
+++ b/tests/qapi-schema/unknown-escape.err
@@ -1 +1 @@
-tests/qapi-schema/unknown-escape.json:3:21: Unknown escape \x
+tests/qapi-schema/unknown-escape.json:3:21: unknown escape \x
diff --git a/tests/qapi-schema/unknown-expr-key.err b/tests/qapi-schema/unknown-expr-key.err
index 4340eaf894..07558edb78 100644
--- a/tests/qapi-schema/unknown-expr-key.err
+++ b/tests/qapi-schema/unknown-expr-key.err
@@ -1,2 +1,2 @@
-tests/qapi-schema/unknown-expr-key.json:2: Unknown keys 'bogus', 'phony' in struct 'bar'
+tests/qapi-schema/unknown-expr-key.json:2: unknown keys 'bogus', 'phony' in struct 'bar'
 Valid keys are 'base', 'data', 'features', 'if', 'struct'.
-- 
2.21.0



^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH 07/25] qapi: Improve reporting of member name clashes
  2019-09-24 13:28 [PATCH 00/25] qapi: Pay back some frontend technical debt Markus Armbruster
                   ` (5 preceding siblings ...)
  2019-09-24 13:28 ` [PATCH 06/25] qapi: Change frontend error messages to start with lower case Markus Armbruster
@ 2019-09-24 13:28 ` Markus Armbruster
  2019-09-24 15:38   ` Eric Blake
  2019-09-24 13:28 ` [PATCH 08/25] qapi: Reorder check_FOO() parameters for consistency Markus Armbruster
                   ` (17 subsequent siblings)
  24 siblings, 1 reply; 63+ messages in thread
From: Markus Armbruster @ 2019-09-24 13:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, mdroth

We report name clashes like this:

    struct-base-clash.json: In struct 'Sub':
    struct-base-clash.json:5: 'name' (member of Sub) collides with 'name' (member of Base)

The "(member of Sub)" is redundant with "In struct 'Sub'".  Comes from
QAPISchemaMember.describe().  Pass info to it, so it can detect the
redundancy and avoid it.  Result:

    struct-base-clash.json: In struct 'Sub':
    struct-base-clash.json:5: member 'name' collides with member 'name' of type 'Base'

Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 scripts/qapi/common.py                        | 35 ++++++++++++-------
 tests/qapi-schema/alternate-clash.err         |  2 +-
 tests/qapi-schema/args-name-clash.err         |  2 +-
 tests/qapi-schema/enum-clash-member.err       |  2 +-
 tests/qapi-schema/features-duplicate-name.err |  2 +-
 tests/qapi-schema/flat-union-bad-base.err     |  2 +-
 tests/qapi-schema/flat-union-clash-member.err |  2 +-
 tests/qapi-schema/struct-base-clash-deep.err  |  2 +-
 tests/qapi-schema/struct-base-clash.err       |  2 +-
 tests/qapi-schema/union-clash-branches.err    |  2 +-
 10 files changed, 31 insertions(+), 22 deletions(-)

diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index 23f1c8eece..749490c517 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -1570,31 +1570,40 @@ class QAPISchemaMember(object):
     def check_clash(self, info, seen):
         cname = c_name(self.name)
         if cname in seen:
-            raise QAPISemError(info, "%s collides with %s" %
-                               (self.describe(), seen[cname].describe()))
+            raise QAPISemError(
+                info, "%s collides with %s"
+                % (self.describe(info), seen[cname].describe(info)))
         seen[cname] = self
 
-    def _pretty_defined_in(self):
+    def describe(self, info):
+        role = self.role
         defined_in = self.defined_in
+        assert defined_in
+
         if defined_in.startswith('q_obj_'):
             # See QAPISchema._make_implicit_object_type() - reverse the
             # mapping there to create a nice human-readable description
             defined_in = defined_in[6:]
             if defined_in.endswith('-arg'):
-                return '(parameter of %s)' % defined_in[:-4]
+                # Implicit type created for a command's dict 'data'
+                assert role == 'member'
+                role = 'parameter'
             elif defined_in.endswith('-base'):
-                return '(base of %s)' % defined_in[:-5]
+                # Implicit type created for a flat union's dict 'base'
+                role = 'base ' + role
             else:
+                # Implicit type created for a simple union's branch
                 assert defined_in.endswith('-wrapper')
                 # Unreachable and not implemented
                 assert False
-        if defined_in.endswith('Kind'):
+        elif defined_in.endswith('Kind'):
             # See QAPISchema._make_implicit_enum_type()
-            return '(branch of %s)' % defined_in[:-4]
-        return '(%s of %s)' % (self.role, defined_in)
-
-    def describe(self):
-        return "'%s' %s" % (self.name, self._pretty_defined_in())
+            # Implicit enum created for simple union's branches
+            assert role == 'value'
+            role = 'branch'
+        elif defined_in != info.defn_name:
+            return "%s '%s' of type '%s'" % (role, self.name, defined_in)
+        return "%s '%s'" % (role, self.name)
 
 
 class QAPISchemaEnumMember(QAPISchemaMember):
@@ -1866,7 +1875,7 @@ class QAPISchema(object):
                 for v in values]
 
     def _make_implicit_enum_type(self, name, info, ifcond, values):
-        # See also QAPISchemaObjectTypeMember._pretty_defined_in()
+        # See also QAPISchemaObjectTypeMember.describe()
         name = name + 'Kind'   # Use namespace reserved by add_name()
         self._def_entity(QAPISchemaEnumType(
             name, info, None, ifcond, self._make_enum_members(values), None))
@@ -1882,7 +1891,7 @@ class QAPISchema(object):
                                    role, members):
         if not members:
             return None
-        # See also QAPISchemaObjectTypeMember._pretty_defined_in()
+        # See also QAPISchemaObjectTypeMember.describe()
         name = 'q_obj_%s-%s' % (name, role)
         typ = self.lookup_entity(name, QAPISchemaObjectType)
         if typ:
diff --git a/tests/qapi-schema/alternate-clash.err b/tests/qapi-schema/alternate-clash.err
index 426ff6a7c4..73a52d69d1 100644
--- a/tests/qapi-schema/alternate-clash.err
+++ b/tests/qapi-schema/alternate-clash.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/alternate-clash.json: In alternate 'Alt1':
-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:7: branch 'a_b' collides with branch 'a-b'
diff --git a/tests/qapi-schema/args-name-clash.err b/tests/qapi-schema/args-name-clash.err
index eeb4e1b4dd..c5916a80fb 100644
--- a/tests/qapi-schema/args-name-clash.err
+++ b/tests/qapi-schema/args-name-clash.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/args-name-clash.json: In command 'oops':
-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:4: parameter 'a_b' collides with parameter 'a-b'
diff --git a/tests/qapi-schema/enum-clash-member.err b/tests/qapi-schema/enum-clash-member.err
index 26944f5e06..84e02db82c 100644
--- a/tests/qapi-schema/enum-clash-member.err
+++ b/tests/qapi-schema/enum-clash-member.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/enum-clash-member.json: In enum 'MyEnum':
-tests/qapi-schema/enum-clash-member.json:2: 'one_two' (value of MyEnum) collides with 'one-two' (value of MyEnum)
+tests/qapi-schema/enum-clash-member.json:2: value 'one_two' collides with value 'one-two'
diff --git a/tests/qapi-schema/features-duplicate-name.err b/tests/qapi-schema/features-duplicate-name.err
index 0ebec8e4b0..a99bbde737 100644
--- a/tests/qapi-schema/features-duplicate-name.err
+++ b/tests/qapi-schema/features-duplicate-name.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/features-duplicate-name.json: In struct 'FeatureStruct0':
-tests/qapi-schema/features-duplicate-name.json:1: 'foo' (feature of FeatureStruct0) collides with 'foo' (feature of FeatureStruct0)
+tests/qapi-schema/features-duplicate-name.json:1: feature 'foo' collides with feature 'foo'
diff --git a/tests/qapi-schema/flat-union-bad-base.err b/tests/qapi-schema/flat-union-bad-base.err
index ae8adc3947..5da7602c20 100644
--- a/tests/qapi-schema/flat-union-bad-base.err
+++ b/tests/qapi-schema/flat-union-bad-base.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/flat-union-bad-base.json: In union 'TestUnion':
-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:8: member 'string' of type 'TestTypeA' collides with base member 'string'
diff --git a/tests/qapi-schema/flat-union-clash-member.err b/tests/qapi-schema/flat-union-clash-member.err
index 48e939db19..40f10681f8 100644
--- a/tests/qapi-schema/flat-union-clash-member.err
+++ b/tests/qapi-schema/flat-union-clash-member.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/flat-union-clash-member.json: In union 'TestUnion':
-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:11: member 'name' of type 'Branch1' collides with member 'name' of type 'Base'
diff --git a/tests/qapi-schema/struct-base-clash-deep.err b/tests/qapi-schema/struct-base-clash-deep.err
index 53e9bb108e..2b12b3c07f 100644
--- a/tests/qapi-schema/struct-base-clash-deep.err
+++ b/tests/qapi-schema/struct-base-clash-deep.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/struct-base-clash-deep.json: In struct 'Sub':
-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:10: member 'name' collides with member 'name' of type 'Base'
diff --git a/tests/qapi-schema/struct-base-clash.err b/tests/qapi-schema/struct-base-clash.err
index bf94eee8b3..8c3ee1c435 100644
--- a/tests/qapi-schema/struct-base-clash.err
+++ b/tests/qapi-schema/struct-base-clash.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/struct-base-clash.json: In struct 'Sub':
-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:5: member 'name' collides with member 'name' of type 'Base'
diff --git a/tests/qapi-schema/union-clash-branches.err b/tests/qapi-schema/union-clash-branches.err
index 145efebd9f..931399f076 100644
--- a/tests/qapi-schema/union-clash-branches.err
+++ b/tests/qapi-schema/union-clash-branches.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/union-clash-branches.json: In union 'TestUnion':
-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:4: branch 'a_b' collides with branch 'a-b'
-- 
2.21.0



^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH 08/25] qapi: Reorder check_FOO() parameters for consistency
  2019-09-24 13:28 [PATCH 00/25] qapi: Pay back some frontend technical debt Markus Armbruster
                   ` (6 preceding siblings ...)
  2019-09-24 13:28 ` [PATCH 07/25] qapi: Improve reporting of member name clashes Markus Armbruster
@ 2019-09-24 13:28 ` Markus Armbruster
  2019-09-24 15:41   ` Eric Blake
  2019-09-24 13:28 ` [PATCH 09/25] qapi: Improve reporting of invalid name errors Markus Armbruster
                   ` (16 subsequent siblings)
  24 siblings, 1 reply; 63+ messages in thread
From: Markus Armbruster @ 2019-09-24 13:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, mdroth

Most check_FOO() take the thing being checked as first argument.
check_name(), check_type(), check_known_keys() don't.  Clean that up.

While there, drop a "Todo" comment that should have been dropped in
commit 87adbbffd4 "qapi: add a dictionary form for TYPE".

Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 scripts/qapi/common.py | 80 ++++++++++++++++++++----------------------
 1 file changed, 39 insertions(+), 41 deletions(-)

diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index 749490c517..25818b0ea0 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -705,7 +705,7 @@ valid_name = re.compile(r'^(__[a-zA-Z0-9.-]+_)?'
                         '[a-zA-Z][a-zA-Z0-9_-]*$')
 
 
-def check_name(info, source, name,
+def check_name(name, info, source,
                allow_optional=False, enum_member=False, permit_upper=False):
     global valid_name
     membername = name
@@ -733,7 +733,7 @@ def check_name(info, source, name,
 
 def add_name(name, info, meta):
     global all_names
-    check_name(info, "'%s'" % meta, name, permit_upper=True)
+    check_name(name, info, "'%s'" % meta, permit_upper=True)
     # FIXME should reject names that differ only in '_' vs. '.'
     # vs. '-', because they're liable to clash in generated C.
     if name in all_names:
@@ -767,7 +767,7 @@ def check_if(expr, info):
         check_if_str(ifcond, info)
 
 
-def check_type(info, source, value,
+def check_type(value, info, source,
                allow_array=False, allow_dict=False, allow_metas=[]):
     global all_names
 
@@ -805,19 +805,17 @@ def check_type(info, source, value,
 
     # value is a dictionary, check that each member is okay
     for (key, arg) in value.items():
-        check_name(info, "member of %s" % source, key,
+        check_name(key, info, "member of %s" % source,
                    allow_optional=True, permit_upper=permit_upper)
         if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
             raise QAPISemError(
                 info, "member of %s uses reserved name '%s'" % (source, key))
-        # Todo: allow dictionaries to represent default values of
-        # an optional argument.
-        check_known_keys(info, "member '%s' of %s" % (key, source),
-                         arg, ['type'], ['if'])
+        check_known_keys(arg, info, "member '%s' of %s" % (key, source),
+                         ['type'], ['if'])
         check_if(arg, info)
         normalize_if(arg)
-        check_type(info, "member '%s' of %s" % (key, source),
-                   arg['type'], allow_array=True,
+        check_type(arg['type'], info, "member '%s' of %s" % (key, source),
+                   allow_array=True,
                    allow_metas=['built-in', 'union', 'alternate', 'struct',
                                 'enum'])
 
@@ -829,15 +827,15 @@ def check_command(expr, info):
     args_meta = ['struct']
     if boxed:
         args_meta += ['union']
-    check_type(info, "'data' for command '%s'" % name,
-               expr.get('data'), allow_dict=not boxed,
-               allow_metas=args_meta)
+    check_type(expr.get('data'), info,
+               "'data' for command '%s'" % name,
+               allow_dict=not boxed, allow_metas=args_meta)
     returns_meta = ['union', 'struct']
     if name in returns_whitelist:
         returns_meta += ['built-in', 'alternate', 'enum']
-    check_type(info, "'returns' for command '%s'" % name,
-               expr.get('returns'), allow_array=True,
-               allow_metas=returns_meta)
+    check_type(expr.get('returns'), info,
+               "'returns' for command '%s'" % name,
+               allow_array=True, allow_metas=returns_meta)
 
 
 def check_event(expr, info):
@@ -847,9 +845,9 @@ def check_event(expr, info):
     meta = ['struct']
     if boxed:
         meta += ['union']
-    check_type(info, "'data' for event '%s'" % name,
-               expr.get('data'), allow_dict=not boxed,
-               allow_metas=meta)
+    check_type(expr.get('data'), info,
+               "'data' for event '%s'" % name,
+               allow_dict=not boxed, allow_metas=meta)
 
 
 def enum_get_names(expr):
@@ -875,9 +873,8 @@ def check_union(expr, info):
     # Else, it's a flat union.
     else:
         # The object must have a string or dictionary 'base'.
-        check_type(info, "'base' for union '%s'" % name,
-                   base, allow_dict=name,
-                   allow_metas=['struct'])
+        check_type(base, info, "'base' for union '%s'" % name,
+                   allow_dict=name, allow_metas=['struct'])
         if not base:
             raise QAPISemError(info, "flat union '%s' must have a base"
                                % name)
@@ -886,8 +883,8 @@ def check_union(expr, info):
 
         # The value of member 'discriminator' must name a non-optional
         # member of the base struct.
-        check_name(info, "discriminator of flat union '%s'" % name,
-                   discriminator)
+        check_name(discriminator, info,
+                   "discriminator of flat union '%s'" % name)
         discriminator_value = base_members.get(discriminator)
         if not discriminator_value:
             raise QAPISemError(info,
@@ -912,15 +909,16 @@ def check_union(expr, info):
         raise QAPISemError(info, "union '%s' has no branches" % name)
 
     for (key, value) in members.items():
-        check_name(info, "member of union '%s'" % name, key)
+        check_name(key, info, "member of union '%s'" % name)
 
-        check_known_keys(info, "member '%s' of union '%s'" % (key, name),
-                         value, ['type'], ['if'])
+        check_known_keys(value, info,
+                         "member '%s' of union '%s'" % (key, name),
+                         ['type'], ['if'])
         check_if(value, info)
         normalize_if(value)
         # Each value must name a known type
-        check_type(info, "member '%s' of union '%s'" % (key, name),
-                   value['type'],
+        check_type(value['type'], info,
+                   "member '%s' of union '%s'" % (key, name),
                    allow_array=not base, allow_metas=allow_metas)
 
         # If the discriminator names an enum type, then all members
@@ -942,16 +940,16 @@ def check_alternate(expr, info):
         raise QAPISemError(info,
                            "alternate '%s' cannot have empty 'data'" % name)
     for (key, value) in members.items():
-        check_name(info, "member of alternate '%s'" % name, key)
-        check_known_keys(info,
+        check_name(key, info, "member of alternate '%s'" % name)
+        check_known_keys(value, info,
                          "member '%s' of alternate '%s'" % (key, name),
-                         value, ['type'], ['if'])
+                         ['type'], ['if'])
         check_if(value, info)
         normalize_if(value)
         typ = value['type']
 
         # Ensure alternates have no type conflicts.
-        check_type(info, "member '%s' of alternate '%s'" % (key, name), typ,
+        check_type(typ, info, "member '%s' of alternate '%s'" % (key, name),
                    allow_metas=['built-in', 'union', 'struct', 'enum'])
         qtype = find_alternate_member_qtype(typ)
         if not qtype:
@@ -996,11 +994,11 @@ def check_enum(expr, info):
     permit_upper = name in name_case_whitelist
 
     for member in members:
-        check_known_keys(info, "member of enum '%s'" % name, member,
+        check_known_keys(member, info, "member of enum '%s'" % name,
                          ['name'], ['if'])
         check_if(member, info)
         normalize_if(member)
-        check_name(info, "member of enum '%s'" % name, member['name'],
+        check_name(member['name'], info, "member of enum '%s'" % name,
                    enum_member=True, permit_upper=permit_upper)
 
 
@@ -1009,9 +1007,9 @@ def check_struct(expr, info):
     members = expr['data']
     features = expr.get('features')
 
-    check_type(info, "'data' for struct '%s'" % name, members,
+    check_type(members, info, "'data' for struct '%s'" % name,
                allow_dict=name)
-    check_type(info, "'base' for struct '%s'" % name, expr.get('base'),
+    check_type(expr.get('base'), info, "'base' for struct '%s'" % name,
                allow_metas=['struct'])
 
     if features:
@@ -1020,15 +1018,15 @@ def check_struct(expr, info):
                 info, "struct '%s' requires an array for 'features'" % name)
         for f in features:
             assert isinstance(f, dict)
-            check_known_keys(info, "feature of struct %s" % name, f,
+            check_known_keys(f, info, "feature of struct %s" % name,
                              ['name'], ['if'])
 
             check_if(f, info)
             normalize_if(f)
-            check_name(info, "feature of struct %s" % name, f['name'])
+            check_name(f['name'], info, "feature of struct %s" % name)
 
 
-def check_known_keys(info, source, value, required, optional):
+def check_known_keys(value, info, source, required, optional):
 
     def pprint(elems):
         return ', '.join("'" + e + "'" for e in sorted(elems))
@@ -1052,7 +1050,7 @@ def check_keys(expr, info, meta, required, optional=[]):
         raise QAPISemError(info, "'%s' key must have a string value" % meta)
     required = required + [meta]
     source = "%s '%s'" % (meta, name)
-    check_known_keys(info, source, expr, required, optional)
+    check_known_keys(expr, info, source, required, optional)
     for (key, value) in expr.items():
         if key in ['gen', 'success-response'] and value is not False:
             raise QAPISemError(info,
-- 
2.21.0



^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH 09/25] qapi: Improve reporting of invalid name errors
  2019-09-24 13:28 [PATCH 00/25] qapi: Pay back some frontend technical debt Markus Armbruster
                   ` (7 preceding siblings ...)
  2019-09-24 13:28 ` [PATCH 08/25] qapi: Reorder check_FOO() parameters for consistency Markus Armbruster
@ 2019-09-24 13:28 ` Markus Armbruster
  2019-09-24 15:48   ` Eric Blake
  2019-09-24 13:28 ` [PATCH 10/25] qapi: Use check_name_str() where it suffices Markus Armbruster
                   ` (15 subsequent siblings)
  24 siblings, 1 reply; 63+ messages in thread
From: Markus Armbruster @ 2019-09-24 13:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, mdroth

Split check_name() into check_name_is_str() and check_name_str(), keep
check_name() as a wrapper.

Move add_name()'s call into its caller check_exprs(), and inline.

This permits delaying check_name_str() there, so its error message
gains an "in definition" line.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 scripts/qapi/common.py                   | 20 ++++++++++++++++----
 tests/qapi-schema/bad-ident.err          |  1 +
 tests/qapi-schema/command-int.err        |  1 +
 tests/qapi-schema/redefined-builtin.err  |  1 +
 tests/qapi-schema/redefined-command.err  |  1 +
 tests/qapi-schema/redefined-event.err    |  1 +
 tests/qapi-schema/redefined-type.err     |  1 +
 tests/qapi-schema/reserved-command-q.err |  1 +
 tests/qapi-schema/reserved-type-kind.err |  1 +
 tests/qapi-schema/reserved-type-list.err |  1 +
 10 files changed, 25 insertions(+), 4 deletions(-)

diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index 25818b0ea0..fdc4379eff 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -707,11 +707,22 @@ valid_name = re.compile(r'^(__[a-zA-Z0-9.-]+_)?'
 
 def check_name(name, info, source,
                allow_optional=False, enum_member=False, permit_upper=False):
+    check_name_is_str(name, info, source)
+    check_name_str(name, info, source,
+                   allow_optional, enum_member, permit_upper)
+
+
+def check_name_is_str(name, info, source):
+    if not isinstance(name, str):
+        raise QAPISemError(info, "%s requires a string name" % source)
+
+
+def check_name_str(name, info, source,
+                   allow_optional=False, enum_member=False,
+                   permit_upper=False):
     global valid_name
     membername = name
 
-    if not isinstance(name, str):
-        raise QAPISemError(info, "%s requires a string name" % source)
     if name.startswith('*'):
         membername = name[1:]
         if not allow_optional:
@@ -733,7 +744,6 @@ def check_name(name, info, source,
 
 def add_name(name, info, meta):
     global all_names
-    check_name(name, info, "'%s'" % meta, permit_upper=True)
     # FIXME should reject names that differ only in '_' vs. '.'
     # vs. '-', because they're liable to clash in generated C.
     if name in all_names:
@@ -1148,8 +1158,10 @@ def check_exprs(exprs):
             raise QAPISemError(info, "expression is missing metatype")
         normalize_if(expr)
         name = expr[meta]
-        add_name(name, info, meta)
+        check_name_is_str(name, info, "'%s'" % meta)
         info.set_defn(meta, name)
+        check_name_str(name, info, "'%s'" % meta, permit_upper=True)
+        add_name(name, info, meta)
         if doc and doc.symbol != name:
             raise QAPISemError(
                 info,
diff --git a/tests/qapi-schema/bad-ident.err b/tests/qapi-schema/bad-ident.err
index c4190602b5..6878889854 100644
--- a/tests/qapi-schema/bad-ident.err
+++ b/tests/qapi-schema/bad-ident.err
@@ -1 +1,2 @@
+tests/qapi-schema/bad-ident.json: In struct '*oops':
 tests/qapi-schema/bad-ident.json:2: 'struct' does not allow optional name '*oops'
diff --git a/tests/qapi-schema/command-int.err b/tests/qapi-schema/command-int.err
index 0f9300679b..56b45bf656 100644
--- a/tests/qapi-schema/command-int.err
+++ b/tests/qapi-schema/command-int.err
@@ -1 +1,2 @@
+tests/qapi-schema/command-int.json: In command 'int':
 tests/qapi-schema/command-int.json:2: built-in 'int' is already defined
diff --git a/tests/qapi-schema/redefined-builtin.err b/tests/qapi-schema/redefined-builtin.err
index b2757225c4..67775fdb41 100644
--- a/tests/qapi-schema/redefined-builtin.err
+++ b/tests/qapi-schema/redefined-builtin.err
@@ -1 +1,2 @@
+tests/qapi-schema/redefined-builtin.json: In struct 'size':
 tests/qapi-schema/redefined-builtin.json:2: built-in 'size' is already defined
diff --git a/tests/qapi-schema/redefined-command.err b/tests/qapi-schema/redefined-command.err
index 82ae256e63..b77a05d354 100644
--- a/tests/qapi-schema/redefined-command.err
+++ b/tests/qapi-schema/redefined-command.err
@@ -1 +1,2 @@
+tests/qapi-schema/redefined-command.json: In command 'foo':
 tests/qapi-schema/redefined-command.json:3: command 'foo' is already defined
diff --git a/tests/qapi-schema/redefined-event.err b/tests/qapi-schema/redefined-event.err
index 35429cb481..fd02d38157 100644
--- a/tests/qapi-schema/redefined-event.err
+++ b/tests/qapi-schema/redefined-event.err
@@ -1 +1,2 @@
+tests/qapi-schema/redefined-event.json: In event 'EVENT_A':
 tests/qapi-schema/redefined-event.json:3: event 'EVENT_A' is already defined
diff --git a/tests/qapi-schema/redefined-type.err b/tests/qapi-schema/redefined-type.err
index 06ea78c478..89acc82c2d 100644
--- a/tests/qapi-schema/redefined-type.err
+++ b/tests/qapi-schema/redefined-type.err
@@ -1 +1,2 @@
+tests/qapi-schema/redefined-type.json: In enum 'foo':
 tests/qapi-schema/redefined-type.json:3: struct 'foo' is already defined
diff --git a/tests/qapi-schema/reserved-command-q.err b/tests/qapi-schema/reserved-command-q.err
index f939e044eb..0844e14b26 100644
--- a/tests/qapi-schema/reserved-command-q.err
+++ b/tests/qapi-schema/reserved-command-q.err
@@ -1 +1,2 @@
+tests/qapi-schema/reserved-command-q.json: In command 'q-unix':
 tests/qapi-schema/reserved-command-q.json:5: 'command' uses invalid name 'q-unix'
diff --git a/tests/qapi-schema/reserved-type-kind.err b/tests/qapi-schema/reserved-type-kind.err
index 0a38efaad8..8d21479000 100644
--- a/tests/qapi-schema/reserved-type-kind.err
+++ b/tests/qapi-schema/reserved-type-kind.err
@@ -1 +1,2 @@
+tests/qapi-schema/reserved-type-kind.json: In enum 'UnionKind':
 tests/qapi-schema/reserved-type-kind.json:2: enum 'UnionKind' should not end in 'Kind'
diff --git a/tests/qapi-schema/reserved-type-list.err b/tests/qapi-schema/reserved-type-list.err
index 4510fa6d90..2bdd7d8a06 100644
--- a/tests/qapi-schema/reserved-type-list.err
+++ b/tests/qapi-schema/reserved-type-list.err
@@ -1 +1,2 @@
+tests/qapi-schema/reserved-type-list.json: In struct 'FooList':
 tests/qapi-schema/reserved-type-list.json:5: struct 'FooList' should not end in 'List'
-- 
2.21.0



^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH 10/25] qapi: Use check_name_str() where it suffices
  2019-09-24 13:28 [PATCH 00/25] qapi: Pay back some frontend technical debt Markus Armbruster
                   ` (8 preceding siblings ...)
  2019-09-24 13:28 ` [PATCH 09/25] qapi: Improve reporting of invalid name errors Markus Armbruster
@ 2019-09-24 13:28 ` Markus Armbruster
  2019-09-24 15:50   ` Eric Blake
  2019-09-24 13:28 ` [PATCH 11/25] qapi: Report invalid '*' prefix like any other invalid name Markus Armbruster
                   ` (14 subsequent siblings)
  24 siblings, 1 reply; 63+ messages in thread
From: Markus Armbruster @ 2019-09-24 13:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, mdroth

Replace check_name() by check_name_str() where the name is known to be
a string.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 scripts/qapi/common.py | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index fdc4379eff..6c870da50b 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -815,8 +815,8 @@ def check_type(value, info, source,
 
     # value is a dictionary, check that each member is okay
     for (key, arg) in value.items():
-        check_name(key, info, "member of %s" % source,
-                   allow_optional=True, permit_upper=permit_upper)
+        check_name_str(key, info, "member of %s" % source,
+                       allow_optional=True, permit_upper=permit_upper)
         if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
             raise QAPISemError(
                 info, "member of %s uses reserved name '%s'" % (source, key))
@@ -919,8 +919,7 @@ def check_union(expr, info):
         raise QAPISemError(info, "union '%s' has no branches" % name)
 
     for (key, value) in members.items():
-        check_name(key, info, "member of union '%s'" % name)
-
+        check_name_str(key, info, "member of union '%s'" % name)
         check_known_keys(value, info,
                          "member '%s' of union '%s'" % (key, name),
                          ['type'], ['if'])
@@ -950,7 +949,7 @@ def check_alternate(expr, info):
         raise QAPISemError(info,
                            "alternate '%s' cannot have empty 'data'" % name)
     for (key, value) in members.items():
-        check_name(key, info, "member of alternate '%s'" % name)
+        check_name_str(key, info, "member of alternate '%s'" % name)
         check_known_keys(value, info,
                          "member '%s' of alternate '%s'" % (key, name),
                          ['type'], ['if'])
-- 
2.21.0



^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH 11/25] qapi: Report invalid '*' prefix like any other invalid name
  2019-09-24 13:28 [PATCH 00/25] qapi: Pay back some frontend technical debt Markus Armbruster
                   ` (9 preceding siblings ...)
  2019-09-24 13:28 ` [PATCH 10/25] qapi: Use check_name_str() where it suffices Markus Armbruster
@ 2019-09-24 13:28 ` Markus Armbruster
  2019-09-24 15:52   ` Eric Blake
  2019-09-24 13:28 ` [PATCH 12/25] qapi: Move check for reserved names out of add_name() Markus Armbruster
                   ` (13 subsequent siblings)
  24 siblings, 1 reply; 63+ messages in thread
From: Markus Armbruster @ 2019-09-24 13:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, mdroth

The special "does not allow optional name" error is well meant, but
confusing in practice.  Drop it.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 scripts/qapi/common.py                                   | 6 ++----
 tests/qapi-schema/bad-ident.err                          | 2 +-
 tests/qapi-schema/flat-union-discriminator-bad-name.err  | 2 +-
 tests/qapi-schema/flat-union-discriminator-bad-name.json | 2 +-
 tests/qapi-schema/union-optional-branch.err              | 2 +-
 5 files changed, 6 insertions(+), 8 deletions(-)

diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index 6c870da50b..39eb6de4a6 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -723,11 +723,8 @@ def check_name_str(name, info, source,
     global valid_name
     membername = name
 
-    if name.startswith('*'):
+    if allow_optional and name.startswith('*'):
         membername = name[1:]
-        if not allow_optional:
-            raise QAPISemError(info, "%s does not allow optional name '%s'"
-                               % (source, name))
     # Enum members can start with a digit, because the generated C
     # code always prefixes it with the enum name
     if enum_member and membername[0].isdigit():
@@ -740,6 +737,7 @@ def check_name_str(name, info, source,
     if not permit_upper and name.lower() != name:
         raise QAPISemError(
             info, "%s uses uppercase in name '%s'" % (source, name))
+    assert not membername.startswith('*')
 
 
 def add_name(name, info, meta):
diff --git a/tests/qapi-schema/bad-ident.err b/tests/qapi-schema/bad-ident.err
index 6878889854..ddc96bd3a9 100644
--- a/tests/qapi-schema/bad-ident.err
+++ b/tests/qapi-schema/bad-ident.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/bad-ident.json: In struct '*oops':
-tests/qapi-schema/bad-ident.json:2: 'struct' does not allow optional name '*oops'
+tests/qapi-schema/bad-ident.json:2: 'struct' uses invalid name '*oops'
diff --git a/tests/qapi-schema/flat-union-discriminator-bad-name.err b/tests/qapi-schema/flat-union-discriminator-bad-name.err
index f7f64c5c1a..44e41883b1 100644
--- a/tests/qapi-schema/flat-union-discriminator-bad-name.err
+++ b/tests/qapi-schema/flat-union-discriminator-bad-name.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/flat-union-discriminator-bad-name.json: In union 'MyUnion':
-tests/qapi-schema/flat-union-discriminator-bad-name.json:7: discriminator of flat union 'MyUnion' does not allow optional name '*switch'
+tests/qapi-schema/flat-union-discriminator-bad-name.json:7: discriminator of flat union 'MyUnion' uses invalid name '*switch'
diff --git a/tests/qapi-schema/flat-union-discriminator-bad-name.json b/tests/qapi-schema/flat-union-discriminator-bad-name.json
index 66376084fc..ea84b75cac 100644
--- a/tests/qapi-schema/flat-union-discriminator-bad-name.json
+++ b/tests/qapi-schema/flat-union-discriminator-bad-name.json
@@ -1,5 +1,5 @@
 # discriminator '*switch' isn't a member of base, 'switch' is
-# reports "does not allow optional name", which is good enough
+# reports "uses invalid name", which is good enough
 { 'enum': 'Enum', 'data': [ 'one', 'two' ] }
 { 'struct': 'Base',
   'data': { '*switch': 'Enum' } }
diff --git a/tests/qapi-schema/union-optional-branch.err b/tests/qapi-schema/union-optional-branch.err
index a5677f74bc..8e9b18d7c6 100644
--- a/tests/qapi-schema/union-optional-branch.err
+++ b/tests/qapi-schema/union-optional-branch.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/union-optional-branch.json: In union 'Union':
-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:2: member of union 'Union' uses invalid name '*a'
-- 
2.21.0



^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH 12/25] qapi: Move check for reserved names out of add_name()
  2019-09-24 13:28 [PATCH 00/25] qapi: Pay back some frontend technical debt Markus Armbruster
                   ` (10 preceding siblings ...)
  2019-09-24 13:28 ` [PATCH 11/25] qapi: Report invalid '*' prefix like any other invalid name Markus Armbruster
@ 2019-09-24 13:28 ` Markus Armbruster
  2019-09-24 15:56   ` Eric Blake
  2019-09-24 13:28 ` [PATCH 13/25] qapi: Make check_type()'s array case a bit more obvious Markus Armbruster
                   ` (12 subsequent siblings)
  24 siblings, 1 reply; 63+ messages in thread
From: Markus Armbruster @ 2019-09-24 13:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, mdroth

The checks for reserved names are spread far and wide.  Move one from
add_name() to new check_defn_name_str().  This is a first step towards
collecting them all in dedicated name checking functions next to
check_name().

While there, drop the quotes around the meta-type in
check_name_str()'s error messages: "'command' uses ... name 'NAME'"
becomes "command uses ... name 'NAME'".

Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 scripts/qapi/common.py                   | 16 ++++++++++------
 tests/qapi-schema/bad-ident.err          |  2 +-
 tests/qapi-schema/reserved-command-q.err |  2 +-
 3 files changed, 12 insertions(+), 8 deletions(-)

diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index 39eb6de4a6..5e708c3b45 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -740,6 +740,13 @@ def check_name_str(name, info, source,
     assert not membername.startswith('*')
 
 
+def check_defn_name_str(name, info, meta):
+    check_name_str(name, info, meta, permit_upper=True)
+    if name.endswith('Kind') or name.endswith('List'):
+        raise QAPISemError(info, "%s '%s' should not end in '%s'"
+                           % (meta, name, name[-4:]))
+
+
 def add_name(name, info, meta):
     global all_names
     # FIXME should reject names that differ only in '_' vs. '.'
@@ -747,9 +754,6 @@ def add_name(name, info, meta):
     if name in all_names:
         raise QAPISemError(info, "%s '%s' is already defined"
                            % (all_names[name], name))
-    if name.endswith('Kind') or name.endswith('List'):
-        raise QAPISemError(info, "%s '%s' should not end in '%s'"
-                           % (meta, name, name[-4:]))
     all_names[name] = meta
 
 
@@ -1157,7 +1161,7 @@ def check_exprs(exprs):
         name = expr[meta]
         check_name_is_str(name, info, "'%s'" % meta)
         info.set_defn(meta, name)
-        check_name_str(name, info, "'%s'" % meta, permit_upper=True)
+        check_defn_name_str(name, info, meta)
         add_name(name, info, meta)
         if doc and doc.symbol != name:
             raise QAPISemError(
@@ -1883,13 +1887,13 @@ class QAPISchema(object):
 
     def _make_implicit_enum_type(self, name, info, ifcond, values):
         # See also QAPISchemaObjectTypeMember.describe()
-        name = name + 'Kind'   # Use namespace reserved by add_name()
+        name = name + 'Kind'    # reserved by check_defn_name_str()
         self._def_entity(QAPISchemaEnumType(
             name, info, None, ifcond, self._make_enum_members(values), None))
         return name
 
     def _make_array_type(self, element_type, info):
-        name = element_type + 'List'   # Use namespace reserved by add_name()
+        name = element_type + 'List'    # reserved by check_defn_name_str()
         if not self.lookup_type(name):
             self._def_entity(QAPISchemaArrayType(name, info, element_type))
         return name
diff --git a/tests/qapi-schema/bad-ident.err b/tests/qapi-schema/bad-ident.err
index ddc96bd3a9..79d14758ce 100644
--- a/tests/qapi-schema/bad-ident.err
+++ b/tests/qapi-schema/bad-ident.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/bad-ident.json: In struct '*oops':
-tests/qapi-schema/bad-ident.json:2: 'struct' uses invalid name '*oops'
+tests/qapi-schema/bad-ident.json:2: struct uses invalid name '*oops'
diff --git a/tests/qapi-schema/reserved-command-q.err b/tests/qapi-schema/reserved-command-q.err
index 0844e14b26..631cb5cdcc 100644
--- a/tests/qapi-schema/reserved-command-q.err
+++ b/tests/qapi-schema/reserved-command-q.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/reserved-command-q.json: In command 'q-unix':
-tests/qapi-schema/reserved-command-q.json:5: 'command' uses invalid name 'q-unix'
+tests/qapi-schema/reserved-command-q.json:5: command uses invalid name 'q-unix'
-- 
2.21.0



^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH 13/25] qapi: Make check_type()'s array case a bit more obvious
  2019-09-24 13:28 [PATCH 00/25] qapi: Pay back some frontend technical debt Markus Armbruster
                   ` (11 preceding siblings ...)
  2019-09-24 13:28 ` [PATCH 12/25] qapi: Move check for reserved names out of add_name() Markus Armbruster
@ 2019-09-24 13:28 ` Markus Armbruster
  2019-09-24 15:57   ` Eric Blake
  2019-09-24 13:28 ` [PATCH 14/25] qapi: Plumb info to the QAPISchemaMember Markus Armbruster
                   ` (11 subsequent siblings)
  24 siblings, 1 reply; 63+ messages in thread
From: Markus Armbruster @ 2019-09-24 13:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, mdroth

check_type() checks the array's contents, then peels off the array and
falls through to the "not array" code without resetting allow_array
and allow_dict to False.  Works because the peeled value is a string,
and allow_array and allow_dict aren't used then.  Tidy up anyway:
recurse instead, defaulting allow_array and allow_dict to False.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 scripts/qapi/common.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index 5e708c3b45..07cf72e72c 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -794,7 +794,8 @@ def check_type(value, info, source,
             raise QAPISemError(info,
                                "%s: array type must contain single type name" %
                                source)
-        value = value[0]
+        check_type(value[0], info, source, allow_metas=allow_metas)
+        return
 
     # Check if type name for value is okay
     if isinstance(value, str):
-- 
2.21.0



^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH 14/25] qapi: Plumb info to the QAPISchemaMember
  2019-09-24 13:28 [PATCH 00/25] qapi: Pay back some frontend technical debt Markus Armbruster
                   ` (12 preceding siblings ...)
  2019-09-24 13:28 ` [PATCH 13/25] qapi: Make check_type()'s array case a bit more obvious Markus Armbruster
@ 2019-09-24 13:28 ` Markus Armbruster
  2019-09-24 16:01   ` Eric Blake
  2019-09-24 13:28 ` [PATCH 15/25] qapi: Inline check_name() into check_union() Markus Armbruster
                   ` (10 subsequent siblings)
  24 siblings, 1 reply; 63+ messages in thread
From: Markus Armbruster @ 2019-09-24 13:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, mdroth

Future commits will need info in the .check() methods of
QAPISchemaMember and its descendants.  Get it there.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 scripts/qapi/common.py | 76 +++++++++++++++++++++++-------------------
 scripts/qapi/events.py |  2 +-
 2 files changed, 43 insertions(+), 35 deletions(-)

diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index 07cf72e72c..2dd794dfe7 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -1569,9 +1569,10 @@ class QAPISchemaMember(object):
     """ Represents object members, enum members and features """
     role = 'member'
 
-    def __init__(self, name, ifcond=None):
+    def __init__(self, name, info, ifcond=None):
         assert isinstance(name, str)
         self.name = name
+        self.info = info
         self.ifcond = ifcond or []
         self.defined_in = None
 
@@ -1627,8 +1628,8 @@ class QAPISchemaFeature(QAPISchemaMember):
 
 
 class QAPISchemaObjectTypeMember(QAPISchemaMember):
-    def __init__(self, name, typ, optional, ifcond=None):
-        QAPISchemaMember.__init__(self, name, ifcond)
+    def __init__(self, name, info, typ, optional, ifcond=None):
+        QAPISchemaMember.__init__(self, name, info, ifcond)
         assert isinstance(typ, str)
         assert isinstance(optional, bool)
         self._type_name = typ
@@ -1642,7 +1643,7 @@ class QAPISchemaObjectTypeMember(QAPISchemaMember):
 
 
 class QAPISchemaObjectTypeVariants(object):
-    def __init__(self, tag_name, tag_member, variants):
+    def __init__(self, tag_name, info, tag_member, variants):
         # Flat unions pass tag_name but not tag_member.
         # Simple unions and alternates pass tag_member but not tag_name.
         # After check(), tag_member is always set, and tag_name remains
@@ -1653,6 +1654,7 @@ class QAPISchemaObjectTypeVariants(object):
         for v in variants:
             assert isinstance(v, QAPISchemaObjectTypeVariant)
         self._tag_name = tag_name
+        self.info = info
         self.tag_member = tag_member
         self.variants = variants
 
@@ -1672,8 +1674,8 @@ class QAPISchemaObjectTypeVariants(object):
             cases = set([v.name for v in self.variants])
             for m in self.tag_member.type.members:
                 if m.name not in cases:
-                    v = QAPISchemaObjectTypeVariant(m.name, 'q_empty',
-                                                    m.ifcond)
+                    v = QAPISchemaObjectTypeVariant(m.name, self.info,
+                                                    'q_empty', m.ifcond)
                     v.set_defined_in(self.tag_member.defined_in)
                     self.variants.append(v)
         assert self.variants
@@ -1697,8 +1699,9 @@ class QAPISchemaObjectTypeVariants(object):
 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
     role = 'branch'
 
-    def __init__(self, name, typ, ifcond=None):
-        QAPISchemaObjectTypeMember.__init__(self, name, typ, False, ifcond)
+    def __init__(self, name, info, typ, ifcond=None):
+        QAPISchemaObjectTypeMember.__init__(self, name, info, typ,
+                                            False, ifcond)
 
 
 class QAPISchemaAlternateType(QAPISchemaType):
@@ -1874,23 +1877,26 @@ class QAPISchema(object):
 
         qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
                   'qbool']
-        qtype_values = self._make_enum_members([{'name': n} for n in qtypes])
+        qtype_values = self._make_enum_members(
+            [{'name': n} for n in qtypes], None)
 
         self._def_entity(QAPISchemaEnumType('QType', None, None, None,
                                             qtype_values, 'QTYPE'))
 
-    def _make_features(self, features):
-        return [QAPISchemaFeature(f['name'], f.get('if')) for f in features]
+    def _make_features(self, features, info):
+        return [QAPISchemaFeature(f['name'], info, f.get('if'))
+                for f in features]
 
-    def _make_enum_members(self, values):
-        return [QAPISchemaEnumMember(v['name'], v.get('if'))
+    def _make_enum_members(self, values, info):
+        return [QAPISchemaEnumMember(v['name'], info, v.get('if'))
                 for v in values]
 
     def _make_implicit_enum_type(self, name, info, ifcond, values):
         # See also QAPISchemaObjectTypeMember.describe()
         name = name + 'Kind'    # reserved by check_defn_name_str()
         self._def_entity(QAPISchemaEnumType(
-            name, info, None, ifcond, self._make_enum_members(values), None))
+            name, info, None, ifcond, self._make_enum_members(values, info),
+            None))
         return name
 
     def _make_array_type(self, element_type, info):
@@ -1929,7 +1935,7 @@ class QAPISchema(object):
         ifcond = expr.get('if')
         self._def_entity(QAPISchemaEnumType(
             name, info, doc, ifcond,
-            self._make_enum_members(data), prefix))
+            self._make_enum_members(data, info), prefix))
 
     def _make_member(self, name, typ, ifcond, info):
         optional = False
@@ -1939,7 +1945,7 @@ class QAPISchema(object):
         if isinstance(typ, list):
             assert len(typ) == 1
             typ = self._make_array_type(typ[0], info)
-        return QAPISchemaObjectTypeMember(name, typ, optional, ifcond)
+        return QAPISchemaObjectTypeMember(name, info, typ, optional, ifcond)
 
     def _make_members(self, data, info):
         return [self._make_member(key, value['type'], value.get('if'), info)
@@ -1951,13 +1957,14 @@ class QAPISchema(object):
         data = expr['data']
         ifcond = expr.get('if')
         features = expr.get('features', [])
-        self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, base,
-                                              self._make_members(data, info),
-                                              None,
-                                              self._make_features(features)))
+        self._def_entity(QAPISchemaObjectType(
+            name, info, doc, ifcond, base,
+            self._make_members(data, info),
+            None,
+            self._make_features(features, info)))
 
-    def _make_variant(self, case, typ, ifcond):
-        return QAPISchemaObjectTypeVariant(case, typ, ifcond)
+    def _make_variant(self, case, typ, ifcond, info):
+        return QAPISchemaObjectTypeVariant(case, info, typ, ifcond)
 
     def _make_simple_variant(self, case, typ, ifcond, info):
         if isinstance(typ, list):
@@ -1966,7 +1973,7 @@ class QAPISchema(object):
         typ = self._make_implicit_object_type(
             typ, info, None, self.lookup_type(typ),
             'wrapper', [self._make_member('data', typ, None, info)])
-        return QAPISchemaObjectTypeVariant(case, typ, ifcond)
+        return QAPISchemaObjectTypeVariant(case, info, typ, ifcond)
 
     def _def_union_type(self, expr, info, doc):
         name = expr['union']
@@ -1980,7 +1987,8 @@ class QAPISchema(object):
                 name, info, doc, ifcond,
                 'base', self._make_members(base, info))
         if tag_name:
-            variants = [self._make_variant(key, value['type'], value.get('if'))
+            variants = [self._make_variant(key, value['type'],
+                                           value.get('if'), info)
                         for (key, value) in data.items()]
             members = []
         else:
@@ -1989,26 +1997,26 @@ class QAPISchema(object):
                         for (key, value) in data.items()]
             enum = [{'name': v.name, 'if': v.ifcond} for v in variants]
             typ = self._make_implicit_enum_type(name, info, ifcond, enum)
-            tag_member = QAPISchemaObjectTypeMember('type', typ, False)
+            tag_member = QAPISchemaObjectTypeMember('type', info, typ, False)
             members = [tag_member]
         self._def_entity(
             QAPISchemaObjectType(name, info, doc, ifcond, base, members,
-                                 QAPISchemaObjectTypeVariants(tag_name,
-                                                              tag_member,
-                                                              variants), []))
+                                 QAPISchemaObjectTypeVariants(
+                                     tag_name, info, tag_member, variants),
+                                 []))
 
     def _def_alternate_type(self, expr, info, doc):
         name = expr['alternate']
         data = expr['data']
         ifcond = expr.get('if')
-        variants = [self._make_variant(key, value['type'], value.get('if'))
+        variants = [self._make_variant(key, value['type'], value.get('if'),
+                                       info)
                     for (key, value) in data.items()]
-        tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
+        tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False)
         self._def_entity(
             QAPISchemaAlternateType(name, info, doc, ifcond,
-                                    QAPISchemaObjectTypeVariants(None,
-                                                                 tag_member,
-                                                                 variants)))
+                                    QAPISchemaObjectTypeVariants(
+                                        None, info, tag_member, variants)))
 
     def _def_command(self, expr, info, doc):
         name = expr['command']
@@ -2300,7 +2308,7 @@ const QEnumLookup %(c_name)s_lookup = {
 
 def gen_enum(name, members, prefix=None):
     # append automatically generated _MAX value
-    enum_members = members + [QAPISchemaEnumMember('_MAX')]
+    enum_members = members + [QAPISchemaEnumMember('_MAX', None)]
 
     ret = mcgen('''
 
diff --git a/scripts/qapi/events.py b/scripts/qapi/events.py
index 7062553cf3..7308e8e589 100644
--- a/scripts/qapi/events.py
+++ b/scripts/qapi/events.py
@@ -194,7 +194,7 @@ void %(event_emit)s(%(event_enum)s event, QDict *qdict);
                                           self._event_emit_name))
         # Note: we generate the enum member regardless of @ifcond, to
         # keep the enumeration usable in target-independent code.
-        self._event_enum_members.append(QAPISchemaEnumMember(name))
+        self._event_enum_members.append(QAPISchemaEnumMember(name, None))
 
 
 def gen_events(schema, output_dir, prefix):
-- 
2.21.0



^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH 15/25] qapi: Inline check_name() into check_union()
  2019-09-24 13:28 [PATCH 00/25] qapi: Pay back some frontend technical debt Markus Armbruster
                   ` (13 preceding siblings ...)
  2019-09-24 13:28 ` [PATCH 14/25] qapi: Plumb info to the QAPISchemaMember Markus Armbruster
@ 2019-09-24 13:28 ` Markus Armbruster
  2019-09-24 16:39   ` Eric Blake
  2019-09-24 13:28 ` [PATCH 16/25] qapi: Move context-sensitive checking to the proper place Markus Armbruster
                   ` (9 subsequent siblings)
  24 siblings, 1 reply; 63+ messages in thread
From: Markus Armbruster @ 2019-09-24 13:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, mdroth

check_name() consists of check_name_is_str() and check_name_str().
check_union() relies on the latter to catch optional discriminators.
The next commit will replace that by a more straightforward check.
Inlining check_name() into check_union() now should make that easier
to review.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 scripts/qapi/common.py | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index 2dd794dfe7..f5599559ac 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -896,8 +896,10 @@ def check_union(expr, info):
 
         # The value of member 'discriminator' must name a non-optional
         # member of the base struct.
-        check_name(discriminator, info,
-                   "discriminator of flat union '%s'" % name)
+        check_name_is_str(discriminator, info,
+                          "discriminator of flat union '%s'" % name)
+        check_name_str(discriminator, info,
+                       "discriminator of flat union '%s'" % name)
         discriminator_value = base_members.get(discriminator)
         if not discriminator_value:
             raise QAPISemError(info,
-- 
2.21.0



^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH 16/25] qapi: Move context-sensitive checking to the proper place
  2019-09-24 13:28 [PATCH 00/25] qapi: Pay back some frontend technical debt Markus Armbruster
                   ` (14 preceding siblings ...)
  2019-09-24 13:28 ` [PATCH 15/25] qapi: Inline check_name() into check_union() Markus Armbruster
@ 2019-09-24 13:28 ` Markus Armbruster
  2019-09-24 17:49   ` Eric Blake
  2019-09-24 13:28 ` [PATCH 17/25] qapi: Move context-free " Markus Armbruster
                   ` (8 subsequent siblings)
  24 siblings, 1 reply; 63+ messages in thread
From: Markus Armbruster @ 2019-09-24 13:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, mdroth

When we introduced the QAPISchema intermediate representation (commit
ac88219a6c7), we took a shortcut: we left check_exprs() & friends
alone instead of moving semantic checks into the
QAPISchemaFOO.check().  The .check() assert check_exprs() did its job.

Time to finish the conversion job.  Move exactly the context-sensitive
checks to the .check().  They replace assertions there.  Context-free
checks stay put.

Fixes the misleading optional tag error demonstrated by test
flat-union-optional-discriminator.

A few other error message improve.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 scripts/qapi/common.py                        | 422 ++++++++----------
 tests/qapi-schema/alternate-any.err           |   2 +-
 .../alternate-conflict-bool-string.err        |   2 +-
 tests/qapi-schema/alternate-conflict-dict.err |   2 +-
 .../alternate-conflict-enum-bool.err          |   2 +-
 .../alternate-conflict-enum-int.err           |   2 +-
 .../alternate-conflict-num-string.err         |   2 +-
 .../qapi-schema/alternate-conflict-string.err |   2 +-
 tests/qapi-schema/alternate-nested.err        |   2 +-
 tests/qapi-schema/alternate-unknown.err       |   2 +-
 tests/qapi-schema/args-alternate.err          |   2 +-
 tests/qapi-schema/args-any.err                |   2 +-
 tests/qapi-schema/args-array-unknown.err      |   2 +-
 tests/qapi-schema/args-boxed-string.err       |   2 +-
 tests/qapi-schema/args-int.err                |   2 +-
 tests/qapi-schema/args-member-unknown.err     |   2 +-
 tests/qapi-schema/args-union.err              |   2 +-
 tests/qapi-schema/args-unknown.err            |   2 +-
 tests/qapi-schema/bad-base.err                |   2 +-
 tests/qapi-schema/command-int.err             |   2 +-
 tests/qapi-schema/flat-union-base-any.err     |   2 +-
 tests/qapi-schema/flat-union-base-union.err   |   2 +-
 .../flat-union-discriminator-bad-name.err     |   2 +-
 .../flat-union-discriminator-bad-name.json    |   1 -
 tests/qapi-schema/flat-union-empty.err        |   2 +-
 tests/qapi-schema/flat-union-int-branch.err   |   2 +-
 .../flat-union-invalid-branch-key.err         |   2 +-
 .../flat-union-invalid-if-discriminator.err   |   2 +-
 .../flat-union-optional-discriminator.err     |   2 +-
 .../flat-union-optional-discriminator.json    |   1 -
 .../flat-union-string-discriminator.err       |   2 +-
 tests/qapi-schema/redefined-builtin.err       |   2 +-
 tests/qapi-schema/redefined-type.err          |   2 +-
 tests/qapi-schema/returns-alternate.err       |   2 +-
 tests/qapi-schema/returns-unknown.err         |   2 +-
 tests/qapi-schema/returns-whitelist.err       |   2 +-
 tests/qapi-schema/union-empty.err             |   2 +-
 tests/qapi-schema/union-invalid-base.err      |   2 +-
 tests/qapi-schema/union-unknown.err           |   2 +-
 tests/qapi-schema/union-unknown.json          |   2 +-
 40 files changed, 228 insertions(+), 270 deletions(-)

diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index f5599559ac..ac4c898e51 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -21,25 +21,6 @@ import string
 import sys
 from collections import OrderedDict
 
-builtin_types = {
-    'null':     'QTYPE_QNULL',
-    'str':      'QTYPE_QSTRING',
-    'int':      'QTYPE_QNUM',
-    'number':   'QTYPE_QNUM',
-    'bool':     'QTYPE_QBOOL',
-    'int8':     'QTYPE_QNUM',
-    'int16':    'QTYPE_QNUM',
-    'int32':    'QTYPE_QNUM',
-    'int64':    'QTYPE_QNUM',
-    'uint8':    'QTYPE_QNUM',
-    'uint16':   'QTYPE_QNUM',
-    'uint32':   'QTYPE_QNUM',
-    'uint64':   'QTYPE_QNUM',
-    'size':     'QTYPE_QNUM',
-    'any':      None,           # any QType possible, actually
-    'QType':    'QTYPE_QSTRING',
-}
-
 # Are documentation comments required?
 doc_required = False
 
@@ -49,11 +30,6 @@ returns_whitelist = []
 # Whitelist of entities allowed to violate case conventions
 name_case_whitelist = []
 
-enum_types = {}
-struct_types = {}
-union_types = {}
-all_names = {}
-
 
 #
 # Parsing the schema into expressions
@@ -670,34 +646,9 @@ class QAPISchemaParser(object):
 
 
 #
-# Semantic analysis of schema expressions
-# TODO fold into QAPISchema
-# TODO catching name collisions in generated code would be nice
+# Check (context-free) schema expression structure
 #
 
-
-def find_base_members(base):
-    if isinstance(base, dict):
-        return base
-    base_struct_define = struct_types.get(base)
-    if not base_struct_define:
-        return None
-    return base_struct_define['data']
-
-
-# Return the qtype of an alternate branch, or None on error.
-def find_alternate_member_qtype(qapi_type):
-    if qapi_type in builtin_types:
-        return builtin_types[qapi_type]
-    elif qapi_type in struct_types:
-        return 'QTYPE_QDICT'
-    elif qapi_type in enum_types:
-        return 'QTYPE_QSTRING'
-    elif qapi_type in union_types:
-        return 'QTYPE_QDICT'
-    return None
-
-
 # Names must be letters, numbers, -, and _.  They must start with letter,
 # except for downstream extensions which must start with __RFQDN_.
 # Dots are only valid in the downstream extension prefix.
@@ -747,16 +698,6 @@ def check_defn_name_str(name, info, meta):
                            % (meta, name, name[-4:]))
 
 
-def add_name(name, info, meta):
-    global all_names
-    # FIXME should reject names that differ only in '_' vs. '.'
-    # vs. '-', because they're liable to clash in generated C.
-    if name in all_names:
-        raise QAPISemError(info, "%s '%s' is already defined"
-                           % (all_names[name], name))
-    all_names[name] = meta
-
-
 def check_if(expr, info):
 
     def check_if_str(ifcond, info):
@@ -780,13 +721,11 @@ def check_if(expr, info):
 
 
 def check_type(value, info, source,
-               allow_array=False, allow_dict=False, allow_metas=[]):
-    global all_names
-
+               allow_array=False, allow_dict=False):
     if value is None:
         return
 
-    # Check if array type for value is okay
+    # Array type
     if isinstance(value, list):
         if not allow_array:
             raise QAPISemError(info, "%s cannot be an array" % source)
@@ -794,19 +733,14 @@ def check_type(value, info, source,
             raise QAPISemError(info,
                                "%s: array type must contain single type name" %
                                source)
-        check_type(value[0], info, source, allow_metas=allow_metas)
         return
 
-    # Check if type name for value is okay
+    # Type name
     if isinstance(value, str):
-        if value not in all_names:
-            raise QAPISemError(info, "%s uses unknown type '%s'"
-                               % (source, value))
-        if not all_names[value] in allow_metas:
-            raise QAPISemError(info, "%s cannot use %s type '%s'" %
-                               (source, all_names[value], value))
         return
 
+    # Anonymous type
+
     if not allow_dict:
         raise QAPISemError(info, "%s should be a type name" % source)
 
@@ -828,43 +762,28 @@ def check_type(value, info, source,
         check_if(arg, info)
         normalize_if(arg)
         check_type(arg['type'], info, "member '%s' of %s" % (key, source),
-                   allow_array=True,
-                   allow_metas=['built-in', 'union', 'alternate', 'struct',
-                                'enum'])
+                   allow_array=True)
 
 
 def check_command(expr, info):
     name = expr['command']
     boxed = expr.get('boxed', False)
 
-    args_meta = ['struct']
-    if boxed:
-        args_meta += ['union']
     check_type(expr.get('data'), info,
                "'data' for command '%s'" % name,
-               allow_dict=not boxed, allow_metas=args_meta)
-    returns_meta = ['union', 'struct']
-    if name in returns_whitelist:
-        returns_meta += ['built-in', 'alternate', 'enum']
+               allow_dict=not boxed)
     check_type(expr.get('returns'), info,
                "'returns' for command '%s'" % name,
-               allow_array=True, allow_metas=returns_meta)
+               allow_array=True)
 
 
 def check_event(expr, info):
     name = expr['event']
     boxed = expr.get('boxed', False)
 
-    meta = ['struct']
-    if boxed:
-        meta += ['union']
     check_type(expr.get('data'), info,
                "'data' for event '%s'" % name,
-               allow_dict=not boxed, allow_metas=meta)
-
-
-def enum_get_names(expr):
-    return [e['name'] for e in expr['data']]
+               allow_dict=not boxed)
 
 
 def check_union(expr, info):
@@ -873,55 +792,18 @@ def check_union(expr, info):
     discriminator = expr.get('discriminator')
     members = expr['data']
 
-    # Two types of unions, determined by discriminator.
-
-    # With no discriminator it is a simple union.
-    if discriminator is None:
-        enum_values = members.keys()
-        allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
+    if discriminator is None:   # simple union
         if base is not None:
             raise QAPISemError(
                 info, "simple union '%s' must not have a base" % name)
-
-    # Else, it's a flat union.
-    else:
-        # The object must have a string or dictionary 'base'.
+    else:                       # flat union
         check_type(base, info, "'base' for union '%s'" % name,
-                   allow_dict=name, allow_metas=['struct'])
+                   allow_dict=name)
         if not base:
             raise QAPISemError(info, "flat union '%s' must have a base"
                                % name)
-        base_members = find_base_members(base)
-        assert base_members is not None
-
-        # The value of member 'discriminator' must name a non-optional
-        # member of the base struct.
         check_name_is_str(discriminator, info,
                           "discriminator of flat union '%s'" % name)
-        check_name_str(discriminator, info,
-                       "discriminator of flat union '%s'" % name)
-        discriminator_value = base_members.get(discriminator)
-        if not discriminator_value:
-            raise QAPISemError(info,
-                               "discriminator '%s' is not a member of 'base'"
-                               % discriminator)
-        if discriminator_value.get('if'):
-            raise QAPISemError(
-                info,
-                "the discriminator '%s' for union %s must not be conditional"
-                % (discriminator, name))
-        enum_define = enum_types.get(discriminator_value['type'])
-        # Do not allow string discriminator
-        if not enum_define:
-            raise QAPISemError(
-                info,
-                "discriminator '%s' must be of enumeration type"
-                % discriminator)
-        enum_values = enum_get_names(enum_define)
-        allow_metas = ['struct']
-
-    if (len(enum_values) == 0):
-        raise QAPISemError(info, "union '%s' has no branches" % name)
 
     for (key, value) in members.items():
         check_name_str(key, info, "member of union '%s'" % name)
@@ -930,25 +812,14 @@ def check_union(expr, info):
                          ['type'], ['if'])
         check_if(value, info)
         normalize_if(value)
-        # Each value must name a known type
         check_type(value['type'], info,
                    "member '%s' of union '%s'" % (key, name),
-                   allow_array=not base, allow_metas=allow_metas)
-
-        # If the discriminator names an enum type, then all members
-        # of 'data' must also be members of the enum type.
-        if discriminator is not None:
-            if key not in enum_values:
-                raise QAPISemError(
-                    info,
-                    "discriminator value '%s' is not found in enum '%s'"
-                    % (key, enum_define['enum']))
+                   allow_array=not base)
 
 
 def check_alternate(expr, info):
     name = expr['alternate']
     members = expr['data']
-    types_seen = {}
 
     if len(members) == 0:
         raise QAPISemError(info,
@@ -960,37 +831,8 @@ def check_alternate(expr, info):
                          ['type'], ['if'])
         check_if(value, info)
         normalize_if(value)
-        typ = value['type']
-
-        # Ensure alternates have no type conflicts.
-        check_type(typ, info, "member '%s' of alternate '%s'" % (key, name),
-                   allow_metas=['built-in', 'union', 'struct', 'enum'])
-        qtype = find_alternate_member_qtype(typ)
-        if not qtype:
-            raise QAPISemError(
-                info,
-                "alternate '%s' member '%s' cannot use type '%s'"
-                % (name, key, typ))
-        conflicting = set([qtype])
-        if qtype == 'QTYPE_QSTRING':
-            enum_expr = enum_types.get(typ)
-            if enum_expr:
-                for v in enum_get_names(enum_expr):
-                    if v in ['on', 'off']:
-                        conflicting.add('QTYPE_QBOOL')
-                    if re.match(r'[-+0-9.]', v): # lazy, could be tightened
-                        conflicting.add('QTYPE_QNUM')
-            else:
-                conflicting.add('QTYPE_QNUM')
-                conflicting.add('QTYPE_QBOOL')
-        for qt in conflicting:
-            if qt in types_seen:
-                raise QAPISemError(
-                    info,
-                    "alternate '%s' member '%s' can't be distinguished "
-                    "from member '%s'"
-                    % (name, key, types_seen[qt]))
-            types_seen[qt] = key
+        check_type(value['type'], info,
+                   "member '%s' of alternate '%s'" % (key, name))
 
 
 def check_enum(expr, info):
@@ -1023,8 +865,7 @@ def check_struct(expr, info):
 
     check_type(members, info, "'data' for struct '%s'" % name,
                allow_dict=name)
-    check_type(expr.get('base'), info, "'base' for struct '%s'" % name,
-               allow_metas=['struct'])
+    check_type(expr.get('base'), info, "'base' for struct '%s'" % name)
 
     if features:
         if not isinstance(features, list):
@@ -1106,13 +947,6 @@ def normalize_if(expr):
 
 
 def check_exprs(exprs):
-    global all_names
-
-    # Populate name table with names of built-in types
-    for builtin in builtin_types.keys():
-        all_names[builtin] = 'built-in'
-
-    # Learn the types and check for valid expression keys
     for expr_elem in exprs:
         expr = expr_elem['expr']
         info = expr_elem['info']
@@ -1129,14 +963,12 @@ def check_exprs(exprs):
             meta = 'enum'
             check_keys(expr, info, 'enum', ['data'], ['if', 'prefix'])
             normalize_enum(expr)
-            enum_types[expr[meta]] = expr
         elif 'union' in expr:
             meta = 'union'
             check_keys(expr, info, 'union', ['data'],
                        ['base', 'discriminator', 'if'])
             normalize_members(expr.get('base'))
             normalize_members(expr['data'])
-            union_types[expr[meta]] = expr
         elif 'alternate' in expr:
             meta = 'alternate'
             check_keys(expr, info, 'alternate', ['data'], ['if'])
@@ -1147,7 +979,6 @@ def check_exprs(exprs):
                        ['base', 'if', 'features'])
             normalize_members(expr['data'])
             normalize_features(expr.get('features'))
-            struct_types[expr[meta]] = expr
         elif 'command' in expr:
             meta = 'command'
             check_keys(expr, info, 'command', [],
@@ -1161,36 +992,29 @@ def check_exprs(exprs):
         else:
             raise QAPISemError(info, "expression is missing metatype")
         normalize_if(expr)
+
         name = expr[meta]
         check_name_is_str(name, info, "'%s'" % meta)
         info.set_defn(meta, name)
         check_defn_name_str(name, info, meta)
-        add_name(name, info, meta)
+
         if doc and doc.symbol != name:
             raise QAPISemError(
                 info,
                 "definition of '%s' follows documentation for '%s'"
                 % (name, doc.symbol))
 
-    # Validate that exprs make sense
-    for expr_elem in exprs:
-        expr = expr_elem['expr']
-        info = expr_elem['info']
-        doc = expr_elem.get('doc')
-
-        if 'include' in expr:
-            continue
-        if 'enum' in expr:
+        if meta == 'enum':
             check_enum(expr, info)
-        elif 'union' in expr:
+        elif meta == 'union':
             check_union(expr, info)
-        elif 'alternate' in expr:
+        elif meta == 'alternate':
             check_alternate(expr, info)
-        elif 'struct' in expr:
+        elif meta == 'struct':
             check_struct(expr, info)
-        elif 'command' in expr:
+        elif meta == 'command':
             check_command(expr, info)
-        elif 'event' in expr:
+        elif meta == 'event':
             check_event(expr, info)
         else:
             assert False, 'unexpected meta type'
@@ -1203,9 +1027,12 @@ def check_exprs(exprs):
 
 #
 # Schema compiler frontend
+# TODO catching name collisions in generated code would be nice
 #
 
 class QAPISchemaEntity(object):
+    meta = None
+
     def __init__(self, name, info, doc, ifcond=None):
         assert name is None or isinstance(name, str)
         self.name = name
@@ -1246,6 +1073,10 @@ class QAPISchemaEntity(object):
     def visit(self, visitor):
         assert self._checked
 
+    def describe(self):
+        assert self.meta
+        return "%s '%s'" % (self.meta, self.name)
+
 
 class QAPISchemaVisitor(object):
     def visit_begin(self, schema):
@@ -1336,8 +1167,14 @@ class QAPISchemaType(QAPISchemaEntity):
             return None
         return self.name
 
+    def describe(self):
+        assert self.meta
+        return "%s type '%s'" % (self.meta, self.name)
+
 
 class QAPISchemaBuiltinType(QAPISchemaType):
+    meta = 'built-in'
+
     def __init__(self, name, json_type, c_type):
         QAPISchemaType.__init__(self, name, None, None)
         assert not c_type or isinstance(c_type, str)
@@ -1369,6 +1206,8 @@ class QAPISchemaBuiltinType(QAPISchemaType):
 
 
 class QAPISchemaEnumType(QAPISchemaType):
+    meta = 'enum'
+
     def __init__(self, name, info, doc, ifcond, members, prefix):
         QAPISchemaType.__init__(self, name, info, doc, ifcond)
         for m in members:
@@ -1406,6 +1245,8 @@ class QAPISchemaEnumType(QAPISchemaType):
 
 
 class QAPISchemaArrayType(QAPISchemaType):
+    meta = 'array'
+
     def __init__(self, name, info, element_type):
         QAPISchemaType.__init__(self, name, info, None, None)
         assert isinstance(element_type, str)
@@ -1414,8 +1255,9 @@ class QAPISchemaArrayType(QAPISchemaType):
 
     def check(self, schema):
         QAPISchemaType.check(self, schema)
-        self.element_type = schema.lookup_type(self._element_type_name)
-        assert self.element_type
+        self.element_type = schema.resolve_type(
+            self._element_type_name, self.info,
+            self.info and self.info.defn_meta)
         assert not isinstance(self.element_type, QAPISchemaArrayType)
 
     @property
@@ -1448,6 +1290,10 @@ class QAPISchemaArrayType(QAPISchemaType):
         visitor.visit_array_type(self.name, self.info, self.ifcond,
                                  self.element_type)
 
+    def describe(self):
+        assert self.meta
+        return "%s type ['%s']" % (self.meta, self._element_type_name)
+
 
 class QAPISchemaObjectType(QAPISchemaType):
     def __init__(self, name, info, doc, ifcond,
@@ -1456,6 +1302,7 @@ class QAPISchemaObjectType(QAPISchemaType):
         # flat union has base, variants, and no local_members
         # simple union has local_members, variants, and no base
         QAPISchemaType.__init__(self, name, info, doc, ifcond)
+        self.meta = 'union' if variants else 'struct'
         assert base is None or isinstance(base, str)
         for m in local_members:
             assert isinstance(m, QAPISchemaObjectTypeMember)
@@ -1490,8 +1337,14 @@ class QAPISchemaObjectType(QAPISchemaType):
 
         seen = OrderedDict()
         if self._base_name:
-            self.base = schema.lookup_type(self._base_name)
-            assert isinstance(self.base, QAPISchemaObjectType)
+            self.base = schema.resolve_type(self._base_name, self.info,
+                                            "'base'")
+            if (not isinstance(self.base, QAPISchemaObjectType)
+                    or self.base.variants):
+                raise QAPISemError(
+                    self.info,
+                    "'base' requires a struct type, %s isn't"
+                    % self.base.describe())
             self.base.check(schema)
             self.base.check_clash(self.info, seen)
         for m in self.local_members:
@@ -1503,7 +1356,6 @@ class QAPISchemaObjectType(QAPISchemaType):
 
         if self.variants:
             self.variants.check(schema, seen)
-            assert self.variants.tag_member in members
             self.variants.check_clash(self.info, seen)
 
         # Features are in a name space separate from members
@@ -1640,8 +1492,8 @@ class QAPISchemaObjectTypeMember(QAPISchemaMember):
 
     def check(self, schema):
         assert self.defined_in
-        self.type = schema.lookup_type(self._type_name)
-        assert self.type
+        self.type = schema.resolve_type(self._type_name, self.info,
+                                        self.describe)
 
 
 class QAPISchemaObjectTypeVariants(object):
@@ -1665,12 +1517,40 @@ class QAPISchemaObjectTypeVariants(object):
             v.set_defined_in(name)
 
     def check(self, schema, seen):
-        if not self.tag_member:    # flat union
-            self.tag_member = seen[c_name(self._tag_name)]
-            assert self._tag_name == self.tag_member.name
-        assert isinstance(self.tag_member.type, QAPISchemaEnumType)
-        assert not self.tag_member.optional
-        assert self.tag_member.ifcond == []
+        if not self.tag_member: # flat union
+            self.tag_member = seen.get(c_name(self._tag_name))
+            base = "'base'"
+            # Pointing to the base type when not implicit would be
+            # nice, but we don't know it here
+            if not self.tag_member or self._tag_name != self.tag_member.name:
+                raise QAPISemError(
+                    self.info,
+                    "discriminator '%s' is not a member of %s"
+                    % (self._tag_name, base))
+            # Here we do:
+            base_type = schema.lookup_type(self.tag_member.defined_in)
+            assert base_type
+            if not base_type.is_implicit():
+                base = "base type '%s'" % self.tag_member.defined_in
+            if not isinstance(self.tag_member.type, QAPISchemaEnumType):
+                raise QAPISemError(
+                    self.info,
+                    "discriminator member '%s' of %s must be of enum type"
+                    % (self._tag_name, base))
+            if self.tag_member.optional:
+                raise QAPISemError(
+                    self.info,
+                    "discriminator member '%s' of %s must not be optional"
+                    % (self._tag_name, base))
+            if self.tag_member.ifcond:
+                raise QAPISemError(
+                    self.info,
+                    "discriminator member '%s' of %s must not be conditional"
+                    % (self._tag_name, base))
+        else:                   # simple union
+            assert isinstance(self.tag_member.type, QAPISchemaEnumType)
+            assert not self.tag_member.optional
+            assert self.tag_member.ifcond == []
         if self._tag_name:    # flat union
             # branches that are not explicitly covered get an empty type
             cases = set([v.name for v in self.variants])
@@ -1680,15 +1560,23 @@ class QAPISchemaObjectTypeVariants(object):
                                                     'q_empty', m.ifcond)
                     v.set_defined_in(self.tag_member.defined_in)
                     self.variants.append(v)
-        assert self.variants
+        if not self.variants:
+            raise QAPISemError(self.info, "union has no branches")
         for v in self.variants:
             v.check(schema)
             # Union names must match enum values; alternate names are
             # checked separately. Use 'seen' to tell the two apart.
             if seen:
-                assert v.name in self.tag_member.type.member_names()
-                assert (isinstance(v.type, QAPISchemaObjectType)
-                        and not v.type.variants)
+                if v.name not in self.tag_member.type.member_names():
+                    raise QAPISemError(
+                        self.info,
+                        "branch '%s' is not a value of %s"
+                        % (v.name, self.tag_member.type.describe()))
+                if (not isinstance(v.type, QAPISchemaObjectType)
+                        or v.type.variants):
+                    raise QAPISemError(
+                        self.info, "%s cannot use %s"
+                        % (v.describe(self.info), v.type.describe()))
                 v.type.check(schema)
 
     def check_clash(self, info, seen):
@@ -1707,6 +1595,8 @@ class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
 
 
 class QAPISchemaAlternateType(QAPISchemaType):
+    meta = 'alternate'
+
     def __init__(self, name, info, doc, ifcond, variants):
         QAPISchemaType.__init__(self, name, info, doc, ifcond)
         assert isinstance(variants, QAPISchemaObjectTypeVariants)
@@ -1724,9 +1614,33 @@ class QAPISchemaAlternateType(QAPISchemaType):
         # Alternate branch names have no relation to the tag enum values;
         # so we have to check for potential name collisions ourselves.
         seen = {}
+        types_seen = {}
         for v in self.variants.variants:
             v.check_clash(self.info, seen)
-            # TODO check conflicting qtypes
+            qtype = v.type.alternate_qtype()
+            if not qtype:
+                raise QAPISemError(
+                    self.info, "%s cannot use %s"
+                    % (v.describe(self.info), v.type.describe()))
+            conflicting = set([qtype])
+            if qtype == 'QTYPE_QSTRING':
+                if isinstance(v.type, QAPISchemaEnumType):
+                    for m in v.type.members:
+                        if m.name in ['on', 'off']:
+                            conflicting.add('QTYPE_QBOOL')
+                        if re.match(r'[-+0-9.]', m.name):
+                            # lazy, could be tightened
+                            conflicting.add('QTYPE_QNUM')
+                else:
+                    conflicting.add('QTYPE_QNUM')
+                    conflicting.add('QTYPE_QBOOL')
+            for qt in conflicting:
+                if qt in types_seen:
+                    raise QAPISemError(
+                        self.info,
+                        "%s can't be distinguished from '%s'"
+                        % (v.describe(self.info), types_seen[qt]))
+                types_seen[qt] = v.name
             if self.doc:
                 self.doc.connect_member(v)
         if self.doc:
@@ -1745,6 +1659,8 @@ class QAPISchemaAlternateType(QAPISchemaType):
 
 
 class QAPISchemaCommand(QAPISchemaEntity):
+    meta = 'command'
+
     def __init__(self, name, info, doc, ifcond, arg_type, ret_type,
                  gen, success_response, boxed, allow_oob, allow_preconfig):
         QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
@@ -1763,14 +1679,32 @@ class QAPISchemaCommand(QAPISchemaEntity):
     def check(self, schema):
         QAPISchemaEntity.check(self, schema)
         if self._arg_type_name:
-            self.arg_type = schema.lookup_type(self._arg_type_name)
-            assert isinstance(self.arg_type, QAPISchemaObjectType)
-            assert not self.arg_type.variants or self.boxed
+            self.arg_type = schema.resolve_type(
+                self._arg_type_name, self.info, "command's 'data'")
+            if not isinstance(self.arg_type, QAPISchemaObjectType):
+                raise QAPISemError(
+                    self.info,
+                    "command's 'data' cannot take %s"
+                    % self.arg_type.describe())
+            if self.arg_type.variants and not self.boxed:
+                raise QAPISemError(
+                    self.info,
+                    "command's 'data' can take %s only with 'boxed': true"
+                    % self.arg_type.describe())
         elif self.boxed:
             raise QAPISemError(self.info, "use of 'boxed' requires 'data'")
         if self._ret_type_name:
-            self.ret_type = schema.lookup_type(self._ret_type_name)
-            assert isinstance(self.ret_type, QAPISchemaType)
+            self.ret_type = schema.resolve_type(
+                self._ret_type_name, self.info, "command's 'returns'")
+            if self.name not in returns_whitelist:
+                if not (isinstance(self.ret_type, QAPISchemaObjectType)
+                        or (isinstance(self.ret_type, QAPISchemaArrayType)
+                            and isinstance(self.ret_type.element_type,
+                                           QAPISchemaObjectType))):
+                    raise QAPISemError(
+                        self.info,
+                        "command's 'returns' cannot take %s"
+                        % self.ret_type.describe())
 
     def visit(self, visitor):
         QAPISchemaEntity.visit(self, visitor)
@@ -1782,6 +1716,8 @@ class QAPISchemaCommand(QAPISchemaEntity):
 
 
 class QAPISchemaEvent(QAPISchemaEntity):
+    meta = 'event'
+
     def __init__(self, name, info, doc, ifcond, arg_type, boxed):
         QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
         assert not arg_type or isinstance(arg_type, str)
@@ -1792,9 +1728,18 @@ class QAPISchemaEvent(QAPISchemaEntity):
     def check(self, schema):
         QAPISchemaEntity.check(self, schema)
         if self._arg_type_name:
-            self.arg_type = schema.lookup_type(self._arg_type_name)
-            assert isinstance(self.arg_type, QAPISchemaObjectType)
-            assert not self.arg_type.variants or self.boxed
+            self.arg_type = schema.resolve_type(
+                self._arg_type_name, self.info, "event's 'data'")
+            if not isinstance(self.arg_type, QAPISchemaObjectType):
+                raise QAPISemError(
+                    self.info,
+                    "event's 'data' cannot take %s"
+                    % self.arg_type.describe())
+            if self.arg_type.variants and not self.boxed:
+                raise QAPISemError(
+                    self.info,
+                    "event's 'data' can take %s only with 'boxed': true"
+                    % self.arg_type.describe())
         elif self.boxed:
             raise QAPISemError(self.info, "use of 'boxed' requires 'data'")
 
@@ -1825,10 +1770,16 @@ class QAPISchema(object):
     def _def_entity(self, ent):
         # Only the predefined types are allowed to not have info
         assert ent.info or self._predefining
-        assert ent.name is None or ent.name not in self._entity_dict
         self._entity_list.append(ent)
-        if ent.name is not None:
-            self._entity_dict[ent.name] = ent
+        if ent.name is None:
+            return
+        # TODO reject names that differ only in '_' vs. '.'  vs. '-',
+        # because they're liable to clash in generated C.
+        other_ent = self._entity_dict.get(ent.name)
+        if other_ent:
+            raise QAPISemError(
+                ent.info, "%s is already defined" % other_ent.describe())
+        self._entity_dict[ent.name] = ent
 
     def lookup_entity(self, name, typ=None):
         ent = self._entity_dict.get(name)
@@ -1839,6 +1790,15 @@ class QAPISchema(object):
     def lookup_type(self, name):
         return self.lookup_entity(name, QAPISchemaType)
 
+    def resolve_type(self, name, info, what):
+        typ = self.lookup_type(name)
+        if not typ:
+            if callable(what):
+                what = what(info)
+            raise QAPISemError(
+                info, "%s uses unknown type '%s'" % (what, name))
+        return typ
+
     def _def_include(self, expr, info, doc):
         include = expr['include']
         assert doc is None
@@ -1924,7 +1884,7 @@ class QAPISchema(object):
             # But it's not tight: the disjunction need not imply it.  We
             # may end up compiling useless wrapper types.
             # TODO kill simple unions or implement the disjunction
-            assert ifcond == typ._ifcond # pylint: disable=protected-access
+            assert (ifcond or []) == typ._ifcond # pylint: disable=protected-access
         else:
             self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond,
                                                   None, members, None, []))
diff --git a/tests/qapi-schema/alternate-any.err b/tests/qapi-schema/alternate-any.err
index 5314760775..03aaf29506 100644
--- a/tests/qapi-schema/alternate-any.err
+++ b/tests/qapi-schema/alternate-any.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/alternate-any.json: In alternate 'Alt':
-tests/qapi-schema/alternate-any.json:2: alternate 'Alt' member 'one' cannot use type 'any'
+tests/qapi-schema/alternate-any.json:2: branch 'one' cannot use built-in type 'any'
diff --git a/tests/qapi-schema/alternate-conflict-bool-string.err b/tests/qapi-schema/alternate-conflict-bool-string.err
index e2b89f3cac..f7513b9cbe 100644
--- a/tests/qapi-schema/alternate-conflict-bool-string.err
+++ b/tests/qapi-schema/alternate-conflict-bool-string.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/alternate-conflict-bool-string.json: In alternate 'Alt':
-tests/qapi-schema/alternate-conflict-bool-string.json:2: alternate 'Alt' member 'two' can't be distinguished from member 'one'
+tests/qapi-schema/alternate-conflict-bool-string.json:2: branch 'two' can't be distinguished from 'one'
diff --git a/tests/qapi-schema/alternate-conflict-dict.err b/tests/qapi-schema/alternate-conflict-dict.err
index e3f7d9072d..e5b42d04c9 100644
--- a/tests/qapi-schema/alternate-conflict-dict.err
+++ b/tests/qapi-schema/alternate-conflict-dict.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/alternate-conflict-dict.json: In alternate 'Alt':
-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:6: branch 'two' can't be distinguished from 'one'
diff --git a/tests/qapi-schema/alternate-conflict-enum-bool.err b/tests/qapi-schema/alternate-conflict-enum-bool.err
index d0fe1433fc..3d23aeba51 100644
--- a/tests/qapi-schema/alternate-conflict-enum-bool.err
+++ b/tests/qapi-schema/alternate-conflict-enum-bool.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/alternate-conflict-enum-bool.json: In alternate 'Alt':
-tests/qapi-schema/alternate-conflict-enum-bool.json:4: alternate 'Alt' member 'two' can't be distinguished from member 'one'
+tests/qapi-schema/alternate-conflict-enum-bool.json:4: branch 'two' can't be distinguished from 'one'
diff --git a/tests/qapi-schema/alternate-conflict-enum-int.err b/tests/qapi-schema/alternate-conflict-enum-int.err
index 866428b1d1..b72768caa4 100644
--- a/tests/qapi-schema/alternate-conflict-enum-int.err
+++ b/tests/qapi-schema/alternate-conflict-enum-int.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/alternate-conflict-enum-int.json: In alternate 'Alt':
-tests/qapi-schema/alternate-conflict-enum-int.json:4: alternate 'Alt' member 'two' can't be distinguished from member 'one'
+tests/qapi-schema/alternate-conflict-enum-int.json:4: branch 'two' can't be distinguished from 'one'
diff --git a/tests/qapi-schema/alternate-conflict-num-string.err b/tests/qapi-schema/alternate-conflict-num-string.err
index d00975453e..b8a2bb1829 100644
--- a/tests/qapi-schema/alternate-conflict-num-string.err
+++ b/tests/qapi-schema/alternate-conflict-num-string.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/alternate-conflict-num-string.json: In alternate 'Alt':
-tests/qapi-schema/alternate-conflict-num-string.json:2: alternate 'Alt' member 'two' can't be distinguished from member 'one'
+tests/qapi-schema/alternate-conflict-num-string.json:2: branch 'two' can't be distinguished from 'one'
diff --git a/tests/qapi-schema/alternate-conflict-string.err b/tests/qapi-schema/alternate-conflict-string.err
index 0231f4fed1..3edec51911 100644
--- a/tests/qapi-schema/alternate-conflict-string.err
+++ b/tests/qapi-schema/alternate-conflict-string.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/alternate-conflict-string.json: In alternate 'Alt':
-tests/qapi-schema/alternate-conflict-string.json:2: alternate 'Alt' member 'two' can't be distinguished from member 'one'
+tests/qapi-schema/alternate-conflict-string.json:2: branch 'two' can't be distinguished from 'one'
diff --git a/tests/qapi-schema/alternate-nested.err b/tests/qapi-schema/alternate-nested.err
index 03fb9fb212..cd7a076ce5 100644
--- a/tests/qapi-schema/alternate-nested.err
+++ b/tests/qapi-schema/alternate-nested.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/alternate-nested.json: In alternate 'Alt2':
-tests/qapi-schema/alternate-nested.json:4: member 'nested' of alternate 'Alt2' cannot use alternate type 'Alt1'
+tests/qapi-schema/alternate-nested.json:4: branch 'nested' cannot use alternate type 'Alt1'
diff --git a/tests/qapi-schema/alternate-unknown.err b/tests/qapi-schema/alternate-unknown.err
index 7a533b2b74..df05860bba 100644
--- a/tests/qapi-schema/alternate-unknown.err
+++ b/tests/qapi-schema/alternate-unknown.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/alternate-unknown.json: In alternate 'Alt':
-tests/qapi-schema/alternate-unknown.json:2: member 'unknown' of alternate 'Alt' uses unknown type 'MissingType'
+tests/qapi-schema/alternate-unknown.json:2: branch 'unknown' uses unknown type 'MissingType'
diff --git a/tests/qapi-schema/args-alternate.err b/tests/qapi-schema/args-alternate.err
index 915f5d463b..852b81b89c 100644
--- a/tests/qapi-schema/args-alternate.err
+++ b/tests/qapi-schema/args-alternate.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/args-alternate.json: In command 'oops':
-tests/qapi-schema/args-alternate.json:3: 'data' for command 'oops' cannot use alternate type 'Alt'
+tests/qapi-schema/args-alternate.json:3: command's 'data' cannot take alternate type 'Alt'
diff --git a/tests/qapi-schema/args-any.err b/tests/qapi-schema/args-any.err
index 8c4f9aeef4..04e11df29f 100644
--- a/tests/qapi-schema/args-any.err
+++ b/tests/qapi-schema/args-any.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/args-any.json: In command 'oops':
-tests/qapi-schema/args-any.json:2: 'data' for command 'oops' cannot use built-in type 'any'
+tests/qapi-schema/args-any.json:2: command's 'data' cannot take built-in type 'any'
diff --git a/tests/qapi-schema/args-array-unknown.err b/tests/qapi-schema/args-array-unknown.err
index 57b0d8972e..218fc4bf9a 100644
--- a/tests/qapi-schema/args-array-unknown.err
+++ b/tests/qapi-schema/args-array-unknown.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/args-array-unknown.json: In command 'oops':
-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:2: command uses unknown type 'NoSuchType'
diff --git a/tests/qapi-schema/args-boxed-string.err b/tests/qapi-schema/args-boxed-string.err
index f284406f0f..dc2b00f217 100644
--- a/tests/qapi-schema/args-boxed-string.err
+++ b/tests/qapi-schema/args-boxed-string.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/args-boxed-string.json: In command 'foo':
-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:2: command's 'data' cannot take built-in type 'str'
diff --git a/tests/qapi-schema/args-int.err b/tests/qapi-schema/args-int.err
index 419268186e..81b6f86b66 100644
--- a/tests/qapi-schema/args-int.err
+++ b/tests/qapi-schema/args-int.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/args-int.json: In command 'oops':
-tests/qapi-schema/args-int.json:2: 'data' for command 'oops' cannot use built-in type 'int'
+tests/qapi-schema/args-int.json:2: command's 'data' cannot take built-in type 'int'
diff --git a/tests/qapi-schema/args-member-unknown.err b/tests/qapi-schema/args-member-unknown.err
index 168e24a4b8..0626e1209d 100644
--- a/tests/qapi-schema/args-member-unknown.err
+++ b/tests/qapi-schema/args-member-unknown.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/args-member-unknown.json: In command 'oops':
-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:2: parameter 'member' uses unknown type 'NoSuchType'
diff --git a/tests/qapi-schema/args-union.err b/tests/qapi-schema/args-union.err
index 30a1534b4c..3a77b2863f 100644
--- a/tests/qapi-schema/args-union.err
+++ b/tests/qapi-schema/args-union.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/args-union.json: In command 'oops':
-tests/qapi-schema/args-union.json:3: 'data' for command 'oops' cannot use union type 'Uni'
+tests/qapi-schema/args-union.json:3: command's 'data' can take union type 'Uni' only with 'boxed': true
diff --git a/tests/qapi-schema/args-unknown.err b/tests/qapi-schema/args-unknown.err
index cb835d7489..6857d6bf48 100644
--- a/tests/qapi-schema/args-unknown.err
+++ b/tests/qapi-schema/args-unknown.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/args-unknown.json: In command 'oops':
-tests/qapi-schema/args-unknown.json:2: 'data' for command 'oops' uses unknown type 'NoSuchType'
+tests/qapi-schema/args-unknown.json:2: command's 'data' uses unknown type 'NoSuchType'
diff --git a/tests/qapi-schema/bad-base.err b/tests/qapi-schema/bad-base.err
index fdfd288f5c..039678a364 100644
--- a/tests/qapi-schema/bad-base.err
+++ b/tests/qapi-schema/bad-base.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/bad-base.json: In struct 'MyType':
-tests/qapi-schema/bad-base.json:3: 'base' for struct 'MyType' cannot use union type 'Union'
+tests/qapi-schema/bad-base.json:3: 'base' requires a struct type, union type 'Union' isn't
diff --git a/tests/qapi-schema/command-int.err b/tests/qapi-schema/command-int.err
index 56b45bf656..3523d50a79 100644
--- a/tests/qapi-schema/command-int.err
+++ b/tests/qapi-schema/command-int.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/command-int.json: In command 'int':
-tests/qapi-schema/command-int.json:2: built-in 'int' is already defined
+tests/qapi-schema/command-int.json:2: built-in type 'int' is already defined
diff --git a/tests/qapi-schema/flat-union-base-any.err b/tests/qapi-schema/flat-union-base-any.err
index 039b9a68b9..7ab3402396 100644
--- a/tests/qapi-schema/flat-union-base-any.err
+++ b/tests/qapi-schema/flat-union-base-any.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/flat-union-base-any.json: In union 'TestUnion':
-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:8: 'base' requires a struct type, built-in type 'any' isn't
diff --git a/tests/qapi-schema/flat-union-base-union.err b/tests/qapi-schema/flat-union-base-union.err
index 93ab91378f..5db7b1e404 100644
--- a/tests/qapi-schema/flat-union-base-union.err
+++ b/tests/qapi-schema/flat-union-base-union.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/flat-union-base-union.json: In union 'TestUnion':
-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:14: 'base' requires a struct type, union type 'UnionBase' isn't
diff --git a/tests/qapi-schema/flat-union-discriminator-bad-name.err b/tests/qapi-schema/flat-union-discriminator-bad-name.err
index 44e41883b1..2a0deb6a0e 100644
--- a/tests/qapi-schema/flat-union-discriminator-bad-name.err
+++ b/tests/qapi-schema/flat-union-discriminator-bad-name.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/flat-union-discriminator-bad-name.json: In union 'MyUnion':
-tests/qapi-schema/flat-union-discriminator-bad-name.json:7: discriminator of flat union 'MyUnion' uses invalid name '*switch'
+tests/qapi-schema/flat-union-discriminator-bad-name.json:6: discriminator '*switch' is not a member of 'base'
diff --git a/tests/qapi-schema/flat-union-discriminator-bad-name.json b/tests/qapi-schema/flat-union-discriminator-bad-name.json
index ea84b75cac..3ae8c06a89 100644
--- a/tests/qapi-schema/flat-union-discriminator-bad-name.json
+++ b/tests/qapi-schema/flat-union-discriminator-bad-name.json
@@ -1,5 +1,4 @@
 # discriminator '*switch' isn't a member of base, 'switch' is
-# reports "uses invalid name", which is good enough
 { 'enum': 'Enum', 'data': [ 'one', 'two' ] }
 { 'struct': 'Base',
   'data': { '*switch': 'Enum' } }
diff --git a/tests/qapi-schema/flat-union-empty.err b/tests/qapi-schema/flat-union-empty.err
index 96fe46b918..91a5b57f5d 100644
--- a/tests/qapi-schema/flat-union-empty.err
+++ b/tests/qapi-schema/flat-union-empty.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/flat-union-empty.json: In union 'Union':
-tests/qapi-schema/flat-union-empty.json:4: union 'Union' has no branches
+tests/qapi-schema/flat-union-empty.json:4: union has no branches
diff --git a/tests/qapi-schema/flat-union-int-branch.err b/tests/qapi-schema/flat-union-int-branch.err
index 2bbb8bf22e..416b696c8f 100644
--- a/tests/qapi-schema/flat-union-int-branch.err
+++ b/tests/qapi-schema/flat-union-int-branch.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/flat-union-int-branch.json: In union 'TestUnion':
-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:8: branch 'value1' cannot use built-in type 'int'
diff --git a/tests/qapi-schema/flat-union-invalid-branch-key.err b/tests/qapi-schema/flat-union-invalid-branch-key.err
index da4377be69..6997b3387d 100644
--- a/tests/qapi-schema/flat-union-invalid-branch-key.err
+++ b/tests/qapi-schema/flat-union-invalid-branch-key.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/flat-union-invalid-branch-key.json: In union 'TestUnion':
-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:13: branch 'value_wrong' is not a value of enum type 'TestEnum'
diff --git a/tests/qapi-schema/flat-union-invalid-if-discriminator.err b/tests/qapi-schema/flat-union-invalid-if-discriminator.err
index be770d6baa..d2b35be9ae 100644
--- a/tests/qapi-schema/flat-union-invalid-if-discriminator.err
+++ b/tests/qapi-schema/flat-union-invalid-if-discriminator.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/flat-union-invalid-if-discriminator.json: In union 'TestUnion':
-tests/qapi-schema/flat-union-invalid-if-discriminator.json:10: the discriminator 'enum1' for union TestUnion must not be conditional
+tests/qapi-schema/flat-union-invalid-if-discriminator.json:10: discriminator member 'enum1' of 'base' must not be conditional
diff --git a/tests/qapi-schema/flat-union-optional-discriminator.err b/tests/qapi-schema/flat-union-optional-discriminator.err
index 536ac775e2..49fbf5b04d 100644
--- a/tests/qapi-schema/flat-union-optional-discriminator.err
+++ b/tests/qapi-schema/flat-union-optional-discriminator.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/flat-union-optional-discriminator.json: In union 'MyUnion':
-tests/qapi-schema/flat-union-optional-discriminator.json:7: discriminator 'switch' is not a member of 'base'
+tests/qapi-schema/flat-union-optional-discriminator.json:6: discriminator member 'switch' of base type 'Base' must not be optional
diff --git a/tests/qapi-schema/flat-union-optional-discriminator.json b/tests/qapi-schema/flat-union-optional-discriminator.json
index 143ab23a0d..2e7f766f60 100644
--- a/tests/qapi-schema/flat-union-optional-discriminator.json
+++ b/tests/qapi-schema/flat-union-optional-discriminator.json
@@ -1,5 +1,4 @@
 # we require the discriminator to be non-optional
-# FIXME reports "discriminator 'switch' is not a member of base struct 'Base'"
 { 'enum': 'Enum', 'data': [ 'one', 'two' ] }
 { 'struct': 'Base',
   'data': { '*switch': 'Enum' } }
diff --git a/tests/qapi-schema/flat-union-string-discriminator.err b/tests/qapi-schema/flat-union-string-discriminator.err
index f5635a5774..fb499188aa 100644
--- a/tests/qapi-schema/flat-union-string-discriminator.err
+++ b/tests/qapi-schema/flat-union-string-discriminator.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/flat-union-string-discriminator.json: In union 'TestUnion':
-tests/qapi-schema/flat-union-string-discriminator.json:13: discriminator 'kind' must be of enumeration type
+tests/qapi-schema/flat-union-string-discriminator.json:13: discriminator member 'kind' of base type 'TestBase' must be of enum type
diff --git a/tests/qapi-schema/redefined-builtin.err b/tests/qapi-schema/redefined-builtin.err
index 67775fdb41..47c8933759 100644
--- a/tests/qapi-schema/redefined-builtin.err
+++ b/tests/qapi-schema/redefined-builtin.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/redefined-builtin.json: In struct 'size':
-tests/qapi-schema/redefined-builtin.json:2: built-in 'size' is already defined
+tests/qapi-schema/redefined-builtin.json:2: built-in type 'size' is already defined
diff --git a/tests/qapi-schema/redefined-type.err b/tests/qapi-schema/redefined-type.err
index 89acc82c2d..39f51c14ea 100644
--- a/tests/qapi-schema/redefined-type.err
+++ b/tests/qapi-schema/redefined-type.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/redefined-type.json: In enum 'foo':
-tests/qapi-schema/redefined-type.json:3: struct 'foo' is already defined
+tests/qapi-schema/redefined-type.json:3: struct type 'foo' is already defined
diff --git a/tests/qapi-schema/returns-alternate.err b/tests/qapi-schema/returns-alternate.err
index b98cf84cef..c1caf98c47 100644
--- a/tests/qapi-schema/returns-alternate.err
+++ b/tests/qapi-schema/returns-alternate.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/returns-alternate.json: In command 'oops':
-tests/qapi-schema/returns-alternate.json:3: 'returns' for command 'oops' cannot use alternate type 'Alt'
+tests/qapi-schema/returns-alternate.json:3: command's 'returns' cannot take alternate type 'Alt'
diff --git a/tests/qapi-schema/returns-unknown.err b/tests/qapi-schema/returns-unknown.err
index cbece00bd2..f0a989a175 100644
--- a/tests/qapi-schema/returns-unknown.err
+++ b/tests/qapi-schema/returns-unknown.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/returns-unknown.json: In command 'oops':
-tests/qapi-schema/returns-unknown.json:2: 'returns' for command 'oops' uses unknown type 'NoSuchType'
+tests/qapi-schema/returns-unknown.json:2: command's 'returns' uses unknown type 'NoSuchType'
diff --git a/tests/qapi-schema/returns-whitelist.err b/tests/qapi-schema/returns-whitelist.err
index 1ccd7d8de2..5b0285220f 100644
--- a/tests/qapi-schema/returns-whitelist.err
+++ b/tests/qapi-schema/returns-whitelist.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/returns-whitelist.json: In command 'no-way-this-will-get-whitelisted':
-tests/qapi-schema/returns-whitelist.json:14: 'returns' for command 'no-way-this-will-get-whitelisted' cannot use built-in type 'int'
+tests/qapi-schema/returns-whitelist.json:14: command's 'returns' cannot take array type ['int']
diff --git a/tests/qapi-schema/union-empty.err b/tests/qapi-schema/union-empty.err
index 522b19e7ae..35c0d62eb0 100644
--- a/tests/qapi-schema/union-empty.err
+++ b/tests/qapi-schema/union-empty.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/union-empty.json: In union 'Union':
-tests/qapi-schema/union-empty.json:2: union 'Union' has no branches
+tests/qapi-schema/union-empty.json:2: union has no branches
diff --git a/tests/qapi-schema/union-invalid-base.err b/tests/qapi-schema/union-invalid-base.err
index be908709d2..10fecf0b56 100644
--- a/tests/qapi-schema/union-invalid-base.err
+++ b/tests/qapi-schema/union-invalid-base.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/union-invalid-base.json: In union 'TestUnion':
-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:8: 'base' requires a struct type, built-in type 'int' isn't
diff --git a/tests/qapi-schema/union-unknown.err b/tests/qapi-schema/union-unknown.err
index 5d4fdd2857..a7f340ddca 100644
--- a/tests/qapi-schema/union-unknown.err
+++ b/tests/qapi-schema/union-unknown.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/union-unknown.json: In union 'Union':
-tests/qapi-schema/union-unknown.json:2: member 'unknown' of union 'Union' uses unknown type 'MissingType'
+tests/qapi-schema/union-unknown.json:2: union uses unknown type 'MissingType'
diff --git a/tests/qapi-schema/union-unknown.json b/tests/qapi-schema/union-unknown.json
index aa7e8143d8..64d3666176 100644
--- a/tests/qapi-schema/union-unknown.json
+++ b/tests/qapi-schema/union-unknown.json
@@ -1,3 +1,3 @@
 # we reject a union with unknown type in branch
 { 'union': 'Union',
-  'data': { 'unknown': 'MissingType' } }
+  'data': { 'unknown': ['MissingType'] } }
-- 
2.21.0



^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH 17/25] qapi: Move context-free checking to the proper place
  2019-09-24 13:28 [PATCH 00/25] qapi: Pay back some frontend technical debt Markus Armbruster
                   ` (15 preceding siblings ...)
  2019-09-24 13:28 ` [PATCH 16/25] qapi: Move context-sensitive checking to the proper place Markus Armbruster
@ 2019-09-24 13:28 ` Markus Armbruster
  2019-09-24 17:59   ` Eric Blake
  2019-09-24 13:28 ` [PATCH 18/25] qapi: Improve reporting of invalid 'if' errors Markus Armbruster
                   ` (7 subsequent siblings)
  24 siblings, 1 reply; 63+ messages in thread
From: Markus Armbruster @ 2019-09-24 13:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, mdroth

QAPISchemaCommand.check() and QAPISchemaEvent().check() check 'data'
is present when 'boxed': true.  That's context-free.  Move to
check_command() and check_event().

Tweak the error message while there.

check_exprs() & friends now check exactly what qapi-code-gen.txt calls
the second layer of syntax.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 scripts/qapi/common.py                  | 16 ++++++++--------
 tests/qapi-schema/event-boxed-empty.err |  2 +-
 2 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index ac4c898e51..7577120f12 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -767,10 +767,12 @@ def check_type(value, info, source,
 
 def check_command(expr, info):
     name = expr['command']
+    args = expr.get('data')
     boxed = expr.get('boxed', False)
 
-    check_type(expr.get('data'), info,
-               "'data' for command '%s'" % name,
+    if boxed and args is None:
+        raise QAPISemError(info, "'boxed': true requires 'data'")
+    check_type(args, info, "'data' for command '%s'" % name,
                allow_dict=not boxed)
     check_type(expr.get('returns'), info,
                "'returns' for command '%s'" % name,
@@ -779,10 +781,12 @@ def check_command(expr, info):
 
 def check_event(expr, info):
     name = expr['event']
+    args = expr.get('data')
     boxed = expr.get('boxed', False)
 
-    check_type(expr.get('data'), info,
-               "'data' for event '%s'" % name,
+    if boxed and args is None:
+        raise QAPISemError(info, "'boxed': true requires 'data'")
+    check_type(args, info, "'data' for event '%s'" % name,
                allow_dict=not boxed)
 
 
@@ -1691,8 +1695,6 @@ class QAPISchemaCommand(QAPISchemaEntity):
                     self.info,
                     "command's 'data' can take %s only with 'boxed': true"
                     % self.arg_type.describe())
-        elif self.boxed:
-            raise QAPISemError(self.info, "use of 'boxed' requires 'data'")
         if self._ret_type_name:
             self.ret_type = schema.resolve_type(
                 self._ret_type_name, self.info, "command's 'returns'")
@@ -1740,8 +1742,6 @@ class QAPISchemaEvent(QAPISchemaEntity):
                     self.info,
                     "event's 'data' can take %s only with 'boxed': true"
                     % self.arg_type.describe())
-        elif self.boxed:
-            raise QAPISemError(self.info, "use of 'boxed' requires 'data'")
 
     def visit(self, visitor):
         QAPISchemaEntity.visit(self, visitor)
diff --git a/tests/qapi-schema/event-boxed-empty.err b/tests/qapi-schema/event-boxed-empty.err
index 9c691b7d97..931c10b036 100644
--- a/tests/qapi-schema/event-boxed-empty.err
+++ b/tests/qapi-schema/event-boxed-empty.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/event-boxed-empty.json: In event 'FOO':
-tests/qapi-schema/event-boxed-empty.json:2: use of 'boxed' requires 'data'
+tests/qapi-schema/event-boxed-empty.json:2: 'boxed': true requires 'data'
-- 
2.21.0



^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH 18/25] qapi: Improve reporting of invalid 'if' errors
  2019-09-24 13:28 [PATCH 00/25] qapi: Pay back some frontend technical debt Markus Armbruster
                   ` (16 preceding siblings ...)
  2019-09-24 13:28 ` [PATCH 17/25] qapi: Move context-free " Markus Armbruster
@ 2019-09-24 13:28 ` Markus Armbruster
  2019-09-24 18:01   ` Eric Blake
  2019-09-24 13:28 ` [PATCH 19/25] qapi: Improve reporting of invalid flags Markus Armbruster
                   ` (6 subsequent siblings)
  24 siblings, 1 reply; 63+ messages in thread
From: Markus Armbruster @ 2019-09-24 13:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, mdroth

Move check_if() from check_keys() to check_exprs() and call it later,
so its error messages gain an "in definition" line.

Checking values in a function named check_keys() is unclean anyway.
The original sin was commit 0545f6b887 "qapi: Better error messages
for bad expressions", which checks the value of key 'name'.  More
sinning in commit 2cbf09925a "qapi: More rigorous checking for type
safety bypass", commit c818408e44 "qapi: Implement boxed types for
commands/events", and commit 967c885108 "qapi: add 'if' to top-level
expressions".  This commit does penance for the latter.  The next
commits will do penance for the others.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 scripts/qapi/common.py                  | 4 ++--
 tests/qapi-schema/bad-if-empty-list.err | 1 +
 tests/qapi-schema/bad-if-empty.err      | 1 +
 tests/qapi-schema/bad-if-list.err       | 1 +
 tests/qapi-schema/bad-if.err            | 1 +
 5 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index 7577120f12..fe99e59153 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -920,8 +920,6 @@ def check_keys(expr, info, meta, required, optional=[]):
             raise QAPISemError(info,
                                "'%s' of %s '%s' should only use true value"
                                % (key, meta, name))
-        if key == 'if':
-            check_if(expr, info)
 
 
 def normalize_enum(expr):
@@ -1023,6 +1021,8 @@ def check_exprs(exprs):
         else:
             assert False, 'unexpected meta type'
 
+        check_if(expr, info)
+
         if doc:
             doc.check_expr(expr)
 
diff --git a/tests/qapi-schema/bad-if-empty-list.err b/tests/qapi-schema/bad-if-empty-list.err
index 75fe6497bc..2218c9279b 100644
--- a/tests/qapi-schema/bad-if-empty-list.err
+++ b/tests/qapi-schema/bad-if-empty-list.err
@@ -1 +1,2 @@
+tests/qapi-schema/bad-if-empty-list.json: In struct 'TestIfStruct':
 tests/qapi-schema/bad-if-empty-list.json:2: 'if' condition [] is useless
diff --git a/tests/qapi-schema/bad-if-empty.err b/tests/qapi-schema/bad-if-empty.err
index 358bdc3e51..a3fdb3009d 100644
--- a/tests/qapi-schema/bad-if-empty.err
+++ b/tests/qapi-schema/bad-if-empty.err
@@ -1 +1,2 @@
+tests/qapi-schema/bad-if-empty.json: In struct 'TestIfStruct':
 tests/qapi-schema/bad-if-empty.json:2: 'if' condition '' makes no sense
diff --git a/tests/qapi-schema/bad-if-list.err b/tests/qapi-schema/bad-if-list.err
index 53af099083..e03bf0fc3a 100644
--- a/tests/qapi-schema/bad-if-list.err
+++ b/tests/qapi-schema/bad-if-list.err
@@ -1 +1,2 @@
+tests/qapi-schema/bad-if-list.json: In struct 'TestIfStruct':
 tests/qapi-schema/bad-if-list.json:2: 'if' condition ' ' makes no sense
diff --git a/tests/qapi-schema/bad-if.err b/tests/qapi-schema/bad-if.err
index c2e3f5f44c..190216c109 100644
--- a/tests/qapi-schema/bad-if.err
+++ b/tests/qapi-schema/bad-if.err
@@ -1 +1,2 @@
+tests/qapi-schema/bad-if.json: In struct 'TestIfStruct':
 tests/qapi-schema/bad-if.json:2: 'if' condition must be a string or a list of strings
-- 
2.21.0



^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH 19/25] qapi: Improve reporting of invalid flags
  2019-09-24 13:28 [PATCH 00/25] qapi: Pay back some frontend technical debt Markus Armbruster
                   ` (17 preceding siblings ...)
  2019-09-24 13:28 ` [PATCH 18/25] qapi: Improve reporting of invalid 'if' errors Markus Armbruster
@ 2019-09-24 13:28 ` Markus Armbruster
  2019-09-24 18:07   ` Eric Blake
  2019-09-24 13:28 ` [PATCH 20/25] qapi: Improve reporting of missing / unknown definition keys Markus Armbruster
                   ` (5 subsequent siblings)
  24 siblings, 1 reply; 63+ messages in thread
From: Markus Armbruster @ 2019-09-24 13:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, mdroth

Split check_flags() off check_keys() and have check_exprs() call it
later, so its error messages gain an "in definition" line.  Tweak the
error messages.

Checking values in a function named check_keys() is unclean anyway.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 scripts/qapi/common.py                     | 22 ++++++++++++----------
 tests/qapi-schema/allow-preconfig-test.err |  3 ++-
 tests/qapi-schema/args-bad-boxed.err       |  3 ++-
 tests/qapi-schema/oob-test.err             |  3 ++-
 tests/qapi-schema/type-bypass-bad-gen.err  |  3 ++-
 5 files changed, 20 insertions(+), 14 deletions(-)

diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index fe99e59153..4bfc007833 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -910,16 +910,17 @@ def check_keys(expr, info, meta, required, optional=[]):
     required = required + [meta]
     source = "%s '%s'" % (meta, name)
     check_known_keys(expr, info, source, required, optional)
-    for (key, value) in expr.items():
-        if key in ['gen', 'success-response'] and value is not False:
-            raise QAPISemError(info,
-                               "'%s' of %s '%s' should only use false value"
-                               % (key, meta, name))
-        if (key in ['boxed', 'allow-oob', 'allow-preconfig']
-                and value is not True):
-            raise QAPISemError(info,
-                               "'%s' of %s '%s' should only use true value"
-                               % (key, meta, name))
+
+
+def check_flags(expr, info):
+    for key in ['gen', 'success-response']:
+        if key in expr and expr[key] is not False:
+            raise QAPISemError(
+                info, "flag '%s' may only use false value" % key)
+    for key in ['boxed', 'allow-oob', 'allow-preconfig']:
+        if key in expr and expr[key] is not True:
+            raise QAPISemError(
+                info, "flag '%s' may only use true value" % key)
 
 
 def normalize_enum(expr):
@@ -1022,6 +1023,7 @@ def check_exprs(exprs):
             assert False, 'unexpected meta type'
 
         check_if(expr, info)
+        check_flags(expr, info)
 
         if doc:
             doc.check_expr(expr)
diff --git a/tests/qapi-schema/allow-preconfig-test.err b/tests/qapi-schema/allow-preconfig-test.err
index 700d583306..2a4e6ce663 100644
--- a/tests/qapi-schema/allow-preconfig-test.err
+++ b/tests/qapi-schema/allow-preconfig-test.err
@@ -1 +1,2 @@
-tests/qapi-schema/allow-preconfig-test.json:2: 'allow-preconfig' of command 'allow-preconfig-test' should only use true value
+tests/qapi-schema/allow-preconfig-test.json: In command 'allow-preconfig-test':
+tests/qapi-schema/allow-preconfig-test.json:2: flag 'allow-preconfig' may only use true value
diff --git a/tests/qapi-schema/args-bad-boxed.err b/tests/qapi-schema/args-bad-boxed.err
index ad0d417321..31d39038fc 100644
--- a/tests/qapi-schema/args-bad-boxed.err
+++ b/tests/qapi-schema/args-bad-boxed.err
@@ -1 +1,2 @@
-tests/qapi-schema/args-bad-boxed.json:2: 'boxed' of command 'foo' should only use true value
+tests/qapi-schema/args-bad-boxed.json: In command 'foo':
+tests/qapi-schema/args-bad-boxed.json:2: flag 'boxed' may only use true value
diff --git a/tests/qapi-schema/oob-test.err b/tests/qapi-schema/oob-test.err
index 35b60f7480..3c2ba6e0fd 100644
--- a/tests/qapi-schema/oob-test.err
+++ b/tests/qapi-schema/oob-test.err
@@ -1 +1,2 @@
-tests/qapi-schema/oob-test.json:2: 'allow-oob' of command 'oob-command-1' should only use true value
+tests/qapi-schema/oob-test.json: In command 'oob-command-1':
+tests/qapi-schema/oob-test.json:2: flag 'allow-oob' may only use true value
diff --git a/tests/qapi-schema/type-bypass-bad-gen.err b/tests/qapi-schema/type-bypass-bad-gen.err
index a83c3c655d..1077651896 100644
--- a/tests/qapi-schema/type-bypass-bad-gen.err
+++ b/tests/qapi-schema/type-bypass-bad-gen.err
@@ -1 +1,2 @@
-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: In command 'foo':
+tests/qapi-schema/type-bypass-bad-gen.json:2: flag 'gen' may only use false value
-- 
2.21.0



^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH 20/25] qapi: Improve reporting of missing / unknown definition keys
  2019-09-24 13:28 [PATCH 00/25] qapi: Pay back some frontend technical debt Markus Armbruster
                   ` (18 preceding siblings ...)
  2019-09-24 13:28 ` [PATCH 19/25] qapi: Improve reporting of invalid flags Markus Armbruster
@ 2019-09-24 13:28 ` Markus Armbruster
  2019-09-24 18:13   ` Eric Blake
  2019-09-24 13:28 ` [PATCH 21/25] qapi: Avoid redundant definition references in error messages Markus Armbruster
                   ` (4 subsequent siblings)
  24 siblings, 1 reply; 63+ messages in thread
From: Markus Armbruster @ 2019-09-24 13:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, mdroth

Have check_exprs() call check_keys() later, so its error messages gain
an "in definition" line.

Both check_keys() and check_name_is_str() check the definition's name
is a string.  Since check_keys() now runs after check_name_is_str()
rather than before, its check is dead.  Bury it.  Checking values in
check_keys() is unclean anyway.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 scripts/qapi/common.py                  | 40 ++++++++++++-------------
 tests/qapi-schema/alternate-base.err    |  1 +
 tests/qapi-schema/bad-type-bool.err     |  2 +-
 tests/qapi-schema/bad-type-dict.err     |  2 +-
 tests/qapi-schema/double-type.err       |  1 +
 tests/qapi-schema/enum-missing-data.err |  1 +
 tests/qapi-schema/unknown-expr-key.err  |  1 +
 7 files changed, 25 insertions(+), 23 deletions(-)

diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index 4bfc007833..297b82db4c 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -905,8 +905,6 @@ def check_known_keys(value, info, source, required, optional):
 
 def check_keys(expr, info, meta, required, optional=[]):
     name = expr[meta]
-    if not isinstance(name, str):
-        raise QAPISemError(info, "'%s' key must have a string value" % meta)
     required = required + [meta]
     source = "%s '%s'" % (meta, name)
     check_known_keys(expr, info, source, required, optional)
@@ -964,37 +962,18 @@ def check_exprs(exprs):
 
         if 'enum' in expr:
             meta = 'enum'
-            check_keys(expr, info, 'enum', ['data'], ['if', 'prefix'])
-            normalize_enum(expr)
         elif 'union' in expr:
             meta = 'union'
-            check_keys(expr, info, 'union', ['data'],
-                       ['base', 'discriminator', 'if'])
-            normalize_members(expr.get('base'))
-            normalize_members(expr['data'])
         elif 'alternate' in expr:
             meta = 'alternate'
-            check_keys(expr, info, 'alternate', ['data'], ['if'])
-            normalize_members(expr['data'])
         elif 'struct' in expr:
             meta = 'struct'
-            check_keys(expr, info, 'struct', ['data'],
-                       ['base', 'if', 'features'])
-            normalize_members(expr['data'])
-            normalize_features(expr.get('features'))
         elif 'command' in expr:
             meta = 'command'
-            check_keys(expr, info, 'command', [],
-                       ['data', 'returns', 'gen', 'success-response',
-                        'boxed', 'allow-oob', 'allow-preconfig', 'if'])
-            normalize_members(expr.get('data'))
         elif 'event' in expr:
             meta = 'event'
-            check_keys(expr, info, 'event', [], ['data', 'boxed', 'if'])
-            normalize_members(expr.get('data'))
         else:
             raise QAPISemError(info, "expression is missing metatype")
-        normalize_if(expr)
 
         name = expr[meta]
         check_name_is_str(name, info, "'%s'" % meta)
@@ -1008,20 +987,39 @@ def check_exprs(exprs):
                 % (name, doc.symbol))
 
         if meta == 'enum':
+            check_keys(expr, info, 'enum', ['data'], ['if', 'prefix'])
+            normalize_enum(expr)
             check_enum(expr, info)
         elif meta == 'union':
+            check_keys(expr, info, 'union', ['data'],
+                       ['base', 'discriminator', 'if'])
+            normalize_members(expr.get('base'))
+            normalize_members(expr['data'])
             check_union(expr, info)
         elif meta == 'alternate':
+            check_keys(expr, info, 'alternate', ['data'], ['if'])
+            normalize_members(expr['data'])
             check_alternate(expr, info)
         elif meta == 'struct':
+            check_keys(expr, info, 'struct', ['data'],
+                       ['base', 'if', 'features'])
+            normalize_members(expr['data'])
+            normalize_features(expr.get('features'))
             check_struct(expr, info)
         elif meta == 'command':
+            check_keys(expr, info, 'command', [],
+                       ['data', 'returns', 'gen', 'success-response',
+                        'boxed', 'allow-oob', 'allow-preconfig', 'if'])
+            normalize_members(expr.get('data'))
             check_command(expr, info)
         elif meta == 'event':
+            check_keys(expr, info, 'event', [], ['data', 'boxed', 'if'])
+            normalize_members(expr.get('data'))
             check_event(expr, info)
         else:
             assert False, 'unexpected meta type'
 
+        normalize_if(expr)
         check_if(expr, info)
         check_flags(expr, info)
 
diff --git a/tests/qapi-schema/alternate-base.err b/tests/qapi-schema/alternate-base.err
index 4c9158db02..6290665ac2 100644
--- a/tests/qapi-schema/alternate-base.err
+++ b/tests/qapi-schema/alternate-base.err
@@ -1,2 +1,3 @@
+tests/qapi-schema/alternate-base.json: In alternate 'Alt':
 tests/qapi-schema/alternate-base.json:4: unknown key 'base' in alternate 'Alt'
 Valid keys are 'alternate', 'data', 'if'.
diff --git a/tests/qapi-schema/bad-type-bool.err b/tests/qapi-schema/bad-type-bool.err
index 62fd70baaf..984a77c4e3 100644
--- a/tests/qapi-schema/bad-type-bool.err
+++ b/tests/qapi-schema/bad-type-bool.err
@@ -1 +1 @@
-tests/qapi-schema/bad-type-bool.json:2: 'struct' key must have a string value
+tests/qapi-schema/bad-type-bool.json:2: 'struct' requires a string name
diff --git a/tests/qapi-schema/bad-type-dict.err b/tests/qapi-schema/bad-type-dict.err
index 0b2a2aeac4..e83b8cfb41 100644
--- a/tests/qapi-schema/bad-type-dict.err
+++ b/tests/qapi-schema/bad-type-dict.err
@@ -1 +1 @@
-tests/qapi-schema/bad-type-dict.json:2: 'command' key must have a string value
+tests/qapi-schema/bad-type-dict.json:2: 'command' requires a string name
diff --git a/tests/qapi-schema/double-type.err b/tests/qapi-schema/double-type.err
index 44a9dfdd55..ddb22af638 100644
--- a/tests/qapi-schema/double-type.err
+++ b/tests/qapi-schema/double-type.err
@@ -1,2 +1,3 @@
+tests/qapi-schema/double-type.json: In struct 'bar':
 tests/qapi-schema/double-type.json:2: unknown key 'command' in struct 'bar'
 Valid keys are 'base', 'data', 'features', 'if', 'struct'.
diff --git a/tests/qapi-schema/enum-missing-data.err b/tests/qapi-schema/enum-missing-data.err
index 3c3c52d037..ffde1082c3 100644
--- a/tests/qapi-schema/enum-missing-data.err
+++ b/tests/qapi-schema/enum-missing-data.err
@@ -1 +1,2 @@
+tests/qapi-schema/enum-missing-data.json: In enum 'MyEnum':
 tests/qapi-schema/enum-missing-data.json:2: key 'data' is missing from enum 'MyEnum'
diff --git a/tests/qapi-schema/unknown-expr-key.err b/tests/qapi-schema/unknown-expr-key.err
index 07558edb78..e401efe148 100644
--- a/tests/qapi-schema/unknown-expr-key.err
+++ b/tests/qapi-schema/unknown-expr-key.err
@@ -1,2 +1,3 @@
+tests/qapi-schema/unknown-expr-key.json: In struct 'bar':
 tests/qapi-schema/unknown-expr-key.json:2: unknown keys 'bogus', 'phony' in struct 'bar'
 Valid keys are 'base', 'data', 'features', 'if', 'struct'.
-- 
2.21.0



^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH 21/25] qapi: Avoid redundant definition references in error messages
  2019-09-24 13:28 [PATCH 00/25] qapi: Pay back some frontend technical debt Markus Armbruster
                   ` (19 preceding siblings ...)
  2019-09-24 13:28 ` [PATCH 20/25] qapi: Improve reporting of missing / unknown definition keys Markus Armbruster
@ 2019-09-24 13:28 ` Markus Armbruster
  2019-09-24 18:46   ` Eric Blake
  2019-09-24 13:28 ` [PATCH 22/25] qapi: Eliminate check_keys(), rename check_known_keys() Markus Armbruster
                   ` (3 subsequent siblings)
  24 siblings, 1 reply; 63+ messages in thread
From: Markus Armbruster @ 2019-09-24 13:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, mdroth

Many error messages refer to the offending definition even though
they're preceded by an "in definition" line.  Rephrase them.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 scripts/qapi/common.py                        | 113 +++++++-----------
 tests/qapi-schema/alternate-array.err         |   2 +-
 tests/qapi-schema/alternate-base.err          |   2 +-
 tests/qapi-schema/alternate-empty.err         |   2 +-
 tests/qapi-schema/alternate-invalid-dict.err  |   2 +-
 tests/qapi-schema/args-array-empty.err        |   2 +-
 tests/qapi-schema/args-boxed-anon.err         |   2 +-
 tests/qapi-schema/args-invalid.err            |   2 +-
 tests/qapi-schema/args-member-array-bad.err   |   2 +-
 tests/qapi-schema/args-member-case.err        |   2 +-
 tests/qapi-schema/bad-data.err                |   2 +-
 tests/qapi-schema/doc-bad-symbol.err          |   2 +-
 tests/qapi-schema/double-type.err             |   2 +-
 tests/qapi-schema/enum-bad-member.err         |   2 +-
 tests/qapi-schema/enum-bad-name.err           |   2 +-
 tests/qapi-schema/enum-bad-prefix.err         |   2 +-
 .../qapi-schema/enum-dict-member-unknown.err  |   2 +-
 tests/qapi-schema/enum-member-case.err        |   2 +-
 tests/qapi-schema/enum-missing-data.err       |   2 +-
 tests/qapi-schema/enum-wrong-data.err         |   2 +-
 .../qapi-schema/event-member-invalid-dict.err |   2 +-
 tests/qapi-schema/event-nest-struct.err       |   2 +-
 tests/qapi-schema/features-bad-type.err       |   2 +-
 tests/qapi-schema/features-missing-name.err   |   2 +-
 tests/qapi-schema/features-name-bad-type.err  |   2 +-
 tests/qapi-schema/features-no-list.err        |   2 +-
 tests/qapi-schema/features-unknown-key.err    |   2 +-
 tests/qapi-schema/flat-union-array-branch.err |   2 +-
 .../flat-union-bad-discriminator.err          |   2 +-
 .../flat-union-inline-invalid-dict.err        |   2 +-
 tests/qapi-schema/flat-union-inline.err       |   2 +-
 tests/qapi-schema/flat-union-no-base.err      |   2 +-
 .../nested-struct-data-invalid-dict.err       |   2 +-
 tests/qapi-schema/nested-struct-data.err      |   2 +-
 tests/qapi-schema/reserved-enum-q.err         |   2 +-
 tests/qapi-schema/reserved-member-has.err     |   2 +-
 tests/qapi-schema/reserved-member-q.err       |   2 +-
 tests/qapi-schema/reserved-member-u.err       |   2 +-
 .../reserved-member-underscore.err            |   2 +-
 tests/qapi-schema/reserved-type-kind.err      |   2 +-
 tests/qapi-schema/reserved-type-list.err      |   2 +-
 tests/qapi-schema/returns-array-bad.err       |   2 +-
 tests/qapi-schema/returns-dict.err            |   2 +-
 tests/qapi-schema/struct-data-invalid.err     |   2 +-
 .../struct-member-invalid-dict.err            |   2 +-
 tests/qapi-schema/struct-member-invalid.err   |   2 +-
 .../union-base-no-discriminator.err           |   2 +-
 tests/qapi-schema/union-branch-case.err       |   2 +-
 .../qapi-schema/union-branch-invalid-dict.err |   2 +-
 tests/qapi-schema/union-optional-branch.err   |   2 +-
 tests/qapi-schema/unknown-expr-key.err        |   2 +-
 51 files changed, 92 insertions(+), 121 deletions(-)

diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index 297b82db4c..3e9c5ae127 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -694,8 +694,8 @@ def check_name_str(name, info, source,
 def check_defn_name_str(name, info, meta):
     check_name_str(name, info, meta, permit_upper=True)
     if name.endswith('Kind') or name.endswith('List'):
-        raise QAPISemError(info, "%s '%s' should not end in '%s'"
-                           % (meta, name, name[-4:]))
+        raise QAPISemError(info, "%s name should not end in '%s'"
+                           % (meta, name[-4:]))
 
 
 def check_if(expr, info):
@@ -752,42 +752,34 @@ def check_type(value, info, source,
 
     # value is a dictionary, check that each member is okay
     for (key, arg) in value.items():
-        check_name_str(key, info, "member of %s" % source,
+        key_source = "%s member '%s'" % (source, key)
+        check_name_str(key, info, key_source,
                        allow_optional=True, permit_upper=permit_upper)
         if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
-            raise QAPISemError(
-                info, "member of %s uses reserved name '%s'" % (source, key))
-        check_known_keys(arg, info, "member '%s' of %s" % (key, source),
-                         ['type'], ['if'])
+            raise QAPISemError(info, "%s uses reserved name" % key_source)
+        check_known_keys(arg, info, key_source, ['type'], ['if'])
         check_if(arg, info)
         normalize_if(arg)
-        check_type(arg['type'], info, "member '%s' of %s" % (key, source),
-                   allow_array=True)
+        check_type(arg['type'], info, key_source, allow_array=True)
 
 
 def check_command(expr, info):
-    name = expr['command']
     args = expr.get('data')
     boxed = expr.get('boxed', False)
 
     if boxed and args is None:
         raise QAPISemError(info, "'boxed': true requires 'data'")
-    check_type(args, info, "'data' for command '%s'" % name,
-               allow_dict=not boxed)
-    check_type(expr.get('returns'), info,
-               "'returns' for command '%s'" % name,
-               allow_array=True)
+    check_type(expr.get('data'), info, "'data'", allow_dict=not boxed)
+    check_type(expr.get('returns'), info, "'returns'", allow_array=True)
 
 
 def check_event(expr, info):
-    name = expr['event']
     args = expr.get('data')
     boxed = expr.get('boxed', False)
 
     if boxed and args is None:
         raise QAPISemError(info, "'boxed': true requires 'data'")
-    check_type(args, info, "'data' for event '%s'" % name,
-               allow_dict=not boxed)
+    check_type(expr.get('data'), info, "'data'", allow_dict=not boxed)
 
 
 def check_union(expr, info):
@@ -798,45 +790,34 @@ def check_union(expr, info):
 
     if discriminator is None:   # simple union
         if base is not None:
-            raise QAPISemError(
-                info, "simple union '%s' must not have a base" % name)
+            raise QAPISemError(info, "'base' requires 'discriminator'")
     else:                       # flat union
-        check_type(base, info, "'base' for union '%s'" % name,
-                   allow_dict=name)
+        check_type(base, info, "'base'", allow_dict=name)
         if not base:
-            raise QAPISemError(info, "flat union '%s' must have a base"
-                               % name)
-        check_name_is_str(discriminator, info,
-                          "discriminator of flat union '%s'" % name)
+            raise QAPISemError(info, "'discriminator' requires 'base'")
+        check_name_is_str(discriminator, info, "'discriminator'")
 
     for (key, value) in members.items():
-        check_name_str(key, info, "member of union '%s'" % name)
-        check_known_keys(value, info,
-                         "member '%s' of union '%s'" % (key, name),
-                         ['type'], ['if'])
+        source = "'data' member '%s'" % key
+        check_name_str(key, info, source)
+        check_known_keys(value, info, source, ['type'], ['if'])
         check_if(value, info)
         normalize_if(value)
-        check_type(value['type'], info,
-                   "member '%s' of union '%s'" % (key, name),
-                   allow_array=not base)
+        check_type(value['type'], info, source, allow_array=not base)
 
 
 def check_alternate(expr, info):
-    name = expr['alternate']
     members = expr['data']
 
     if len(members) == 0:
-        raise QAPISemError(info,
-                           "alternate '%s' cannot have empty 'data'" % name)
+        raise QAPISemError(info, "'data' must not be empty")
     for (key, value) in members.items():
-        check_name_str(key, info, "member of alternate '%s'" % name)
-        check_known_keys(value, info,
-                         "member '%s' of alternate '%s'" % (key, name),
-                         ['type'], ['if'])
+        source = "'data' member '%s'" % key
+        check_name_str(key, info, source)
+        check_known_keys(value, info, source, ['type'], ['if'])
         check_if(value, info)
         normalize_if(value)
-        check_type(value['type'], info,
-                   "member '%s' of alternate '%s'" % (key, name))
+        check_type(value['type'], info, source)
 
 
 def check_enum(expr, info):
@@ -845,20 +826,18 @@ def check_enum(expr, info):
     prefix = expr.get('prefix')
 
     if not isinstance(members, list):
-        raise QAPISemError(info,
-                           "enum '%s' requires an array for 'data'" % name)
+        raise QAPISemError(info, "'data' must be an array")
     if prefix is not None and not isinstance(prefix, str):
-        raise QAPISemError(info,
-                           "enum '%s' requires a string for 'prefix'" % name)
+        raise QAPISemError(info, "'prefix' must be a string")
 
     permit_upper = name in name_case_whitelist
 
     for member in members:
-        check_known_keys(member, info, "member of enum '%s'" % name,
-                         ['name'], ['if'])
+        source = "'data' member"
+        check_known_keys(member, info, source, ['name'], ['if'])
         check_if(member, info)
         normalize_if(member)
-        check_name(member['name'], info, "member of enum '%s'" % name,
+        check_name(member['name'], info, source,
                    enum_member=True, permit_upper=permit_upper)
 
 
@@ -867,22 +846,19 @@ def check_struct(expr, info):
     members = expr['data']
     features = expr.get('features')
 
-    check_type(members, info, "'data' for struct '%s'" % name,
-               allow_dict=name)
-    check_type(expr.get('base'), info, "'base' for struct '%s'" % name)
+    check_type(members, info, "'data'", allow_dict=name)
+    check_type(expr.get('base'), info, "'base'")
 
     if features:
         if not isinstance(features, list):
-            raise QAPISemError(
-                info, "struct '%s' requires an array for 'features'" % name)
+            raise QAPISemError(info, "'features' must be an array")
         for f in features:
+            source = "'features' member"
             assert isinstance(f, dict)
-            check_known_keys(f, info, "feature of struct %s" % name,
-                             ['name'], ['if'])
-
+            check_known_keys(f, info, source, ['name'], ['if'])
             check_if(f, info)
             normalize_if(f)
-            check_name(f['name'], info, "feature of struct %s" % name)
+            check_name(f['name'], info, source)
 
 
 def check_known_keys(value, info, source, required, optional):
@@ -892,22 +868,19 @@ def check_known_keys(value, info, source, required, optional):
 
     missing = set(required) - set(value)
     if missing:
-        raise QAPISemError(info, "key%s %s %s missing from %s"
-                           % ('s' if len(missing) > 1 else '', pprint(missing),
-                              'are' if len(missing) > 1 else 'is', source))
+        raise QAPISemError(info, "%s misses key%s %s"
+                           % (source, 's' if len(missing) > 1 else '',
+                              pprint(missing)))
     allowed = set(required + optional)
     unknown = set(value) - allowed
     if unknown:
-        raise QAPISemError(info, "unknown key%s %s in %s\nValid keys are %s."
-                           % ('s' if len(unknown) > 1 else '', pprint(unknown),
-                              source, pprint(allowed)))
+        raise QAPISemError(info, "%s has unknown key%s %s\nValid keys are %s."
+                           % (source, 's' if len(unknown) > 1 else '',
+                              pprint(unknown), pprint(allowed)))
 
 
 def check_keys(expr, info, meta, required, optional=[]):
-    name = expr[meta]
-    required = required + [meta]
-    source = "%s '%s'" % (meta, name)
-    check_known_keys(expr, info, source, required, optional)
+    check_known_keys(expr, info, meta, required + [meta], optional)
 
 
 def check_flags(expr, info):
@@ -982,9 +955,7 @@ def check_exprs(exprs):
 
         if doc and doc.symbol != name:
             raise QAPISemError(
-                info,
-                "definition of '%s' follows documentation for '%s'"
-                % (name, doc.symbol))
+                info, "documentation comment is for '%s'" % doc.symbol)
 
         if meta == 'enum':
             check_keys(expr, info, 'enum', ['data'], ['if', 'prefix'])
diff --git a/tests/qapi-schema/alternate-array.err b/tests/qapi-schema/alternate-array.err
index a72e4b274d..dfbe3ee998 100644
--- a/tests/qapi-schema/alternate-array.err
+++ b/tests/qapi-schema/alternate-array.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/alternate-array.json: In alternate 'Alt':
-tests/qapi-schema/alternate-array.json:5: member 'two' of alternate 'Alt' cannot be an array
+tests/qapi-schema/alternate-array.json:5: 'data' member 'two' cannot be an array
diff --git a/tests/qapi-schema/alternate-base.err b/tests/qapi-schema/alternate-base.err
index 6290665ac2..04cea97e5c 100644
--- a/tests/qapi-schema/alternate-base.err
+++ b/tests/qapi-schema/alternate-base.err
@@ -1,3 +1,3 @@
 tests/qapi-schema/alternate-base.json: In alternate 'Alt':
-tests/qapi-schema/alternate-base.json:4: unknown key 'base' in alternate 'Alt'
+tests/qapi-schema/alternate-base.json:4: alternate has unknown key 'base'
 Valid keys are 'alternate', 'data', 'if'.
diff --git a/tests/qapi-schema/alternate-empty.err b/tests/qapi-schema/alternate-empty.err
index db92d70f10..908c309518 100644
--- a/tests/qapi-schema/alternate-empty.err
+++ b/tests/qapi-schema/alternate-empty.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/alternate-empty.json: In alternate 'Alt':
-tests/qapi-schema/alternate-empty.json:2: alternate 'Alt' cannot have empty 'data'
+tests/qapi-schema/alternate-empty.json:2: 'data' must not be empty
diff --git a/tests/qapi-schema/alternate-invalid-dict.err b/tests/qapi-schema/alternate-invalid-dict.err
index f85b941750..d6a18a294b 100644
--- a/tests/qapi-schema/alternate-invalid-dict.err
+++ b/tests/qapi-schema/alternate-invalid-dict.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/alternate-invalid-dict.json: In alternate 'Alt':
-tests/qapi-schema/alternate-invalid-dict.json:2: key 'type' is missing from member 'two' of alternate 'Alt'
+tests/qapi-schema/alternate-invalid-dict.json:2: 'data' member 'two' misses key 'type'
diff --git a/tests/qapi-schema/args-array-empty.err b/tests/qapi-schema/args-array-empty.err
index fe1480671b..c7d367730e 100644
--- a/tests/qapi-schema/args-array-empty.err
+++ b/tests/qapi-schema/args-array-empty.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/args-array-empty.json: In command 'oops':
-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:2: 'data' member 'empty': array type must contain single type name
diff --git a/tests/qapi-schema/args-boxed-anon.err b/tests/qapi-schema/args-boxed-anon.err
index 27460e6c07..5e0c2979b7 100644
--- a/tests/qapi-schema/args-boxed-anon.err
+++ b/tests/qapi-schema/args-boxed-anon.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/args-boxed-anon.json: In command 'foo':
-tests/qapi-schema/args-boxed-anon.json:2: 'data' for command 'foo' should be a type name
+tests/qapi-schema/args-boxed-anon.json:2: 'data' should be a type name
diff --git a/tests/qapi-schema/args-invalid.err b/tests/qapi-schema/args-invalid.err
index 212c2076fc..c4971e1399 100644
--- a/tests/qapi-schema/args-invalid.err
+++ b/tests/qapi-schema/args-invalid.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/args-invalid.json: In command 'foo':
-tests/qapi-schema/args-invalid.json:1: 'data' for command 'foo' should be an object or type name
+tests/qapi-schema/args-invalid.json:1: 'data' should be an object or type name
diff --git a/tests/qapi-schema/args-member-array-bad.err b/tests/qapi-schema/args-member-array-bad.err
index 89b8b1ce01..f95ac01372 100644
--- a/tests/qapi-schema/args-member-array-bad.err
+++ b/tests/qapi-schema/args-member-array-bad.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/args-member-array-bad.json: In command 'oops':
-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:2: 'data' member 'member': array type must contain single type name
diff --git a/tests/qapi-schema/args-member-case.err b/tests/qapi-schema/args-member-case.err
index faa8168d36..7f10c829de 100644
--- a/tests/qapi-schema/args-member-case.err
+++ b/tests/qapi-schema/args-member-case.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/args-member-case.json: In command 'no-way-this-will-get-whitelisted':
-tests/qapi-schema/args-member-case.json:2: member of 'data' for command 'no-way-this-will-get-whitelisted' uses uppercase in name 'Arg'
+tests/qapi-schema/args-member-case.json:2: 'data' member 'Arg' uses uppercase in name 'Arg'
diff --git a/tests/qapi-schema/bad-data.err b/tests/qapi-schema/bad-data.err
index 8ef6bbd2b5..5227bdce7e 100644
--- a/tests/qapi-schema/bad-data.err
+++ b/tests/qapi-schema/bad-data.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/bad-data.json: In command 'oops':
-tests/qapi-schema/bad-data.json:2: 'data' for command 'oops' cannot be an array
+tests/qapi-schema/bad-data.json:2: 'data' cannot be an array
diff --git a/tests/qapi-schema/doc-bad-symbol.err b/tests/qapi-schema/doc-bad-symbol.err
index 205d0ed619..b23e99d160 100644
--- a/tests/qapi-schema/doc-bad-symbol.err
+++ b/tests/qapi-schema/doc-bad-symbol.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/doc-bad-symbol.json: In command 'foo':
-tests/qapi-schema/doc-bad-symbol.json:6: definition of 'foo' follows documentation for 'food'
+tests/qapi-schema/doc-bad-symbol.json:6: documentation comment is for 'food'
diff --git a/tests/qapi-schema/double-type.err b/tests/qapi-schema/double-type.err
index ddb22af638..23f88aae99 100644
--- a/tests/qapi-schema/double-type.err
+++ b/tests/qapi-schema/double-type.err
@@ -1,3 +1,3 @@
 tests/qapi-schema/double-type.json: In struct 'bar':
-tests/qapi-schema/double-type.json:2: unknown key 'command' in struct 'bar'
+tests/qapi-schema/double-type.json:2: struct has unknown key 'command'
 Valid keys are 'base', 'data', 'features', 'if', 'struct'.
diff --git a/tests/qapi-schema/enum-bad-member.err b/tests/qapi-schema/enum-bad-member.err
index 1e59d42fca..2b1b4f98d0 100644
--- a/tests/qapi-schema/enum-bad-member.err
+++ b/tests/qapi-schema/enum-bad-member.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/enum-bad-member.json: In enum 'MyEnum':
-tests/qapi-schema/enum-bad-member.json:2: member of enum 'MyEnum' requires a string name
+tests/qapi-schema/enum-bad-member.json:2: 'data' member requires a string name
diff --git a/tests/qapi-schema/enum-bad-name.err b/tests/qapi-schema/enum-bad-name.err
index ed2d608098..c4525411cf 100644
--- a/tests/qapi-schema/enum-bad-name.err
+++ b/tests/qapi-schema/enum-bad-name.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/enum-bad-name.json: In enum 'MyEnum':
-tests/qapi-schema/enum-bad-name.json:3: member of enum 'MyEnum' uses invalid name 'not\possible'
+tests/qapi-schema/enum-bad-name.json:3: 'data' member uses invalid name 'not\possible'
diff --git a/tests/qapi-schema/enum-bad-prefix.err b/tests/qapi-schema/enum-bad-prefix.err
index 4f92736e52..933e33aa18 100644
--- a/tests/qapi-schema/enum-bad-prefix.err
+++ b/tests/qapi-schema/enum-bad-prefix.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/enum-bad-prefix.json: In enum 'MyEnum':
-tests/qapi-schema/enum-bad-prefix.json:2: enum 'MyEnum' requires a string for 'prefix'
+tests/qapi-schema/enum-bad-prefix.json:2: 'prefix' must be a string
diff --git a/tests/qapi-schema/enum-dict-member-unknown.err b/tests/qapi-schema/enum-dict-member-unknown.err
index 79062729a1..5df0236343 100644
--- a/tests/qapi-schema/enum-dict-member-unknown.err
+++ b/tests/qapi-schema/enum-dict-member-unknown.err
@@ -1,3 +1,3 @@
 tests/qapi-schema/enum-dict-member-unknown.json: In enum 'MyEnum':
-tests/qapi-schema/enum-dict-member-unknown.json:2: unknown key 'bad-key' in member of enum 'MyEnum'
+tests/qapi-schema/enum-dict-member-unknown.json:2: 'data' member has unknown key 'bad-key'
 Valid keys are 'if', 'name'.
diff --git a/tests/qapi-schema/enum-member-case.err b/tests/qapi-schema/enum-member-case.err
index c3c6f8d709..b327c41528 100644
--- a/tests/qapi-schema/enum-member-case.err
+++ b/tests/qapi-schema/enum-member-case.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/enum-member-case.json: In enum 'NoWayThisWillGetWhitelisted':
-tests/qapi-schema/enum-member-case.json:4: member of enum 'NoWayThisWillGetWhitelisted' uses uppercase in name 'Value'
+tests/qapi-schema/enum-member-case.json:4: 'data' member uses uppercase in name 'Value'
diff --git a/tests/qapi-schema/enum-missing-data.err b/tests/qapi-schema/enum-missing-data.err
index ffde1082c3..4809b01f34 100644
--- a/tests/qapi-schema/enum-missing-data.err
+++ b/tests/qapi-schema/enum-missing-data.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/enum-missing-data.json: In enum 'MyEnum':
-tests/qapi-schema/enum-missing-data.json:2: key 'data' is missing from enum 'MyEnum'
+tests/qapi-schema/enum-missing-data.json:2: enum misses key 'data'
diff --git a/tests/qapi-schema/enum-wrong-data.err b/tests/qapi-schema/enum-wrong-data.err
index ab9af5e995..ad5f0ce46f 100644
--- a/tests/qapi-schema/enum-wrong-data.err
+++ b/tests/qapi-schema/enum-wrong-data.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/enum-wrong-data.json: In enum 'MyEnum':
-tests/qapi-schema/enum-wrong-data.json:2: enum 'MyEnum' requires an array for 'data'
+tests/qapi-schema/enum-wrong-data.json:2: 'data' must be an array
diff --git a/tests/qapi-schema/event-member-invalid-dict.err b/tests/qapi-schema/event-member-invalid-dict.err
index 8bf89b7a3a..8406c43df7 100644
--- a/tests/qapi-schema/event-member-invalid-dict.err
+++ b/tests/qapi-schema/event-member-invalid-dict.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/event-member-invalid-dict.json: In event 'EVENT_A':
-tests/qapi-schema/event-member-invalid-dict.json:1: key 'type' is missing from member 'a' of 'data' for event 'EVENT_A'
+tests/qapi-schema/event-member-invalid-dict.json:1: 'data' member 'a' misses key 'type'
diff --git a/tests/qapi-schema/event-nest-struct.err b/tests/qapi-schema/event-nest-struct.err
index 8900052e83..1a3254a73c 100644
--- a/tests/qapi-schema/event-nest-struct.err
+++ b/tests/qapi-schema/event-nest-struct.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/event-nest-struct.json: In event 'EVENT_A':
-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:1: 'data' member 'a' should be a type name
diff --git a/tests/qapi-schema/features-bad-type.err b/tests/qapi-schema/features-bad-type.err
index 2182c3ec75..30deb8b624 100644
--- a/tests/qapi-schema/features-bad-type.err
+++ b/tests/qapi-schema/features-bad-type.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/features-bad-type.json: In struct 'FeatureStruct0':
-tests/qapi-schema/features-bad-type.json:1: feature of struct FeatureStruct0 requires a string name
+tests/qapi-schema/features-bad-type.json:1: 'features' member requires a string name
diff --git a/tests/qapi-schema/features-missing-name.err b/tests/qapi-schema/features-missing-name.err
index 8cbf1ef3f0..b8db328acc 100644
--- a/tests/qapi-schema/features-missing-name.err
+++ b/tests/qapi-schema/features-missing-name.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/features-missing-name.json: In struct 'FeatureStruct0':
-tests/qapi-schema/features-missing-name.json:1: key 'name' is missing from feature of struct FeatureStruct0
+tests/qapi-schema/features-missing-name.json:1: 'features' member misses key 'name'
diff --git a/tests/qapi-schema/features-name-bad-type.err b/tests/qapi-schema/features-name-bad-type.err
index 19a7b61214..86db2c0ea2 100644
--- a/tests/qapi-schema/features-name-bad-type.err
+++ b/tests/qapi-schema/features-name-bad-type.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/features-name-bad-type.json: In struct 'FeatureStruct0':
-tests/qapi-schema/features-name-bad-type.json:1: feature of struct FeatureStruct0 requires a string name
+tests/qapi-schema/features-name-bad-type.json:1: 'features' member requires a string name
diff --git a/tests/qapi-schema/features-no-list.err b/tests/qapi-schema/features-no-list.err
index 28f91824bd..e493f85057 100644
--- a/tests/qapi-schema/features-no-list.err
+++ b/tests/qapi-schema/features-no-list.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/features-no-list.json: In struct 'FeatureStruct0':
-tests/qapi-schema/features-no-list.json:1: struct 'FeatureStruct0' requires an array for 'features'
+tests/qapi-schema/features-no-list.json:1: 'features' must be an array
diff --git a/tests/qapi-schema/features-unknown-key.err b/tests/qapi-schema/features-unknown-key.err
index 78e63c8cf7..22f5dcf4b0 100644
--- a/tests/qapi-schema/features-unknown-key.err
+++ b/tests/qapi-schema/features-unknown-key.err
@@ -1,3 +1,3 @@
 tests/qapi-schema/features-unknown-key.json: In struct 'FeatureStruct0':
-tests/qapi-schema/features-unknown-key.json:1: unknown key 'colour' in feature of struct FeatureStruct0
+tests/qapi-schema/features-unknown-key.json:1: 'features' member has unknown key 'colour'
 Valid keys are 'if', 'name'.
diff --git a/tests/qapi-schema/flat-union-array-branch.err b/tests/qapi-schema/flat-union-array-branch.err
index 323d79737c..de07a7b32a 100644
--- a/tests/qapi-schema/flat-union-array-branch.err
+++ b/tests/qapi-schema/flat-union-array-branch.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/flat-union-array-branch.json: In union 'TestUnion':
-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:8: 'data' member 'value1' cannot be an array
diff --git a/tests/qapi-schema/flat-union-bad-discriminator.err b/tests/qapi-schema/flat-union-bad-discriminator.err
index 27a6c9f3fb..c1b4209ffd 100644
--- a/tests/qapi-schema/flat-union-bad-discriminator.err
+++ b/tests/qapi-schema/flat-union-bad-discriminator.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/flat-union-bad-discriminator.json: In union 'TestUnion':
-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:11: 'discriminator' requires a string name
diff --git a/tests/qapi-schema/flat-union-inline-invalid-dict.err b/tests/qapi-schema/flat-union-inline-invalid-dict.err
index 85739c2733..d353bdd338 100644
--- a/tests/qapi-schema/flat-union-inline-invalid-dict.err
+++ b/tests/qapi-schema/flat-union-inline-invalid-dict.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/flat-union-inline-invalid-dict.json: In union 'TestUnion':
-tests/qapi-schema/flat-union-inline-invalid-dict.json:7: key 'type' is missing from member 'value1' of union 'TestUnion'
+tests/qapi-schema/flat-union-inline-invalid-dict.json:7: 'data' member 'value1' misses key 'type'
diff --git a/tests/qapi-schema/flat-union-inline.err b/tests/qapi-schema/flat-union-inline.err
index 33a8d6e3bd..95b1e8c1b7 100644
--- a/tests/qapi-schema/flat-union-inline.err
+++ b/tests/qapi-schema/flat-union-inline.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/flat-union-inline.json: In union 'TestUnion':
-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:7: 'data' member 'value1' should be a type name
diff --git a/tests/qapi-schema/flat-union-no-base.err b/tests/qapi-schema/flat-union-no-base.err
index c845259824..a16f3231f1 100644
--- a/tests/qapi-schema/flat-union-no-base.err
+++ b/tests/qapi-schema/flat-union-no-base.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/flat-union-no-base.json: In union 'TestUnion':
-tests/qapi-schema/flat-union-no-base.json:9: flat union 'TestUnion' must have a base
+tests/qapi-schema/flat-union-no-base.json:9: 'discriminator' requires 'base'
diff --git a/tests/qapi-schema/nested-struct-data-invalid-dict.err b/tests/qapi-schema/nested-struct-data-invalid-dict.err
index f2c7a8096c..ed42d6323e 100644
--- a/tests/qapi-schema/nested-struct-data-invalid-dict.err
+++ b/tests/qapi-schema/nested-struct-data-invalid-dict.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/nested-struct-data-invalid-dict.json: In command 'foo':
-tests/qapi-schema/nested-struct-data-invalid-dict.json:2: key 'type' is missing from member 'a' of 'data' for command 'foo'
+tests/qapi-schema/nested-struct-data-invalid-dict.json:2: 'data' member 'a' misses key 'type'
diff --git a/tests/qapi-schema/nested-struct-data.err b/tests/qapi-schema/nested-struct-data.err
index b5e136674c..b0ec410eb7 100644
--- a/tests/qapi-schema/nested-struct-data.err
+++ b/tests/qapi-schema/nested-struct-data.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/nested-struct-data.json: In command 'foo':
-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:2: 'data' member 'a' should be a type name
diff --git a/tests/qapi-schema/reserved-enum-q.err b/tests/qapi-schema/reserved-enum-q.err
index d9c0af5a05..32618a955c 100644
--- a/tests/qapi-schema/reserved-enum-q.err
+++ b/tests/qapi-schema/reserved-enum-q.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/reserved-enum-q.json: In enum 'Foo':
-tests/qapi-schema/reserved-enum-q.json:4: member of enum 'Foo' uses invalid name 'q-Unix'
+tests/qapi-schema/reserved-enum-q.json:4: 'data' member uses invalid name 'q-Unix'
diff --git a/tests/qapi-schema/reserved-member-has.err b/tests/qapi-schema/reserved-member-has.err
index 6f405ec2a9..c7ad721ad1 100644
--- a/tests/qapi-schema/reserved-member-has.err
+++ b/tests/qapi-schema/reserved-member-has.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/reserved-member-has.json: In command 'oops':
-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:5: 'data' member 'has-a' uses reserved name
diff --git a/tests/qapi-schema/reserved-member-q.err b/tests/qapi-schema/reserved-member-q.err
index ece2664005..a65f38e0ce 100644
--- a/tests/qapi-schema/reserved-member-q.err
+++ b/tests/qapi-schema/reserved-member-q.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/reserved-member-q.json: In struct 'Foo':
-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:4: 'data' member 'q-unix' uses invalid name 'q-unix'
diff --git a/tests/qapi-schema/reserved-member-u.err b/tests/qapi-schema/reserved-member-u.err
index e812a1e404..2e92c11ba5 100644
--- a/tests/qapi-schema/reserved-member-u.err
+++ b/tests/qapi-schema/reserved-member-u.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/reserved-member-u.json: In struct 'Oops':
-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:7: 'data' member 'u' uses reserved name
diff --git a/tests/qapi-schema/reserved-member-underscore.err b/tests/qapi-schema/reserved-member-underscore.err
index e1d54f0a27..476a57ba1e 100644
--- a/tests/qapi-schema/reserved-member-underscore.err
+++ b/tests/qapi-schema/reserved-member-underscore.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/reserved-member-underscore.json: In struct 'Oops':
-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:4: 'data' member '_oops' uses invalid name '_oops'
diff --git a/tests/qapi-schema/reserved-type-kind.err b/tests/qapi-schema/reserved-type-kind.err
index 8d21479000..f8112cf92e 100644
--- a/tests/qapi-schema/reserved-type-kind.err
+++ b/tests/qapi-schema/reserved-type-kind.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/reserved-type-kind.json: In enum 'UnionKind':
-tests/qapi-schema/reserved-type-kind.json:2: enum 'UnionKind' should not end in 'Kind'
+tests/qapi-schema/reserved-type-kind.json:2: enum name should not end in 'Kind'
diff --git a/tests/qapi-schema/reserved-type-list.err b/tests/qapi-schema/reserved-type-list.err
index 2bdd7d8a06..c6eee0585c 100644
--- a/tests/qapi-schema/reserved-type-list.err
+++ b/tests/qapi-schema/reserved-type-list.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/reserved-type-list.json: In struct 'FooList':
-tests/qapi-schema/reserved-type-list.json:5: struct 'FooList' should not end in 'List'
+tests/qapi-schema/reserved-type-list.json:5: struct name should not end in 'List'
diff --git a/tests/qapi-schema/returns-array-bad.err b/tests/qapi-schema/returns-array-bad.err
index 6295ba89c0..1b86777d8f 100644
--- a/tests/qapi-schema/returns-array-bad.err
+++ b/tests/qapi-schema/returns-array-bad.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/returns-array-bad.json: In command 'oops':
-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:2: 'returns': array type must contain single type name
diff --git a/tests/qapi-schema/returns-dict.err b/tests/qapi-schema/returns-dict.err
index 7329b9526f..52e4f3ad71 100644
--- a/tests/qapi-schema/returns-dict.err
+++ b/tests/qapi-schema/returns-dict.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/returns-dict.json: In command 'oops':
-tests/qapi-schema/returns-dict.json:2: 'returns' for command 'oops' should be a type name
+tests/qapi-schema/returns-dict.json:2: 'returns' should be a type name
diff --git a/tests/qapi-schema/struct-data-invalid.err b/tests/qapi-schema/struct-data-invalid.err
index a88754869f..aa868bf974 100644
--- a/tests/qapi-schema/struct-data-invalid.err
+++ b/tests/qapi-schema/struct-data-invalid.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/struct-data-invalid.json: In struct 'foo':
-tests/qapi-schema/struct-data-invalid.json:1: 'data' for struct 'foo' should be an object or type name
+tests/qapi-schema/struct-data-invalid.json:1: 'data' should be an object or type name
diff --git a/tests/qapi-schema/struct-member-invalid-dict.err b/tests/qapi-schema/struct-member-invalid-dict.err
index 0c770bb1e8..46ec991c28 100644
--- a/tests/qapi-schema/struct-member-invalid-dict.err
+++ b/tests/qapi-schema/struct-member-invalid-dict.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/struct-member-invalid-dict.json: In struct 'foo':
-tests/qapi-schema/struct-member-invalid-dict.json:2: key 'type' is missing from member '*a' of 'data' for struct 'foo'
+tests/qapi-schema/struct-member-invalid-dict.json:2: 'data' member '*a' misses key 'type'
diff --git a/tests/qapi-schema/struct-member-invalid.err b/tests/qapi-schema/struct-member-invalid.err
index e5a19fc8af..92d4973832 100644
--- a/tests/qapi-schema/struct-member-invalid.err
+++ b/tests/qapi-schema/struct-member-invalid.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/struct-member-invalid.json: In struct 'foo':
-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:1: 'data' member 'a' should be a type name
diff --git a/tests/qapi-schema/union-base-no-discriminator.err b/tests/qapi-schema/union-base-no-discriminator.err
index 883a98866b..f4c16a2c14 100644
--- a/tests/qapi-schema/union-base-no-discriminator.err
+++ b/tests/qapi-schema/union-base-no-discriminator.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/union-base-no-discriminator.json: In union 'TestUnion':
-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:11: 'base' requires 'discriminator'
diff --git a/tests/qapi-schema/union-branch-case.err b/tests/qapi-schema/union-branch-case.err
index f111210281..cf55c085ec 100644
--- a/tests/qapi-schema/union-branch-case.err
+++ b/tests/qapi-schema/union-branch-case.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/union-branch-case.json: In union 'Uni':
-tests/qapi-schema/union-branch-case.json:2: member of union 'Uni' uses uppercase in name 'Branch'
+tests/qapi-schema/union-branch-case.json:2: 'data' member 'Branch' uses uppercase in name 'Branch'
diff --git a/tests/qapi-schema/union-branch-invalid-dict.err b/tests/qapi-schema/union-branch-invalid-dict.err
index d11a739674..2967cd6260 100644
--- a/tests/qapi-schema/union-branch-invalid-dict.err
+++ b/tests/qapi-schema/union-branch-invalid-dict.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/union-branch-invalid-dict.json: In union 'UnionInvalidBranch':
-tests/qapi-schema/union-branch-invalid-dict.json:2: key 'type' is missing from member 'integer' of union 'UnionInvalidBranch'
+tests/qapi-schema/union-branch-invalid-dict.json:2: 'data' member 'integer' misses key 'type'
diff --git a/tests/qapi-schema/union-optional-branch.err b/tests/qapi-schema/union-optional-branch.err
index 8e9b18d7c6..779d3d4969 100644
--- a/tests/qapi-schema/union-optional-branch.err
+++ b/tests/qapi-schema/union-optional-branch.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/union-optional-branch.json: In union 'Union':
-tests/qapi-schema/union-optional-branch.json:2: member of union 'Union' uses invalid name '*a'
+tests/qapi-schema/union-optional-branch.json:2: 'data' member '*a' uses invalid name '*a'
diff --git a/tests/qapi-schema/unknown-expr-key.err b/tests/qapi-schema/unknown-expr-key.err
index e401efe148..be9f99c4ef 100644
--- a/tests/qapi-schema/unknown-expr-key.err
+++ b/tests/qapi-schema/unknown-expr-key.err
@@ -1,3 +1,3 @@
 tests/qapi-schema/unknown-expr-key.json: In struct 'bar':
-tests/qapi-schema/unknown-expr-key.json:2: unknown keys 'bogus', 'phony' in struct 'bar'
+tests/qapi-schema/unknown-expr-key.json:2: struct has unknown keys 'bogus', 'phony'
 Valid keys are 'base', 'data', 'features', 'if', 'struct'.
-- 
2.21.0



^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH 22/25] qapi: Eliminate check_keys(), rename check_known_keys()
  2019-09-24 13:28 [PATCH 00/25] qapi: Pay back some frontend technical debt Markus Armbruster
                   ` (20 preceding siblings ...)
  2019-09-24 13:28 ` [PATCH 21/25] qapi: Avoid redundant definition references in error messages Markus Armbruster
@ 2019-09-24 13:28 ` Markus Armbruster
  2019-09-24 18:49   ` Eric Blake
  2019-09-24 13:28 ` [PATCH 23/25] qapi: Improve reporting of missing documentation comment Markus Armbruster
                   ` (2 subsequent siblings)
  24 siblings, 1 reply; 63+ messages in thread
From: Markus Armbruster @ 2019-09-24 13:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, mdroth

check_keys() has become a trivial wrapper for check_known_keys().
Eliminate it.

This makes its name available.  Rename check_known_keys().

Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 scripts/qapi/common.py | 40 +++++++++++++++++++++-------------------
 1 file changed, 21 insertions(+), 19 deletions(-)

diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index 3e9c5ae127..018e58bc50 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -757,7 +757,7 @@ def check_type(value, info, source,
                        allow_optional=True, permit_upper=permit_upper)
         if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
             raise QAPISemError(info, "%s uses reserved name" % key_source)
-        check_known_keys(arg, info, key_source, ['type'], ['if'])
+        check_keys(arg, info, key_source, ['type'], ['if'])
         check_if(arg, info)
         normalize_if(arg)
         check_type(arg['type'], info, key_source, allow_array=True)
@@ -800,7 +800,7 @@ def check_union(expr, info):
     for (key, value) in members.items():
         source = "'data' member '%s'" % key
         check_name_str(key, info, source)
-        check_known_keys(value, info, source, ['type'], ['if'])
+        check_keys(value, info, source, ['type'], ['if'])
         check_if(value, info)
         normalize_if(value)
         check_type(value['type'], info, source, allow_array=not base)
@@ -814,7 +814,7 @@ def check_alternate(expr, info):
     for (key, value) in members.items():
         source = "'data' member '%s'" % key
         check_name_str(key, info, source)
-        check_known_keys(value, info, source, ['type'], ['if'])
+        check_keys(value, info, source, ['type'], ['if'])
         check_if(value, info)
         normalize_if(value)
         check_type(value['type'], info, source)
@@ -834,7 +834,7 @@ def check_enum(expr, info):
 
     for member in members:
         source = "'data' member"
-        check_known_keys(member, info, source, ['name'], ['if'])
+        check_keys(member, info, source, ['name'], ['if'])
         check_if(member, info)
         normalize_if(member)
         check_name(member['name'], info, source,
@@ -855,13 +855,13 @@ def check_struct(expr, info):
         for f in features:
             source = "'features' member"
             assert isinstance(f, dict)
-            check_known_keys(f, info, source, ['name'], ['if'])
+            check_keys(f, info, source, ['name'], ['if'])
             check_if(f, info)
             normalize_if(f)
             check_name(f['name'], info, source)
 
 
-def check_known_keys(value, info, source, required, optional):
+def check_keys(value, info, source, required, optional):
 
     def pprint(elems):
         return ', '.join("'" + e + "'" for e in sorted(elems))
@@ -879,10 +879,6 @@ def check_known_keys(value, info, source, required, optional):
                               pprint(unknown), pprint(allowed)))
 
 
-def check_keys(expr, info, meta, required, optional=[]):
-    check_known_keys(expr, info, meta, required + [meta], optional)
-
-
 def check_flags(expr, info):
     for key in ['gen', 'success-response']:
         if key in expr and expr[key] is not False:
@@ -958,33 +954,39 @@ def check_exprs(exprs):
                 info, "documentation comment is for '%s'" % doc.symbol)
 
         if meta == 'enum':
-            check_keys(expr, info, 'enum', ['data'], ['if', 'prefix'])
+            check_keys(expr, info, meta,
+                       ['enum', 'data'], ['if', 'prefix'])
             normalize_enum(expr)
             check_enum(expr, info)
         elif meta == 'union':
-            check_keys(expr, info, 'union', ['data'],
+            check_keys(expr, info, meta,
+                       ['union', 'data'],
                        ['base', 'discriminator', 'if'])
             normalize_members(expr.get('base'))
             normalize_members(expr['data'])
             check_union(expr, info)
         elif meta == 'alternate':
-            check_keys(expr, info, 'alternate', ['data'], ['if'])
+            check_keys(expr, info, meta,
+                       ['alternate', 'data'], ['if'])
             normalize_members(expr['data'])
             check_alternate(expr, info)
         elif meta == 'struct':
-            check_keys(expr, info, 'struct', ['data'],
-                       ['base', 'if', 'features'])
+            check_keys(expr, info, meta,
+                       ['struct', 'data'], ['base', 'if', 'features'])
             normalize_members(expr['data'])
             normalize_features(expr.get('features'))
             check_struct(expr, info)
         elif meta == 'command':
-            check_keys(expr, info, 'command', [],
-                       ['data', 'returns', 'gen', 'success-response',
-                        'boxed', 'allow-oob', 'allow-preconfig', 'if'])
+            check_keys(expr, info, meta,
+                       ['command'],
+                       ['data', 'returns', 'boxed', 'if',
+                        'gen', 'success-response', 'allow-oob',
+                        'allow-preconfig'])
             normalize_members(expr.get('data'))
             check_command(expr, info)
         elif meta == 'event':
-            check_keys(expr, info, 'event', [], ['data', 'boxed', 'if'])
+            check_keys(expr, info, meta,
+                       ['event'], ['data', 'boxed', 'if'])
             normalize_members(expr.get('data'))
             check_event(expr, info)
         else:
-- 
2.21.0



^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH 23/25] qapi: Improve reporting of missing documentation comment
  2019-09-24 13:28 [PATCH 00/25] qapi: Pay back some frontend technical debt Markus Armbruster
                   ` (21 preceding siblings ...)
  2019-09-24 13:28 ` [PATCH 22/25] qapi: Eliminate check_keys(), rename check_known_keys() Markus Armbruster
@ 2019-09-24 13:28 ` Markus Armbruster
  2019-09-24 19:44   ` Eric Blake
  2019-09-24 13:28 ` [PATCH 24/25] qapi: Improve reporting of redefinition Markus Armbruster
  2019-09-24 13:28 ` [PATCH 25/25] qapi: Improve source file read error handling Markus Armbruster
  24 siblings, 1 reply; 63+ messages in thread
From: Markus Armbruster @ 2019-09-24 13:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, mdroth

Have check_exprs() check this later, so the error message gains an "in
definition line".  Tweak the error message.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 scripts/qapi/common.py            | 18 ++++++++----------
 tests/qapi-schema/doc-missing.err |  3 ++-
 2 files changed, 10 insertions(+), 11 deletions(-)

diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index 018e58bc50..29b75345bb 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -925,10 +925,6 @@ def check_exprs(exprs):
         if 'include' in expr:
             continue
 
-        if not doc and doc_required:
-            raise QAPISemError(info,
-                               "definition missing documentation comment")
-
         if 'enum' in expr:
             meta = 'enum'
         elif 'union' in expr:
@@ -949,9 +945,14 @@ def check_exprs(exprs):
         info.set_defn(meta, name)
         check_defn_name_str(name, info, meta)
 
-        if doc and doc.symbol != name:
-            raise QAPISemError(
-                info, "documentation comment is for '%s'" % doc.symbol)
+        if doc:
+            if doc.symbol != name:
+                raise QAPISemError(
+                    info, "documentation comment is for '%s'" % doc.symbol)
+            doc.check_expr(expr)
+        elif doc_required:
+            raise QAPISemError(info,
+                               "documentation comment required")
 
         if meta == 'enum':
             check_keys(expr, info, meta,
@@ -996,9 +997,6 @@ def check_exprs(exprs):
         check_if(expr, info)
         check_flags(expr, info)
 
-        if doc:
-            doc.check_expr(expr)
-
     return exprs
 
 
diff --git a/tests/qapi-schema/doc-missing.err b/tests/qapi-schema/doc-missing.err
index 08c827931a..7fbf54ff65 100644
--- a/tests/qapi-schema/doc-missing.err
+++ b/tests/qapi-schema/doc-missing.err
@@ -1 +1,2 @@
-tests/qapi-schema/doc-missing.json:5: definition missing documentation comment
+tests/qapi-schema/doc-missing.json: In command 'undocumented':
+tests/qapi-schema/doc-missing.json:5: documentation comment required
-- 
2.21.0



^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH 24/25] qapi: Improve reporting of redefinition
  2019-09-24 13:28 [PATCH 00/25] qapi: Pay back some frontend technical debt Markus Armbruster
                   ` (22 preceding siblings ...)
  2019-09-24 13:28 ` [PATCH 23/25] qapi: Improve reporting of missing documentation comment Markus Armbruster
@ 2019-09-24 13:28 ` Markus Armbruster
  2019-09-24 19:53   ` Eric Blake
  2019-09-24 13:28 ` [PATCH 25/25] qapi: Improve source file read error handling Markus Armbruster
  24 siblings, 1 reply; 63+ messages in thread
From: Markus Armbruster @ 2019-09-24 13:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, mdroth

Point to the previous definition, unless it's a built-in.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 scripts/qapi/common.py                  | 5 +++++
 tests/qapi-schema/redefined-command.err | 4 +++-
 tests/qapi-schema/redefined-event.err   | 4 +++-
 tests/qapi-schema/redefined-type.err    | 4 +++-
 4 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index 29b75345bb..c38e7cf27d 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -1748,6 +1748,11 @@ class QAPISchema(object):
         # because they're liable to clash in generated C.
         other_ent = self._entity_dict.get(ent.name)
         if other_ent:
+            if other_ent.info:
+                where = QAPIError(other_ent.info, None, "previous definition")
+                raise QAPISemError(
+                    ent.info,
+                    "'%s' is already defined\n%s" % (ent.name, where))
             raise QAPISemError(
                 ent.info, "%s is already defined" % other_ent.describe())
         self._entity_dict[ent.name] = ent
diff --git a/tests/qapi-schema/redefined-command.err b/tests/qapi-schema/redefined-command.err
index b77a05d354..54e366bbf3 100644
--- a/tests/qapi-schema/redefined-command.err
+++ b/tests/qapi-schema/redefined-command.err
@@ -1,2 +1,4 @@
 tests/qapi-schema/redefined-command.json: In command 'foo':
-tests/qapi-schema/redefined-command.json:3: command 'foo' is already defined
+tests/qapi-schema/redefined-command.json:3: 'foo' is already defined
+tests/qapi-schema/redefined-command.json: In command 'foo':
+tests/qapi-schema/redefined-command.json:2: previous definition
diff --git a/tests/qapi-schema/redefined-event.err b/tests/qapi-schema/redefined-event.err
index fd02d38157..606c6e4497 100644
--- a/tests/qapi-schema/redefined-event.err
+++ b/tests/qapi-schema/redefined-event.err
@@ -1,2 +1,4 @@
 tests/qapi-schema/redefined-event.json: In event 'EVENT_A':
-tests/qapi-schema/redefined-event.json:3: event 'EVENT_A' is already defined
+tests/qapi-schema/redefined-event.json:3: 'EVENT_A' is already defined
+tests/qapi-schema/redefined-event.json: In event 'EVENT_A':
+tests/qapi-schema/redefined-event.json:2: previous definition
diff --git a/tests/qapi-schema/redefined-type.err b/tests/qapi-schema/redefined-type.err
index 39f51c14ea..77786f98ae 100644
--- a/tests/qapi-schema/redefined-type.err
+++ b/tests/qapi-schema/redefined-type.err
@@ -1,2 +1,4 @@
 tests/qapi-schema/redefined-type.json: In enum 'foo':
-tests/qapi-schema/redefined-type.json:3: struct type 'foo' is already defined
+tests/qapi-schema/redefined-type.json:3: 'foo' is already defined
+tests/qapi-schema/redefined-type.json: In struct 'foo':
+tests/qapi-schema/redefined-type.json:2: previous definition
-- 
2.21.0



^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH 25/25] qapi: Improve source file read error handling
  2019-09-24 13:28 [PATCH 00/25] qapi: Pay back some frontend technical debt Markus Armbruster
                   ` (23 preceding siblings ...)
  2019-09-24 13:28 ` [PATCH 24/25] qapi: Improve reporting of redefinition Markus Armbruster
@ 2019-09-24 13:28 ` Markus Armbruster
  2019-09-24 19:57   ` Eric Blake
  24 siblings, 1 reply; 63+ messages in thread
From: Markus Armbruster @ 2019-09-24 13:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, mdroth

qap-gen.py crashes when it can't open the main schema file, and when
it can't read from any schema file.  Lazy.

Change QAPISchema.__init__() to take a file name instead of a file
object.  Move the open code from _include() to __init__(), so it's
used for the main schema file, too.

Move the read into the try for good measure, and rephrase the error
message.

Reporting open or read failure for the main schema file needs a
QAPISourceInfo representing "no source".  Make QAPISourceInfo cope
with fname=None.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 scripts/qapi/common.py                | 46 +++++++++++++++------------
 tests/qapi-schema/include-no-file.err |  2 +-
 2 files changed, 27 insertions(+), 21 deletions(-)

diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index c38e7cf27d..0c169a640e 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -53,7 +53,12 @@ class QAPISourceInfo(object):
         return info
 
     def loc(self):
-        return '%s:%d' % (self.fname, self.line)
+        if self.fname is None:
+            return sys.argv[0]
+        ret = self.fname
+        if self.line is not None:
+            ret += ':%d' % self.line
+        return ret
 
     def in_defn(self):
         if self.defn_name:
@@ -382,14 +387,26 @@ class QAPIDoc(object):
 
 class QAPISchemaParser(object):
 
-    def __init__(self, fp, previously_included=[], incl_info=None):
-        self.fname = fp.name
-        previously_included.append(os.path.abspath(fp.name))
-        self.src = fp.read()
+    def __init__(self, fname, previously_included=[], incl_info=None):
+        previously_included.append(os.path.abspath(fname))
+
+        try:
+            if sys.version_info[0] >= 3:
+                fp = open(fname, 'r', encoding='utf-8')
+            else:
+                fp = open(fname, 'r')
+            self.src = fp.read()
+        except IOError as e:
+            raise QAPISemError(incl_info or QAPISourceInfo(None, None, None),
+                               "can't read %s file '%s': %s"
+                               % ("include" if incl_info else "schema",
+                                  fname,
+                                  e.strerror))
+
         if self.src == '' or self.src[-1] != '\n':
             self.src += '\n'
         self.cursor = 0
-        self.info = QAPISourceInfo(self.fname, 1, incl_info)
+        self.info = QAPISourceInfo(fname, 1, incl_info)
         self.line_pos = 0
         self.exprs = []
         self.docs = []
@@ -413,7 +430,7 @@ class QAPISchemaParser(object):
                 if not isinstance(include, str):
                     raise QAPISemError(info,
                                        "value of 'include' must be a string")
-                incl_fname = os.path.join(os.path.dirname(self.fname),
+                incl_fname = os.path.join(os.path.dirname(fname),
                                           include)
                 self.exprs.append({'expr': {'include': incl_fname},
                                    'info': info})
@@ -465,14 +482,7 @@ class QAPISchemaParser(object):
         if incl_abs_fname in previously_included:
             return None
 
-        try:
-            if sys.version_info[0] >= 3:
-                fobj = open(incl_fname, 'r', encoding='utf-8')
-            else:
-                fobj = open(incl_fname, 'r')
-        except IOError as e:
-            raise QAPISemError(info, "%s: %s" % (e.strerror, incl_fname))
-        return QAPISchemaParser(fobj, previously_included, info)
+        return QAPISchemaParser(incl_fname, previously_included, info)
 
     def _pragma(self, name, value, info):
         global doc_required, returns_whitelist, name_case_whitelist
@@ -1723,11 +1733,7 @@ class QAPISchemaEvent(QAPISchemaEntity):
 class QAPISchema(object):
     def __init__(self, fname):
         self.fname = fname
-        if sys.version_info[0] >= 3:
-            f = open(fname, 'r', encoding='utf-8')
-        else:
-            f = open(fname, 'r')
-        parser = QAPISchemaParser(f)
+        parser = QAPISchemaParser(fname)
         exprs = check_exprs(parser.exprs)
         self.docs = parser.docs
         self._entity_list = []
diff --git a/tests/qapi-schema/include-no-file.err b/tests/qapi-schema/include-no-file.err
index e42bcf4bc1..0a6c6bb4a9 100644
--- a/tests/qapi-schema/include-no-file.err
+++ b/tests/qapi-schema/include-no-file.err
@@ -1 +1 @@
-tests/qapi-schema/include-no-file.json:1: No such file or directory: tests/qapi-schema/include-no-file-sub.json
+tests/qapi-schema/include-no-file.json:1: can't read include file 'tests/qapi-schema/include-no-file-sub.json': No such file or directory
-- 
2.21.0



^ permalink raw reply related	[flat|nested] 63+ messages in thread

* Re: [PATCH 01/25] qapi: Tighten QAPISchemaFOO.check() assertions
  2019-09-24 13:28 ` [PATCH 01/25] qapi: Tighten QAPISchemaFOO.check() assertions Markus Armbruster
@ 2019-09-24 14:39   ` Eric Blake
  2019-09-24 20:18     ` Markus Armbruster
  0 siblings, 1 reply; 63+ messages in thread
From: Eric Blake @ 2019-09-24 14:39 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel; +Cc: marcandre.lureau, mdroth


[-- Attachment #1.1: Type: text/plain, Size: 1736 bytes --]

On 9/24/19 8:28 AM, Markus Armbruster wrote:
> When we introduced the QAPISchema intermediate representation (commit
> ac88219a6c7), we took a shortcut: we left check_exprs() & friends
> alone instead of moving semantic checks into the
> QAPISchemaFOO.check().  check_exprs() still checks and reports errors,
> and the .check() assert check_exprs() did the job.  There are a few
> gaps, though.
> 
> QAPISchemaArrayType.check() neglects to assert the element type is not
> an array.  Add the assertion.
> 
> QAPISchemaObjectTypeVariants.check() neglects to assert the tag member
> is not optional.  Add the assertion.
> 
> It neglects to assert the tag member is not conditional.  Add the
> assertion.
> 
> It neglects to assert we actually have variants.  Add the assertion.
> 
> It asserts the variants are object types, but neglects to assert they
> don't have variants.  Tighten the assertion.
> 
> QAPISchemaObjectTypeVariants.check_clash() has the same issue.
> However, it can run only after .check().  Delete the assertion instead
> of tightening it.
> 
> QAPISchemaAlternateType.check() neglects to assert the branch types
> don't conflict.  Fixing that isn't trivial, so add just a TODO comment
> for now.  It'll be resolved later in this series.

I'm guessing you found these by deleting check_exprs() and seeing what
failed due to inadequate .check()

> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> ---
>  scripts/qapi/common.py | 9 +++++++--
>  1 file changed, 7 insertions(+), 2 deletions(-)

Reviewed-by: Eric Blake <eblake@redhat.com>

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3226
Virtualization:  qemu.org | libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH 02/25] qapi: Rename .owner to .defined_in
  2019-09-24 13:28 ` [PATCH 02/25] qapi: Rename .owner to .defined_in Markus Armbruster
@ 2019-09-24 14:41   ` Eric Blake
  0 siblings, 0 replies; 63+ messages in thread
From: Eric Blake @ 2019-09-24 14:41 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel; +Cc: marcandre.lureau, mdroth


[-- Attachment #1.1: Type: text/plain, Size: 683 bytes --]

On 9/24/19 8:28 AM, Markus Armbruster wrote:
> QAPISchemaMember.owner is the name of the defining entity.  That's a
> confusing name when an object type inherits members from a base type.
> Rename it to .defined_in.  Rename .set_owner() and ._pretty_owner() to
> match.
> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> ---
>  scripts/qapi/common.py | 61 +++++++++++++++++++++---------------------
>  1 file changed, 31 insertions(+), 30 deletions(-)

Mechanical, and more legible.

Reviewed-by: Eric Blake <eblake@redhat.com>

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3226
Virtualization:  qemu.org | libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH 03/25] qapi: New QAPISourceInfo, replacing dict
  2019-09-24 13:28 ` [PATCH 03/25] qapi: New QAPISourceInfo, replacing dict Markus Armbruster
@ 2019-09-24 14:51   ` Eric Blake
  2019-09-24 20:18     ` Markus Armbruster
  2019-09-24 19:12   ` Eric Blake
  1 sibling, 1 reply; 63+ messages in thread
From: Eric Blake @ 2019-09-24 14:51 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel; +Cc: marcandre.lureau, mdroth


[-- Attachment #1.1: Type: text/plain, Size: 854 bytes --]

On 9/24/19 8:28 AM, Markus Armbruster wrote:
> We track source locations with a dict of the form
> 
>     {'file': FNAME, 'line': LINENO, parent': PARENT}

Missing ' on parent

> 
> where PARENT is None for the main file, and the include directive's
> source location for included files.
> 
> This is servicable enough, but the next commit will add information,

serviceable

> and that's going to come out cleaner if we turn this into a class.  So
> do that.
> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> ---
>  scripts/qapi/common.py | 69 +++++++++++++++++++++++++-----------------
>  1 file changed, 41 insertions(+), 28 deletions(-)
> 
Reviewed-by: Eric Blake <eblake@redhat.com>

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3226
Virtualization:  qemu.org | libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH 04/25] qapi: Prefix frontend errors with an "in definition" line
  2019-09-24 13:28 ` [PATCH 04/25] qapi: Prefix frontend errors with an "in definition" line Markus Armbruster
@ 2019-09-24 14:58   ` Eric Blake
  0 siblings, 0 replies; 63+ messages in thread
From: Eric Blake @ 2019-09-24 14:58 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel; +Cc: marcandre.lureau, mdroth


[-- Attachment #1.1: Type: text/plain, Size: 2949 bytes --]

On 9/24/19 8:28 AM, Markus Armbruster wrote:
> We take pains to include the offending expression in error messages,
> e.g.
> 
>     tests/qapi-schema/alternate-any.json:2: alternate 'Alt' member 'one' cannot use type 'any'
> 
> But not always:
> 
>     tests/qapi-schema/enum-if-invalid.json:2: 'if' condition must be a string or a list of strings
> 
> Instead of improving them one by one, report the offending expression
> whenever it is known, like this:
> 
>     tests/qapi-schema/enum-if-invalid.json: In enum 'TestIfEnum':
>     tests/qapi-schema/enum-if-invalid.json:2: 'if' condition must be a string or a list of strings

Works for me.

> 
> Error messages that mention the offending expression become a bit
> redundant, e.g.
> 
>     tests/qapi-schema/alternate-any.json: In alternate 'Alt':
>     tests/qapi-schema/alternate-any.json:2: alternate 'Alt' member 'one' cannot use type 'any'
> 
> I'll take care of that later in this series.

Temporary verboseness is not a problem.

> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> ---
>  scripts/qapi/common.py                            | 15 ++++++++++++++-
>  tests/qapi-schema/alternate-any.err               |  1 +
>  tests/qapi-schema/alternate-array.err             |  1 +

Touches a lot.  But such is refactoring life.

> +++ b/scripts/qapi/common.py
> @@ -64,6 +64,12 @@ class QAPISourceInfo(object):
>          self.fname = fname
>          self.line = line
>          self.parent = parent
> +        self.defn_meta = None
> +        self.defn_name = None
> +
> +    def set_defn(self, meta, name):
> +        self.defn_meta = meta
> +        self.defn_name = name
>  
>      def next_line(self):
>          info = copy.copy(self)
> @@ -73,6 +79,12 @@ class QAPISourceInfo(object):
>      def loc(self):
>          return '%s:%d' % (self.fname, self.line)
>  
> +    def in_defn(self):
> +        if self.defn_name:
> +            return "%s: In %s '%s':\n" % (self.fname,
> +                                          self.defn_meta, self.defn_name)
> +        return ''
> +
>      def include_path(self):
>          ret = ''
>          parent = self.parent
> @@ -82,7 +94,7 @@ class QAPISourceInfo(object):
>          return ret
>  
>      def __str__(self):
> -        return self.include_path() + self.loc()
> +        return self.include_path() + self.in_defn() + self.loc()
>  
>  
>  class QAPIError(Exception):
> @@ -1127,6 +1139,7 @@ def check_exprs(exprs):
>          normalize_if(expr)
>          name = expr[meta]
>          add_name(name, info, meta)
> +        info.set_defn(meta, name)
>          if doc and doc.symbol != name:

Rather simple addition.  Everything else in the patch is fallout.

Reviewed-by: Eric Blake <eblake@redhat.com>

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3226
Virtualization:  qemu.org | libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH 05/25] qapi: Clean up member name case checking
  2019-09-24 13:28 ` [PATCH 05/25] qapi: Clean up member name case checking Markus Armbruster
@ 2019-09-24 15:07   ` Eric Blake
  2019-09-24 20:20     ` Markus Armbruster
  0 siblings, 1 reply; 63+ messages in thread
From: Eric Blake @ 2019-09-24 15:07 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel; +Cc: marcandre.lureau, mdroth


[-- Attachment #1.1: Type: text/plain, Size: 4389 bytes --]

On 9/24/19 8:28 AM, Markus Armbruster wrote:
> QAPISchemaMember.check_clash() checks for member names that map to the
> same c_name().  Takes care of rejecting duplicate names.
> 
> It also checks a naming rule: no uppercase in member names.  That's a
> rather odd place to do it.  Enforcing naming rules is
> check_name_str()'s job.
> 
> qapi-code-gen.txt specifies the name case rule applies to the name as
> it appears in the schema.  check_clash() checks c_name(name) instead.
> No difference, as c_name() leaves alone case, but unclean.
> 
> Move the name case check into check_name_str(), less the c_name().
> New argument @permit_upper suppresses it.  Pass permit_upper=True for
> definitions (which are not members), and when the member's owner is
> whitelisted with pragma name-case-whitelist.
> 
> Bonus: name-case-whitelist now applies to a union's inline base, too.
> Update qapi/qapi-schema.json pragma to whitelist union CpuInfo instead
> of CpuInfo's implicit base type's name q_obj_CpuInfo-base.
> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> ---

> +++ b/qapi/qapi-schema.json
> @@ -71,7 +71,7 @@
>          'QapiErrorClass',           # all members, visible through errors
>          'UuidInfo',                 # UUID, visible through query-uuid
>          'X86CPURegister32',         # all members, visible indirectly through qom-get
> -        'q_obj_CpuInfo-base'        # CPU, visible through query-cpu
> +        'CpuInfo'                   # CPU, visible through query-cpu

Yes, much nicer.

>      ] } }
>  
>  # Documentation generated with qapi-gen.py is in source order, with
> diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
> index f0e7d5ad34..ed4bff4479 100644
> --- a/scripts/qapi/common.py
> +++ b/scripts/qapi/common.py
> @@ -704,8 +704,8 @@ valid_name = re.compile(r'^(__[a-zA-Z0-9.-]+_)?'
>                          '[a-zA-Z][a-zA-Z0-9_-]*$')
>  
>  
> -def check_name(info, source, name, allow_optional=False,
> -               enum_member=False):
> +def check_name(info, source, name,
> +               allow_optional=False, enum_member=False, permit_upper=False):
>      global valid_name
>      membername = name
>  
> @@ -725,11 +725,14 @@ def check_name(info, source, name, allow_optional=False,
>      if not valid_name.match(membername) or \
>         c_name(membername, False).startswith('q_'):
>          raise QAPISemError(info, "%s uses invalid name '%s'" % (source, name))
> +    if not permit_upper and name.lower() != name:
> +        raise QAPISemError(
> +            info, "%s uses uppercase in name '%s'" % (source, name))
>  
>  
>  def add_name(name, info, meta):
>      global all_names
> -    check_name(info, "'%s'" % meta, name)
> +    check_name(info, "'%s'" % meta, name, permit_upper=True)
>      # FIXME should reject names that differ only in '_' vs. '.'
>      # vs. '-', because they're liable to clash in generated C.
>      if name in all_names:
> @@ -797,10 +800,12 @@ def check_type(info, source, value,
>          raise QAPISemError(info,
>                             "%s should be an object or type name" % source)
>  
> +    permit_upper = allow_dict in name_case_whitelist
> +

so allow_dict changes from a bool to a string to be looked up...

>      # value is a dictionary, check that each member is okay
>      for (key, arg) in value.items():
>          check_name(info, "Member of %s" % source, key,
> -                   allow_optional=True)
> +                   allow_optional=True, permit_upper=permit_upper)
>          if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
>              raise QAPISemError(info, "Member of %s uses reserved name '%s'"
>                                 % (source, key))
> @@ -870,7 +875,7 @@ def check_union(expr, info):
>      else:
>          # The object must have a string or dictionary 'base'.
>          check_type(info, "'base' for union '%s'" % name,
> -                   base, allow_dict=True,
> +                   base, allow_dict=name,

...and this is an example client affected by the change.  That threw me
for a bit, but seems to work.

Reviewed-by: Eric Blake <eblake@redhat.com>

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3226
Virtualization:  qemu.org | libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH 06/25] qapi: Change frontend error messages to start with lower case
  2019-09-24 13:28 ` [PATCH 06/25] qapi: Change frontend error messages to start with lower case Markus Armbruster
@ 2019-09-24 15:17   ` Eric Blake
  2019-09-24 20:35     ` Markus Armbruster
  0 siblings, 1 reply; 63+ messages in thread
From: Eric Blake @ 2019-09-24 15:17 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel; +Cc: marcandre.lureau, mdroth


[-- Attachment #1.1: Type: text/plain, Size: 3566 bytes --]

On 9/24/19 8:28 AM, Markus Armbruster wrote:
> Starting error messages with a capital letter complicates things when
> text can get interpolated both at the beginning and in the middle of
> an error message.  The next patch will do that.  Switch to lower case
> to keep it simpler.
> 
> For what it's worth, the GNU Coding Standards advise the message
> "should not begin with a capital letter when it follows a program name
> and/or file name, because that isn’t the beginning of a sentence. (The
> sentence conceptually starts at the beginning of the line.)"

We're inconsistent throughout the code base, but this is one place where
I like the GCS rationale.  Fixing it everywhere may not be worth the
churn, but fixing it within the subset of the qapi generator is worthwhile.

> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> ---
>  scripts/qapi/common.py                        | 175 +++++++++---------
>  tests/qapi-schema/alternate-any.err           |   2 +-

>  tests/qapi-schema/unknown-expr-key.err        |   2 +-
>  125 files changed, 215 insertions(+), 204 deletions(-)
>  create mode 100644 tests/qapi-schema/escape-too-big.err
>  create mode 100644 tests/qapi-schema/string-control.err
>  create mode 100644 tests/qapi-schema/string-unclosed.err
>  create mode 100644 tests/qapi-schema/string-unicode.err

Umm, what's going on here?

You'll want to either drop these files (if they were leftovers in your
working directory from previous points in time), or defer their addition
to when the corresponding actual tests exist.

>      def get_doc(self, info):
>          if self.val != '##':
> -            raise QAPIParseError(self, "Junk after '##' at start of "
> -                                 "documentation comment")
> +            raise QAPIParseError(
> +                self, "junk after '##' at start of documentation comment")

Reformatting like this also makes grepping for a particular message easier.


> @@ -868,8 +869,8 @@ def check_union(expr, info):
>          enum_values = members.keys()
>          allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
>          if base is not None:
> -            raise QAPISemError(info, "Simple union '%s' must not have a base" %
> -                               name)
> +            raise QAPISemError(
> +                info, "simple union '%s' must not have a base" % name)
>  

A bit odd that you reformat here to get the second argument all on one
line...

>      # Else, it's a flat union.
>      else:
> @@ -878,46 +879,47 @@ def check_union(expr, info):
>                     base, allow_dict=name,
>                     allow_metas=['struct'])
>          if not base:
> -            raise QAPISemError(info, "Flat union '%s' must have a base"
> +            raise QAPISemError(info, "flat union '%s' must have a base"
>                                 % name)

...but not here.  The reformatting is not the primary focus of the
patch, and doesn't hurt semantically whether or not you do it, but maybe
it is worth calling out in the commit message the criteria you used for
deciding when to reformat, and/or make the patch strive for more
consistency.  I'll leave that up to you; fixing the spurious new files,
and making your choice of where to place the linebreaks, doesn't affect
my ability to offer:

Reviewed-by: Eric Blake <eblake@redhat.com>

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3226
Virtualization:  qemu.org | libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH 07/25] qapi: Improve reporting of member name clashes
  2019-09-24 13:28 ` [PATCH 07/25] qapi: Improve reporting of member name clashes Markus Armbruster
@ 2019-09-24 15:38   ` Eric Blake
  0 siblings, 0 replies; 63+ messages in thread
From: Eric Blake @ 2019-09-24 15:38 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel; +Cc: marcandre.lureau, mdroth


[-- Attachment #1.1: Type: text/plain, Size: 853 bytes --]

On 9/24/19 8:28 AM, Markus Armbruster wrote:
> We report name clashes like this:
> 
>     struct-base-clash.json: In struct 'Sub':
>     struct-base-clash.json:5: 'name' (member of Sub) collides with 'name' (member of Base)
> 
> The "(member of Sub)" is redundant with "In struct 'Sub'".  Comes from
> QAPISchemaMember.describe().  Pass info to it, so it can detect the
> redundancy and avoid it.  Result:
> 
>     struct-base-clash.json: In struct 'Sub':
>     struct-base-clash.json:5: member 'name' collides with member 'name' of type 'Base'
> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> ---

The resulting error messages are indeed nicer.

Reviewed-by: Eric Blake <eblake@redhat.com>

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3226
Virtualization:  qemu.org | libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH 08/25] qapi: Reorder check_FOO() parameters for consistency
  2019-09-24 13:28 ` [PATCH 08/25] qapi: Reorder check_FOO() parameters for consistency Markus Armbruster
@ 2019-09-24 15:41   ` Eric Blake
  0 siblings, 0 replies; 63+ messages in thread
From: Eric Blake @ 2019-09-24 15:41 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel; +Cc: marcandre.lureau, mdroth


[-- Attachment #1.1: Type: text/plain, Size: 702 bytes --]

On 9/24/19 8:28 AM, Markus Armbruster wrote:
> Most check_FOO() take the thing being checked as first argument.
> check_name(), check_type(), check_known_keys() don't.  Clean that up.
> 
> While there, drop a "Todo" comment that should have been dropped in
> commit 87adbbffd4 "qapi: add a dictionary form for TYPE".
> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> ---
>  scripts/qapi/common.py | 80 ++++++++++++++++++++----------------------
>  1 file changed, 39 insertions(+), 41 deletions(-)
> 

Reviewed-by: Eric Blake <eblake@redhat.com>

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3226
Virtualization:  qemu.org | libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH 09/25] qapi: Improve reporting of invalid name errors
  2019-09-24 13:28 ` [PATCH 09/25] qapi: Improve reporting of invalid name errors Markus Armbruster
@ 2019-09-24 15:48   ` Eric Blake
  0 siblings, 0 replies; 63+ messages in thread
From: Eric Blake @ 2019-09-24 15:48 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel; +Cc: marcandre.lureau, mdroth


[-- Attachment #1.1: Type: text/plain, Size: 577 bytes --]

On 9/24/19 8:28 AM, Markus Armbruster wrote:
> Split check_name() into check_name_is_str() and check_name_str(), keep
> check_name() as a wrapper.
> 
> Move add_name()'s call into its caller check_exprs(), and inline.
> 
> This permits delaying check_name_str() there, so its error message
> gains an "in definition" line.
> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> ---

Reviewed-by: Eric Blake <eblake@redhat.com>

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3226
Virtualization:  qemu.org | libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH 10/25] qapi: Use check_name_str() where it suffices
  2019-09-24 13:28 ` [PATCH 10/25] qapi: Use check_name_str() where it suffices Markus Armbruster
@ 2019-09-24 15:50   ` Eric Blake
  0 siblings, 0 replies; 63+ messages in thread
From: Eric Blake @ 2019-09-24 15:50 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel; +Cc: marcandre.lureau, mdroth


[-- Attachment #1.1: Type: text/plain, Size: 476 bytes --]

On 9/24/19 8:28 AM, Markus Armbruster wrote:
> Replace check_name() by check_name_str() where the name is known to be
> a string.
> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> ---
>  scripts/qapi/common.py | 9 ++++-----
>  1 file changed, 4 insertions(+), 5 deletions(-)
> 

Reviewed-by: Eric Blake <eblake@redhat.com>

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3226
Virtualization:  qemu.org | libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH 11/25] qapi: Report invalid '*' prefix like any other invalid name
  2019-09-24 13:28 ` [PATCH 11/25] qapi: Report invalid '*' prefix like any other invalid name Markus Armbruster
@ 2019-09-24 15:52   ` Eric Blake
  0 siblings, 0 replies; 63+ messages in thread
From: Eric Blake @ 2019-09-24 15:52 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel; +Cc: marcandre.lureau, mdroth


[-- Attachment #1.1: Type: text/plain, Size: 397 bytes --]

On 9/24/19 8:28 AM, Markus Armbruster wrote:
> The special "does not allow optional name" error is well meant, but
> confusing in practice.  Drop it.
> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> ---
Reviewed-by: Eric Blake <eblake@redhat.com>

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3226
Virtualization:  qemu.org | libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH 12/25] qapi: Move check for reserved names out of add_name()
  2019-09-24 13:28 ` [PATCH 12/25] qapi: Move check for reserved names out of add_name() Markus Armbruster
@ 2019-09-24 15:56   ` Eric Blake
  0 siblings, 0 replies; 63+ messages in thread
From: Eric Blake @ 2019-09-24 15:56 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel; +Cc: marcandre.lureau, mdroth


[-- Attachment #1.1: Type: text/plain, Size: 698 bytes --]

On 9/24/19 8:28 AM, Markus Armbruster wrote:
> The checks for reserved names are spread far and wide.  Move one from
> add_name() to new check_defn_name_str().  This is a first step towards
> collecting them all in dedicated name checking functions next to
> check_name().
> 
> While there, drop the quotes around the meta-type in
> check_name_str()'s error messages: "'command' uses ... name 'NAME'"
> becomes "command uses ... name 'NAME'".
> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> ---

Reviewed-by: Eric Blake <eblake@redhat.com>

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3226
Virtualization:  qemu.org | libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH 13/25] qapi: Make check_type()'s array case a bit more obvious
  2019-09-24 13:28 ` [PATCH 13/25] qapi: Make check_type()'s array case a bit more obvious Markus Armbruster
@ 2019-09-24 15:57   ` Eric Blake
  0 siblings, 0 replies; 63+ messages in thread
From: Eric Blake @ 2019-09-24 15:57 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel; +Cc: marcandre.lureau, mdroth


[-- Attachment #1.1: Type: text/plain, Size: 1344 bytes --]

On 9/24/19 8:28 AM, Markus Armbruster wrote:
> check_type() checks the array's contents, then peels off the array and
> falls through to the "not array" code without resetting allow_array
> and allow_dict to False.  Works because the peeled value is a string,
> and allow_array and allow_dict aren't used then.  Tidy up anyway:
> recurse instead, defaulting allow_array and allow_dict to False.
> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> ---
>  scripts/qapi/common.py | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)

Reviewed-by: Eric Blake <eblake@redhat.com>

> 
> diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
> index 5e708c3b45..07cf72e72c 100644
> --- a/scripts/qapi/common.py
> +++ b/scripts/qapi/common.py
> @@ -794,7 +794,8 @@ def check_type(value, info, source,
>              raise QAPISemError(info,
>                                 "%s: array type must contain single type name" %
>                                 source)
> -        value = value[0]
> +        check_type(value[0], info, source, allow_metas=allow_metas)
> +        return
>  
>      # Check if type name for value is okay
>      if isinstance(value, str):
> 

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3226
Virtualization:  qemu.org | libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH 14/25] qapi: Plumb info to the QAPISchemaMember
  2019-09-24 13:28 ` [PATCH 14/25] qapi: Plumb info to the QAPISchemaMember Markus Armbruster
@ 2019-09-24 16:01   ` Eric Blake
  0 siblings, 0 replies; 63+ messages in thread
From: Eric Blake @ 2019-09-24 16:01 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel; +Cc: marcandre.lureau, mdroth


[-- Attachment #1.1: Type: text/plain, Size: 577 bytes --]

On 9/24/19 8:28 AM, Markus Armbruster wrote:
> Future commits will need info in the .check() methods of
> QAPISchemaMember and its descendants.  Get it there.
> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> ---
>  scripts/qapi/common.py | 76 +++++++++++++++++++++++-------------------
>  scripts/qapi/events.py |  2 +-
>  2 files changed, 43 insertions(+), 35 deletions(-)
> 

Reviewed-by: Eric Blake <eblake@redhat.com>

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3226
Virtualization:  qemu.org | libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH 15/25] qapi: Inline check_name() into check_union()
  2019-09-24 13:28 ` [PATCH 15/25] qapi: Inline check_name() into check_union() Markus Armbruster
@ 2019-09-24 16:39   ` Eric Blake
  0 siblings, 0 replies; 63+ messages in thread
From: Eric Blake @ 2019-09-24 16:39 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel; +Cc: marcandre.lureau, mdroth


[-- Attachment #1.1: Type: text/plain, Size: 684 bytes --]

On 9/24/19 8:28 AM, Markus Armbruster wrote:
> check_name() consists of check_name_is_str() and check_name_str().
> check_union() relies on the latter to catch optional discriminators.
> The next commit will replace that by a more straightforward check.
> Inlining check_name() into check_union() now should make that easier
> to review.
> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> ---
>  scripts/qapi/common.py | 6 ++++--
>  1 file changed, 4 insertions(+), 2 deletions(-)
> 

Reviewed-by: Eric Blake <eblake@redhat.com>

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3226
Virtualization:  qemu.org | libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH 16/25] qapi: Move context-sensitive checking to the proper place
  2019-09-24 13:28 ` [PATCH 16/25] qapi: Move context-sensitive checking to the proper place Markus Armbruster
@ 2019-09-24 17:49   ` Eric Blake
  2019-09-24 20:41     ` Markus Armbruster
  0 siblings, 1 reply; 63+ messages in thread
From: Eric Blake @ 2019-09-24 17:49 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel; +Cc: marcandre.lureau, mdroth


[-- Attachment #1.1: Type: text/plain, Size: 5025 bytes --]

On 9/24/19 8:28 AM, Markus Armbruster wrote:
> When we introduced the QAPISchema intermediate representation (commit
> ac88219a6c7), we took a shortcut: we left check_exprs() & friends
> alone instead of moving semantic checks into the
> QAPISchemaFOO.check().  The .check() assert check_exprs() did its job.
> 
> Time to finish the conversion job.  Move exactly the context-sensitive
> checks to the .check().  They replace assertions there.  Context-free
> checks stay put.
> 
> Fixes the misleading optional tag error demonstrated by test
> flat-union-optional-discriminator.
> 
> A few other error message improve.
> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> ---

> diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
> index f5599559ac..ac4c898e51 100644
> --- a/scripts/qapi/common.py

Thankfully, our large coverage of tests goes a long way to show that the
conversion is correct.  I didn't notice anything obvious that might have
been overlooked (we may still find things down the road, but I'm not
going to hold up this patch trying to find those things).  Meanwhile,
the conversion from assert to conditionals inside .check() looks complete.


> +++ b/tests/qapi-schema/args-union.err
> @@ -1,2 +1,2 @@
>  tests/qapi-schema/args-union.json: In command 'oops':
> -tests/qapi-schema/args-union.json:3: 'data' for command 'oops' cannot use union type 'Uni'
> +tests/qapi-schema/args-union.json:3: command's 'data' can take union type 'Uni' only with 'boxed': true

This one is definitely nicer.

> +++ b/tests/qapi-schema/flat-union-discriminator-bad-name.err
> @@ -1,2 +1,2 @@
>  tests/qapi-schema/flat-union-discriminator-bad-name.json: In union 'MyUnion':
> -tests/qapi-schema/flat-union-discriminator-bad-name.json:7: discriminator of flat union 'MyUnion' uses invalid name '*switch'
> +tests/qapi-schema/flat-union-discriminator-bad-name.json:6: discriminator '*switch' is not a member of 'base'
> diff --git a/tests/qapi-schema/flat-union-discriminator-bad-name.json b/tests/qapi-schema/flat-union-discriminator-bad-name.json
> index ea84b75cac..3ae8c06a89 100644
> --- a/tests/qapi-schema/flat-union-discriminator-bad-name.json
> +++ b/tests/qapi-schema/flat-union-discriminator-bad-name.json
> @@ -1,5 +1,4 @@
>  # discriminator '*switch' isn't a member of base, 'switch' is
> -# reports "uses invalid name", which is good enough
>  { 'enum': 'Enum', 'data': [ 'one', 'two' ] }
>  { 'struct': 'Base',
>    'data': { '*switch': 'Enum' } }

I find this one to be borderline in quality (if we have '*switch' in the
base, claiming that '*switch' is not a member of base is confusing until
you realize that base actually has an optional member named 'switch') -
but anyone that actually stumbles into this one will probably quickly
figure out their problem, and we may be revisiting it later anyways when
we finally include patches for a default discriminator.

> +++ b/tests/qapi-schema/flat-union-optional-discriminator.err
> @@ -1,2 +1,2 @@
>  tests/qapi-schema/flat-union-optional-discriminator.json: In union 'MyUnion':
> -tests/qapi-schema/flat-union-optional-discriminator.json:7: discriminator 'switch' is not a member of 'base'
> +tests/qapi-schema/flat-union-optional-discriminator.json:6: discriminator member 'switch' of base type 'Base' must not be optional
> diff --git a/tests/qapi-schema/flat-union-optional-discriminator.json b/tests/qapi-schema/flat-union-optional-discriminator.json
> index 143ab23a0d..2e7f766f60 100644
> --- a/tests/qapi-schema/flat-union-optional-discriminator.json
> +++ b/tests/qapi-schema/flat-union-optional-discriminator.json
> @@ -1,5 +1,4 @@
>  # we require the discriminator to be non-optional
> -# FIXME reports "discriminator 'switch' is not a member of base struct 'Base'"
>  { 'enum': 'Enum', 'data': [ 'one', 'two' ] }
>  { 'struct': 'Base',
>    'data': { '*switch': 'Enum' } }

And while the other one is borderline, I agree that this one is better.

> +++ b/tests/qapi-schema/union-unknown.err
> @@ -1,2 +1,2 @@
>  tests/qapi-schema/union-unknown.json: In union 'Union':
> -tests/qapi-schema/union-unknown.json:2: member 'unknown' of union 'Union' uses unknown type 'MissingType'
> +tests/qapi-schema/union-unknown.json:2: union uses unknown type 'MissingType'
> diff --git a/tests/qapi-schema/union-unknown.json b/tests/qapi-schema/union-unknown.json
> index aa7e8143d8..64d3666176 100644
> --- a/tests/qapi-schema/union-unknown.json
> +++ b/tests/qapi-schema/union-unknown.json
> @@ -1,3 +1,3 @@
>  # we reject a union with unknown type in branch
>  { 'union': 'Union',
> -  'data': { 'unknown': 'MissingType' } }
> +  'data': { 'unknown': ['MissingType'] } }
> 

And here you covered one more code path by going through an array type.

Overall looks good.

Reviewed-by: Eric Blake <eblake@redhat.com>

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3226
Virtualization:  qemu.org | libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH 17/25] qapi: Move context-free checking to the proper place
  2019-09-24 13:28 ` [PATCH 17/25] qapi: Move context-free " Markus Armbruster
@ 2019-09-24 17:59   ` Eric Blake
  0 siblings, 0 replies; 63+ messages in thread
From: Eric Blake @ 2019-09-24 17:59 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel; +Cc: marcandre.lureau, mdroth


[-- Attachment #1.1: Type: text/plain, Size: 795 bytes --]

On 9/24/19 8:28 AM, Markus Armbruster wrote:
> QAPISchemaCommand.check() and QAPISchemaEvent().check() check 'data'
> is present when 'boxed': true.  That's context-free.  Move to
> check_command() and check_event().
> 
> Tweak the error message while there.
> 
> check_exprs() & friends now check exactly what qapi-code-gen.txt calls
> the second layer of syntax.
> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> ---
>  scripts/qapi/common.py                  | 16 ++++++++--------
>  tests/qapi-schema/event-boxed-empty.err |  2 +-
>  2 files changed, 9 insertions(+), 9 deletions(-)
> 

Reviewed-by: Eric Blake <eblake@redhat.com>

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3226
Virtualization:  qemu.org | libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH 18/25] qapi: Improve reporting of invalid 'if' errors
  2019-09-24 13:28 ` [PATCH 18/25] qapi: Improve reporting of invalid 'if' errors Markus Armbruster
@ 2019-09-24 18:01   ` Eric Blake
  0 siblings, 0 replies; 63+ messages in thread
From: Eric Blake @ 2019-09-24 18:01 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel; +Cc: marcandre.lureau, mdroth


[-- Attachment #1.1: Type: text/plain, Size: 960 bytes --]

On 9/24/19 8:28 AM, Markus Armbruster wrote:
> Move check_if() from check_keys() to check_exprs() and call it later,
> so its error messages gain an "in definition" line.
> 
> Checking values in a function named check_keys() is unclean anyway.
> The original sin was commit 0545f6b887 "qapi: Better error messages
> for bad expressions", which checks the value of key 'name'.  More
> sinning in commit 2cbf09925a "qapi: More rigorous checking for type
> safety bypass", commit c818408e44 "qapi: Implement boxed types for
> commands/events", and commit 967c885108 "qapi: add 'if' to top-level
> expressions".  This commit does penance for the latter.  The next
> commits will do penance for the others.
> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> ---

Reviewed-by: Eric Blake <eblake@redhat.com>

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3226
Virtualization:  qemu.org | libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH 19/25] qapi: Improve reporting of invalid flags
  2019-09-24 13:28 ` [PATCH 19/25] qapi: Improve reporting of invalid flags Markus Armbruster
@ 2019-09-24 18:07   ` Eric Blake
  2019-09-24 20:43     ` Markus Armbruster
  0 siblings, 1 reply; 63+ messages in thread
From: Eric Blake @ 2019-09-24 18:07 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel; +Cc: marcandre.lureau, mdroth


[-- Attachment #1.1: Type: text/plain, Size: 1389 bytes --]

On 9/24/19 8:28 AM, Markus Armbruster wrote:
> Split check_flags() off check_keys() and have check_exprs() call it
> later, so its error messages gain an "in definition" line.  Tweak the
> error messages.
> 
> Checking values in a function named check_keys() is unclean anyway.
> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> ---
>  scripts/qapi/common.py                     | 22 ++++++++++++----------
>  tests/qapi-schema/allow-preconfig-test.err |  3 ++-
>  tests/qapi-schema/args-bad-boxed.err       |  3 ++-
>  tests/qapi-schema/oob-test.err             |  3 ++-
>  tests/qapi-schema/type-bypass-bad-gen.err  |  3 ++-
>  5 files changed, 20 insertions(+), 14 deletions(-)
> 

> +
> +def check_flags(expr, info):
> +    for key in ['gen', 'success-response']:
> +        if key in expr and expr[key] is not False:

Is it any more pythonic and/or a micro-optimization to compress this to:

if expr.get(key, False) is not False:

> +            raise QAPISemError(
> +                info, "flag '%s' may only use false value" % key)
> +    for key in ['boxed', 'allow-oob', 'allow-preconfig']:
> +        if key in expr and expr[key] is not True:

and here too.

Reviewed-by: Eric Blake <eblake@redhat.com>

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3226
Virtualization:  qemu.org | libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH 20/25] qapi: Improve reporting of missing / unknown definition keys
  2019-09-24 13:28 ` [PATCH 20/25] qapi: Improve reporting of missing / unknown definition keys Markus Armbruster
@ 2019-09-24 18:13   ` Eric Blake
  2019-09-24 20:46     ` Markus Armbruster
  0 siblings, 1 reply; 63+ messages in thread
From: Eric Blake @ 2019-09-24 18:13 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel; +Cc: marcandre.lureau, mdroth


[-- Attachment #1.1: Type: text/plain, Size: 1163 bytes --]

On 9/24/19 8:28 AM, Markus Armbruster wrote:
> Have check_exprs() call check_keys() later, so its error messages gain
> an "in definition" line.
> 
> Both check_keys() and check_name_is_str() check the definition's name
> is a string.  Since check_keys() now runs after check_name_is_str()
> rather than before, its check is dead.  Bury it.  Checking values in
> check_keys() is unclean anyway.
> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> ---

> +++ b/scripts/qapi/common.py
> @@ -905,8 +905,6 @@ def check_known_keys(value, info, source, required, optional):
>  
>  def check_keys(expr, info, meta, required, optional=[]):
>      name = expr[meta]
> -    if not isinstance(name, str):
> -        raise QAPISemError(info, "'%s' key must have a string value" % meta)

Should this be replaced with an assert?  But I'm also okay just dropping
it, since our testsuite shows that we still flag the problems that this
message was originally used for.

Reviewed-by: Eric Blake <eblake@redhat.com>

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3226
Virtualization:  qemu.org | libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH 21/25] qapi: Avoid redundant definition references in error messages
  2019-09-24 13:28 ` [PATCH 21/25] qapi: Avoid redundant definition references in error messages Markus Armbruster
@ 2019-09-24 18:46   ` Eric Blake
  2019-09-24 20:59     ` Markus Armbruster
  0 siblings, 1 reply; 63+ messages in thread
From: Eric Blake @ 2019-09-24 18:46 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel; +Cc: marcandre.lureau, mdroth


[-- Attachment #1.1: Type: text/plain, Size: 3641 bytes --]

On 9/24/19 8:28 AM, Markus Armbruster wrote:
> Many error messages refer to the offending definition even though
> they're preceded by an "in definition" line.  Rephrase them.

This is the cleanup promised earlier in the series.

> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> ---
>  scripts/qapi/common.py                        | 113 +++++++-----------
>  tests/qapi-schema/alternate-array.err         |   2 +-

>  def check_command(expr, info):
> -    name = expr['command']
>      args = expr.get('data')
>      boxed = expr.get('boxed', False)
>  
>      if boxed and args is None:
>          raise QAPISemError(info, "'boxed': true requires 'data'")
> -    check_type(args, info, "'data' for command '%s'" % name,
> -               allow_dict=not boxed)
> -    check_type(expr.get('returns'), info,
> -               "'returns' for command '%s'" % name,
> -               allow_array=True)
> +    check_type(expr.get('data'), info, "'data'", allow_dict=not boxed)
> +    check_type(expr.get('returns'), info, "'returns'", allow_array=True)

Why are you repeating expr.get('dat') here instead of reusing args?  I
guess it adds consistency with the expr.get('returns') in the next line.

>  
>  
>  def check_event(expr, info):
> -    name = expr['event']
>      args = expr.get('data')
>      boxed = expr.get('boxed', False)
>  
>      if boxed and args is None:
>          raise QAPISemError(info, "'boxed': true requires 'data'")
> -    check_type(args, info, "'data' for event '%s'" % name,
> -               allow_dict=not boxed)
> +    check_type(expr.get('data'), info, "'data'", allow_dict=not boxed)
Again, why not reuse args?


> +++ b/tests/qapi-schema/args-member-case.err
> @@ -1,2 +1,2 @@
>  tests/qapi-schema/args-member-case.json: In command 'no-way-this-will-get-whitelisted':
> -tests/qapi-schema/args-member-case.json:2: member of 'data' for command 'no-way-this-will-get-whitelisted' uses uppercase in name 'Arg'
> +tests/qapi-schema/args-member-case.json:2: 'data' member 'Arg' uses uppercase in name 'Arg'

Better, but still feels redundant for calling out 'Arg' twice.  Maybe
you further clean this one later?

> +++ b/tests/qapi-schema/enum-member-case.err
> @@ -1,2 +1,2 @@
>  tests/qapi-schema/enum-member-case.json: In enum 'NoWayThisWillGetWhitelisted':
> -tests/qapi-schema/enum-member-case.json:4: member of enum 'NoWayThisWillGetWhitelisted' uses uppercase in name 'Value'
> +tests/qapi-schema/enum-member-case.json:4: 'data' member uses uppercase in name 'Value'

Here's a similar error about uppercase that does not have the
redundancy, for comparison.


> +++ b/tests/qapi-schema/union-branch-case.err
> @@ -1,2 +1,2 @@
>  tests/qapi-schema/union-branch-case.json: In union 'Uni':
> -tests/qapi-schema/union-branch-case.json:2: member of union 'Uni' uses uppercase in name 'Branch'
> +tests/qapi-schema/union-branch-case.json:2: 'data' member 'Branch' uses uppercase in name 'Branch'

Another related one.

> +++ b/tests/qapi-schema/union-optional-branch.err
> @@ -1,2 +1,2 @@
>  tests/qapi-schema/union-optional-branch.json: In union 'Union':
> -tests/qapi-schema/union-optional-branch.json:2: member of union 'Union' uses invalid name '*a'
> +tests/qapi-schema/union-optional-branch.json:2: 'data' member '*a' uses invalid name '*a'

Similar type of redundancy, but this time not related to uppercase.

Overall an improvement.

Reviewed-by: Eric Blake <eblake@redhat.com>

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3226
Virtualization:  qemu.org | libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH 22/25] qapi: Eliminate check_keys(), rename check_known_keys()
  2019-09-24 13:28 ` [PATCH 22/25] qapi: Eliminate check_keys(), rename check_known_keys() Markus Armbruster
@ 2019-09-24 18:49   ` Eric Blake
  0 siblings, 0 replies; 63+ messages in thread
From: Eric Blake @ 2019-09-24 18:49 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel; +Cc: marcandre.lureau, mdroth


[-- Attachment #1.1: Type: text/plain, Size: 575 bytes --]

On 9/24/19 8:28 AM, Markus Armbruster wrote:
> check_keys() has become a trivial wrapper for check_known_keys().
> Eliminate it.
> 
> This makes its name available.  Rename check_known_keys().
> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> ---
>  scripts/qapi/common.py | 40 +++++++++++++++++++++-------------------
>  1 file changed, 21 insertions(+), 19 deletions(-)
> 

Reviewed-by: Eric Blake <eblake@redhat.com>

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3226
Virtualization:  qemu.org | libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH 03/25] qapi: New QAPISourceInfo, replacing dict
  2019-09-24 13:28 ` [PATCH 03/25] qapi: New QAPISourceInfo, replacing dict Markus Armbruster
  2019-09-24 14:51   ` Eric Blake
@ 2019-09-24 19:12   ` Eric Blake
  2019-09-25  6:40     ` Markus Armbruster
  1 sibling, 1 reply; 63+ messages in thread
From: Eric Blake @ 2019-09-24 19:12 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel; +Cc: marcandre.lureau, mdroth


[-- Attachment #1.1: Type: text/plain, Size: 1519 bytes --]

On 9/24/19 8:28 AM, Markus Armbruster wrote:
> We track source locations with a dict of the form
> 
>     {'file': FNAME, 'line': LINENO, parent': PARENT}
> 
> where PARENT is None for the main file, and the include directive's
> source location for included files.
> 
> This is servicable enough, but the next commit will add information,
> and that's going to come out cleaner if we turn this into a class.  So
> do that.
> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> ---

>  class QAPIError(Exception):
> -    def __init__(self, fname, line, col, incl_info, msg):
> +    def __init__(self, info, col, msg):
>          Exception.__init__(self)

Unrelated to this patch, but I just noticed
https://docs.quantifiedcode.com/python-anti-patterns/ today (in part
based on my question on another patch about using 'list.get(key, False)'
rather than 'key in list and list[key]').  In particular, I found
https://docs.quantifiedcode.com/python-anti-patterns/correctness/missing_argument_to_super.html
which recommends using:

def __init__(...):
    super(QAPIError, self).__init__()

(because of Python 2), while other sits state that with python 3, you
can further get away with:

def __init__(...):
    super().__init(...)

Should we be switching our code base to use super() in more places,
rather than hard-coding the parent class name?

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3226
Virtualization:  qemu.org | libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH 23/25] qapi: Improve reporting of missing documentation comment
  2019-09-24 13:28 ` [PATCH 23/25] qapi: Improve reporting of missing documentation comment Markus Armbruster
@ 2019-09-24 19:44   ` Eric Blake
  0 siblings, 0 replies; 63+ messages in thread
From: Eric Blake @ 2019-09-24 19:44 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel; +Cc: marcandre.lureau, mdroth


[-- Attachment #1.1: Type: text/plain, Size: 581 bytes --]

On 9/24/19 8:28 AM, Markus Armbruster wrote:
> Have check_exprs() check this later, so the error message gains an "in
> definition line".  Tweak the error message.
> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> ---
>  scripts/qapi/common.py            | 18 ++++++++----------
>  tests/qapi-schema/doc-missing.err |  3 ++-
>  2 files changed, 10 insertions(+), 11 deletions(-)
> 

Reviewed-by: Eric Blake <eblake@redhat.com>

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3226
Virtualization:  qemu.org | libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH 24/25] qapi: Improve reporting of redefinition
  2019-09-24 13:28 ` [PATCH 24/25] qapi: Improve reporting of redefinition Markus Armbruster
@ 2019-09-24 19:53   ` Eric Blake
  0 siblings, 0 replies; 63+ messages in thread
From: Eric Blake @ 2019-09-24 19:53 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel; +Cc: marcandre.lureau, mdroth


[-- Attachment #1.1: Type: text/plain, Size: 771 bytes --]

On 9/24/19 8:28 AM, Markus Armbruster wrote:
> Point to the previous definition, unless it's a built-in.
> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> ---

>          if other_ent:
> +            if other_ent.info:
> +                where = QAPIError(other_ent.info, None, "previous definition")
> +                raise QAPISemError(
> +                    ent.info,
> +                    "'%s' is already defined\n%s" % (ent.name, where))
>              raise QAPISemError(
>                  ent.info, "%s is already defined" % other_ent.describe())

Nice.

Reviewed-by: Eric Blake <eblake@redhat.com>

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3226
Virtualization:  qemu.org | libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH 25/25] qapi: Improve source file read error handling
  2019-09-24 13:28 ` [PATCH 25/25] qapi: Improve source file read error handling Markus Armbruster
@ 2019-09-24 19:57   ` Eric Blake
  2019-09-24 20:59     ` Markus Armbruster
  0 siblings, 1 reply; 63+ messages in thread
From: Eric Blake @ 2019-09-24 19:57 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel; +Cc: marcandre.lureau, mdroth


[-- Attachment #1.1: Type: text/plain, Size: 1042 bytes --]

On 9/24/19 8:28 AM, Markus Armbruster wrote:
> qap-gen.py crashes when it can't open the main schema file, and when

qapi-gen.py

> it can't read from any schema file.  Lazy.
> 
> Change QAPISchema.__init__() to take a file name instead of a file
> object.  Move the open code from _include() to __init__(), so it's
> used for the main schema file, too.
> 
> Move the read into the try for good measure, and rephrase the error
> message.
> 
> Reporting open or read failure for the main schema file needs a
> QAPISourceInfo representing "no source".  Make QAPISourceInfo cope
> with fname=None.
> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> ---
>  scripts/qapi/common.py                | 46 +++++++++++++++------------
>  tests/qapi-schema/include-no-file.err |  2 +-
>  2 files changed, 27 insertions(+), 21 deletions(-)
> 

Reviewed-by: Eric Blake <eblake@redhat.com>

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3226
Virtualization:  qemu.org | libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH 01/25] qapi: Tighten QAPISchemaFOO.check() assertions
  2019-09-24 14:39   ` Eric Blake
@ 2019-09-24 20:18     ` Markus Armbruster
  0 siblings, 0 replies; 63+ messages in thread
From: Markus Armbruster @ 2019-09-24 20:18 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, mdroth

Eric Blake <eblake@redhat.com> writes:

> On 9/24/19 8:28 AM, Markus Armbruster wrote:
>> When we introduced the QAPISchema intermediate representation (commit
>> ac88219a6c7), we took a shortcut: we left check_exprs() & friends
>> alone instead of moving semantic checks into the
>> QAPISchemaFOO.check().  check_exprs() still checks and reports errors,
>> and the .check() assert check_exprs() did the job.  There are a few
>> gaps, though.
>> 
>> QAPISchemaArrayType.check() neglects to assert the element type is not
>> an array.  Add the assertion.
>> 
>> QAPISchemaObjectTypeVariants.check() neglects to assert the tag member
>> is not optional.  Add the assertion.
>> 
>> It neglects to assert the tag member is not conditional.  Add the
>> assertion.
>> 
>> It neglects to assert we actually have variants.  Add the assertion.
>> 
>> It asserts the variants are object types, but neglects to assert they
>> don't have variants.  Tighten the assertion.
>> 
>> QAPISchemaObjectTypeVariants.check_clash() has the same issue.
>> However, it can run only after .check().  Delete the assertion instead
>> of tightening it.
>> 
>> QAPISchemaAlternateType.check() neglects to assert the branch types
>> don't conflict.  Fixing that isn't trivial, so add just a TODO comment
>> for now.  It'll be resolved later in this series.
>
> I'm guessing you found these by deleting check_exprs() and seeing what
> failed due to inadequate .check()

The first two or three I found by staring at the code.  The remainder I
found when I moved checks from check_exprs() to .check() [PATCH 16]: any
check that doesn't replace an assertion means the assertion was missing.

>> 
>> Signed-off-by: Markus Armbruster <armbru@redhat.com>
>> ---
>>  scripts/qapi/common.py | 9 +++++++--
>>  1 file changed, 7 insertions(+), 2 deletions(-)
>
> Reviewed-by: Eric Blake <eblake@redhat.com>

Thanks!


^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH 03/25] qapi: New QAPISourceInfo, replacing dict
  2019-09-24 14:51   ` Eric Blake
@ 2019-09-24 20:18     ` Markus Armbruster
  0 siblings, 0 replies; 63+ messages in thread
From: Markus Armbruster @ 2019-09-24 20:18 UTC (permalink / raw)
  To: Eric Blake; +Cc: mdroth, marcandre.lureau, Markus Armbruster, qemu-devel

Eric Blake <eblake@redhat.com> writes:

> On 9/24/19 8:28 AM, Markus Armbruster wrote:
>> We track source locations with a dict of the form
>> 
>>     {'file': FNAME, 'line': LINENO, parent': PARENT}
>
> Missing ' on parent
>
>> 
>> where PARENT is None for the main file, and the include directive's
>> source location for included files.
>> 
>> This is servicable enough, but the next commit will add information,
>
> serviceable

Will fix both.

>> and that's going to come out cleaner if we turn this into a class.  So
>> do that.
>> 
>> Signed-off-by: Markus Armbruster <armbru@redhat.com>
>> ---
>>  scripts/qapi/common.py | 69 +++++++++++++++++++++++++-----------------
>>  1 file changed, 41 insertions(+), 28 deletions(-)
>> 
> Reviewed-by: Eric Blake <eblake@redhat.com>

Thanks!


^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH 05/25] qapi: Clean up member name case checking
  2019-09-24 15:07   ` Eric Blake
@ 2019-09-24 20:20     ` Markus Armbruster
  0 siblings, 0 replies; 63+ messages in thread
From: Markus Armbruster @ 2019-09-24 20:20 UTC (permalink / raw)
  To: Eric Blake; +Cc: mdroth, marcandre.lureau, Markus Armbruster, qemu-devel

Eric Blake <eblake@redhat.com> writes:

> On 9/24/19 8:28 AM, Markus Armbruster wrote:
>> QAPISchemaMember.check_clash() checks for member names that map to the
>> same c_name().  Takes care of rejecting duplicate names.
>> 
>> It also checks a naming rule: no uppercase in member names.  That's a
>> rather odd place to do it.  Enforcing naming rules is
>> check_name_str()'s job.
>> 
>> qapi-code-gen.txt specifies the name case rule applies to the name as
>> it appears in the schema.  check_clash() checks c_name(name) instead.
>> No difference, as c_name() leaves alone case, but unclean.
>> 
>> Move the name case check into check_name_str(), less the c_name().
>> New argument @permit_upper suppresses it.  Pass permit_upper=True for
>> definitions (which are not members), and when the member's owner is
>> whitelisted with pragma name-case-whitelist.
>> 
>> Bonus: name-case-whitelist now applies to a union's inline base, too.
>> Update qapi/qapi-schema.json pragma to whitelist union CpuInfo instead
>> of CpuInfo's implicit base type's name q_obj_CpuInfo-base.
>> 
>> Signed-off-by: Markus Armbruster <armbru@redhat.com>
>> ---
>
>> +++ b/qapi/qapi-schema.json
>> @@ -71,7 +71,7 @@
>>          'QapiErrorClass',           # all members, visible through errors
>>          'UuidInfo',                 # UUID, visible through query-uuid
>>          'X86CPURegister32',         # all members, visible indirectly through qom-get
>> -        'q_obj_CpuInfo-base'        # CPU, visible through query-cpu
>> +        'CpuInfo'                   # CPU, visible through query-cpu
>
> Yes, much nicer.
>
>>      ] } }
>>  
>>  # Documentation generated with qapi-gen.py is in source order, with
>> diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
>> index f0e7d5ad34..ed4bff4479 100644
>> --- a/scripts/qapi/common.py
>> +++ b/scripts/qapi/common.py
>> @@ -704,8 +704,8 @@ valid_name = re.compile(r'^(__[a-zA-Z0-9.-]+_)?'
>>                          '[a-zA-Z][a-zA-Z0-9_-]*$')
>>  
>>  
>> -def check_name(info, source, name, allow_optional=False,
>> -               enum_member=False):
>> +def check_name(info, source, name,
>> +               allow_optional=False, enum_member=False, permit_upper=False):
>>      global valid_name
>>      membername = name
>>  
>> @@ -725,11 +725,14 @@ def check_name(info, source, name, allow_optional=False,
>>      if not valid_name.match(membername) or \
>>         c_name(membername, False).startswith('q_'):
>>          raise QAPISemError(info, "%s uses invalid name '%s'" % (source, name))
>> +    if not permit_upper and name.lower() != name:
>> +        raise QAPISemError(
>> +            info, "%s uses uppercase in name '%s'" % (source, name))
>>  
>>  
>>  def add_name(name, info, meta):
>>      global all_names
>> -    check_name(info, "'%s'" % meta, name)
>> +    check_name(info, "'%s'" % meta, name, permit_upper=True)
>>      # FIXME should reject names that differ only in '_' vs. '.'
>>      # vs. '-', because they're liable to clash in generated C.
>>      if name in all_names:
>> @@ -797,10 +800,12 @@ def check_type(info, source, value,
>>          raise QAPISemError(info,
>>                             "%s should be an object or type name" % source)
>>  
>> +    permit_upper = allow_dict in name_case_whitelist
>> +
>
> so allow_dict changes from a bool to a string to be looked up...
>
>>      # value is a dictionary, check that each member is okay
>>      for (key, arg) in value.items():
>>          check_name(info, "Member of %s" % source, key,
>> -                   allow_optional=True)
>> +                   allow_optional=True, permit_upper=permit_upper)
>>          if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
>>              raise QAPISemError(info, "Member of %s uses reserved name '%s'"
>>                                 % (source, key))
>> @@ -870,7 +875,7 @@ def check_union(expr, info):
>>      else:
>>          # The object must have a string or dictionary 'base'.
>>          check_type(info, "'base' for union '%s'" % name,
>> -                   base, allow_dict=True,
>> +                   base, allow_dict=name,
>
> ...and this is an example client affected by the change.  That threw me
> for a bit, but seems to work.

I'm not proud of this interface, but I couldn't think of anything
better.

> Reviewed-by: Eric Blake <eblake@redhat.com>

Thanks!


^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH 06/25] qapi: Change frontend error messages to start with lower case
  2019-09-24 15:17   ` Eric Blake
@ 2019-09-24 20:35     ` Markus Armbruster
  0 siblings, 0 replies; 63+ messages in thread
From: Markus Armbruster @ 2019-09-24 20:35 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, mdroth

Eric Blake <eblake@redhat.com> writes:

> On 9/24/19 8:28 AM, Markus Armbruster wrote:
>> Starting error messages with a capital letter complicates things when
>> text can get interpolated both at the beginning and in the middle of
>> an error message.  The next patch will do that.  Switch to lower case
>> to keep it simpler.
>> 
>> For what it's worth, the GNU Coding Standards advise the message
>> "should not begin with a capital letter when it follows a program name
>> and/or file name, because that isn’t the beginning of a sentence. (The
>> sentence conceptually starts at the beginning of the line.)"
>
> We're inconsistent throughout the code base, but this is one place where
> I like the GCS rationale.  Fixing it everywhere may not be worth the
> churn, but fixing it within the subset of the qapi generator is worthwhile.
>
>> 
>> Signed-off-by: Markus Armbruster <armbru@redhat.com>
>> ---
>>  scripts/qapi/common.py                        | 175 +++++++++---------
>>  tests/qapi-schema/alternate-any.err           |   2 +-
>
>>  tests/qapi-schema/unknown-expr-key.err        |   2 +-
>>  125 files changed, 215 insertions(+), 204 deletions(-)
>>  create mode 100644 tests/qapi-schema/escape-too-big.err
>>  create mode 100644 tests/qapi-schema/string-control.err
>>  create mode 100644 tests/qapi-schema/string-unclosed.err
>>  create mode 100644 tests/qapi-schema/string-unicode.err
>
> Umm, what's going on here?

Accident.

> You'll want to either drop these files (if they were leftovers in your
> working directory from previous points in time), or defer their addition
> to when the corresponding actual tests exist.

I'll drop them,

>>      def get_doc(self, info):
>>          if self.val != '##':
>> -            raise QAPIParseError(self, "Junk after '##' at start of "
>> -                                 "documentation comment")
>> +            raise QAPIParseError(
>> +                self, "junk after '##' at start of documentation comment")
>
> Reformatting like this also makes grepping for a particular message easier.
>
>
>> @@ -868,8 +869,8 @@ def check_union(expr, info):
>>          enum_values = members.keys()
>>          allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
>>          if base is not None:
>> -            raise QAPISemError(info, "Simple union '%s' must not have a base" %
>> -                               name)
>> +            raise QAPISemError(
>> +                info, "simple union '%s' must not have a base" % name)
>>  
>
> A bit odd that you reformat here to get the second argument all on one
> line...
>
>>      # Else, it's a flat union.
>>      else:
>> @@ -878,46 +879,47 @@ def check_union(expr, info):
>>                     base, allow_dict=name,
>>                     allow_metas=['struct'])
>>          if not base:
>> -            raise QAPISemError(info, "Flat union '%s' must have a base"
>> +            raise QAPISemError(info, "flat union '%s' must have a base"
>>                                 % name)
>
> ...but not here.  The reformatting is not the primary focus of the
> patch, and doesn't hurt semantically whether or not you do it, but maybe
> it is worth calling out in the commit message the criteria you used for
> deciding when to reformat, and/or make the patch strive for more
> consistency.

I admit I wobble between

    raise QAPISemError(info, "some lengthy error message text with %s"
                       % argument)

and

    raise QAPISemError(info,
                       "some lengthy error message text with %s" % argument)

and

    raise QAPISemError(
        info, "some lengthy error message text with %s" % argument)

The first looks okay, but as a rule, lines should be broken at an
operator with lowest precedence.

The second tends to produce long lines.

I'm still getting used to the third.

>               I'll leave that up to you; fixing the spurious new files,
> and making your choice of where to place the linebreaks, doesn't affect
> my ability to offer:
>
> Reviewed-by: Eric Blake <eblake@redhat.com>

I'll have another look.

Thanks!


^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH 16/25] qapi: Move context-sensitive checking to the proper place
  2019-09-24 17:49   ` Eric Blake
@ 2019-09-24 20:41     ` Markus Armbruster
  0 siblings, 0 replies; 63+ messages in thread
From: Markus Armbruster @ 2019-09-24 20:41 UTC (permalink / raw)
  To: Eric Blake; +Cc: mdroth, marcandre.lureau, Markus Armbruster, qemu-devel

Eric Blake <eblake@redhat.com> writes:

> On 9/24/19 8:28 AM, Markus Armbruster wrote:
>> When we introduced the QAPISchema intermediate representation (commit
>> ac88219a6c7), we took a shortcut: we left check_exprs() & friends
>> alone instead of moving semantic checks into the
>> QAPISchemaFOO.check().  The .check() assert check_exprs() did its job.
>> 
>> Time to finish the conversion job.  Move exactly the context-sensitive
>> checks to the .check().  They replace assertions there.  Context-free
>> checks stay put.
>> 
>> Fixes the misleading optional tag error demonstrated by test
>> flat-union-optional-discriminator.
>> 
>> A few other error message improve.
>> 
>> Signed-off-by: Markus Armbruster <armbru@redhat.com>
>> ---
>
>> diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
>> index f5599559ac..ac4c898e51 100644
>> --- a/scripts/qapi/common.py
>
> Thankfully, our large coverage of tests goes a long way to show that the
> conversion is correct.  I didn't notice anything obvious that might have
> been overlooked (we may still find things down the road, but I'm not
> going to hold up this patch trying to find those things).  Meanwhile,
> the conversion from assert to conditionals inside .check() looks complete.

Additional arguments supporting correctness:

* Every deleted check gets added back.

* Every added check replaces an assertion.

>> +++ b/tests/qapi-schema/args-union.err
>> @@ -1,2 +1,2 @@
>>  tests/qapi-schema/args-union.json: In command 'oops':
>> -tests/qapi-schema/args-union.json:3: 'data' for command 'oops' cannot use union type 'Uni'
>> +tests/qapi-schema/args-union.json:3: command's 'data' can take union type 'Uni' only with 'boxed': true
>
> This one is definitely nicer.
>
>> +++ b/tests/qapi-schema/flat-union-discriminator-bad-name.err
>> @@ -1,2 +1,2 @@
>>  tests/qapi-schema/flat-union-discriminator-bad-name.json: In union 'MyUnion':
>> -tests/qapi-schema/flat-union-discriminator-bad-name.json:7: discriminator of flat union 'MyUnion' uses invalid name '*switch'
>> +tests/qapi-schema/flat-union-discriminator-bad-name.json:6: discriminator '*switch' is not a member of 'base'
>> diff --git a/tests/qapi-schema/flat-union-discriminator-bad-name.json b/tests/qapi-schema/flat-union-discriminator-bad-name.json
>> index ea84b75cac..3ae8c06a89 100644
>> --- a/tests/qapi-schema/flat-union-discriminator-bad-name.json
>> +++ b/tests/qapi-schema/flat-union-discriminator-bad-name.json
>> @@ -1,5 +1,4 @@
>>  # discriminator '*switch' isn't a member of base, 'switch' is
>> -# reports "uses invalid name", which is good enough
>>  { 'enum': 'Enum', 'data': [ 'one', 'two' ] }
>>  { 'struct': 'Base',
>>    'data': { '*switch': 'Enum' } }
>
> I find this one to be borderline in quality (if we have '*switch' in the
> base, claiming that '*switch' is not a member of base is confusing until
> you realize that base actually has an optional member named 'switch') -
> but anyone that actually stumbles into this one will probably quickly
> figure out their problem, and we may be revisiting it later anyways when
> we finally include patches for a default discriminator.
>
>> +++ b/tests/qapi-schema/flat-union-optional-discriminator.err
>> @@ -1,2 +1,2 @@
>>  tests/qapi-schema/flat-union-optional-discriminator.json: In union 'MyUnion':
>> -tests/qapi-schema/flat-union-optional-discriminator.json:7: discriminator 'switch' is not a member of 'base'
>> +tests/qapi-schema/flat-union-optional-discriminator.json:6: discriminator member 'switch' of base type 'Base' must not be optional
>> diff --git a/tests/qapi-schema/flat-union-optional-discriminator.json b/tests/qapi-schema/flat-union-optional-discriminator.json
>> index 143ab23a0d..2e7f766f60 100644
>> --- a/tests/qapi-schema/flat-union-optional-discriminator.json
>> +++ b/tests/qapi-schema/flat-union-optional-discriminator.json
>> @@ -1,5 +1,4 @@
>>  # we require the discriminator to be non-optional
>> -# FIXME reports "discriminator 'switch' is not a member of base struct 'Base'"
>>  { 'enum': 'Enum', 'data': [ 'one', 'two' ] }
>>  { 'struct': 'Base',
>>    'data': { '*switch': 'Enum' } }
>
> And while the other one is borderline, I agree that this one is better.
>
>> +++ b/tests/qapi-schema/union-unknown.err
>> @@ -1,2 +1,2 @@
>>  tests/qapi-schema/union-unknown.json: In union 'Union':
>> -tests/qapi-schema/union-unknown.json:2: member 'unknown' of union 'Union' uses unknown type 'MissingType'
>> +tests/qapi-schema/union-unknown.json:2: union uses unknown type 'MissingType'
>> diff --git a/tests/qapi-schema/union-unknown.json b/tests/qapi-schema/union-unknown.json
>> index aa7e8143d8..64d3666176 100644
>> --- a/tests/qapi-schema/union-unknown.json
>> +++ b/tests/qapi-schema/union-unknown.json
>> @@ -1,3 +1,3 @@
>>  # we reject a union with unknown type in branch
>>  { 'union': 'Union',
>> -  'data': { 'unknown': 'MissingType' } }
>> +  'data': { 'unknown': ['MissingType'] } }
>> 
>
> And here you covered one more code path by going through an array type.
>
> Overall looks good.
>
> Reviewed-by: Eric Blake <eblake@redhat.com>

Thanks!


^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH 19/25] qapi: Improve reporting of invalid flags
  2019-09-24 18:07   ` Eric Blake
@ 2019-09-24 20:43     ` Markus Armbruster
  2019-09-25  6:13       ` Markus Armbruster
  0 siblings, 1 reply; 63+ messages in thread
From: Markus Armbruster @ 2019-09-24 20:43 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, mdroth

Eric Blake <eblake@redhat.com> writes:

> On 9/24/19 8:28 AM, Markus Armbruster wrote:
>> Split check_flags() off check_keys() and have check_exprs() call it
>> later, so its error messages gain an "in definition" line.  Tweak the
>> error messages.
>> 
>> Checking values in a function named check_keys() is unclean anyway.
>> 
>> Signed-off-by: Markus Armbruster <armbru@redhat.com>
>> ---
>>  scripts/qapi/common.py                     | 22 ++++++++++++----------
>>  tests/qapi-schema/allow-preconfig-test.err |  3 ++-
>>  tests/qapi-schema/args-bad-boxed.err       |  3 ++-
>>  tests/qapi-schema/oob-test.err             |  3 ++-
>>  tests/qapi-schema/type-bypass-bad-gen.err  |  3 ++-
>>  5 files changed, 20 insertions(+), 14 deletions(-)
>> 
>
>> +
>> +def check_flags(expr, info):
>> +    for key in ['gen', 'success-response']:
>> +        if key in expr and expr[key] is not False:
>
> Is it any more pythonic and/or a micro-optimization to compress this to:
>
> if expr.get(key, False) is not False:
>
>> +            raise QAPISemError(
>> +                info, "flag '%s' may only use false value" % key)
>> +    for key in ['boxed', 'allow-oob', 'allow-preconfig']:
>> +        if key in expr and expr[key] is not True:
>
> and here too.

Will do.

> Reviewed-by: Eric Blake <eblake@redhat.com>

Thanks!


^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH 20/25] qapi: Improve reporting of missing / unknown definition keys
  2019-09-24 18:13   ` Eric Blake
@ 2019-09-24 20:46     ` Markus Armbruster
  0 siblings, 0 replies; 63+ messages in thread
From: Markus Armbruster @ 2019-09-24 20:46 UTC (permalink / raw)
  To: Eric Blake; +Cc: mdroth, marcandre.lureau, Markus Armbruster, qemu-devel

Eric Blake <eblake@redhat.com> writes:

> On 9/24/19 8:28 AM, Markus Armbruster wrote:
>> Have check_exprs() call check_keys() later, so its error messages gain
>> an "in definition" line.
>> 
>> Both check_keys() and check_name_is_str() check the definition's name
>> is a string.  Since check_keys() now runs after check_name_is_str()
>> rather than before, its check is dead.  Bury it.  Checking values in
>> check_keys() is unclean anyway.
>> 
>> Signed-off-by: Markus Armbruster <armbru@redhat.com>
>> ---
>
>> +++ b/scripts/qapi/common.py
>> @@ -905,8 +905,6 @@ def check_known_keys(value, info, source, required, optional):
>>  
>>  def check_keys(expr, info, meta, required, optional=[]):
>>      name = expr[meta]
>> -    if not isinstance(name, str):
>> -        raise QAPISemError(info, "'%s' key must have a string value" % meta)
>
> Should this be replaced with an assert?  But I'm also okay just dropping
> it, since our testsuite shows that we still flag the problems that this
> message was originally used for.

I'd prefer not to assert, because as of this patch, check_keys() *only*
checks keys, just like its name suggests.

> Reviewed-by: Eric Blake <eblake@redhat.com>

Thanks!


^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH 21/25] qapi: Avoid redundant definition references in error messages
  2019-09-24 18:46   ` Eric Blake
@ 2019-09-24 20:59     ` Markus Armbruster
  0 siblings, 0 replies; 63+ messages in thread
From: Markus Armbruster @ 2019-09-24 20:59 UTC (permalink / raw)
  To: Eric Blake; +Cc: mdroth, marcandre.lureau, Markus Armbruster, qemu-devel

Eric Blake <eblake@redhat.com> writes:

> On 9/24/19 8:28 AM, Markus Armbruster wrote:
>> Many error messages refer to the offending definition even though
>> they're preceded by an "in definition" line.  Rephrase them.
>
> This is the cleanup promised earlier in the series.
>
>> 
>> Signed-off-by: Markus Armbruster <armbru@redhat.com>
>> ---
>>  scripts/qapi/common.py                        | 113 +++++++-----------
>>  tests/qapi-schema/alternate-array.err         |   2 +-
>
>>  def check_command(expr, info):
>> -    name = expr['command']
>>      args = expr.get('data')
>>      boxed = expr.get('boxed', False)
>>  
>>      if boxed and args is None:
>>          raise QAPISemError(info, "'boxed': true requires 'data'")
>> -    check_type(args, info, "'data' for command '%s'" % name,
>> -               allow_dict=not boxed)
>> -    check_type(expr.get('returns'), info,
>> -               "'returns' for command '%s'" % name,
>> -               allow_array=True)
>> +    check_type(expr.get('data'), info, "'data'", allow_dict=not boxed)
>> +    check_type(expr.get('returns'), info, "'returns'", allow_array=True)
>
> Why are you repeating expr.get('dat') here instead of reusing args?  I
> guess it adds consistency with the expr.get('returns') in the next line.

Don't remember, might have been an accident.  If I want consistency, I
guess I should add rets = expr.get('returns').

>>  
>>  
>>  def check_event(expr, info):
>> -    name = expr['event']
>>      args = expr.get('data')
>>      boxed = expr.get('boxed', False)
>>  
>>      if boxed and args is None:
>>          raise QAPISemError(info, "'boxed': true requires 'data'")
>> -    check_type(args, info, "'data' for event '%s'" % name,
>> -               allow_dict=not boxed)
>> +    check_type(expr.get('data'), info, "'data'", allow_dict=not boxed)
> Again, why not reuse args?
>
>
>> +++ b/tests/qapi-schema/args-member-case.err
>> @@ -1,2 +1,2 @@
>>  tests/qapi-schema/args-member-case.json: In command 'no-way-this-will-get-whitelisted':
>> -tests/qapi-schema/args-member-case.json:2: member of 'data' for command 'no-way-this-will-get-whitelisted' uses uppercase in name 'Arg'
>> +tests/qapi-schema/args-member-case.json:2: 'data' member 'Arg' uses uppercase in name 'Arg'
>
> Better, but still feels redundant for calling out 'Arg' twice.  Maybe
> you further clean this one later?

check_type()'s error messages interpolate @source received from by
caller.  Need to review all its messages and callers.

>> +++ b/tests/qapi-schema/enum-member-case.err
>> @@ -1,2 +1,2 @@
>>  tests/qapi-schema/enum-member-case.json: In enum 'NoWayThisWillGetWhitelisted':
>> -tests/qapi-schema/enum-member-case.json:4: member of enum 'NoWayThisWillGetWhitelisted' uses uppercase in name 'Value'
>> +tests/qapi-schema/enum-member-case.json:4: 'data' member uses uppercase in name 'Value'
>
> Here's a similar error about uppercase that does not have the
> redundancy, for comparison.
>
>
>> +++ b/tests/qapi-schema/union-branch-case.err
>> @@ -1,2 +1,2 @@
>>  tests/qapi-schema/union-branch-case.json: In union 'Uni':
>> -tests/qapi-schema/union-branch-case.json:2: member of union 'Uni' uses uppercase in name 'Branch'
>> +tests/qapi-schema/union-branch-case.json:2: 'data' member 'Branch' uses uppercase in name 'Branch'
>
> Another related one.
>
>> +++ b/tests/qapi-schema/union-optional-branch.err
>> @@ -1,2 +1,2 @@
>>  tests/qapi-schema/union-optional-branch.json: In union 'Union':
>> -tests/qapi-schema/union-optional-branch.json:2: member of union 'Union' uses invalid name '*a'
>> +tests/qapi-schema/union-optional-branch.json:2: 'data' member '*a' uses invalid name '*a'
>
> Similar type of redundancy, but this time not related to uppercase.

Same problem, different function: check_name_str().

> Overall an improvement.
>
> Reviewed-by: Eric Blake <eblake@redhat.com>

Thanks!


^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH 25/25] qapi: Improve source file read error handling
  2019-09-24 19:57   ` Eric Blake
@ 2019-09-24 20:59     ` Markus Armbruster
  0 siblings, 0 replies; 63+ messages in thread
From: Markus Armbruster @ 2019-09-24 20:59 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, mdroth

Eric Blake <eblake@redhat.com> writes:

> On 9/24/19 8:28 AM, Markus Armbruster wrote:
>> qap-gen.py crashes when it can't open the main schema file, and when
>
> qapi-gen.py

Will fix.

>> it can't read from any schema file.  Lazy.
>> 
>> Change QAPISchema.__init__() to take a file name instead of a file
>> object.  Move the open code from _include() to __init__(), so it's
>> used for the main schema file, too.
>> 
>> Move the read into the try for good measure, and rephrase the error
>> message.
>> 
>> Reporting open or read failure for the main schema file needs a
>> QAPISourceInfo representing "no source".  Make QAPISourceInfo cope
>> with fname=None.
>> 
>> Signed-off-by: Markus Armbruster <armbru@redhat.com>
>> ---
>>  scripts/qapi/common.py                | 46 +++++++++++++++------------
>>  tests/qapi-schema/include-no-file.err |  2 +-
>>  2 files changed, 27 insertions(+), 21 deletions(-)
>> 
>
> Reviewed-by: Eric Blake <eblake@redhat.com>

Thanks!


^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH 19/25] qapi: Improve reporting of invalid flags
  2019-09-24 20:43     ` Markus Armbruster
@ 2019-09-25  6:13       ` Markus Armbruster
  0 siblings, 0 replies; 63+ messages in thread
From: Markus Armbruster @ 2019-09-25  6:13 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, mdroth

Markus Armbruster <armbru@redhat.com> writes:

> Eric Blake <eblake@redhat.com> writes:
>
>> On 9/24/19 8:28 AM, Markus Armbruster wrote:
>>> Split check_flags() off check_keys() and have check_exprs() call it
>>> later, so its error messages gain an "in definition" line.  Tweak the
>>> error messages.
>>> 
>>> Checking values in a function named check_keys() is unclean anyway.
>>> 
>>> Signed-off-by: Markus Armbruster <armbru@redhat.com>
>>> ---
>>>  scripts/qapi/common.py                     | 22 ++++++++++++----------
>>>  tests/qapi-schema/allow-preconfig-test.err |  3 ++-
>>>  tests/qapi-schema/args-bad-boxed.err       |  3 ++-
>>>  tests/qapi-schema/oob-test.err             |  3 ++-
>>>  tests/qapi-schema/type-bypass-bad-gen.err  |  3 ++-
>>>  5 files changed, 20 insertions(+), 14 deletions(-)
>>> 
>>
>>> +
>>> +def check_flags(expr, info):
>>> +    for key in ['gen', 'success-response']:
>>> +        if key in expr and expr[key] is not False:
>>
>> Is it any more pythonic and/or a micro-optimization to compress this to:
>>
>> if expr.get(key, False) is not False:
>>
>>> +            raise QAPISemError(
>>> +                info, "flag '%s' may only use false value" % key)
>>> +    for key in ['boxed', 'allow-oob', 'allow-preconfig']:
>>> +        if key in expr and expr[key] is not True:
>>
>> and here too.
>
> Will do.

Second thoughts in the morning:

    if key in expr and expr[key] is not VALUE:

feels slightly clearer than

    if expr.get(key, VALUE) is not VALUE:

>> Reviewed-by: Eric Blake <eblake@redhat.com>
>
> Thanks!


^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH 03/25] qapi: New QAPISourceInfo, replacing dict
  2019-09-24 19:12   ` Eric Blake
@ 2019-09-25  6:40     ` Markus Armbruster
  0 siblings, 0 replies; 63+ messages in thread
From: Markus Armbruster @ 2019-09-25  6:40 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, mdroth

Eric Blake <eblake@redhat.com> writes:

> On 9/24/19 8:28 AM, Markus Armbruster wrote:
>> We track source locations with a dict of the form
>> 
>>     {'file': FNAME, 'line': LINENO, parent': PARENT}
>> 
>> where PARENT is None for the main file, and the include directive's
>> source location for included files.
>> 
>> This is servicable enough, but the next commit will add information,
>> and that's going to come out cleaner if we turn this into a class.  So
>> do that.
>> 
>> Signed-off-by: Markus Armbruster <armbru@redhat.com>
>> ---
>
>>  class QAPIError(Exception):
>> -    def __init__(self, fname, line, col, incl_info, msg):
>> +    def __init__(self, info, col, msg):
>>          Exception.__init__(self)
>
> Unrelated to this patch, but I just noticed
> https://docs.quantifiedcode.com/python-anti-patterns/ today (in part
> based on my question on another patch about using 'list.get(key, False)'
> rather than 'key in list and list[key]').  In particular, I found
> https://docs.quantifiedcode.com/python-anti-patterns/correctness/missing_argument_to_super.html
> which recommends using:
>
> def __init__(...):
>     super(QAPIError, self).__init__()
>
> (because of Python 2), while other sits state that with python 3, you
> can further get away with:
>
> def __init__(...):
>     super().__init(...)
>
> Should we be switching our code base to use super() in more places,
> rather than hard-coding the parent class name?

I intend to switch to super() in a future 'bye Python 2' series.


^ permalink raw reply	[flat|nested] 63+ messages in thread

end of thread, other threads:[~2019-09-25  6:42 UTC | newest]

Thread overview: 63+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-09-24 13:28 [PATCH 00/25] qapi: Pay back some frontend technical debt Markus Armbruster
2019-09-24 13:28 ` [PATCH 01/25] qapi: Tighten QAPISchemaFOO.check() assertions Markus Armbruster
2019-09-24 14:39   ` Eric Blake
2019-09-24 20:18     ` Markus Armbruster
2019-09-24 13:28 ` [PATCH 02/25] qapi: Rename .owner to .defined_in Markus Armbruster
2019-09-24 14:41   ` Eric Blake
2019-09-24 13:28 ` [PATCH 03/25] qapi: New QAPISourceInfo, replacing dict Markus Armbruster
2019-09-24 14:51   ` Eric Blake
2019-09-24 20:18     ` Markus Armbruster
2019-09-24 19:12   ` Eric Blake
2019-09-25  6:40     ` Markus Armbruster
2019-09-24 13:28 ` [PATCH 04/25] qapi: Prefix frontend errors with an "in definition" line Markus Armbruster
2019-09-24 14:58   ` Eric Blake
2019-09-24 13:28 ` [PATCH 05/25] qapi: Clean up member name case checking Markus Armbruster
2019-09-24 15:07   ` Eric Blake
2019-09-24 20:20     ` Markus Armbruster
2019-09-24 13:28 ` [PATCH 06/25] qapi: Change frontend error messages to start with lower case Markus Armbruster
2019-09-24 15:17   ` Eric Blake
2019-09-24 20:35     ` Markus Armbruster
2019-09-24 13:28 ` [PATCH 07/25] qapi: Improve reporting of member name clashes Markus Armbruster
2019-09-24 15:38   ` Eric Blake
2019-09-24 13:28 ` [PATCH 08/25] qapi: Reorder check_FOO() parameters for consistency Markus Armbruster
2019-09-24 15:41   ` Eric Blake
2019-09-24 13:28 ` [PATCH 09/25] qapi: Improve reporting of invalid name errors Markus Armbruster
2019-09-24 15:48   ` Eric Blake
2019-09-24 13:28 ` [PATCH 10/25] qapi: Use check_name_str() where it suffices Markus Armbruster
2019-09-24 15:50   ` Eric Blake
2019-09-24 13:28 ` [PATCH 11/25] qapi: Report invalid '*' prefix like any other invalid name Markus Armbruster
2019-09-24 15:52   ` Eric Blake
2019-09-24 13:28 ` [PATCH 12/25] qapi: Move check for reserved names out of add_name() Markus Armbruster
2019-09-24 15:56   ` Eric Blake
2019-09-24 13:28 ` [PATCH 13/25] qapi: Make check_type()'s array case a bit more obvious Markus Armbruster
2019-09-24 15:57   ` Eric Blake
2019-09-24 13:28 ` [PATCH 14/25] qapi: Plumb info to the QAPISchemaMember Markus Armbruster
2019-09-24 16:01   ` Eric Blake
2019-09-24 13:28 ` [PATCH 15/25] qapi: Inline check_name() into check_union() Markus Armbruster
2019-09-24 16:39   ` Eric Blake
2019-09-24 13:28 ` [PATCH 16/25] qapi: Move context-sensitive checking to the proper place Markus Armbruster
2019-09-24 17:49   ` Eric Blake
2019-09-24 20:41     ` Markus Armbruster
2019-09-24 13:28 ` [PATCH 17/25] qapi: Move context-free " Markus Armbruster
2019-09-24 17:59   ` Eric Blake
2019-09-24 13:28 ` [PATCH 18/25] qapi: Improve reporting of invalid 'if' errors Markus Armbruster
2019-09-24 18:01   ` Eric Blake
2019-09-24 13:28 ` [PATCH 19/25] qapi: Improve reporting of invalid flags Markus Armbruster
2019-09-24 18:07   ` Eric Blake
2019-09-24 20:43     ` Markus Armbruster
2019-09-25  6:13       ` Markus Armbruster
2019-09-24 13:28 ` [PATCH 20/25] qapi: Improve reporting of missing / unknown definition keys Markus Armbruster
2019-09-24 18:13   ` Eric Blake
2019-09-24 20:46     ` Markus Armbruster
2019-09-24 13:28 ` [PATCH 21/25] qapi: Avoid redundant definition references in error messages Markus Armbruster
2019-09-24 18:46   ` Eric Blake
2019-09-24 20:59     ` Markus Armbruster
2019-09-24 13:28 ` [PATCH 22/25] qapi: Eliminate check_keys(), rename check_known_keys() Markus Armbruster
2019-09-24 18:49   ` Eric Blake
2019-09-24 13:28 ` [PATCH 23/25] qapi: Improve reporting of missing documentation comment Markus Armbruster
2019-09-24 19:44   ` Eric Blake
2019-09-24 13:28 ` [PATCH 24/25] qapi: Improve reporting of redefinition Markus Armbruster
2019-09-24 19:53   ` Eric Blake
2019-09-24 13:28 ` [PATCH 25/25] qapi: Improve source file read error handling Markus Armbruster
2019-09-24 19:57   ` Eric Blake
2019-09-24 20:59     ` Markus Armbruster

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).