qapi: Track location that created an implicit type

A future patch will move some error checking from the parser
to the various QAPISchema*.check() methods, which run only
after parsing completes. It will thus be possible to create
a python instance representing an implicit QAPI type that
parses fine but will fail validation during check(). Since
all errors have to have an associated 'info' location, we
need a location to be associated with those implicit types.
The intuitive info to use is the location of the enclosing
entity that caused the creation of the implicit type.

Note that we do not anticipate builtin types being used in
an error message (as they are not part of the user's QAPI
input, the user can't cause a semantic error in their
behavior), so we exempt those types from requiring info, by
setting a flag to track the completion of _def_predefineds(),
and tracking that flag in _def_entity().

No change to the generated code.

Backports commit 99df5289d8c7ebf373c3570d8fba3f3a73360281 from qemu
This commit is contained in:
Eric Blake 2018-02-19 19:04:36 -05:00 committed by Lioncash
parent 738fd1e5ad
commit 5e62ed79f9
No known key found for this signature in database
GPG Key ID: 4E3C3CC1031BA9C7

View File

@ -790,6 +790,11 @@ class QAPISchemaEntity(object):
def __init__(self, name, info): def __init__(self, name, info):
assert isinstance(name, str) assert isinstance(name, str)
self.name = name self.name = name
# For explicitly defined entities, info points to the (explicit)
# definition. For builtins (and their arrays), info is None.
# For implicitly defined entities, info points to a place that
# triggered the implicit definition (there may be more than one
# such place).
self.info = info self.info = info
def c_name(self): def c_name(self):
@ -903,6 +908,10 @@ class QAPISchemaEnumType(QAPISchemaType):
def check(self, schema): def check(self, schema):
assert len(set(self.values)) == len(self.values) assert len(set(self.values)) == len(self.values)
def is_implicit(self):
# See QAPISchema._make_implicit_enum_type()
return self.name[-4:] == 'Kind'
def c_type(self, is_param=False): def c_type(self, is_param=False):
return c_name(self.name) return c_name(self.name)
@ -929,6 +938,9 @@ class QAPISchemaArrayType(QAPISchemaType):
self.element_type = schema.lookup_type(self._element_type_name) self.element_type = schema.lookup_type(self._element_type_name)
assert self.element_type assert self.element_type
def is_implicit(self):
return True
def json_type(self): def json_type(self):
return 'array' return 'array'
@ -973,6 +985,10 @@ class QAPISchemaObjectType(QAPISchemaType):
self.variants.check(schema, members, seen) self.variants.check(schema, members, seen)
self.members = members self.members = members
def is_implicit(self):
# See QAPISchema._make_implicit_object_type()
return self.name[0] == ':'
def c_name(self): def c_name(self):
assert not self.is_implicit() assert not self.is_implicit()
return QAPISchemaType.c_name(self) return QAPISchemaType.c_name(self)
@ -1119,7 +1135,9 @@ class QAPISchema(object):
try: try:
self.exprs = check_exprs(QAPISchemaParser(open(fname, "r")).exprs) self.exprs = check_exprs(QAPISchemaParser(open(fname, "r")).exprs)
self._entity_dict = {} self._entity_dict = {}
self._predefining = True
self._def_predefineds() self._def_predefineds()
self._predefining = False
self._def_exprs() self._def_exprs()
self.check() self.check()
except (QAPISchemaError, QAPIExprError), err: except (QAPISchemaError, QAPIExprError), err:
@ -1127,6 +1145,8 @@ class QAPISchema(object):
exit(1) exit(1)
def _def_entity(self, ent): def _def_entity(self, ent):
# Only the predefined types are allowed to not have info
assert ent.info or self._predefining
assert ent.name not in self._entity_dict assert ent.name not in self._entity_dict
self._entity_dict[ent.name] = ent self._entity_dict[ent.name] = ent
@ -1147,7 +1167,7 @@ class QAPISchema(object):
# declared in the first file whether or not they are used. Nicer # declared in the first file whether or not they are used. Nicer
# would be to use lazy instantiation, while figuring out how to # would be to use lazy instantiation, while figuring out how to
# avoid compilation issues with multiple qapi-types.h. # avoid compilation issues with multiple qapi-types.h.
self._make_array_type(name) self._make_array_type(name, None)
def _def_predefineds(self): def _def_predefineds(self):
for t in [('str', 'string', 'char' + pointer_suffix, 'NULL'), for t in [('str', 'string', 'char' + pointer_suffix, 'NULL'),
@ -1169,25 +1189,25 @@ class QAPISchema(object):
[], None) [], None)
self._def_entity(self.the_empty_object_type) self._def_entity(self.the_empty_object_type)
def _make_implicit_enum_type(self, name, values): def _make_implicit_enum_type(self, name, info, values):
name = name + 'Kind' # Use namespace reserved by add_name() name = name + 'Kind' # Use namespace reserved by add_name()
self._def_entity(QAPISchemaEnumType(name, None, values, None)) self._def_entity(QAPISchemaEnumType(name, info, values, None))
return name return name
def _make_array_type(self, element_type): def _make_array_type(self, element_type, info):
# TODO fooList namespace is not reserved; user can create collisions, # TODO fooList namespace is not reserved; user can create collisions,
# or abuse our type system with ['fooList'] for 2D array # or abuse our type system with ['fooList'] for 2D array
name = element_type + 'List' name = element_type + 'List'
if not self.lookup_type(name): if not self.lookup_type(name):
self._def_entity(QAPISchemaArrayType(name, None, element_type)) self._def_entity(QAPISchemaArrayType(name, info, element_type))
return name return name
def _make_implicit_object_type(self, name, role, members): def _make_implicit_object_type(self, name, info, role, members):
if not members: if not members:
return None return None
name = ':obj-%s-%s' % (name, role) name = ':obj-%s-%s' % (name, role)
if not self.lookup_entity(name, QAPISchemaObjectType): if not self.lookup_entity(name, QAPISchemaObjectType):
self._def_entity(QAPISchemaObjectType(name, None, None, self._def_entity(QAPISchemaObjectType(name, info, None,
members, None)) members, None))
return name return name
@ -1197,18 +1217,18 @@ class QAPISchema(object):
prefix = expr.get('prefix') prefix = expr.get('prefix')
self._def_entity(QAPISchemaEnumType(name, info, data, prefix)) self._def_entity(QAPISchemaEnumType(name, info, data, prefix))
def _make_member(self, name, typ): def _make_member(self, name, typ, info):
optional = False optional = False
if name.startswith('*'): if name.startswith('*'):
name = name[1:] name = name[1:]
optional = True optional = True
if isinstance(typ, list): if isinstance(typ, list):
assert len(typ) == 1 assert len(typ) == 1
typ = self._make_array_type(typ[0]) typ = self._make_array_type(typ[0], info)
return QAPISchemaObjectTypeMember(name, typ, optional) return QAPISchemaObjectTypeMember(name, typ, optional)
def _make_members(self, data): def _make_members(self, data, info):
return [self._make_member(key, value) return [self._make_member(key, value, info)
for (key, value) in data.iteritems()] for (key, value) in data.iteritems()]
def _def_struct_type(self, expr, info): def _def_struct_type(self, expr, info):
@ -1216,22 +1236,22 @@ class QAPISchema(object):
base = expr.get('base') base = expr.get('base')
data = expr['data'] data = expr['data']
self._def_entity(QAPISchemaObjectType(name, info, base, self._def_entity(QAPISchemaObjectType(name, info, base,
self._make_members(data), self._make_members(data, info),
None)) None))
def _make_variant(self, case, typ): def _make_variant(self, case, typ):
return QAPISchemaObjectTypeVariant(case, typ) return QAPISchemaObjectTypeVariant(case, typ)
def _make_simple_variant(self, case, typ): def _make_simple_variant(self, case, typ, info):
if isinstance(typ, list): if isinstance(typ, list):
assert len(typ) == 1 assert len(typ) == 1
typ = self._make_array_type(typ[0]) typ = self._make_array_type(typ[0], info)
typ = self._make_implicit_object_type(typ, 'wrapper', typ = self._make_implicit_object_type(
[self._make_member('data', typ)]) typ, info, 'wrapper', [self._make_member('data', typ, info)])
return QAPISchemaObjectTypeVariant(case, typ) return QAPISchemaObjectTypeVariant(case, typ)
def _make_implicit_tag(self, type_name, variants): def _make_implicit_tag(self, type_name, info, variants):
typ = self._make_implicit_enum_type(type_name, typ = self._make_implicit_enum_type(type_name, info,
[v.name for v in variants]) [v.name for v in variants])
return QAPISchemaObjectTypeMember('type', typ, False) return QAPISchemaObjectTypeMember('type', typ, False)
@ -1245,12 +1265,12 @@ class QAPISchema(object):
variants = [self._make_variant(key, value) variants = [self._make_variant(key, value)
for (key, value) in data.iteritems()] for (key, value) in data.iteritems()]
else: else:
variants = [self._make_simple_variant(key, value) variants = [self._make_simple_variant(key, value, info)
for (key, value) in data.iteritems()] for (key, value) in data.iteritems()]
tag_member = self._make_implicit_tag(name, variants) tag_member = self._make_implicit_tag(name, info, variants)
self._def_entity( self._def_entity(
QAPISchemaObjectType(name, info, base, QAPISchemaObjectType(name, info, base,
self._make_members(OrderedDict()), self._make_members(OrderedDict(), info),
QAPISchemaObjectTypeVariants(tag_name, QAPISchemaObjectTypeVariants(tag_name,
tag_member, tag_member,
variants))) variants)))
@ -1260,7 +1280,7 @@ class QAPISchema(object):
data = expr['data'] data = expr['data']
variants = [self._make_variant(key, value) variants = [self._make_variant(key, value)
for (key, value) in data.iteritems()] for (key, value) in data.iteritems()]
tag_member = self._make_implicit_tag(name, variants) tag_member = self._make_implicit_tag(name, info, variants)
self._def_entity( self._def_entity(
QAPISchemaAlternateType(name, info, QAPISchemaAlternateType(name, info,
QAPISchemaObjectTypeVariants(None, QAPISchemaObjectTypeVariants(None,
@ -1274,11 +1294,11 @@ class QAPISchema(object):
gen = expr.get('gen', True) gen = expr.get('gen', True)
success_response = expr.get('success-response', True) success_response = expr.get('success-response', True)
if isinstance(data, OrderedDict): if isinstance(data, OrderedDict):
data = self._make_implicit_object_type(name, 'arg', data = self._make_implicit_object_type(
self._make_members(data)) name, info, 'arg', self._make_members(data, info))
if isinstance(rets, list): if isinstance(rets, list):
assert len(rets) == 1 assert len(rets) == 1
rets = self._make_array_type(rets[0]) rets = self._make_array_type(rets[0], info)
self._def_entity(QAPISchemaCommand(name, info, data, rets, gen, self._def_entity(QAPISchemaCommand(name, info, data, rets, gen,
success_response)) success_response))
@ -1286,8 +1306,8 @@ class QAPISchema(object):
name = expr['event'] name = expr['event']
data = expr.get('data') data = expr.get('data')
if isinstance(data, OrderedDict): if isinstance(data, OrderedDict):
data = self._make_implicit_object_type(name, 'arg', data = self._make_implicit_object_type(
self._make_members(data)) name, info, 'arg', self._make_members(data, info))
self._def_entity(QAPISchemaEvent(name, info, data)) self._def_entity(QAPISchemaEvent(name, info, data))
def _def_exprs(self): def _def_exprs(self):