diff options
author | Markus Armbruster <armbru@redhat.com> | 2019-09-27 15:46:29 +0200 |
---|---|---|
committer | Markus Armbruster <armbru@redhat.com> | 2019-09-28 17:17:19 +0200 |
commit | fa110c6a9e6c0ae0ce2d4bcf5771cdb8c3e53a7e (patch) | |
tree | 6f9a4338cdd579a82e4144a0b04eabce8b7ae5e3 | |
parent | 77daece3d95dc7edaa5982fbbfd7afe3bc4121ac (diff) |
qapi: Move context-sensitive checking to the proper place
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>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-Id: <20190927134639.4284-17-armbru@redhat.com>
40 files changed, 230 insertions, 270 deletions
diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 9acff01d3e..f22e84c4a8 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 @@ -671,34 +647,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. @@ -748,16 +699,6 @@ def check_defn_name_str(name, info, meta): 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. '.' - # 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): @@ -781,13 +722,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) @@ -795,19 +734,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) @@ -829,43 +763,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): @@ -874,55 +793,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) @@ -931,25 +813,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, @@ -961,37 +832,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): @@ -1024,8 +866,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): @@ -1111,13 +952,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'] @@ -1134,14 +968,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']) @@ -1152,7 +984,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', [], @@ -1166,36 +997,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' @@ -1208,9 +1032,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 @@ -1251,6 +1078,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): @@ -1341,8 +1172,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) @@ -1374,6 +1211,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: @@ -1411,6 +1250,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) @@ -1419,8 +1260,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 @@ -1453,6 +1295,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, @@ -1461,6 +1307,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) @@ -1495,8 +1342,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: @@ -1508,7 +1361,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 @@ -1646,8 +1498,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): @@ -1671,12 +1523,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]) @@ -1686,15 +1566,24 @@ 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): @@ -1713,6 +1602,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) @@ -1730,9 +1621,34 @@ 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: @@ -1751,6 +1667,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) @@ -1769,14 +1687,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) @@ -1788,6 +1724,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) @@ -1798,9 +1736,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'") @@ -1831,10 +1778,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) @@ -1845,6 +1798,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 @@ -1930,7 +1892,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'] } } |