QAPI patches for 2019-09-24

-----BEGIN PGP SIGNATURE-----
 
 iQJGBAABCAAwFiEENUvIs9frKmtoZ05fOHC0AOuRhlMFAl2KB7wSHGFybWJydUBy
 ZWRoYXQuY29tAAoJEDhwtADrkYZTHAYP+gJ0rd9PH1rLcUA0UvxIy10sPAw+Oghn
 GtsTlKGZzyD1xLLKSLP+zcsJAAxAW0ixGl41vNnPzYQ7iHtNgZQeZKXErbpSz3/S
 4ubmgAHaEVQ2Jo3wzyqCk+21RtsLDi+i4NLBSwUyIlqHCbHecBuOFzHClzeJldxi
 qws+jFKMQtvOM7ShpM0QC2GSpMeUy4pIEqGG4cJ+IdBuya+qXlc1FDkRgzmUEQI7
 otQ79AAkVBfw9rwQrAKGMZHmJWAWQWPRAf3MwDhJuutgIZn26Vv4DfpsjUz/07WR
 ZTYaMKGY3KqVUDkQIBmZEjJ32Ho529DqkDa24vnvqYIYcfd8FiNMGXzXPfb/2/cU
 gX4Ao7B8G1svMtbRJpSIVgfdQqHGMRDl+OFPTn/r78NKKcwTmmhOCwEIH8+fAyQL
 mqUIABVX/RXSvmeGuhCrUqCCt+23VipIcXUUFFIOvtbpHXjaVKP+RDMjiQwgjYrj
 wYPa+dK1RwKVSH2yxGvLBm2qhkl3XFVsoDiZ2jgOK4vZw1L8ObM9fn04eLvonvXz
 oalfKA28k2ux2FpwYU9Xpc/T4EyRWxhoUKnV6pjZ2Y08jWb1QN+v3T/q8ehxRXJd
 /iVaCaBpIW7fMwC+TceOIWM/iBILgJL5H1IRnPmcs0NExo7RCOtkNEPY0n3WGWKq
 +RRAlYx2I5yk
 =1oxz
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/armbru/tags/pull-qapi-2019-09-24' into staging

QAPI patches for 2019-09-24

# gpg: Signature made Tue 24 Sep 2019 13:10:36 BST
# gpg:                using RSA key 354BC8B3D7EB2A6B68674E5F3870B400EB918653
# gpg:                issuer "armbru@redhat.com"
# gpg: Good signature from "Markus Armbruster <armbru@redhat.com>" [full]
# gpg:                 aka "Markus Armbruster <armbru@pond.sub.org>" [full]
# Primary key fingerprint: 354B C8B3 D7EB 2A6B 6867  4E5F 3870 B400 EB91 8653

* remotes/armbru/tags/pull-qapi-2019-09-24: (37 commits)
  qapi: Assert .visit() and .check_clash() run only after .check()
  qapi: Fix excessive QAPISchemaEntity.check() recursion
  qapi: Fix to .check() empty structs just once
  qapi: Delete useless check_exprs() code for simple union kind
  qapi: Clean up around check_known_keys()
  qapi: Simplify check_keys()
  qapi: Normalize 'if' in check_exprs(), like other sugar
  qapi: Fix missing 'if' checks in struct, union, alternate 'data'
  qapi: Reject blank 'if' conditions in addition to empty ones
  qapi: Fix broken discriminator error messages
  qapi: Remove null from schema language
  qapi: Improve reporting of lexical errors
  qapi: Use quotes more consistently in frontend error messages
  tests/qapi-schema: Demonstrate suboptimal lexical errors
  tests/qapi-schema: Demonstrate insufficient 'if' checking
  tests/qapi-schema: Demonstrate broken discriminator errors
  tests/qapi-schema: Demonstrate misleading optional tag error
  tests/qapi-schema: Delete two redundant tests
  tests/qapi-schema: Cover unknown pragma
  qapi: Tweak code to match docs/devel/qapi-code-gen.txt
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2019-09-25 13:31:21 +01:00
commit 4142b011ca
111 changed files with 1194 additions and 1008 deletions

File diff suppressed because it is too large Load Diff

View File

@ -364,10 +364,10 @@ void visit_start_list(Visitor *v, const char *name, GenericList **list,
* @tail must not be NULL; on the first call, @tail is the value of
* *list after visit_start_list(), and on subsequent calls @tail must
* be the previously returned value. Should be called in a loop until
* a NULL return or error occurs; for each non-NULL return, the caller
* then calls the appropriate visit_type_*() for the element type of
* the list, with that function's name parameter set to NULL and obj
* set to the address of @tail->value.
* a NULL return; for each non-NULL return, the caller then calls the
* appropriate visit_type_*() for the element type of the list, with
* that function's name parameter set to NULL and obj set to the
* address of @tail->value.
*/
GenericList *visit_next_list(Visitor *v, GenericList *tail, size_t size);

View File

@ -24,7 +24,8 @@ enum ListMode
{
LM_NONE, /* not traversing a list of repeated options */
LM_IN_PROGRESS, /* opts_next_list() ready to be called.
LM_IN_PROGRESS, /*
* opts_next_list() ready to be called.
*
* Generating the next list link will consume the most
* recently parsed QemuOpt instance of the repeated
@ -36,7 +37,8 @@ enum ListMode
* LM_UNSIGNED_INTERVAL.
*/
LM_SIGNED_INTERVAL, /* opts_next_list() has been called.
LM_SIGNED_INTERVAL, /*
* opts_next_list() has been called.
*
* Generating the next list link will consume the most
* recently stored element from the signed interval,
@ -48,7 +50,14 @@ enum ListMode
* next element of the signed interval.
*/
LM_UNSIGNED_INTERVAL /* Same as above, only for an unsigned interval. */
LM_UNSIGNED_INTERVAL, /* Same as above, only for an unsigned interval. */
LM_TRAVERSED /*
* opts_next_list() has been called.
*
* No more QemuOpt instance in the list.
* The traversal has been completed.
*/
};
typedef enum ListMode ListMode;
@ -238,6 +247,8 @@ opts_next_list(Visitor *v, GenericList *tail, size_t size)
OptsVisitor *ov = to_ov(v);
switch (ov->list_mode) {
case LM_TRAVERSED:
return NULL;
case LM_SIGNED_INTERVAL:
case LM_UNSIGNED_INTERVAL:
if (ov->list_mode == LM_SIGNED_INTERVAL) {
@ -258,6 +269,8 @@ opts_next_list(Visitor *v, GenericList *tail, size_t size)
opt = g_queue_pop_head(ov->repeated_opts);
if (g_queue_is_empty(ov->repeated_opts)) {
g_hash_table_remove(ov->unprocessed_opts, opt->name);
ov->repeated_opts = NULL;
ov->list_mode = LM_TRAVERSED;
return NULL;
}
break;
@ -289,7 +302,8 @@ opts_end_list(Visitor *v, void **obj)
assert(ov->list_mode == LM_IN_PROGRESS ||
ov->list_mode == LM_SIGNED_INTERVAL ||
ov->list_mode == LM_UNSIGNED_INTERVAL);
ov->list_mode == LM_UNSIGNED_INTERVAL ||
ov->list_mode == LM_TRAVERSED);
ov->repeated_opts = NULL;
ov->list_mode = LM_NONE;
}
@ -306,6 +320,10 @@ lookup_scalar(const OptsVisitor *ov, const char *name, Error **errp)
list = lookup_distinct(ov, name, errp);
return list ? g_queue_peek_tail(list) : NULL;
}
if (ov->list_mode == LM_TRAVERSED) {
error_setg(errp, "Fewer list elements than expected");
return NULL;
}
assert(ov->list_mode == LM_IN_PROGRESS);
return g_queue_peek_head(ov->repeated_opts);
}

View File

