diff mbox

[v5,20/28] qapi: More rigourous checking of types

Message ID 1427227433-5030-21-git-send-email-eblake@redhat.com
State New
Headers show

Commit Message

Eric Blake March 24, 2015, 8:03 p.m. UTC
Now that we know every expression is valid with regards to
its keys, we can add further tests that those keys refer to
valid types.  With this patch, all uses of a type (the 'data':
of command, type, union, alternate, and event; the 'returns':
of command; the 'base': of type and union) must resolve to an
appropriate subset of metatypes  declared by the current qapi
parse; this includes recursing into each member of a data
dictionary.  Dealing with '**' and nested anonymous structs
will be done in later patches.

Update the testsuite to match improved output.

Signed-off-by: Eric Blake <eblake@redhat.com>
---
 scripts/qapi.py                              | 94 +++++++++++++++++++++++++---
 tests/qapi-schema/alternate-array.err        |  2 +-
 tests/qapi-schema/alternate-nested.err       |  2 +-
 tests/qapi-schema/alternate-unknown.err      |  2 +-
 tests/qapi-schema/bad-base.err               |  1 +
 tests/qapi-schema/bad-base.exit              |  2 +-
 tests/qapi-schema/bad-base.json              |  2 +-
 tests/qapi-schema/bad-base.out               |  4 --
 tests/qapi-schema/data-array-empty.err       |  1 +
 tests/qapi-schema/data-array-empty.exit      |  2 +-
 tests/qapi-schema/data-array-empty.json      |  2 +-
 tests/qapi-schema/data-array-empty.out       |  3 -
 tests/qapi-schema/data-array-unknown.err     |  1 +
 tests/qapi-schema/data-array-unknown.exit    |  2 +-
 tests/qapi-schema/data-array-unknown.json    |  2 +-
 tests/qapi-schema/data-array-unknown.out     |  3 -
 tests/qapi-schema/data-int.err               |  1 +
 tests/qapi-schema/data-int.exit              |  2 +-
 tests/qapi-schema/data-int.json              |  2 +-
 tests/qapi-schema/data-int.out               |  3 -
 tests/qapi-schema/data-member-array-bad.err  |  1 +
 tests/qapi-schema/data-member-array-bad.exit |  2 +-
 tests/qapi-schema/data-member-array-bad.json |  2 +-
 tests/qapi-schema/data-member-array-bad.out  |  3 -
 tests/qapi-schema/data-member-unknown.err    |  1 +
 tests/qapi-schema/data-member-unknown.exit   |  2 +-
 tests/qapi-schema/data-member-unknown.json   |  2 +-
 tests/qapi-schema/data-member-unknown.out    |  3 -
 tests/qapi-schema/data-unknown.err           |  1 +
 tests/qapi-schema/data-unknown.exit          |  2 +-
 tests/qapi-schema/data-unknown.json          |  2 +-
 tests/qapi-schema/data-unknown.out           |  3 -
 tests/qapi-schema/returns-array-bad.err      |  1 +
 tests/qapi-schema/returns-array-bad.exit     |  2 +-
 tests/qapi-schema/returns-array-bad.json     |  2 +-
 tests/qapi-schema/returns-array-bad.out      |  3 -
 tests/qapi-schema/returns-unknown.err        |  1 +
 tests/qapi-schema/returns-unknown.exit       |  2 +-
 tests/qapi-schema/returns-unknown.json       |  2 +-
 tests/qapi-schema/returns-unknown.out        |  3 -
 tests/qapi-schema/union-unknown.err          |  1 +
 tests/qapi-schema/union-unknown.exit         |  2 +-
 tests/qapi-schema/union-unknown.json         |  2 +-
 tests/qapi-schema/union-unknown.out          |  3 -
 44 files changed, 118 insertions(+), 63 deletions(-)

Comments

Markus Armbruster March 27, 2015, 8:23 a.m. UTC | #1
Eric Blake <eblake@redhat.com> writes:

> Now that we know every expression is valid with regards to
> its keys, we can add further tests that those keys refer to
> valid types.  With this patch, all uses of a type (the 'data':
> of command, type, union, alternate, and event; the 'returns':
> of command; the 'base': of type and union) must resolve to an
> appropriate subset of metatypes  declared by the current qapi
> parse; this includes recursing into each member of a data
> dictionary.  Dealing with '**' and nested anonymous structs
> will be done in later patches.
>
> Update the testsuite to match improved output.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
> ---
[...]
> diff --git a/scripts/qapi.py b/scripts/qapi.py
> index 6ed6a34..c42683b 100644
> --- a/scripts/qapi.py
> +++ b/scripts/qapi.py
> @@ -276,6 +276,63 @@ def discriminator_find_enum_define(expr):
>
>      return find_enum(discriminator_type)
>
> +def check_type(expr_info, source, data, allow_array = False,
> +               allowed_metas = [], allow_dict = True):

