|
@@ -102,6 +102,24 @@ def __init__(self, info, msg):
|
|
|
|
|
|
|
|
|
|
class QAPIDoc(object):
|
|
class QAPIDoc(object):
|
|
|
|
+ """
|
|
|
|
+ A documentation comment block, either expression or free-form
|
|
|
|
+
|
|
|
|
+ Expression documentation blocks consist of
|
|
|
|
+
|
|
|
|
+ * a body section: one line naming the expression, followed by an
|
|
|
|
+ overview (any number of lines)
|
|
|
|
+
|
|
|
|
+ * argument sections: a description of each argument (for commands
|
|
|
|
+ and events) or member (for structs, unions and alternates)
|
|
|
|
+
|
|
|
|
+ * features sections: a description of each feature flag
|
|
|
|
+
|
|
|
|
+ * additional (non-argument) sections, possibly tagged
|
|
|
|
+
|
|
|
|
+ Free-form documentation blocks consist only of a body section.
|
|
|
|
+ """
|
|
|
|
+
|
|
class Section(object):
|
|
class Section(object):
|
|
def __init__(self, name=None):
|
|
def __init__(self, name=None):
|
|
# optional section name (argument/member or section name)
|
|
# optional section name (argument/member or section name)
|
|
@@ -131,10 +149,12 @@ def __init__(self, parser, info):
|
|
self.body = QAPIDoc.Section()
|
|
self.body = QAPIDoc.Section()
|
|
# dict mapping parameter name to ArgSection
|
|
# dict mapping parameter name to ArgSection
|
|
self.args = OrderedDict()
|
|
self.args = OrderedDict()
|
|
|
|
+ self.features = OrderedDict()
|
|
# a list of Section
|
|
# a list of Section
|
|
self.sections = []
|
|
self.sections = []
|
|
# the current section
|
|
# the current section
|
|
self._section = self.body
|
|
self._section = self.body
|
|
|
|
+ self._append_line = self._append_body_line
|
|
|
|
|
|
def has_section(self, name):
|
|
def has_section(self, name):
|
|
"""Return True if we have a section with this name."""
|
|
"""Return True if we have a section with this name."""
|
|
@@ -144,7 +164,16 @@ def has_section(self, name):
|
|
return False
|
|
return False
|
|
|
|
|
|
def append(self, line):
|
|
def append(self, line):
|
|
- """Parse a comment line and add it to the documentation."""
|
|
|
|
|
|
+ """
|
|
|
|
+ Parse a comment line and add it to the documentation.
|
|
|
|
+
|
|
|
|
+ The way that the line is dealt with depends on which part of
|
|
|
|
+ the documentation we're parsing right now:
|
|
|
|
+ * The body section: ._append_line is ._append_body_line
|
|
|
|
+ * An argument section: ._append_line is ._append_args_line
|
|
|
|
+ * A features section: ._append_line is ._append_features_line
|
|
|
|
+ * An additional section: ._append_line is ._append_various_line
|
|
|
|
+ """
|
|
line = line[1:]
|
|
line = line[1:]
|
|
if not line:
|
|
if not line:
|
|
self._append_freeform(line)
|
|
self._append_freeform(line)
|
|
@@ -153,54 +182,155 @@ def append(self, line):
|
|
if line[0] != ' ':
|
|
if line[0] != ' ':
|
|
raise QAPIParseError(self._parser, "Missing space after #")
|
|
raise QAPIParseError(self._parser, "Missing space after #")
|
|
line = line[1:]
|
|
line = line[1:]
|
|
|
|
+ self._append_line(line)
|
|
|
|
+
|
|
|
|
+ def end_comment(self):
|
|
|
|
+ self._end_section()
|
|
|
|
|
|
|
|
+ @staticmethod
|
|
|
|
+ def _is_section_tag(name):
|
|
|
|
+ return name in ('Returns:', 'Since:',
|
|
|
|
+ # those are often singular or plural
|
|
|
|
+ 'Note:', 'Notes:',
|
|
|
|
+ 'Example:', 'Examples:',
|
|
|
|
+ 'TODO:')
|
|
|
|
+
|
|
|
|
+ def _append_body_line(self, line):
|
|
|
|
+ """
|
|
|
|
+ Process a line of documentation text in the body section.
|
|
|
|
+
|
|
|
|
+ If this a symbol line and it is the section's first line, this
|
|
|
|
+ is an expression documentation block for that symbol.
|
|
|
|
+
|
|
|
|
+ If it's an expression documentation block, another symbol line
|
|
|
|
+ begins the argument section for the argument named by it, and
|
|
|
|
+ a section tag begins an additional section. Start that
|
|
|
|
+ section and append the line to it.
|
|
|
|
+
|
|
|
|
+ Else, append the line to the current section.
|
|
|
|
+ """
|
|
|
|
+ name = line.split(' ', 1)[0]
|
|
# FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
|
|
# FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
|
|
# recognized, and get silently treated as ordinary text
|
|
# recognized, and get silently treated as ordinary text
|
|
- if self.symbol:
|
|
|
|
- self._append_symbol_line(line)
|
|
|
|
- elif not self.body.text and line.startswith('@'):
|
|
|
|
|
|
+ if not self.symbol and not self.body.text and line.startswith('@'):
|
|
if not line.endswith(':'):
|
|
if not line.endswith(':'):
|
|
raise QAPIParseError(self._parser, "Line should end with :")
|
|
raise QAPIParseError(self._parser, "Line should end with :")
|
|
self.symbol = line[1:-1]
|
|
self.symbol = line[1:-1]
|
|
# FIXME invalid names other than the empty string aren't flagged
|
|
# FIXME invalid names other than the empty string aren't flagged
|
|
if not self.symbol:
|
|
if not self.symbol:
|
|
raise QAPIParseError(self._parser, "Invalid name")
|
|
raise QAPIParseError(self._parser, "Invalid name")
|
|
|
|
+ elif self.symbol:
|
|
|
|
+ # This is an expression documentation block
|
|
|
|
+ if name.startswith('@') and name.endswith(':'):
|
|
|
|
+ self._append_line = self._append_args_line
|
|
|
|
+ self._append_args_line(line)
|
|
|
|
+ elif line == 'Features:':
|
|
|
|
+ self._append_line = self._append_features_line
|
|
|
|
+ elif self._is_section_tag(name):
|
|
|
|
+ self._append_line = self._append_various_line
|
|
|
|
+ self._append_various_line(line)
|
|
|
|
+ else:
|
|
|
|
+ self._append_freeform(line.strip())
|
|
else:
|
|
else:
|
|
- self._append_freeform(line)
|
|
|
|
|
|
+ # This is a free-form documentation block
|
|
|
|
+ self._append_freeform(line.strip())
|
|
|
|
|
|
- def end_comment(self):
|
|
|
|
- self._end_section()
|
|
|
|
|
|
+ def _append_args_line(self, line):
|
|
|
|
+ """
|
|
|
|
+ Process a line of documentation text in an argument section.
|
|
|
|
+
|
|
|
|
+ A symbol line begins the next argument section, a section tag
|
|
|
|
+ section or a non-indented line after a blank line begins an
|
|
|
|
+ additional section. Start that section and append the line to
|
|
|
|
+ it.
|
|
|
|
+
|
|
|
|
+ Else, append the line to the current section.
|
|
|
|
|
|
- def _append_symbol_line(self, line):
|
|
|
|
|
|
+ """
|
|
name = line.split(' ', 1)[0]
|
|
name = line.split(' ', 1)[0]
|
|
|
|
|
|
if name.startswith('@') and name.endswith(':'):
|
|
if name.startswith('@') and name.endswith(':'):
|
|
line = line[len(name)+1:]
|
|
line = line[len(name)+1:]
|
|
self._start_args_section(name[1:-1])
|
|
self._start_args_section(name[1:-1])
|
|
- elif name in ('Returns:', 'Since:',
|
|
|
|
- # those are often singular or plural
|
|
|
|
- 'Note:', 'Notes:',
|
|
|
|
- 'Example:', 'Examples:',
|
|
|
|
- 'TODO:'):
|
|
|
|
|
|
+ elif self._is_section_tag(name):
|
|
|
|
+ self._append_line = self._append_various_line
|
|
|
|
+ self._append_various_line(line)
|
|
|
|
+ return
|
|
|
|
+ elif (self._section.text.endswith('\n\n')
|
|
|
|
+ and line and not line[0].isspace()):
|
|
|
|
+ if line == 'Features:':
|
|
|
|
+ self._append_line = self._append_features_line
|
|
|
|
+ else:
|
|
|
|
+ self._start_section()
|
|
|
|
+ self._append_line = self._append_various_line
|
|
|
|
+ self._append_various_line(line)
|
|
|
|
+ return
|
|
|
|
+
|
|
|
|
+ self._append_freeform(line.strip())
|
|
|
|
+
|
|
|
|
+ def _append_features_line(self, line):
|
|
|
|
+ name = line.split(' ', 1)[0]
|
|
|
|
+
|
|
|
|
+ if name.startswith('@') and name.endswith(':'):
|
|
|
|
+ line = line[len(name)+1:]
|
|
|
|
+ self._start_features_section(name[1:-1])
|
|
|
|
+ elif self._is_section_tag(name):
|
|
|
|
+ self._append_line = self._append_various_line
|
|
|
|
+ self._append_various_line(line)
|
|
|
|
+ return
|
|
|
|
+ elif (self._section.text.endswith('\n\n')
|
|
|
|
+ and line and not line[0].isspace()):
|
|
|
|
+ self._start_section()
|
|
|
|
+ self._append_line = self._append_various_line
|
|
|
|
+ self._append_various_line(line)
|
|
|
|
+ return
|
|
|
|
+
|
|
|
|
+ self._append_freeform(line.strip())
|
|
|
|
+
|
|
|
|
+ def _append_various_line(self, line):
|
|
|
|
+ """
|
|
|
|
+ Process a line of documentation text in an additional section.
|
|
|
|
+
|
|
|
|
+ A symbol line is an error.
|
|
|
|
+
|
|
|
|
+ A section tag begins an additional section. Start that
|
|
|
|
+ section and append the line to it.
|
|
|
|
+
|
|
|
|
+ Else, append the line to the current section.
|
|
|
|
+ """
|
|
|
|
+ name = line.split(' ', 1)[0]
|
|
|
|
+
|
|
|
|
+ if name.startswith('@') and name.endswith(':'):
|
|
|
|
+ raise QAPIParseError(self._parser,
|
|
|
|
+ "'%s' can't follow '%s' section"
|
|
|
|
+ % (name, self.sections[0].name))
|
|
|
|
+ elif self._is_section_tag(name):
|
|
line = line[len(name)+1:]
|
|
line = line[len(name)+1:]
|
|
self._start_section(name[:-1])
|
|
self._start_section(name[:-1])
|
|
|
|
|
|
|
|
+ if (not self._section.name or
|
|
|
|
+ not self._section.name.startswith('Example')):
|
|
|
|
+ line = line.strip()
|
|
|
|
+
|
|
self._append_freeform(line)
|
|
self._append_freeform(line)
|
|
|
|
|
|
- def _start_args_section(self, name):
|
|
|
|
|
|
+ def _start_symbol_section(self, symbols_dict, name):
|
|
# FIXME invalid names other than the empty string aren't flagged
|
|
# FIXME invalid names other than the empty string aren't flagged
|
|
if not name:
|
|
if not name:
|
|
raise QAPIParseError(self._parser, "Invalid parameter name")
|
|
raise QAPIParseError(self._parser, "Invalid parameter name")
|
|
- if name in self.args:
|
|
|
|
|
|
+ if name in symbols_dict:
|
|
raise QAPIParseError(self._parser,
|
|
raise QAPIParseError(self._parser,
|
|
"'%s' parameter name duplicated" % name)
|
|
"'%s' parameter name duplicated" % name)
|
|
- if self.sections:
|
|
|
|
- raise QAPIParseError(self._parser,
|
|
|
|
- "'@%s:' can't follow '%s' section"
|
|
|
|
- % (name, self.sections[0].name))
|
|
|
|
|
|
+ assert not self.sections
|
|
self._end_section()
|
|
self._end_section()
|
|
self._section = QAPIDoc.ArgSection(name)
|
|
self._section = QAPIDoc.ArgSection(name)
|
|
- self.args[name] = self._section
|
|
|
|
|
|
+ symbols_dict[name] = self._section
|
|
|
|
+
|
|
|
|
+ def _start_args_section(self, name):
|
|
|
|
+ self._start_symbol_section(self.args, name)
|
|
|
|
+
|
|
|
|
+ def _start_features_section(self, name):
|
|
|
|
+ self._start_symbol_section(self.features, name)
|
|
|
|
|
|
def _start_section(self, name=None):
|
|
def _start_section(self, name=None):
|
|
if name in ('Returns', 'Since') and self.has_section(name):
|
|
if name in ('Returns', 'Since') and self.has_section(name):
|
|
@@ -219,13 +349,6 @@ def _end_section(self):
|
|
self._section = None
|
|
self._section = None
|
|
|
|
|
|
def _append_freeform(self, line):
|
|
def _append_freeform(self, line):
|
|
- in_arg = isinstance(self._section, QAPIDoc.ArgSection)
|
|
|
|
- if (in_arg and self._section.text.endswith('\n\n')
|
|
|
|
- and line and not line[0].isspace()):
|
|
|
|
- self._start_section()
|
|
|
|
- if (in_arg or not self._section.name
|
|
|
|
- or not self._section.name.startswith('Example')):
|
|
|
|
- line = line.strip()
|
|
|
|
match = re.match(r'(@\S+:)', line)
|
|
match = re.match(r'(@\S+:)', line)
|
|
if match:
|
|
if match:
|
|
raise QAPIParseError(self._parser,
|
|
raise QAPIParseError(self._parser,
|
|
@@ -886,12 +1009,26 @@ def check_enum(expr, info):
|
|
def check_struct(expr, info):
|
|
def check_struct(expr, info):
|
|
name = expr['struct']
|
|
name = expr['struct']
|
|
members = expr['data']
|
|
members = expr['data']
|
|
|
|
+ features = expr.get('features')
|
|
|
|
|
|
check_type(info, "'data' for struct '%s'" % name, members,
|
|
check_type(info, "'data' for struct '%s'" % name, members,
|
|
allow_dict=True, allow_optional=True)
|
|
allow_dict=True, allow_optional=True)
|
|
check_type(info, "'base' for struct '%s'" % name, expr.get('base'),
|
|
check_type(info, "'base' for struct '%s'" % name, expr.get('base'),
|
|
allow_metas=['struct'])
|
|
allow_metas=['struct'])
|
|
|
|
|
|
|
|
+ if features:
|
|
|
|
+ if not isinstance(features, list):
|
|
|
|
+ raise QAPISemError(info,
|
|
|
|
+ "Struct '%s' requires an array for 'features'" %
|
|
|
|
+ name)
|
|
|
|
+ for f in features:
|
|
|
|
+ assert isinstance(f, dict)
|
|
|
|
+ check_known_keys(info, "feature of struct %s" % name, f,
|
|
|
|
+ ['name'], ['if'])
|
|
|
|
+
|
|
|
|
+ check_if(f, info)
|
|
|
|
+ check_name(info, "Feature of struct %s" % name, f['name'])
|
|
|
|
+
|
|
|
|
|
|
def check_known_keys(info, source, keys, required, optional):
|
|
def check_known_keys(info, source, keys, required, optional):
|
|
|
|
|
|
@@ -948,6 +1085,12 @@ def normalize_members(members):
|
|
members[key] = {'type': arg}
|
|
members[key] = {'type': arg}
|
|
|
|
|
|
|
|
|
|
|
|
+def normalize_features(features):
|
|
|
|
+ if isinstance(features, list):
|
|
|
|
+ features[:] = [f if isinstance(f, dict) else {'name': f}
|
|
|
|
+ for f in features]
|
|
|
|
+
|
|
|
|
+
|
|
def check_exprs(exprs):
|
|
def check_exprs(exprs):
|
|
global all_names
|
|
global all_names
|
|
|
|
|
|
@@ -986,8 +1129,10 @@ def check_exprs(exprs):
|
|
normalize_members(expr['data'])
|
|
normalize_members(expr['data'])
|
|
elif 'struct' in expr:
|
|
elif 'struct' in expr:
|
|
meta = 'struct'
|
|
meta = 'struct'
|
|
- check_keys(expr_elem, 'struct', ['data'], ['base', 'if'])
|
|
|
|
|
|
+ check_keys(expr_elem, 'struct', ['data'],
|
|
|
|
+ ['base', 'if', 'features'])
|
|
normalize_members(expr['data'])
|
|
normalize_members(expr['data'])
|
|
|
|
+ normalize_features(expr.get('features'))
|
|
struct_types[expr[meta]] = expr
|
|
struct_types[expr[meta]] = expr
|
|
elif 'command' in expr:
|
|
elif 'command' in expr:
|
|
meta = 'command'
|
|
meta = 'command'
|
|
@@ -1126,10 +1271,12 @@ def visit_enum_type(self, name, info, ifcond, members, prefix):
|
|
def visit_array_type(self, name, info, ifcond, element_type):
|
|
def visit_array_type(self, name, info, ifcond, element_type):
|
|
pass
|
|
pass
|
|
|
|
|
|
- def visit_object_type(self, name, info, ifcond, base, members, variants):
|
|
|
|
|
|
+ def visit_object_type(self, name, info, ifcond, base, members, variants,
|
|
|
|
+ features):
|
|
pass
|
|
pass
|
|
|
|
|
|
- def visit_object_type_flat(self, name, info, ifcond, members, variants):
|
|
|
|
|
|
+ def visit_object_type_flat(self, name, info, ifcond, members, variants,
|
|
|
|
+ features):
|
|
pass
|
|
pass
|
|
|
|
|
|
def visit_alternate_type(self, name, info, ifcond, variants):
|
|
def visit_alternate_type(self, name, info, ifcond, variants):
|
|
@@ -1290,7 +1437,7 @@ def visit(self, visitor):
|
|
|
|
|
|
class QAPISchemaObjectType(QAPISchemaType):
|
|
class QAPISchemaObjectType(QAPISchemaType):
|
|
def __init__(self, name, info, doc, ifcond,
|
|
def __init__(self, name, info, doc, ifcond,
|
|
- base, local_members, variants):
|
|
|
|
|
|
+ base, local_members, variants, features):
|
|
# struct has local_members, optional base, and no variants
|
|
# struct has local_members, optional base, and no variants
|
|
# flat union has base, variants, and no local_members
|
|
# flat union has base, variants, and no local_members
|
|
# simple union has local_members, variants, and no base
|
|
# simple union has local_members, variants, and no base
|
|
@@ -1302,11 +1449,15 @@ def __init__(self, name, info, doc, ifcond,
|
|
if variants is not None:
|
|
if variants is not None:
|
|
assert isinstance(variants, QAPISchemaObjectTypeVariants)
|
|
assert isinstance(variants, QAPISchemaObjectTypeVariants)
|
|
variants.set_owner(name)
|
|
variants.set_owner(name)
|
|
|
|
+ for f in features:
|
|
|
|
+ assert isinstance(f, QAPISchemaFeature)
|
|
|
|
+ f.set_owner(name)
|
|
self._base_name = base
|
|
self._base_name = base
|
|
self.base = None
|
|
self.base = None
|
|
self.local_members = local_members
|
|
self.local_members = local_members
|
|
self.variants = variants
|
|
self.variants = variants
|
|
self.members = None
|
|
self.members = None
|
|
|
|
+ self.features = features
|
|
|
|
|
|
def check(self, schema):
|
|
def check(self, schema):
|
|
QAPISchemaType.check(self, schema)
|
|
QAPISchemaType.check(self, schema)
|
|
@@ -1332,6 +1483,12 @@ def check(self, schema):
|
|
self.variants.check(schema, seen)
|
|
self.variants.check(schema, seen)
|
|
assert self.variants.tag_member in self.members
|
|
assert self.variants.tag_member in self.members
|
|
self.variants.check_clash(self.info, seen)
|
|
self.variants.check_clash(self.info, seen)
|
|
|
|
+
|
|
|
|
+ # Features are in a name space separate from members
|
|
|
|
+ seen = {}
|
|
|
|
+ for f in self.features:
|
|
|
|
+ f.check_clash(self.info, seen)
|
|
|
|
+
|
|
if self.doc:
|
|
if self.doc:
|
|
self.doc.check()
|
|
self.doc.check()
|
|
|
|
|
|
@@ -1368,12 +1525,15 @@ def json_type(self):
|
|
|
|
|
|
def visit(self, visitor):
|
|
def visit(self, visitor):
|
|
visitor.visit_object_type(self.name, self.info, self.ifcond,
|
|
visitor.visit_object_type(self.name, self.info, self.ifcond,
|
|
- self.base, self.local_members, self.variants)
|
|
|
|
|
|
+ self.base, self.local_members, self.variants,
|
|
|
|
+ self.features)
|
|
visitor.visit_object_type_flat(self.name, self.info, self.ifcond,
|
|
visitor.visit_object_type_flat(self.name, self.info, self.ifcond,
|
|
- self.members, self.variants)
|
|
|
|
|
|
+ self.members, self.variants,
|
|
|
|
+ self.features)
|
|
|
|
|
|
|
|
|
|
class QAPISchemaMember(object):
|
|
class QAPISchemaMember(object):
|
|
|
|
+ """ Represents object members, enum members and features """
|
|
role = 'member'
|
|
role = 'member'
|
|
|
|
|
|
def __init__(self, name, ifcond=None):
|
|
def __init__(self, name, ifcond=None):
|
|
@@ -1419,6 +1579,10 @@ def describe(self):
|
|
return "'%s' %s" % (self.name, self._pretty_owner())
|
|
return "'%s' %s" % (self.name, self._pretty_owner())
|
|
|
|
|
|
|
|
|
|
|
|
+class QAPISchemaFeature(QAPISchemaMember):
|
|
|
|
+ role = 'feature'
|
|
|
|
+
|
|
|
|
+
|
|
class QAPISchemaObjectTypeMember(QAPISchemaMember):
|
|
class QAPISchemaObjectTypeMember(QAPISchemaMember):
|
|
def __init__(self, name, typ, optional, ifcond=None):
|
|
def __init__(self, name, typ, optional, ifcond=None):
|
|
QAPISchemaMember.__init__(self, name, ifcond)
|
|
QAPISchemaMember.__init__(self, name, ifcond)
|
|
@@ -1675,7 +1839,7 @@ def _def_predefineds(self):
|
|
('null', 'null', 'QNull' + pointer_suffix)]:
|
|
('null', 'null', 'QNull' + pointer_suffix)]:
|
|
self._def_builtin_type(*t)
|
|
self._def_builtin_type(*t)
|
|
self.the_empty_object_type = QAPISchemaObjectType(
|
|
self.the_empty_object_type = QAPISchemaObjectType(
|
|
- 'q_empty', None, None, None, None, [], None)
|
|
|
|
|
|
+ 'q_empty', None, None, None, None, [], None, [])
|
|
self._def_entity(self.the_empty_object_type)
|
|
self._def_entity(self.the_empty_object_type)
|
|
|
|
|
|
qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
|
|
qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
|
|
@@ -1685,6 +1849,9 @@ def _def_predefineds(self):
|
|
self._def_entity(QAPISchemaEnumType('QType', None, None, None,
|
|
self._def_entity(QAPISchemaEnumType('QType', None, None, None,
|
|
qtype_values, 'QTYPE'))
|
|
qtype_values, 'QTYPE'))
|
|
|
|
|
|
|
|
+ def _make_features(self, features):
|
|
|
|
+ return [QAPISchemaFeature(f['name'], f.get('if')) for f in features]
|
|
|
|
+
|
|
def _make_enum_members(self, values):
|
|
def _make_enum_members(self, values):
|
|
return [QAPISchemaMember(v['name'], v.get('if')) for v in values]
|
|
return [QAPISchemaMember(v['name'], v.get('if')) for v in values]
|
|
|
|
|
|
@@ -1721,7 +1888,7 @@ def _make_implicit_object_type(self, name, info, doc, ifcond,
|
|
assert ifcond == typ._ifcond # pylint: disable=protected-access
|
|
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, []))
|
|
return name
|
|
return name
|
|
|
|
|
|
def _def_enum_type(self, expr, info, doc):
|
|
def _def_enum_type(self, expr, info, doc):
|
|
@@ -1752,9 +1919,11 @@ def _def_struct_type(self, expr, info, doc):
|
|
base = expr.get('base')
|
|
base = expr.get('base')
|
|
data = expr['data']
|
|
data = expr['data']
|
|
ifcond = expr.get('if')
|
|
ifcond = expr.get('if')
|
|
|
|
+ features = expr.get('features', [])
|
|
self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, base,
|
|
self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, base,
|
|
self._make_members(data, info),
|
|
self._make_members(data, info),
|
|
- None))
|
|
|
|
|
|
+ None,
|
|
|
|
+ self._make_features(features)))
|
|
|
|
|
|
def _make_variant(self, case, typ, ifcond):
|
|
def _make_variant(self, case, typ, ifcond):
|
|
return QAPISchemaObjectTypeVariant(case, typ, ifcond)
|
|
return QAPISchemaObjectTypeVariant(case, typ, ifcond)
|
|
@@ -1795,7 +1964,7 @@ def _def_union_type(self, expr, info, doc):
|
|
QAPISchemaObjectType(name, info, doc, ifcond, base, members,
|
|
QAPISchemaObjectType(name, info, doc, ifcond, base, members,
|
|
QAPISchemaObjectTypeVariants(tag_name,
|
|
QAPISchemaObjectTypeVariants(tag_name,
|
|
tag_member,
|
|
tag_member,
|
|
- variants)))
|
|
|
|
|
|
+ variants), []))
|
|
|
|
|
|
def _def_alternate_type(self, expr, info, doc):
|
|
def _def_alternate_type(self, expr, info, doc):
|
|
name = expr['alternate']
|
|
name = expr['alternate']
|