@ -19,11 +19,11 @@ Makefile*
*.mak
# qapi schema
*.json
qapi/*.json
qga/*.json
# headers
*.h
# code
*.c

View File

@ -30,7 +30,7 @@ def gen_call(name, arg_type, boxed, ret_type):
argstr = ''
if boxed:
assert arg_type and not arg_type.is_empty()
assert arg_type
argstr = '&arg, '
elif arg_type:
assert not arg_type.variants
@ -96,7 +96,7 @@ def gen_marshal_decl(name):
def gen_marshal(name, arg_type, boxed, ret_type):
have_args = arg_type and not arg_type.is_empty()
have_args = boxed or (arg_type and not arg_type.is_empty())
ret = mcgen('''

View File

@ -103,11 +103,11 @@ class QAPISemError(QAPIError):
class QAPIDoc(object):
"""
A documentation comment block, either expression or free-form
A documentation comment block, either definition or free-form
Expression documentation blocks consist of
Definition documentation blocks consist of
* a body section: one line naming the expression, followed by an
* a body section: one line naming the definition, followed by an
overview (any number of lines)
* argument sections: a description of each argument (for commands
@ -200,9 +200,9 @@ class QAPIDoc(object):
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.
is a definition documentation block for that symbol.
If it's an expression documentation block, another symbol line
If it's a definition 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.
@ -214,13 +214,13 @@ class QAPIDoc(object):
# recognized, and get silently treated as ordinary text
if not self.symbol and not self.body.text and line.startswith('@'):
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]
# FIXME invalid names other than the empty string aren't flagged
if not self.symbol:
raise QAPIParseError(self._parser, "Invalid name")
elif self.symbol:
# This is an expression documentation block
# This is a definition documentation block
if name.startswith('@') and name.endswith(':'):
self._append_line = self._append_args_line
self._append_args_line(line)
@ -428,7 +428,7 @@ class QAPISchemaParser(object):
pragma = expr['pragma']
if not isinstance(pragma, dict):
raise QAPISemError(
info, "Value of 'pragma' must be a dictionary")
info, "Value of 'pragma' must be an object")
for name, value in pragma.items():
self._pragma(name, value, info)
else:
@ -437,7 +437,7 @@ class QAPISchemaParser(object):
if cur_doc:
if not cur_doc.symbol:
raise QAPISemError(
cur_doc.info, "Expression documentation required")
cur_doc.info, "Definition documentation required")
expr_elem['doc'] = cur_doc
self.exprs.append(expr_elem)
cur_doc = None
@ -470,7 +470,7 @@ class QAPISchemaParser(object):
else:
fobj = open(incl_fname, 'r')
except IOError as e:
raise QAPISemError(info, '%s: %s' % (e.strerror, incl_fname))
raise QAPISemError(info, "%s: %s" % (e.strerror, incl_fname))
return QAPISchemaParser(fobj, previously_included, info)
def _pragma(self, name, value, info):
@ -515,57 +515,31 @@ class QAPISchemaParser(object):
elif self.tok in '{}:,[]':
return
elif self.tok == "'":
# Note: we accept only printable ASCII
string = ''
esc = False
while True:
ch = self.src[self.cursor]
self.cursor += 1
if ch == '\n':
raise QAPIParseError(self, 'Missing terminating "\'"')
raise QAPIParseError(self, "Missing terminating \"'\"")
if esc:
if ch == 'b':
string += '\b'
elif ch == 'f':
string += '\f'
elif ch == 'n':
string += '\n'
elif ch == 'r':
string += '\r'
elif ch == 't':
string += '\t'
elif ch == 'u':
value = 0
for _ in range(0, 4):
ch = self.src[self.cursor]
self.cursor += 1
if ch not in '0123456789abcdefABCDEF':
raise QAPIParseError(self,
'\\u escape needs 4 '
'hex digits')
value = (value << 4) + int(ch, 16)
# If Python 2 and 3 didn't disagree so much on
# how to handle Unicode, then we could allow
# Unicode string defaults. But most of QAPI is
# ASCII-only, so we aren't losing much for now.
if not value or value > 0x7f:
raise QAPIParseError(self,
'For now, \\u escape '
'only supports non-zero '
'values up to \\u007f')
string += chr(value)
elif ch in '\\/\'"':
string += ch
else:
# Note: we recognize only \\ because we have
# no use for funny characters in strings
if ch != '\\':
raise QAPIParseError(self,
"Unknown escape \\%s" % ch)
esc = False
elif ch == '\\':
esc = True
continue
elif ch == "'":
self.val = string
return
else:
string += ch
if ord(ch) < 32 or ord(ch) >= 127:
raise QAPIParseError(
self, "Funny character in string")
string += ch
elif self.src.startswith('true', self.pos):
self.val = True
self.cursor += 3
@ -574,10 +548,6 @@ class QAPISchemaParser(object):
self.val = False
self.cursor += 4
return
elif self.src.startswith('null', self.pos):
self.val = None
self.cursor += 3
return
elif self.tok == '\n':
if self.cursor == len(self.src):
self.tok = None
@ -585,7 +555,11 @@ class QAPISchemaParser(object):
self.line += 1
self.line_pos = self.cursor
elif not self.tok.isspace():
raise QAPIParseError(self, 'Stray "%s"' % self.tok)
# Show up to next structural, whitespace or quote
# character
match = re.match('[^[\\]{}:,\\s\'"]+',
self.src[self.cursor-1:])
raise QAPIParseError(self, "Stray '%s'" % match.group(0))
def get_members(self):
expr = OrderedDict()
@ -593,24 +567,24 @@ class QAPISchemaParser(object):
self.accept()
return expr
if self.tok != "'":
raise QAPIParseError(self, 'Expected string or "}"')
raise QAPIParseError(self, "Expected string or '}'")
while True:
key = self.val
self.accept()
if self.tok != ':':
raise QAPIParseError(self, 'Expected ":"')
raise QAPIParseError(self, "Expected ':'")
self.accept()
if key in expr:
raise QAPIParseError(self, 'Duplicate key "%s"' % key)
raise QAPIParseError(self, "Duplicate key '%s'" % key)
expr[key] = self.get_expr(True)
if self.tok == '}':
self.accept()
return expr
if self.tok != ',':
raise QAPIParseError(self, 'Expected "," or "}"')
raise QAPIParseError(self, "Expected ',' or '}'")
self.accept()
if self.tok != "'":
raise QAPIParseError(self, 'Expected string')
raise QAPIParseError(self, "Expected string")
def get_values(self):
expr = []
@ -618,20 +592,20 @@ class QAPISchemaParser(object):
self.accept()
return expr
if self.tok not in "{['tfn":
raise QAPIParseError(self, 'Expected "{", "[", "]", string, '
'boolean or "null"')
raise QAPIParseError(
self, "Expected '{', '[', ']', string, boolean or 'null'")
while True:
expr.append(self.get_expr(True))
if self.tok == ']':
self.accept()
return expr
if self.tok != ',':
raise QAPIParseError(self, 'Expected "," or "]"')
raise QAPIParseError(self, "Expected ',' or ']'")
self.accept()
def get_expr(self, nested):
if self.tok != '{' and not nested:
raise QAPIParseError(self, 'Expected "{"')
raise QAPIParseError(self, "Expected '{'")
if self.tok == '{':
self.accept()
expr = self.get_members()
@ -642,8 +616,8 @@ class QAPISchemaParser(object):
expr = self.val
self.accept()
else:
raise QAPIParseError(self, 'Expected "{", "[", string, '
'boolean or "null"')
raise QAPIParseError(
self, "Expected '{', '[', string, boolean or 'null'")
return expr
def get_doc(self, info):
@ -698,26 +672,6 @@ def find_alternate_member_qtype(qapi_type):
return None
# Return the discriminator enum define if discriminator is specified as an
# enum type, otherwise return None.
def discriminator_find_enum_define(expr):
base = expr.get('base')
discriminator = expr.get('discriminator')
if not (discriminator and base):
return None
base_members = find_base_members(base)
if not base_members:
return None
discriminator_value = base_members.get(discriminator)
if not discriminator_value:
return None
return enum_types.get(discriminator_value['type'])
# 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,7 +702,7 @@ def check_name(info, source, name, allow_optional=False,
raise QAPISemError(info, "%s uses invalid name '%s'" % (source, name))
def add_name(name, info, meta, implicit=False):
def add_name(name, info, meta):
global all_names
check_name(info, "'%s'" % meta, name)
# FIXME should reject names that differ only in '_' vs. '.'
@ -756,7 +710,7 @@ def add_name(name, info, meta, implicit=False):
if name in all_names:
raise QAPISemError(info, "%s '%s' is already defined"
% (all_names[name], name))
if not implicit and (name.endswith('Kind') or name.endswith('List')):
if name.endswith('Kind') or name.endswith('List'):
raise QAPISemError(info, "%s '%s' should not end in '%s'"
% (meta, name, name[-4:]))
all_names[name] = meta
@ -768,8 +722,9 @@ def check_if(expr, info):
if not isinstance(ifcond, str):
raise QAPISemError(
info, "'if' condition must be a string or a list of strings")
if ifcond == '':
raise QAPISemError(info, "'if' condition '' makes no sense")
if ifcond.strip() == '':
raise QAPISemError(info, "'if' condition '%s' makes no sense"
% ifcond)
ifcond = expr.get('if')
if ifcond is None:
@ -783,9 +738,8 @@ def check_if(expr, info):
check_if_str(ifcond, info)
def check_type(info, source, value, allow_array=False,
allow_dict=False, allow_optional=False,
allow_metas=[]):
def check_type(info, source, value,
allow_array=False, allow_dict=False, allow_metas=[]):
global all_names
if value is None:
@ -816,12 +770,12 @@ def check_type(info, source, value, allow_array=False,
if not isinstance(value, OrderedDict):
raise QAPISemError(info,
"%s should be a dictionary or type name" % source)
"%s should be an object or type name" % source)
# value is a dictionary, check that each member is okay
for (key, arg) in value.items():
check_name(info, "Member of %s" % source, key,
allow_optional=allow_optional)
allow_optional=True)
if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
raise QAPISemError(info, "Member of %s uses reserved name '%s'"
% (source, key))
@ -829,6 +783,8 @@ def check_type(info, source, value, allow_array=False,
# an optional argument.
check_known_keys(info, "member '%s' of %s" % (key, source),
arg, ['type'], ['if'])
check_if(arg, info)
normalize_if(arg)
check_type(info, "Member '%s' of %s" % (key, source),
arg['type'], allow_array=True,
allow_metas=['built-in', 'union', 'alternate', 'struct',
@ -841,16 +797,16 @@ def check_command(expr, info):
args_meta = ['struct']
if boxed:
args_meta += ['union', 'alternate']
args_meta += ['union']
check_type(info, "'data' for command '%s'" % name,
expr.get('data'), allow_dict=not boxed, allow_optional=True,
expr.get('data'), allow_dict=not boxed,
allow_metas=args_meta)
returns_meta = ['union', 'struct']
if name in returns_whitelist:
returns_meta += ['built-in', 'alternate', 'enum']
check_type(info, "'returns' for command '%s'" % name,
expr.get('returns'), allow_array=True,
allow_optional=True, allow_metas=returns_meta)
allow_metas=returns_meta)
def check_event(expr, info):
@ -859,9 +815,9 @@ def check_event(expr, info):
meta = ['struct']
if boxed:
meta += ['union', 'alternate']
meta += ['union']
check_type(info, "'data' for event '%s'" % name,
expr.get('data'), allow_dict=not boxed, allow_optional=True,
expr.get('data'), allow_dict=not boxed,
allow_metas=meta)
@ -879,7 +835,7 @@ def check_union(expr, info):
# With no discriminator it is a simple union.
if discriminator is None:
enum_define = None
enum_values = members.keys()
allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
if base is not None:
raise QAPISemError(info, "Simple union '%s' must not have a base" %
@ -889,7 +845,7 @@ def check_union(expr, info):
else:
# The object must have a string or dictionary 'base'.
check_type(info, "'base' for union '%s'" % name,
base, allow_dict=True, allow_optional=True,
base, allow_dict=True,
allow_metas=['struct'])
if not base:
raise QAPISemError(info, "Flat union '%s' must have a base"
@ -904,29 +860,32 @@ def check_union(expr, info):
discriminator_value = base_members.get(discriminator)
if not discriminator_value:
raise QAPISemError(info,
"Discriminator '%s' is not a member of base "
"struct '%s'"
% (discriminator, base))
"Discriminator '%s' is not a member of 'base'"
% discriminator)
if discriminator_value.get('if'):
raise QAPISemError(info, 'The discriminator %s.%s for union %s '
'must not be conditional' %
(base, discriminator, name))
raise QAPISemError(
info,
"The discriminator '%s' for union %s must not be conditional"
% (discriminator, name))
enum_define = enum_types.get(discriminator_value['type'])
allow_metas = ['struct']
# 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)
# Check every branch; don't allow an empty union
if len(members) == 0:
raise QAPISemError(info, "Union '%s' cannot have empty 'data'" % name)
for (key, value) in members.items():
check_name(info, "Member of union '%s'" % name, key)
check_known_keys(info, "member '%s' of union '%s'" % (key, name),
value, ['type'], ['if'])
check_if(value, info)
normalize_if(value)
# Each value must name a known type
check_type(info, "Member '%s' of union '%s'" % (key, name),
value['type'],
@ -934,8 +893,8 @@ def check_union(expr, info):
# If the discriminator names an enum type, then all members
# of 'data' must also be members of the enum type.
if enum_define:
if key not in enum_get_names(enum_define):
if discriminator is not None:
if key not in enum_values:
raise QAPISemError(info,
"Discriminator value '%s' is not found in "
"enum '%s'"
@ -947,16 +906,16 @@ def check_alternate(expr, info):
members = expr['data']
types_seen = {}
# Check every branch; require at least two branches
if len(members) < 2:
if len(members) == 0:
raise QAPISemError(info,
"Alternate '%s' should have at least two branches "
"in 'data'" % name)
"Alternate '%s' cannot have empty 'data'" % name)
for (key, value) in members.items():
check_name(info, "Member of alternate '%s'" % name, key)
check_known_keys(info,
"member '%s' of alternate '%s'" % (key, name),
value, ['type'], ['if'])
check_if(value, info)
normalize_if(value)
typ = value['type']
# Ensure alternates have no type conflicts.
@ -999,9 +958,10 @@ def check_enum(expr, info):
"Enum '%s' requires a string for 'prefix'" % name)
for member in members:
source = "dictionary member of enum '%s'" % name
check_known_keys(info, source, member, ['name'], ['if'])
check_known_keys(info, "member of enum '%s'" % name, member,
['name'], ['if'])
check_if(member, info)
normalize_if(member)
check_name(info, "Member of enum '%s'" % name, member['name'],
enum_member=True)
@ -1012,7 +972,7 @@ def check_struct(expr, info):
features = expr.get('features')
check_type(info, "'data' for struct '%s'" % name, members,
allow_dict=True, allow_optional=True)
allow_dict=True)
check_type(info, "'base' for struct '%s'" % name, expr.get('base'),
allow_metas=['struct'])
@ -1027,36 +987,35 @@ def check_struct(expr, info):
['name'], ['if'])
check_if(f, info)
normalize_if(f)
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, value, required, optional):
def pprint(elems):
return ', '.join("'" + e + "'" for e in sorted(elems))
missing = set(required) - set(keys)
missing = set(required) - set(value)
if missing:
raise QAPISemError(info, "Key%s %s %s missing from %s"
% ('s' if len(missing) > 1 else '', pprint(missing),
'are' if len(missing) > 1 else 'is', source))
allowed = set(required + optional)
unknown = set(keys) - allowed
unknown = set(value) - allowed
if unknown:
raise QAPISemError(info, "Unknown key%s %s in %s\nValid keys are %s."
% ('s' if len(unknown) > 1 else '', pprint(unknown),
source, pprint(allowed)))
def check_keys(expr_elem, meta, required, optional=[]):
expr = expr_elem['expr']
info = expr_elem['info']
def check_keys(expr, info, meta, required, optional=[]):
name = expr[meta]
if not isinstance(name, str):
raise QAPISemError(info, "'%s' key must have a string value" % meta)
required = required + [meta]
source = "%s '%s'" % (meta, name)
check_known_keys(info, source, expr.keys(), required, optional)
check_known_keys(info, source, expr, required, optional)
for (key, value) in expr.items():
if key in ['gen', 'success-response'] and value is not False:
raise QAPISemError(info,
@ -1091,6 +1050,12 @@ def normalize_features(features):
for f in features]
def normalize_if(expr):
ifcond = expr.get('if')
if isinstance(ifcond, str):
expr['if'] = [ifcond]
def check_exprs(exprs):
global all_names
@ -1109,65 +1074,50 @@ def check_exprs(exprs):
if not doc and doc_required:
raise QAPISemError(info,
"Expression missing documentation comment")
"Definition missing documentation comment")
if 'enum' in expr:
meta = 'enum'
check_keys(expr_elem, 'enum', ['data'], ['if', 'prefix'])
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_elem, 'union', ['data'],
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_elem, 'alternate', ['data'], ['if'])
check_keys(expr, info, 'alternate', ['data'], ['if'])
normalize_members(expr['data'])
elif 'struct' in expr:
meta = 'struct'
check_keys(expr_elem, 'struct', ['data'],
check_keys(expr, info, 'struct', ['data'],
['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_elem, 'command', [],
check_keys(expr, info, 'command', [],
['data', 'returns', 'gen', 'success-response',
'boxed', 'allow-oob', 'allow-preconfig', 'if'])
normalize_members(expr.get('data'))
elif 'event' in expr:
meta = 'event'
check_keys(expr_elem, 'event', [], ['data', 'boxed', 'if'])
check_keys(expr, info, 'event', [], ['data', 'boxed', 'if'])
normalize_members(expr.get('data'))
else:
raise QAPISemError(expr_elem['info'],
"Expression is missing metatype")
raise QAPISemError(info, "Expression is missing metatype")
normalize_if(expr)
name = expr[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))
# Try again for hidden UnionKind enum
for expr_elem in exprs:
expr = expr_elem['expr']
if 'include' in expr:
continue
if 'union' in expr and not discriminator_find_enum_define(expr):
name = '%sKind' % expr['union']
elif 'alternate' in expr:
name = '%sKind' % expr['alternate']
else:
continue
enum_types[name] = {'enum': name}
add_name(name, info, 'enum', implicit=True)
# Validate that exprs make sense
for expr_elem in exprs:
expr = expr_elem['expr']
@ -1201,19 +1151,11 @@ def check_exprs(exprs):
# Schema compiler frontend
#
def listify_cond(ifcond):
if not ifcond:
return []
if not isinstance(ifcond, list):
return [ifcond]
return ifcond
class QAPISchemaEntity(object):
def __init__(self, name, info, doc, ifcond=None):
assert name is None or isinstance(name, str)
self.name = name
self.module = None
self._module = None
# 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
@ -1221,28 +1163,34 @@ class QAPISchemaEntity(object):
# such place).
self.info = info
self.doc = doc
self._ifcond = ifcond # self.ifcond is set only after .check()
self._ifcond = ifcond or []
self._checked = False
def c_name(self):
return c_name(self.name)
def check(self, schema):
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)
assert not self._checked
if self.info:
self.module = os.path.relpath(self.info['file'],
os.path.dirname(schema.fname))
self._module = os.path.relpath(self.info['file'],
os.path.dirname(schema.fname))
self._checked = True
@property
def ifcond(self):
assert self._checked
return self._ifcond
@property
def module(self):
assert self._checked
return self._module
def is_implicit(self):
return not self.info
def visit(self, visitor):
pass
assert self._checked
class QAPISchemaVisitor(object):
@ -1297,6 +1245,7 @@ class QAPISchemaInclude(QAPISchemaEntity):
self.fname = fname
def visit(self, visitor):
QAPISchemaEntity.visit(self, visitor)
visitor.visit_include(self.fname, self.info)
@ -1361,6 +1310,7 @@ class QAPISchemaBuiltinType(QAPISchemaType):
return self.json_type()
def visit(self, visitor):
QAPISchemaType.visit(self, visitor)
visitor.visit_builtin_type(self.name, self.info, self.json_type())
@ -1368,7 +1318,7 @@ class QAPISchemaEnumType(QAPISchemaType):
def __init__(self, name, info, doc, ifcond, members, prefix):
QAPISchemaType.__init__(self, name, info, doc, ifcond)
for m in members:
assert isinstance(m, QAPISchemaMember)
assert isinstance(m, QAPISchemaEnumMember)
m.set_owner(name)
assert prefix is None or isinstance(prefix, str)
self.members = members
@ -1396,6 +1346,7 @@ class QAPISchemaEnumType(QAPISchemaType):
return 'string'
def visit(self, visitor):
QAPISchemaType.visit(self, visitor)
visitor.visit_enum_type(self.name, self.info, self.ifcond,
self.members, self.prefix)
@ -1411,9 +1362,16 @@ class QAPISchemaArrayType(QAPISchemaType):
QAPISchemaType.check(self, schema)
self.element_type = schema.lookup_type(self._element_type_name)
assert self.element_type
self.element_type.check(schema)
self.module = self.element_type.module
self.ifcond = self.element_type.ifcond
@property
def ifcond(self):
assert self._checked
return self.element_type.ifcond
@property
def module(self):
assert self._checked
return self.element_type.module
def is_implicit(self):
return True
@ -1431,6 +1389,7 @@ class QAPISchemaArrayType(QAPISchemaType):
return 'array of ' + elt_doc_type
def visit(self, visitor):
QAPISchemaType.visit(self, visitor)
visitor.visit_array_type(self.name, self.info, self.ifcond,
self.element_type)
@ -1460,13 +1419,20 @@ class QAPISchemaObjectType(QAPISchemaType):
self.features = features
def check(self, schema):
QAPISchemaType.check(self, schema)
if self.members is False: # check for cycles
# This calls another type T's .check() exactly when the C
# struct emitted by gen_object() contains that T's C struct
# (pointers don't count).
if self.members is not None:
# A previous .check() completed: nothing to do
return
if self._checked:
# Recursed: C struct contains itself
raise QAPISemError(self.info,
"Object %s contains itself" % self.name)
if self.members:
return
self.members = False # mark as being checked
QAPISchemaType.check(self, schema)
assert self._checked and self.members is None
seen = OrderedDict()
if self._base_name:
self.base = schema.lookup_type(self._base_name)
@ -1478,10 +1444,11 @@ class QAPISchemaObjectType(QAPISchemaType):
m.check_clash(self.info, seen)
if self.doc:
self.doc.connect_member(m)
self.members = seen.values()
members = seen.values()
if self.variants:
self.variants.check(schema, seen)
assert self.variants.tag_member in self.members
assert self.variants.tag_member in members
self.variants.check_clash(self.info, seen)
# Features are in a name space separate from members
@ -1492,14 +1459,26 @@ class QAPISchemaObjectType(QAPISchemaType):
if self.doc:
self.doc.check()
self.members = members # mark completed
# Check that the members of this type do not cause duplicate JSON members,
# and update seen to track the members seen so far. Report any errors
# on behalf of info, which is not necessarily self.info
def check_clash(self, info, seen):
assert self._checked
assert not self.variants # not implemented
for m in self.members:
m.check_clash(info, seen)
@property
def ifcond(self):
assert self._checked
if isinstance(self._ifcond, QAPISchemaType):
# Simple union wrapper type inherits from wrapped type;
# see _make_implicit_object_type()
return self._ifcond.ifcond
return self._ifcond
def is_implicit(self):
# See QAPISchema._make_implicit_object_type(), as well as
# _def_predefineds()
@ -1524,6 +1503,7 @@ class QAPISchemaObjectType(QAPISchemaType):
return 'object'
def visit(self, visitor):
QAPISchemaType.visit(self, visitor)
visitor.visit_object_type(self.name, self.info, self.ifcond,
self.base, self.local_members, self.variants,
self.features)
@ -1539,7 +1519,7 @@ class QAPISchemaMember(object):
def __init__(self, name, ifcond=None):
assert isinstance(name, str)
self.name = name
self.ifcond = listify_cond(ifcond)
self.ifcond = ifcond or []
self.owner = None
def set_owner(self, name):
@ -1579,6 +1559,10 @@ class QAPISchemaMember(object):
return "'%s' %s" % (self.name, self._pretty_owner())
class QAPISchemaEnumMember(QAPISchemaMember):
role = 'value'
class QAPISchemaFeature(QAPISchemaMember):
role = 'feature'
@ -1607,7 +1591,6 @@ class QAPISchemaObjectTypeVariants(object):
assert bool(tag_member) != bool(tag_name)
assert (isinstance(tag_name, str) or
isinstance(tag_member, QAPISchemaObjectTypeMember))
assert len(variants) > 0
for v in variants:
assert isinstance(v, QAPISchemaObjectTypeVariant)
self._tag_name = tag_name
@ -1688,12 +1671,10 @@ class QAPISchemaAlternateType(QAPISchemaType):
return 'value'
def visit(self, visitor):
QAPISchemaType.visit(self, visitor)
visitor.visit_alternate_type(self.name, self.info, self.ifcond,
self.variants)
def is_empty(self):
return False
class QAPISchemaCommand(QAPISchemaEntity):
def __init__(self, name, info, doc, ifcond, arg_type, ret_type,
@ -1715,16 +1696,8 @@ class QAPISchemaCommand(QAPISchemaEntity):
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) or
isinstance(self.arg_type, QAPISchemaAlternateType))
self.arg_type.check(schema)
if self.boxed:
if self.arg_type.is_empty():
raise QAPISemError(self.info,
"Cannot use 'boxed' with empty type")
else:
assert not isinstance(self.arg_type, QAPISchemaAlternateType)
assert not self.arg_type.variants
assert isinstance(self.arg_type, QAPISchemaObjectType)
assert not self.arg_type.variants or self.boxed
elif self.boxed:
raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
if self._ret_type_name:
@ -1732,6 +1705,7 @@ class QAPISchemaCommand(QAPISchemaEntity):
assert isinstance(self.ret_type, QAPISchemaType)
def visit(self, visitor):
QAPISchemaEntity.visit(self, visitor)
visitor.visit_command(self.name, self.info, self.ifcond,
self.arg_type, self.ret_type,
self.gen, self.success_response,
@ -1751,20 +1725,13 @@ class QAPISchemaEvent(QAPISchemaEntity):
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) or
isinstance(self.arg_type, QAPISchemaAlternateType))
self.arg_type.check(schema)
if self.boxed:
if self.arg_type.is_empty():
raise QAPISemError(self.info,
"Cannot use 'boxed' with empty type")
else:
assert not isinstance(self.arg_type, QAPISchemaAlternateType)
assert not self.arg_type.variants
assert isinstance(self.arg_type, QAPISchemaObjectType)
assert not self.arg_type.variants or self.boxed
elif self.boxed:
raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
def visit(self, visitor):
QAPISchemaEntity.visit(self, visitor)
visitor.visit_event(self.name, self.info, self.ifcond,
self.arg_type, self.boxed)
@ -1853,7 +1820,8 @@ class QAPISchema(object):
return [QAPISchemaFeature(f['name'], f.get('if')) for f in features]
def _make_enum_members(self, values):
return [QAPISchemaMember(v['name'], v.get('if')) for v in values]
return [QAPISchemaEnumMember(v['name'], v.get('if'))
for v in values]
def _make_implicit_enum_type(self, name, info, ifcond, values):
# See also QAPISchemaObjectTypeMember._pretty_owner()
@ -2269,7 +2237,7 @@ const QEnumLookup %(c_name)s_lookup = {
def gen_enum(name, members, prefix=None):
# append automatically generated _MAX value
enum_members = members + [QAPISchemaMember('_MAX')]
enum_members = members + [QAPISchemaEnumMember('_MAX')]
ret = mcgen('''

View File

@ -65,6 +65,8 @@ def gen_event_send(name, arg_type, boxed, event_enum_name, event_emit):
# practice, we can rename our local variables with a leading _ prefix,
# or split the code into a wrapper function that creates a boxed
# 'param' object then calls another to do the real work.
have_args = boxed or (arg_type and not arg_type.is_empty())
ret = mcgen('''
%(proto)s
@ -73,15 +75,13 @@ def gen_event_send(name, arg_type, boxed, event_enum_name, event_emit):
''',
proto=build_event_send_proto(name, arg_type, boxed))
if arg_type and not arg_type.is_empty():
if have_args:
ret += mcgen('''
QObject *obj;
Visitor *v;
''')
if not boxed:
ret += gen_param_var(arg_type)
else:
assert not boxed
ret += mcgen('''
@ -90,7 +90,7 @@ def gen_event_send(name, arg_type, boxed, event_enum_name, event_emit):
''',
name=name)
if arg_type and not arg_type.is_empty():
if have_args:
ret += mcgen('''
v = qobject_output_visitor_new(&obj);
''')
@ -121,7 +121,7 @@ def gen_event_send(name, arg_type, boxed, event_enum_name, event_emit):
event_emit=event_emit,
c_enum=c_enum_const(event_enum_name, name))
if arg_type and not arg_type.is_empty():
if have_args:
ret += mcgen('''
visit_free(v);
''')
@ -194,7 +194,7 @@ void %(event_emit)s(%(event_enum)s event, QDict *qdict);
self._event_emit_name))
# Note: we generate the enum member regardless of @ifcond, to
# keep the enumeration usable in target-independent code.
self._event_enum_members.append(QAPISchemaMember(name))
self._event_enum_members.append(QAPISchemaEnumMember(name))
def gen_events(schema, output_dir, prefix):

View File

@ -294,6 +294,7 @@ check-qtest-generic-y += tests/test-hmp$(EXESUF)
qapi-schema += alternate-any.json
qapi-schema += alternate-array.json
qapi-schema += alternate-base.json
qapi-schema += alternate-branch-if-invalid.json
qapi-schema += alternate-clash.json
qapi-schema += alternate-conflict-dict.json
qapi-schema += alternate-conflict-enum-bool.json
@ -311,7 +312,6 @@ qapi-schema += args-array-empty.json
qapi-schema += args-array-unknown.json
qapi-schema += args-bad-boxed.json
qapi-schema += args-boxed-anon.json
qapi-schema += args-boxed-empty.json
qapi-schema += args-boxed-string.json
qapi-schema += args-int.json
qapi-schema += args-invalid.json
@ -360,7 +360,6 @@ qapi-schema += doc-missing-expr.json
qapi-schema += doc-missing-space.json
qapi-schema += doc-missing.json
qapi-schema += doc-no-symbol.json
qapi-schema += double-data.json
qapi-schema += double-type.json
qapi-schema += duplicate-key.json
qapi-schema += empty.json
@ -374,15 +373,13 @@ qapi-schema += enum-int-member.json
qapi-schema += enum-member-case.json
qapi-schema += enum-missing-data.json
qapi-schema += enum-wrong-data.json
qapi-schema += escape-outside-string.json
qapi-schema += escape-too-big.json
qapi-schema += escape-too-short.json
qapi-schema += event-boxed-empty.json
qapi-schema += event-case.json
qapi-schema += event-member-invalid-dict.json
qapi-schema += event-nest-struct.json
qapi-schema += features-bad-type.json
qapi-schema += features-duplicate-name.json
qapi-schema += features-if-invalid.json
qapi-schema += features-missing-name.json
qapi-schema += features-name-bad-type.json
qapi-schema += features-no-list.json
@ -393,6 +390,7 @@ qapi-schema += flat-union-bad-discriminator.json
qapi-schema += flat-union-base-any.json
qapi-schema += flat-union-base-union.json
qapi-schema += flat-union-clash-member.json
qapi-schema += flat-union-discriminator-bad-name.json
qapi-schema += flat-union-empty.json
qapi-schema += flat-union-inline.json
qapi-schema += flat-union-inline-invalid-dict.json
@ -404,11 +402,11 @@ qapi-schema += flat-union-no-base.json
qapi-schema += flat-union-optional-discriminator.json
qapi-schema += flat-union-string-discriminator.json
qapi-schema += funny-char.json
qapi-schema += funny-word.json
qapi-schema += ident-with-escape.json
qapi-schema += include-before-err.json
qapi-schema += include-cycle.json
qapi-schema += include-extra-junk.json
qapi-schema += include-format-err.json
qapi-schema += include-nested-err.json
qapi-schema += include-no-file.json
qapi-schema += include-non-file.json
@ -431,6 +429,7 @@ qapi-schema += pragma-doc-required-crap.json
qapi-schema += pragma-extra-junk.json
qapi-schema += pragma-name-case-whitelist-crap.json
qapi-schema += pragma-non-dict.json
qapi-schema += pragma-unknown.json
qapi-schema += pragma-returns-whitelist-crap.json
qapi-schema += qapi-schema-test.json
qapi-schema += quoted-structural-chars.json
@ -451,9 +450,12 @@ qapi-schema += returns-array-bad.json
qapi-schema += returns-dict.json
qapi-schema += returns-unknown.json
qapi-schema += returns-whitelist.json
qapi-schema += string-code-point-31.json
qapi-schema += string-code-point-127.json
qapi-schema += struct-base-clash-deep.json
qapi-schema += struct-base-clash.json
qapi-schema += struct-data-invalid.json
qapi-schema += struct-member-if-invalid.json
qapi-schema += struct-member-invalid-dict.json
qapi-schema += struct-member-invalid.json
qapi-schema += trailing-comma-list.json
@ -462,10 +464,10 @@ qapi-schema += type-bypass-bad-gen.json
qapi-schema += unclosed-list.json
qapi-schema += unclosed-object.json
qapi-schema += unclosed-string.json
qapi-schema += unicode-str.json
qapi-schema += union-base-empty.json
qapi-schema += union-base-no-discriminator.json
qapi-schema += union-branch-case.json
qapi-schema += union-branch-if-invalid.json
qapi-schema += union-branch-invalid-dict.json
qapi-schema += union-clash-branches.json
qapi-schema += union-empty.json

View File

@ -0,0 +1 @@
tests/qapi-schema/alternate-branch-if-invalid.json:2: 'if' condition ' ' makes no sense

View File

@ -0,0 +1,3 @@
# Cover alternative with invalid 'if'
{ 'alternate': 'Alt',
'data': { 'branch': { 'type': 'int', 'if': ' ' } } }

View File

@ -1 +1 @@
tests/qapi-schema/alternate-empty.json:2: Alternate 'Alt' should have at least two branches in 'data'
tests/qapi-schema/alternate-empty.json:2: Alternate 'Alt' cannot have empty 'data'

View File

@ -1,2 +1,2 @@
# alternates must list at least two types to be useful
{ 'alternate': 'Alt', 'data': { 'i': 'int' } }
# alternates cannot be empty
{ 'alternate': 'Alt', 'data': { } }

View File

@ -1 +0,0 @@
tests/qapi-schema/args-boxed-empty.json:3: Cannot use 'boxed' with empty type

View File

@ -1,3 +0,0 @@
# 'boxed' requires a non-empty type
{ 'struct': 'Empty', 'data': {} }
{ 'command': 'foo', 'boxed': true, 'data': 'Empty' }

View File

@ -1 +1 @@
tests/qapi-schema/args-invalid.json:1: 'data' for command 'foo' should be a dictionary or type name
tests/qapi-schema/args-invalid.json:1: 'data' for command 'foo' should be an object or type name

View File

@ -1 +1 @@
tests/qapi-schema/bad-if-list.json:2: 'if' condition '' makes no sense
tests/qapi-schema/bad-if-list.json:2: 'if' condition ' ' makes no sense

View File

@ -1,3 +1,3 @@
# check invalid 'if' content
{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' },
'if': ['foo', ''] }
'if': ['foo', ' '] }

View File

@ -1 +1 @@
tests/qapi-schema/bad-type-int.json:3:13: Stray "1"
tests/qapi-schema/bad-type-int.json:3:13: Stray '123'

View File

@ -1,3 +1,3 @@
# we reject an expression with a metatype that is not a string
# FIXME: once the parser understands integer inputs, improve the error message
{ 'struct': 1, 'data': { } }
{ 'struct': 123, 'data': { } }

View File

@ -1 +1 @@
tests/qapi-schema/doc-missing-colon.json:4:1: Line should end with :
tests/qapi-schema/doc-missing-colon.json:4:1: Line should end with ':'

View File

@ -1 +1 @@
tests/qapi-schema/doc-missing.json:5: Expression missing documentation comment
tests/qapi-schema/doc-missing.json:5: Definition missing documentation comment

View File

@ -1 +1 @@
tests/qapi-schema/doc-no-symbol.json:3: Expression documentation required
tests/qapi-schema/doc-no-symbol.json:3: Definition documentation required

View File

@ -1 +0,0 @@
tests/qapi-schema/double-data.json:2:41: Duplicate key "data"

View File

@ -1,2 +0,0 @@
# we reject an expression with duplicate top-level keys
{ 'struct': 'bar', 'data': { }, 'data': { 'string': 'str'} }

View File

@ -1 +1 @@
tests/qapi-schema/duplicate-key.json:3:10: Duplicate key "key"
tests/qapi-schema/duplicate-key.json:3:10: Duplicate key 'key'

View File

@ -1,3 +1,3 @@
# QAPI cannot include the same key more than once in any {}
# Cannot include the same key more than once in any {}
{ 'key': 'value',
'key': 'value' }

View File

@ -1 +1 @@
tests/qapi-schema/enum-bad-name.json:2: Member of enum 'MyEnum' uses invalid name 'not^possible'
tests/qapi-schema/enum-bad-name.json:3: Member of enum 'MyEnum' uses invalid name 'not\possible'

View File

@ -1,2 +1,3 @@
# we ensure all enum names can map to C
{ 'enum': 'MyEnum', 'data': [ 'not^possible' ] }
# FIXME reports 'not\possible' instead of 'not\\possible'
{ 'enum': 'MyEnum', 'data': [ 'not\\possible' ] }

View File

@ -1 +1 @@
tests/qapi-schema/enum-clash-member.json:2: 'one_two' (member of MyEnum) collides with 'one-two' (member of MyEnum)
tests/qapi-schema/enum-clash-member.json:2: 'one_two' (value of MyEnum) collides with 'one-two' (value of MyEnum)

View File

@ -1,2 +1,2 @@
tests/qapi-schema/enum-dict-member-unknown.json:2: Unknown key 'bad-key' in dictionary member of enum 'MyEnum'
tests/qapi-schema/enum-dict-member-unknown.json:2: Unknown key 'bad-key' in member of enum 'MyEnum'
Valid keys are 'if', 'name'.

View File

@ -1 +1 @@
tests/qapi-schema/enum-int-member.json:3:31: Stray "1"
tests/qapi-schema/enum-int-member.json:3:31: Stray '1'

View File

@ -1 +1 @@
tests/qapi-schema/enum-member-case.json:4: 'Value' (member of NoWayThisWillGetWhitelisted) should not use uppercase
tests/qapi-schema/enum-member-case.json:4: 'Value' (value of NoWayThisWillGetWhitelisted) should not use uppercase

View File

@ -1 +1 @@
tests/qapi-schema/escape-outside-string.json:3:27: Stray "\"
tests/qapi-schema/escape-outside-string.json:3:27: Stray '\'

View File

@ -1,3 +0,0 @@
# escape sequences are permitted only inside strings
# { 'command': 'foo', 'data': {} }
{ 'command': 'foo', 'data'\u003a{} }

View File

@ -1 +0,0 @@
tests/qapi-schema/escape-too-big.json:3:14: For now, \u escape only supports non-zero values up to \u007f

View File

@ -1,3 +0,0 @@
# we don't support full Unicode strings, yet
# { 'command': 'é' }
{ 'command': '\u00e9' }

View File

@ -1 +0,0 @@
tests/qapi-schema/escape-too-short.json:3:14: \u escape needs 4 hex digits

View File

@ -1,3 +0,0 @@
# the \u escape requires 4 hex digits
# { 'command': 'a' }
{ 'command': '\u61' }

View File

@ -0,0 +1 @@
tests/qapi-schema/features-if-invalid.json:2: 'if' condition must be a string or a list of strings

View File

@ -0,0 +1,4 @@
# Cover feature with invalid 'if'
{ 'struct': 'Stru',
'data': {},
'features': [{'name': 'f', 'if': false }] }

View File

@ -0,0 +1 @@
tests/qapi-schema/flat-union-discriminator-bad-name.json:7: Discriminator of flat union 'MyUnion' does not allow optional name '*switch'

View File

@ -0,0 +1,11 @@
# discriminator '*switch' isn't a member of base, 'switch' is
# reports "does not allow optional name", which is good enough
{ 'enum': 'Enum', 'data': [ 'one', 'two' ] }
{ 'struct': 'Base',
'data': { '*switch': 'Enum' } }
{ 'struct': 'Branch', 'data': { 'name': 'str' } }
{ 'union': 'MyUnion',
'base': 'Base',
'discriminator': '*switch',
'data': { 'one': 'Branch',
'two': 'Branch' } }

View File

@ -1 +1 @@
tests/qapi-schema/flat-union-empty.json:4: Union 'Union' cannot have empty 'data'
tests/qapi-schema/flat-union-empty.json:4: Union 'Union' has no branches

View File

@ -1,4 +1,4 @@
# flat unions cannot be empty
# flat union discriminator cannot be empty
{ 'enum': 'Empty', 'data': [ ] }
{ 'struct': 'Base', 'data': { 'type': 'Empty' } }
{ 'union': 'Union', 'base': 'Base', 'discriminator': 'type', 'data': { } }

View File

@ -1 +1 @@
tests/qapi-schema/flat-union-invalid-discriminator.json:13: Discriminator 'enum_wrong' is not a member of base struct 'TestBase'
tests/qapi-schema/flat-union-invalid-discriminator.json:10: Discriminator 'enum_wrong' is not a member of 'base'

View File

@ -1,9 +1,6 @@
{ 'enum': 'TestEnum',
'data': [ 'value1', 'value2' ] }
{ 'struct': 'TestBase',
'data': { 'enum1': 'TestEnum' } }
{ 'struct': 'TestTypeA',
'data': { 'string': 'str' } }
@ -11,7 +8,7 @@
'data': { 'integer': 'int' } }
{ 'union': 'TestUnion',
'base': 'TestBase',
'base': { 'enum1': 'TestEnum' },
'discriminator': 'enum_wrong',
'data': { 'value1': 'TestTypeA',
'value2': 'TestTypeB' } }

View File

@ -1 +1 @@
tests/qapi-schema/flat-union-invalid-if-discriminator.json:13: The discriminator TestBase.enum1 for union TestUnion must not be conditional
tests/qapi-schema/flat-union-invalid-if-discriminator.json:10: The discriminator 'enum1' for union TestUnion must not be conditional

View File

@ -1,9 +1,6 @@
{ 'enum': 'TestEnum',
'data': [ 'value1', 'value2' ] }
{ 'struct': 'TestBase',
'data': { 'enum1': { 'type': 'TestEnum', 'if': 'FOO' } } }
{ 'struct': 'TestTypeA',
'data': { 'string': 'str' } }
@ -11,7 +8,7 @@
'data': { 'integer': 'int' } }
{ 'union': 'TestUnion',
'base': 'TestBase',
'base': { 'enum1': { 'type': 'TestEnum', 'if': 'FOO' } },
'discriminator': 'enum1',
'data': { 'value1': 'TestTypeA',
'value2': 'TestTypeB' } }

View File

@ -1 +1 @@
tests/qapi-schema/flat-union-optional-discriminator.json:6: Discriminator of flat union 'MyUnion' does not allow optional name '*switch'
tests/qapi-schema/flat-union-optional-discriminator.json:7: Discriminator 'switch' is not a member of 'base'

View File

@ -1,10 +1,11 @@
# 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' } }
{ 'struct': 'Branch', 'data': { 'name': 'str' } }
{ 'union': 'MyUnion',
'base': 'Base',
'discriminator': '*switch',
'discriminator': 'switch',
'data': { 'one': 'Branch',
'two': 'Branch' } }

View File

@ -1 +1 @@
tests/qapi-schema/funny-char.json:2:36: Stray ";"
tests/qapi-schema/funny-char.json:2:36: Stray ';'

View File

@ -0,0 +1 @@
tests/qapi-schema/funny-word.json:1:3: Stray 'command'

View File

@ -0,0 +1 @@
{ command: 'foo' }

View File

@ -0,0 +1 @@
tests/qapi-schema/ident-with-escape.json:3:3: Unknown escape \u

View File

@ -1 +1 @@
0
1

View File

@ -1,4 +1,4 @@
# we allow escape sequences in strings, if they map back to ASCII
# we don't recognize any \ escapes other than \\ (tested elsewhere)
# { 'command': 'fooA', 'data': { 'bar1': 'str' } }
{ 'c\u006fmmand': '\u0066\u006f\u006FA',
'd\u0061ta': { '\u0062\u0061\u00721': '\u0073\u0074\u0072' } }

View File

@ -1,16 +0,0 @@
module None
object q_empty
enum QType
prefix QTYPE
member none
member qnull
member qnum
member qstring
member qdict
member qlist
member qbool
module ident-with-escape.json
object q_obj_fooA-arg
member bar1: str optional=False
command fooA q_obj_fooA-arg -> None
gen=True success_response=True boxed=False oob=False preconfig=False

View File

@ -1 +1 @@
tests/qapi-schema/include-before-err.json:2:13: Expected ":"
tests/qapi-schema/include-before-err.json:2:13: Expected ':'

View File

@ -1 +0,0 @@
tests/qapi-schema/include-format-err.json:1: Invalid 'include' directive

View File

@ -1,2 +0,0 @@
{ 'include': 'include-simple-sub.json',
'foo': 'bar' }

View File

@ -1,2 +1,2 @@
In file included from tests/qapi-schema/include-nested-err.json:1:
tests/qapi-schema/missing-colon.json:1:10: Expected ":"
tests/qapi-schema/missing-colon.json:1:10: Expected ':'

View File

@ -1 +1 @@
tests/qapi-schema/leading-comma-list.json:2:13: Expected "{", "[", "]", string, boolean or "null"
tests/qapi-schema/leading-comma-list.json:2:13: Expected '{', '[', ']', string, boolean or 'null'

View File

@ -1 +1 @@
tests/qapi-schema/leading-comma-object.json:1:3: Expected string or "}"
tests/qapi-schema/leading-comma-object.json:1:3: Expected string or '}'

View File

@ -1 +1 @@
tests/qapi-schema/missing-colon.json:1:10: Expected ":"
tests/qapi-schema/missing-colon.json:1:10: Expected ':'

View File

@ -1 +1 @@
tests/qapi-schema/missing-comma-list.json:2:20: Expected "," or "]"
tests/qapi-schema/missing-comma-list.json:2:20: Expected ',' or ']'

View File

@ -1 +1 @@
tests/qapi-schema/missing-comma-object.json:2:3: Expected "," or "}"
tests/qapi-schema/missing-comma-object.json:2:3: Expected ',' or '}'

View File

@ -1 +1 @@
tests/qapi-schema/non-objects.json:1:1: Expected "{"
tests/qapi-schema/non-objects.json:1:1: Expected '{'

View File

@ -1,3 +1,3 @@
# 'name-case-whitelist' must be list of strings
{ 'pragma': { 'name-case-whitelist': null } }
{ 'pragma': { 'name-case-whitelist': false } }

View File

@ -1 +1 @@
tests/qapi-schema/pragma-non-dict.json:3: Value of 'pragma' must be a dictionary
tests/qapi-schema/pragma-non-dict.json:3: Value of 'pragma' must be an object

View File

@ -0,0 +1 @@
tests/qapi-schema/pragma-unknown.json:1: Unknown pragma 'no-such-pragma'

View File

@ -0,0 +1 @@
{ 'pragma': { 'no-such-pragma': false } }

View File

@ -25,6 +25,11 @@
{ 'struct': 'Empty1', 'data': { } }
{ 'struct': 'Empty2', 'base': 'Empty1', 'data': { } }
# Likewise for an empty flat union
{ 'union': 'Union',
'base': { 'type': 'EnumOne' }, 'discriminator': 'type',
'data': { } }
{ 'command': 'user_def_cmd0', 'data': 'Empty2', 'returns': 'Empty2' }
# for testing override of default naming heuristic
@ -149,6 +154,7 @@
{ 'command': 'guest-sync', 'data': { 'arg': 'any' }, 'returns': 'any' }
{ 'command': 'boxed-struct', 'boxed': true, 'data': 'UserDefZero' }
{ 'command': 'boxed-union', 'data': 'UserDefListUnion', 'boxed': true }
{ 'command': 'boxed-empty', 'boxed': true, 'data': 'Empty1' }
# Smoke test on out-of-band and allow-preconfig-test
{ 'command': 'test-flags-command', 'allow-oob': true, 'allow-preconfig': true }
@ -180,23 +186,26 @@
{ 'event': 'EVENT_D',
'data': { 'a' : 'EventStructOne', 'b' : 'str', '*c': 'str', '*enum3': 'EnumOne' } }
{ 'event': 'EVENT_E', 'boxed': true, 'data': 'UserDefZero' }
{ 'event': 'EVENT_F', 'boxed': true, 'data': 'UserDefAlternate' }
{ 'event': 'EVENT_F', 'boxed': true, 'data': 'UserDefFlatUnion' }
{ 'event': 'EVENT_G', 'boxed': true, 'data': 'Empty1' }
# test that we correctly compile downstream extensions, as well as munge
# ticklish names
# also test union and alternate with just one branch
{ 'enum': '__org.qemu_x-Enum', 'data': [ '__org.qemu_x-value' ] }
{ 'struct': '__org.qemu_x-Base',
'data': { '__org.qemu_x-member1': '__org.qemu_x-Enum' } }
{ 'struct': '__org.qemu_x-Struct', 'base': '__org.qemu_x-Base',
'data': { '__org.qemu_x-member2': 'str', '*wchar-t': 'int' } }
{ 'union': '__org.qemu_x-Union1', 'data': { '__org.qemu_x-branch': 'str' } }
{ 'alternate': '__org.qemu_x-Alt1', 'data': { '__org.qemu_x-branch': 'str' } }
{ 'struct': '__org.qemu_x-Struct2',
'data': { 'array': ['__org.qemu_x-Union1'] } }
{ 'union': '__org.qemu_x-Union2', 'base': '__org.qemu_x-Base',
'discriminator': '__org.qemu_x-member1',
'data': { '__org.qemu_x-value': '__org.qemu_x-Struct2' } }
{ 'alternate': '__org.qemu_x-Alt',
'data': { '__org.qemu_x-branch': 'str', 'b': '__org.qemu_x-Base' } }
'data': { '__org.qemu_x-branch': '__org.qemu_x-Base' } }
{ 'event': '__ORG.QEMU_X-EVENT', 'data': '__org.qemu_x-Struct' }
{ 'command': '__org.qemu_x-command',
'data': { 'a': ['__org.qemu_x-Enum'], 'b': ['__org.qemu_x-Struct'],

View File

@ -23,6 +23,15 @@ enum MyEnum
object Empty1
object Empty2
base Empty1
object q_obj_Union-base
member type: EnumOne optional=False
object Union
base q_obj_Union-base
tag type
case value1: q_empty
case value2: q_empty
case value3: q_empty
case value4: q_empty
command user_def_cmd0 Empty2 -> Empty2
gen=True success_response=True boxed=False oob=False preconfig=False
enum QEnumTwo
@ -221,6 +230,8 @@ command boxed-struct UserDefZero -> None
gen=True success_response=True boxed=True oob=False preconfig=False
command boxed-union UserDefListUnion -> None
gen=True success_response=True boxed=True oob=False preconfig=False
command boxed-empty Empty1 -> None
gen=True success_response=True boxed=True oob=False preconfig=False
command test-flags-command None -> None
gen=True success_response=True boxed=False oob=True preconfig=True
object UserDefOptions
@ -252,7 +263,9 @@ event EVENT_D q_obj_EVENT_D-arg
boxed=False
event EVENT_E UserDefZero
boxed=True
event EVENT_F UserDefAlternate
event EVENT_F UserDefFlatUnion
boxed=True
event EVENT_G Empty1
boxed=True
enum __org.qemu_x-Enum
member __org.qemu_x-value
@ -270,6 +283,9 @@ object __org.qemu_x-Union1
member type: __org.qemu_x-Union1Kind optional=False
tag type
case __org.qemu_x-branch: q_obj_str-wrapper
alternate __org.qemu_x-Alt1
tag type
case __org.qemu_x-branch: str
array __org.qemu_x-Union1List __org.qemu_x-Union1
object __org.qemu_x-Struct2
member array: __org.qemu_x-Union1List optional=False
@ -279,8 +295,7 @@ object __org.qemu_x-Union2
case __org.qemu_x-value: __org.qemu_x-Struct2
alternate __org.qemu_x-Alt
tag type
case __org.qemu_x-branch: str
case b: __org.qemu_x-Base
case __org.qemu_x-branch: __org.qemu_x-Base
event __ORG.QEMU_X-EVENT __org.qemu_x-Struct
boxed=False
array __org.qemu_x-EnumList __org.qemu_x-Enum

View File

@ -1 +1 @@
tests/qapi-schema/quoted-structural-chars.json:1:1: Expected "{"
tests/qapi-schema/quoted-structural-chars.json:1:1: Expected '{'

View File

@ -0,0 +1 @@
tests/qapi-schema/string-code-point-127.json:2:14: Funny character in string

View File

@ -0,0 +1,2 @@
# We accept printable ASCII: code points 32..126. Test code point 127:
{ 'command': '' }

View File

@ -0,0 +1 @@
tests/qapi-schema/string-code-point-31.json:2:14: Funny character in string

View File

@ -0,0 +1,2 @@
# We accept printable ASCII: code points 32..126. Test code point 127:
{ 'command': '' }

View File

@ -1 +1 @@
tests/qapi-schema/struct-data-invalid.json:1: 'data' for struct 'foo' should be a dictionary or type name
tests/qapi-schema/struct-data-invalid.json:1: 'data' for struct 'foo' should be an object or type name

View File

@ -0,0 +1 @@
tests/qapi-schema/struct-member-if-invalid.json:2: 'if' condition must be a string or a list of strings

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,3 @@
# Cover member with invalid 'if'
{ 'struct': 'Stru',
'data': { 'member': { 'type': 'int', 'if': true } } }

View File

@ -1 +1 @@
tests/qapi-schema/trailing-comma-list.json:2:36: Expected "{", "[", string, boolean or "null"
tests/qapi-schema/trailing-comma-list.json:2:36: Expected '{', '[', string, boolean or 'null'

View File

@ -1 +1 @@
tests/qapi-schema/unclosed-list.json:1:20: Expected "," or "]"
tests/qapi-schema/unclosed-list.json:1:20: Expected ',' or ']'

View File

@ -1 +1 @@
tests/qapi-schema/unclosed-object.json:1:21: Expected "," or "}"
tests/qapi-schema/unclosed-object.json:1:21: Expected ',' or '}'

Some files were not shown because too many files have changed in this diff Show More