I'd allow_dict = False here, because I like defaulting to false.  Matter
of taste.

I'd name the third parameter def rather than data.  Matter of taste
again.

> +    global all_names
> +
> +    if data is None:
> +        return
> +
> +    if data == '**':
> +        return
> +
> +    # Check if array type for data is okay
> +    if isinstance(data, list):
> +        if not allow_array:
> +            raise QAPIExprError(expr_info,
> +                                "%s cannot be an array" % source)
> +        if len(data) != 1 or not isinstance(data[0], str):
> +            raise QAPIExprError(expr_info,
> +                                "%s: array type must contain single type name"
> +                                % source)
> +        data = data[0]
> +
> +    # Check if type name for data is okay
> +    if isinstance(data, str):
> +        if not data in all_names:
> +            raise QAPIExprError(expr_info,
> +                                "%s uses unknown type '%s'"
> +                                % (source, data))
> +        if not all_names[data] in allowed_metas:
> +            raise QAPIExprError(expr_info,
> +                                "%s cannot use %s type '%s'"
> +                                % (source, all_names[data], data))
> +        return
> +
> +    # data is a dictionary, check that each member is okay
> +    if not isinstance(data, OrderedDict):
> +        raise QAPIExprError(expr_info,
> +                            "%s should be a dictionary" % source)
> +    if not allow_dict:
> +        raise QAPIExprError(expr_info,
> +                            "%s should be a type name" % source)
> +    for (key, value) in data.items():
> +        check_type(expr_info, "Member '%s' of %s" % (key, source), value,

This can produce messages like

    Member 'inner' of Member 'outer' of ...

I figure the problem will go away when you ditch nested structs.  Not
worth worrying about it then.

> +                   allow_array=True,
> +                   allowed_metas=['built-in', 'union', 'alternate', 'struct',
> +                                  'enum'],
> +                   allow_dict=True)
> +
> +def check_command(expr, expr_info):
> +    name = expr['command']
> +    check_type(expr_info, "'data' for command '%s'" % name,
> +               expr.get('data'),
> +               allowed_metas=['union', 'struct'])
> +    check_type(expr_info, "'returns' for command '%s'" % name,
> +               expr.get('returns'), allow_array=True,
> +               allowed_metas=['built-in', 'union', 'alternate', 'struct',
> +                              'enum'])
> +
>  def check_event(expr, expr_info):
>      global events
>      name = expr['event']
> @@ -287,7 +344,8 @@ def check_event(expr, expr_info):
>          raise QAPIExprError(expr_info, "Event name '%s' should be upper case"
>                              % name)
>      events.append(name)
> -
> +    check_type(expr_info, "'data' for event '%s'" % name,
> +               expr.get('data'), allowed_metas=['union', 'struct'])
>      if params:
>          for argname, argentry, optional, structured in parse_args(params):
>              if structured:
> @@ -348,6 +406,13 @@ def check_union(expr, expr_info):
>
>      # Check every branch
>      for (key, value) in members.items():
> +        # Each value must name a known type
> +        check_type(expr_info, "Member '%s' of union '%s'" % (key, name),
> +                   value, allow_array=True,
> +                   allowed_metas=['built-in', 'union', 'alternate', 'struct',
> +                                  'enum'],
> +                   allow_dict=False)
> +
>          # If the discriminator names an enum type, then all members
>          # of 'data' must also be members of the enum type.
>          if enum_define:
> @@ -383,15 +448,12 @@ def check_alternate(expr, expr_info):
>          values[c_key] = key
>
>          # Ensure alternates have no type conflicts.
> -        if isinstance(value, list):
> -            raise QAPIExprError(expr_info,
> -                                "Alternate '%s' member '%s' must "
> -                                "not be array type" % (name, key))
> +        check_type(expr_info, "Member '%s' of alternate '%s'" % (key, name),
> +                   value,
> +                   allowed_metas=['built-in', 'union', 'struct', 'enum'],
> +                   allow_dict=False)
>          qtype = find_alternate_member_qtype(value)
> -        if not qtype:
> -            raise QAPIExprError(expr_info,
> -                                "Alternate '%s' member '%s' has "
> -                                "invalid type '%s'" % (name, key, value))
> +        assert qtype
>          if qtype in types_seen:
>              raise QAPIExprError(expr_info,
>                                  "Alternate '%s' member '%s' has "
> @@ -419,6 +481,14 @@ def check_enum(expr, expr_info):
>                                  % (name, member, values[key]))
>          values[key] = member
>
> +def check_struct(expr, expr_info):
> +    name = expr['type']
> +    members = expr['data']
> +
> +    check_type(expr_info, "'data' for type '%s'" % name, members)

