qapi: leave the ifcond attribute undefined until check()

We commonly initialize attributes to None in .init(), then set their
real value in .check().  Accessing the attribute before .check()
yields None.  If we're lucky, the code that accesses the attribute
prematurely chokes on None.

It won't for .ifcond, because None is a legitimate value.

Leave the ifcond attribute undefined until check().

Suggested-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <20180703155648.11933-4-marcandre.lureau@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
This commit is contained in:
Marc-André Lureau 2018-07-03 17:56:37 +02:00 committed by Markus Armbruster
parent 2cbc94376e
commit 4fca21c1b0

View File

@ -1021,13 +1021,19 @@ def __init__(self, name, info, doc, ifcond=None):
# such place). # such place).
self.info = info self.info = info
self.doc = doc self.doc = doc
self.ifcond = listify_cond(ifcond) self._ifcond = ifcond # self.ifcond is set only after .check()
def c_name(self): def c_name(self):
return c_name(self.name) return c_name(self.name)
def check(self, schema): def check(self, schema):
pass if isinstance(self._ifcond, QAPISchemaType):
# inherit the condition from a type
typ = self._ifcond
typ.check(schema)
self.ifcond = typ.ifcond
else:
self.ifcond = listify_cond(self._ifcond)
def is_implicit(self): def is_implicit(self):
return not self.info return not self.info
@ -1164,6 +1170,7 @@ def __init__(self, name, info, doc, ifcond, values, prefix):
self.prefix = prefix self.prefix = prefix
def check(self, schema): def check(self, schema):
QAPISchemaType.check(self, schema)
seen = {} seen = {}
for v in self.values: for v in self.values:
v.check_clash(self.info, seen) v.check_clash(self.info, seen)
@ -1196,8 +1203,10 @@ def __init__(self, name, info, element_type):
self.element_type = None self.element_type = None
def check(self, schema): def check(self, schema):
QAPISchemaType.check(self, schema)
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
self.element_type.check(schema)
self.ifcond = self.element_type.ifcond self.ifcond = self.element_type.ifcond
def is_implicit(self): def is_implicit(self):
@ -1240,6 +1249,7 @@ def __init__(self, name, info, doc, ifcond,
self.members = None self.members = None
def check(self, schema): def check(self, schema):
QAPISchemaType.check(self, schema)
if self.members is False: # check for cycles if self.members is False: # check for cycles
raise QAPISemError(self.info, raise QAPISemError(self.info,
"Object %s contains itself" % self.name) "Object %s contains itself" % self.name)
@ -1430,6 +1440,7 @@ def __init__(self, name, info, doc, ifcond, variants):
self.variants = variants self.variants = variants
def check(self, schema): def check(self, schema):
QAPISchemaType.check(self, schema)
self.variants.tag_member.check(schema) self.variants.tag_member.check(schema)
# Not calling self.variants.check_clash(), because there's nothing # Not calling self.variants.check_clash(), because there's nothing
# to clash with # to clash with
@ -1474,6 +1485,7 @@ def __init__(self, name, info, doc, ifcond, arg_type, ret_type,
self.allow_preconfig = allow_preconfig self.allow_preconfig = allow_preconfig
def check(self, schema): def check(self, schema):
QAPISchemaEntity.check(self, schema)
if self._arg_type_name: if self._arg_type_name:
self.arg_type = schema.lookup_type(self._arg_type_name) self.arg_type = schema.lookup_type(self._arg_type_name)
assert (isinstance(self.arg_type, QAPISchemaObjectType) or assert (isinstance(self.arg_type, QAPISchemaObjectType) or
@ -1509,6 +1521,7 @@ def __init__(self, name, info, doc, ifcond, arg_type, boxed):
self.boxed = boxed self.boxed = boxed
def check(self, schema): def check(self, schema):
QAPISchemaEntity.check(self, schema)
if self._arg_type_name: if self._arg_type_name:
self.arg_type = schema.lookup_type(self._arg_type_name) self.arg_type = schema.lookup_type(self._arg_type_name)
assert (isinstance(self.arg_type, QAPISchemaObjectType) or assert (isinstance(self.arg_type, QAPISchemaObjectType) or
@ -1642,7 +1655,7 @@ def _make_implicit_object_type(self, name, info, doc, ifcond,
# But it's not tight: the disjunction need not imply it. We # But it's not tight: the disjunction need not imply it. We
# may end up compiling useless wrapper types. # may end up compiling useless wrapper types.
# TODO kill simple unions or implement the disjunction # TODO kill simple unions or implement the disjunction
assert ifcond == typ.ifcond assert ifcond == typ._ifcond # pylint: disable=protected-access
else: else:
self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond,
None, members, None)) None, members, None))
@ -1688,7 +1701,7 @@ def _make_simple_variant(self, case, typ, info):
assert len(typ) == 1 assert len(typ) == 1
typ = self._make_array_type(typ[0], info) typ = self._make_array_type(typ[0], info)
typ = self._make_implicit_object_type( typ = self._make_implicit_object_type(
typ, info, None, self.lookup_type(typ).ifcond, typ, info, None, self.lookup_type(typ),
'wrapper', [self._make_member('data', typ, info)]) 'wrapper', [self._make_member('data', typ, info)])
return QAPISchemaObjectTypeVariant(case, typ) return QAPISchemaObjectTypeVariant(case, typ)