This one gave me pause, until I realized that allowed_metas=[],
allow_dict=True accepts exactly dictionary members, which is what we
want.

> +    check_type(expr_info, "'base' for type '%s'" % name, expr.get('base'),
> +               allowed_metas=['struct'], allow_dict=False)
> +
>  def check_exprs(schema):
>      for expr_elem in schema.exprs:
>          expr = expr_elem['expr']
> @@ -430,8 +500,14 @@ def check_exprs(schema):
>              check_union(expr, info)
>          elif expr.has_key('alternate'):
>              check_alternate(expr, info)
> +        elif expr.has_key('type'):
> +            check_struct(expr, info)
> +        elif expr.has_key('command'):
> +            check_command(expr, info)
>          elif expr.has_key('event'):
>              check_event(expr, info)
> +        else:
> +            assert False, 'unexpected meta type'
>
>  def check_keys(expr_elem, meta, required, optional=[]):
>      expr = expr_elem['expr']
[...]

Reviewed-by: Markus Armbruster <armbru@redhat.com>
Eric Blake March 27, 2015, 8:03 p.m. UTC | #2
On 03/27/2015 02:23 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> Now that we know every expression is valid with regards to
>> its keys, we can add further tests that those keys refer to
>> valid types.  With this patch, all uses of a type (the 'data':
>> of command, type, union, alternate, and event; the 'returns':
>> of command; the 'base': of type and union) must resolve to an
>> appropriate subset of metatypes  declared by the current qapi
>> parse; this includes recursing into each member of a data
>> dictionary.  Dealing with '**' and nested anonymous structs
>> will be done in later patches.
>>
>> Update the testsuite to match improved output.
>>
>> Signed-off-by: Eric Blake <eblake@redhat.com>
>> ---
> [...]
>> diff --git a/scripts/qapi.py b/scripts/qapi.py
>> index 6ed6a34..c42683b 100644
>> --- a/scripts/qapi.py
>> +++ b/scripts/qapi.py
>> @@ -276,6 +276,63 @@ def discriminator_find_enum_define(expr):
>>
>>      return find_enum(discriminator_type)
>>
>> +def check_type(expr_info, source, data, allow_array = False,
>> +               allowed_metas = [], allow_dict = True):
> 
> I'd allow_dict = False here, because I like defaulting to false.  Matter
> of taste.

Not too hard.

> 
> I'd name the third parameter def rather than data.  Matter of taste
> again.
> 

Oh, definitely a better name. When I first prototyped the function, I
was only crawling the 'data' member, but since I now also use it to
crawl 'returns' and 'base' it does make a difference.


>> +    for (key, value) in data.items():
>> +        check_type(expr_info, "Member '%s' of %s" % (key, source), value,
> 
> This can produce messages like
> 
>     Member 'inner' of Member 'outer' of ...
> 
> I figure the problem will go away when you ditch nested structs.  Not
> worth worrying about it then.

Yep, it disappears quite nicely.  (I was torn on whether to make the
message start with lower-case, to avoid the mid-sentence Cap, but the
prevailing practice in this file before my patches was to start all
QAPIExprError with a capital)


>>
>> +def check_struct(expr, expr_info):
>> +    name = expr['type']
>> +    members = expr['data']
>> +
>> +    check_type(expr_info, "'data' for type '%s'" % name, members)
> 
> This one gave me pause, until I realized that allowed_metas=[],
> allow_dict=True accepts exactly dictionary members, which is what we
> want.

And your idea of having allow_dict default to False in the prototype to
make it explicit here might make it easier to understand (where the
default allows nothing, and then callers add the pieces that they want).
 Indeed, just typing this email, that sounds nicer than the current case
of defaults cater to the majority, but force some callers to have to
explicitly opt out.
diff mbox

Patch

diff --git a/scripts/qapi.py b/scripts/qapi.py
index 6ed6a34..c42683b 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -276,6 +276,63 @@  def discriminator_find_enum_define(expr):

     return find_enum(discriminator_type)

+def check_type(expr_info, source, data, allow_array = False,
+               allowed_metas = [], allow_dict = True):
+    global all_names
+
+    if data is None:
+        return
+
+    if data == '**':
+        return
+
+    # Check if array type for data is okay
+    if isinstance(data, list):
+        if not allow_array:
+            raise QAPIExprError(expr_info,
+                                "%s cannot be an array" % source)
+        if len(data) != 1 or not isinstance(data[0], str):
+            raise QAPIExprError(expr_info,
+                                "%s: array type must contain single type name"
+                                % source)
+        data = data[0]
+
+    # Check if type name for data is okay
+    if isinstance(data, str):
+        if not data in all_names:
+            raise QAPIExprError(expr_info,
+                                "%s uses unknown type '%s'"
+                                % (source, data))
+        if not all_names[data] in allowed_metas:
+            raise QAPIExprError(expr_info,
+                                "%s cannot use %s type '%s'"
+                                % (source, all_names[data], data))
+        return
+
+    # data is a dictionary, check that each member is okay
+    if not isinstance(data, OrderedDict):
+        raise QAPIExprError(expr_info,
+                            "%s should be a dictionary" % source)
+    if not allow_dict:
+        raise QAPIExprError(expr_info,
+                            "%s should be a type name" % source)
+    for (key, value) in data.items():
+        check_type(expr_info, "Member '%s' of %s" % (key, source), value,
+                   allow_array=True,
+                   allowed_metas=['built-in', 'union', 'alternate', 'struct',
+                                  'enum'],
+                   allow_dict=True)
+
+def check_command(expr, expr_info):
+    name = expr['command']
+    check_type(expr_info, "'data' for command '%s'" % name,
+               expr.get('data'),
+               allowed_metas=['union', 'struct'])
+    check_type(expr_info, "'returns' for command '%s'" % name,
+               expr.get('returns'), allow_array=True,
+               allowed_metas=['built-in', 'union', 'alternate', 'struct',
+                              'enum'])
+
 def check_event(expr, expr_info):
     global events
     name = expr['event']
@@ -287,7 +344,8 @@  def check_event(expr, expr_info):
         raise QAPIExprError(expr_info, "Event name '%s' should be upper case"
                             % name)
     events.append(name)
-
+    check_type(expr_info, "'data' for event '%s'" % name,
+               expr.get('data'), allowed_metas=['union', 'struct'])
     if params:
         for argname, argentry, optional, structured in parse_args(params):
             if structured:
@@ -348,6 +406,13 @@  def check_union(expr, expr_info):

     # Check every branch
     for (key, value) in members.items():
+        # Each value must name a known type
+        check_type(expr_info, "Member '%s' of union '%s'" % (key, name),
+                   value, allow_array=True,
+                   allowed_metas=['built-in', 'union', 'alternate', 'struct',
+                                  'enum'],
+                   allow_dict=False)
+
         # If the discriminator names an enum type, then all members
         # of 'data' must also be members of the enum type.
         if enum_define:
@@ -383,15 +448,12 @@  def check_alternate(expr, expr_info):
         values[c_key] = key

         # Ensure alternates have no type conflicts.
-        if isinstance(value, list):
-            raise QAPIExprError(expr_info,
-                                "Alternate '%s' member '%s' must "
-                                "not be array type" % (name, key))
+        check_type(expr_info, "Member '%s' of alternate '%s'" % (key, name),
+                   value,
+                   allowed_metas=['built-in', 'union', 'struct', 'enum'],
+                   allow_dict=False)
         qtype = find_alternate_member_qtype(value)
-        if not qtype:
-            raise QAPIExprError(expr_info,
-                                "Alternate '%s' member '%s' has "
-                                "invalid type '%s'" % (name, key, value))
+        assert qtype
         if qtype in types_seen:
             raise QAPIExprError(expr_info,
                                 "Alternate '%s' member '%s' has "
@@ -419,6 +481,14 @@  def check_enum(expr, expr_info):
                                 % (name, member, values[key]))
         values[key] = member

+def check_struct(expr, expr_info):
+    name = expr['type']
+    members = expr['data']
+
+    check_type(expr_info, "'data' for type '%s'" % name, members)
+    check_type(expr_info, "'base' for type '%s'" % name, expr.get('base'),
+               allowed_metas=['struct'], allow_dict=False)
+
 def check_exprs(schema):
     for expr_elem in schema.exprs:
         expr = expr_elem['expr']
@@ -430,8 +500,14 @@  def check_exprs(schema):
             check_union(expr, info)
         elif expr.has_key('alternate'):
             check_alternate(expr, info)
+        elif expr.has_key('type'):
+            check_struct(expr, info)
+        elif expr.has_key('command'):
+            check_command(expr, info)
         elif expr.has_key('event'):
             check_event(expr, info)
+        else:
+            assert False, 'unexpected meta type'

 def check_keys(expr_elem, meta, required, optional=[]):
     expr = expr_elem['expr']
diff --git a/tests/qapi-schema/alternate-array.err b/tests/qapi-schema/alternate-array.err
index 5721ed2..f4f0c1d 100644
--- a/tests/qapi-schema/alternate-array.err
+++ b/tests/qapi-schema/alternate-array.err
@@ -1 +1 @@ 
-tests/qapi-schema/alternate-array.json:4: Alternate 'Alt' member 'two' must not be array type
+tests/qapi-schema/alternate-array.json:4: Member 'two' of alternate 'Alt' cannot be an array
diff --git a/tests/qapi-schema/alternate-nested.err b/tests/qapi-schema/alternate-nested.err
index 00b05c6..4d1187e 100644
--- a/tests/qapi-schema/alternate-nested.err
+++ b/tests/qapi-schema/alternate-nested.err
@@ -1 +1 @@ 
-tests/qapi-schema/alternate-nested.json:4: Alternate 'Alt2' member 'nested' has invalid 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 7af1b4c..dea45dc 100644
--- a/tests/qapi-schema/alternate-unknown.err
+++ b/tests/qapi-schema/alternate-unknown.err
@@ -1 +1 @@ 
-tests/qapi-schema/alternate-unknown.json:2: Alternate 'Alt' member 'unknown' has invalid type 'MissingType'
+tests/qapi-schema/alternate-unknown.json:2: Member 'unknown' of alternate 'Alt' uses unknown type 'MissingType'
diff --git a/tests/qapi-schema/bad-base.err b/tests/qapi-schema/bad-base.err
index e69de29..f398bbb 100644
--- a/tests/qapi-schema/bad-base.err
+++ b/tests/qapi-schema/bad-base.err
@@ -0,0 +1 @@ 
+tests/qapi-schema/bad-base.json:3: 'base' for type 'MyType' cannot use union type 'Union'
diff --git a/tests/qapi-schema/bad-base.exit b/tests/qapi-schema/bad-base.exit
index 573541a..d00491f 100644
--- a/tests/qapi-schema/bad-base.exit
+++ b/tests/qapi-schema/bad-base.exit
@@ -1 +1 @@ 
-0
+1
diff --git a/tests/qapi-schema/bad-base.json b/tests/qapi-schema/bad-base.json
index de964a0..a690470 100644
--- a/tests/qapi-schema/bad-base.json
+++ b/tests/qapi-schema/bad-base.json
@@ -1,3 +1,3 @@ 
-# FIXME: we should reject a base that is not a struct
+# we reject a base that is not a struct
 { 'union': 'Union', 'data': { 'a': 'int', 'b': 'str' } }
 { 'type': 'MyType', 'base': 'Union', 'data': { 'c': 'int' } }
diff --git a/tests/qapi-schema/bad-base.out b/tests/qapi-schema/bad-base.out
index 91d12fc..e69de29 100644
--- a/tests/qapi-schema/bad-base.out
+++ b/tests/qapi-schema/bad-base.out
@@ -1,4 +0,0 @@ 
-[OrderedDict([('union', 'Union'), ('data', OrderedDict([('a', 'int'), ('b', 'str')]))]),
- OrderedDict([('type', 'MyType'), ('base', 'Union'), ('data', OrderedDict([('c', 'int')]))])]
-[{'enum_name': 'UnionKind', 'enum_values': None}]
-[OrderedDict([('type', 'MyType'), ('base', 'Union'), ('data', OrderedDict([('c', 'int')]))])]
diff --git a/tests/qapi-schema/data-array-empty.err b/tests/qapi-schema/data-array-empty.err
index e69de29..f713f14 100644
--- a/tests/qapi-schema/data-array-empty.err
+++ b/tests/qapi-schema/data-array-empty.err
@@ -0,0 +1 @@ 
+tests/qapi-schema/data-array-empty.json:2: Member 'empty' of 'data' for command 'oops': array type must contain single type name
diff --git a/tests/qapi-schema/data-array-empty.exit b/tests/qapi-schema/data-array-empty.exit
index 573541a..d00491f 100644
--- a/tests/qapi-schema/data-array-empty.exit
+++ b/tests/qapi-schema/data-array-empty.exit
@@ -1 +1 @@ 
-0
+1
diff --git a/tests/qapi-schema/data-array-empty.json b/tests/qapi-schema/data-array-empty.json
index edb543a..652dcfb 100644
--- a/tests/qapi-schema/data-array-empty.json
+++ b/tests/qapi-schema/data-array-empty.json
@@ -1,2 +1,2 @@ 
-# FIXME: we should reject an array for data if it does not contain a known type
+# we reject an array for data if it does not contain a known type
 { 'command': 'oops', 'data': { 'empty': [ ] } }
diff --git a/tests/qapi-schema/data-array-empty.out b/tests/qapi-schema/data-array-empty.out
index 6f25c9e..e69de29 100644
--- a/tests/qapi-schema/data-array-empty.out
+++ b/tests/qapi-schema/data-array-empty.out
@@ -1,3 +0,0 @@ 
-[OrderedDict([('command', 'oops'), ('data', OrderedDict([('empty', [])]))])]
-[]
-[]
diff --git a/tests/qapi-schema/data-array-unknown.err b/tests/qapi-schema/data-array-unknown.err
index e69de29..21c39ca 100644
--- a/tests/qapi-schema/data-array-unknown.err
+++ b/tests/qapi-schema/data-array-unknown.err
@@ -0,0 +1 @@ 
+tests/qapi-schema/data-array-unknown.json:2: Member 'array' of 'data' for command 'oops' uses unknown type 'NoSuchType'
diff --git a/tests/qapi-schema/data-array-unknown.exit b/tests/qapi-schema/data-array-unknown.exit
index 573541a..d00491f 100644
--- a/tests/qapi-schema/data-array-unknown.exit
+++ b/tests/qapi-schema/data-array-unknown.exit
@@ -1 +1 @@ 
-0
+1
diff --git a/tests/qapi-schema/data-array-unknown.json b/tests/qapi-schema/data-array-unknown.json
index 20cd3c0..6f3e883 100644
--- a/tests/qapi-schema/data-array-unknown.json
+++ b/tests/qapi-schema/data-array-unknown.json
@@ -1,2 +1,2 @@ 
-# FIXME: we should reject an array for data if it does not contain a known type
+# we reject an array for data if it does not contain a known type
 { 'command': 'oops', 'data': { 'array': [ 'NoSuchType' ] } }
diff --git a/tests/qapi-schema/data-array-unknown.out b/tests/qapi-schema/data-array-unknown.out
index 4314ab5..e69de29 100644
--- a/tests/qapi-schema/data-array-unknown.out
+++ b/tests/qapi-schema/data-array-unknown.out
@@ -1,3 +0,0 @@ 
-[OrderedDict([('command', 'oops'), ('data', OrderedDict([('array', ['NoSuchType'])]))])]
-[]
-[]
diff --git a/tests/qapi-schema/data-int.err b/tests/qapi-schema/data-int.err
index e69de29..1a9b077 100644
--- a/tests/qapi-schema/data-int.err
+++ b/tests/qapi-schema/data-int.err
@@ -0,0 +1 @@ 
+tests/qapi-schema/data-int.json:2: 'data' for command 'oops' cannot use built-in type 'int'
diff --git a/tests/qapi-schema/data-int.exit b/tests/qapi-schema/data-int.exit
index 573541a..d00491f 100644
--- a/tests/qapi-schema/data-int.exit
+++ b/tests/qapi-schema/data-int.exit
@@ -1 +1 @@ 
-0
+1
diff --git a/tests/qapi-schema/data-int.json b/tests/qapi-schema/data-int.json
index 37916e0..a334d92 100644
--- a/tests/qapi-schema/data-int.json
+++ b/tests/qapi-schema/data-int.json
@@ -1,2 +1,2 @@ 
-# FIXME: we should reject commands where data is not an array or complex type
+# we reject commands where data is not an array or complex type
 { 'command': 'oops', 'data': 'int' }
diff --git a/tests/qapi-schema/data-int.out b/tests/qapi-schema/data-int.out
index e589a4f..e69de29 100644
--- a/tests/qapi-schema/data-int.out
+++ b/tests/qapi-schema/data-int.out
@@ -1,3 +0,0 @@ 
-[OrderedDict([('command', 'oops'), ('data', 'int')])]
-[]
-[]
diff --git a/tests/qapi-schema/data-member-array-bad.err b/tests/qapi-schema/data-member-array-bad.err
index e69de29..2c072d5 100644
--- a/tests/qapi-schema/data-member-array-bad.err
+++ b/tests/qapi-schema/data-member-array-bad.err
@@ -0,0 +1 @@ 
+tests/qapi-schema/data-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/data-member-array-bad.exit b/tests/qapi-schema/data-member-array-bad.exit
index 573541a..d00491f 100644
--- a/tests/qapi-schema/data-member-array-bad.exit
+++ b/tests/qapi-schema/data-member-array-bad.exit
@@ -1 +1 @@ 
-0
+1
diff --git a/tests/qapi-schema/data-member-array-bad.json b/tests/qapi-schema/data-member-array-bad.json
index c954af1..b2ff144 100644
--- a/tests/qapi-schema/data-member-array-bad.json
+++ b/tests/qapi-schema/data-member-array-bad.json
@@ -1,2 +1,2 @@ 
-# FIXME: we should reject data if it does not contain a valid array type
+# we reject data if it does not contain a valid array type
 { 'command': 'oops', 'data': { 'member': [ { 'nested': 'str' } ] } }
diff --git a/tests/qapi-schema/data-member-array-bad.out b/tests/qapi-schema/data-member-array-bad.out
index 0e00c41..e69de29 100644
--- a/tests/qapi-schema/data-member-array-bad.out
+++ b/tests/qapi-schema/data-member-array-bad.out
@@ -1,3 +0,0 @@ 
-[OrderedDict([('command', 'oops'), ('data', OrderedDict([('member', [OrderedDict([('nested', 'str')])])]))])]
-[]
-[]
diff --git a/tests/qapi-schema/data-member-unknown.err b/tests/qapi-schema/data-member-unknown.err
index e69de29..ab905db 100644
--- a/tests/qapi-schema/data-member-unknown.err
+++ b/tests/qapi-schema/data-member-unknown.err
@@ -0,0 +1 @@ 
+tests/qapi-schema/data-member-unknown.json:2: Member 'member' of 'data' for command 'oops' uses unknown type 'NoSuchType'
diff --git a/tests/qapi-schema/data-member-unknown.exit b/tests/qapi-schema/data-member-unknown.exit
index 573541a..d00491f 100644
--- a/tests/qapi-schema/data-member-unknown.exit
+++ b/tests/qapi-schema/data-member-unknown.exit
@@ -1 +1 @@ 
-0
+1
diff --git a/tests/qapi-schema/data-member-unknown.json b/tests/qapi-schema/data-member-unknown.json
index 40e6252..342a41e 100644
--- a/tests/qapi-schema/data-member-unknown.json
+++ b/tests/qapi-schema/data-member-unknown.json
@@ -1,2 +1,2 @@ 
-# FIXME: we should reject data if it does not contain a known type
+# we reject data if it does not contain a known type
 { 'command': 'oops', 'data': { 'member': 'NoSuchType' } }
diff --git a/tests/qapi-schema/data-member-unknown.out b/tests/qapi-schema/data-member-unknown.out
index 36252a5..e69de29 100644
--- a/tests/qapi-schema/data-member-unknown.out
+++ b/tests/qapi-schema/data-member-unknown.out
@@ -1,3 +0,0 @@ 
-[OrderedDict([('command', 'oops'), ('data', OrderedDict([('member', 'NoSuchType')]))])]
-[]
-[]
diff --git a/tests/qapi-schema/data-unknown.err b/tests/qapi-schema/data-unknown.err
index e69de29..5b07277 100644
--- a/tests/qapi-schema/data-unknown.err
+++ b/tests/qapi-schema/data-unknown.err
@@ -0,0 +1 @@ 
+tests/qapi-schema/data-unknown.json:2: 'data' for command 'oops' uses unknown type 'NoSuchType'
diff --git a/tests/qapi-schema/data-unknown.exit b/tests/qapi-schema/data-unknown.exit
index 573541a..d00491f 100644
--- a/tests/qapi-schema/data-unknown.exit
+++ b/tests/qapi-schema/data-unknown.exit
@@ -1 +1 @@ 
-0
+1
diff --git a/tests/qapi-schema/data-unknown.json b/tests/qapi-schema/data-unknown.json
index 776bd34..32aba43 100644
--- a/tests/qapi-schema/data-unknown.json
+++ b/tests/qapi-schema/data-unknown.json
@@ -1,2 +1,2 @@ 
-# FIXME: we should reject data if it does not contain a known type
+# we reject data if it does not contain a known type
 { 'command': 'oops', 'data': 'NoSuchType' }
diff --git a/tests/qapi-schema/data-unknown.out b/tests/qapi-schema/data-unknown.out
index 2c60726..e69de29 100644
--- a/tests/qapi-schema/data-unknown.out
+++ b/tests/qapi-schema/data-unknown.out
@@ -1,3 +0,0 @@ 
-[OrderedDict([('command', 'oops'), ('data', 'NoSuchType')])]
-[]
-[]
diff --git a/tests/qapi-schema/returns-array-bad.err b/tests/qapi-schema/returns-array-bad.err
index e69de29..138095c 100644
--- a/tests/qapi-schema/returns-array-bad.err
+++ b/tests/qapi-schema/returns-array-bad.err
@@ -0,0 +1 @@ 
+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-array-bad.exit b/tests/qapi-schema/returns-array-bad.exit
index 573541a..d00491f 100644
--- a/tests/qapi-schema/returns-array-bad.exit
+++ b/tests/qapi-schema/returns-array-bad.exit
@@ -1 +1 @@ 
-0
+1
diff --git a/tests/qapi-schema/returns-array-bad.json b/tests/qapi-schema/returns-array-bad.json
index 14882c1..09b0b1f 100644
--- a/tests/qapi-schema/returns-array-bad.json
+++ b/tests/qapi-schema/returns-array-bad.json
@@ -1,2 +1,2 @@ 
-# FIXME: we should reject an array return that is not a single type
+# we reject an array return that is not a single type
 { 'command': 'oops', 'returns': [ 'str', 'str' ] }
diff --git a/tests/qapi-schema/returns-array-bad.out b/tests/qapi-schema/returns-array-bad.out
index eccad55..e69de29 100644
--- a/tests/qapi-schema/returns-array-bad.out
+++ b/tests/qapi-schema/returns-array-bad.out
@@ -1,3 +0,0 @@ 
-[OrderedDict([('command', 'oops'), ('returns', ['str', 'str'])])]
-[]
-[]
diff --git a/tests/qapi-schema/returns-unknown.err b/tests/qapi-schema/returns-unknown.err
index e69de29..1f43e3a 100644
--- a/tests/qapi-schema/returns-unknown.err
+++ b/tests/qapi-schema/returns-unknown.err
@@ -0,0 +1 @@ 
+tests/qapi-schema/returns-unknown.json:2: 'returns' for command 'oops' uses unknown type 'NoSuchType'
diff --git a/tests/qapi-schema/returns-unknown.exit b/tests/qapi-schema/returns-unknown.exit
index 573541a..d00491f 100644
--- a/tests/qapi-schema/returns-unknown.exit
+++ b/tests/qapi-schema/returns-unknown.exit
@@ -1 +1 @@ 
-0
+1
diff --git a/tests/qapi-schema/returns-unknown.json b/tests/qapi-schema/returns-unknown.json
index 61f20eb..25bd498 100644
--- a/tests/qapi-schema/returns-unknown.json
+++ b/tests/qapi-schema/returns-unknown.json
@@ -1,2 +1,2 @@ 
-# FIXME: we should reject returns if it does not contain a known type
+# we reject returns if it does not contain a known type
 { 'command': 'oops', 'returns': 'NoSuchType' }
diff --git a/tests/qapi-schema/returns-unknown.out b/tests/qapi-schema/returns-unknown.out
index a482c83..e69de29 100644
--- a/tests/qapi-schema/returns-unknown.out
+++ b/tests/qapi-schema/returns-unknown.out
@@ -1,3 +0,0 @@ 
-[OrderedDict([('command', 'oops'), ('returns', 'NoSuchType')])]
-[]
-[]
diff --git a/tests/qapi-schema/union-unknown.err b/tests/qapi-schema/union-unknown.err
index e69de29..54fe456 100644
--- a/tests/qapi-schema/union-unknown.err
+++ b/tests/qapi-schema/union-unknown.err
@@ -0,0 +1 @@ 
+tests/qapi-schema/union-unknown.json:2: Member 'unknown' of union 'Union' uses unknown type 'MissingType'
diff --git a/tests/qapi-schema/union-unknown.exit b/tests/qapi-schema/union-unknown.exit
index 573541a..d00491f 100644
--- a/tests/qapi-schema/union-unknown.exit
+++ b/tests/qapi-schema/union-unknown.exit
@@ -1 +1 @@ 
-0
+1
diff --git a/tests/qapi-schema/union-unknown.json b/tests/qapi-schema/union-unknown.json
index 258f1d3..aa7e814 100644
--- a/tests/qapi-schema/union-unknown.json
+++ b/tests/qapi-schema/union-unknown.json
@@ -1,3 +1,3 @@ 
-# FIXME: we should reject a union with unknown type in branch
+# we reject a union with unknown type in branch
 { 'union': 'Union',
   'data': { 'unknown': 'MissingType' } }
diff --git a/tests/qapi-schema/union-unknown.out b/tests/qapi-schema/union-unknown.out
index 8223dcf..e69de29 100644
--- a/tests/qapi-schema/union-unknown.out
+++ b/tests/qapi-schema/union-unknown.out
@@ -1,3 +0,0 @@ 
-[OrderedDict([('union', 'Union'), ('data', OrderedDict([('unknown', 'MissingType')]))])]
-[{'enum_name': 'UnionKind', 'enum_values': None}]
-[]