From 0545f6b8874c28d97369f2c83e5077e0461d4f12 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 4 May 2015 09:05:15 -0600 Subject: qapi: Better error messages for bad expressions The previous commit demonstrated that the generator overlooked some fairly basic broken expressions: - missing metataype - metatype key has a non-string value - unknown key in relation to the metatype - conflicting metatype (this patch treats the second metatype as an unknown key of the first key visited, which is not necessarily the first key the user typed) Add check_keys to cover these situations, and update testcases to match. A couple other tests (enum-missing-data, indented-expr) had to change since the validation added here occurs so early. Conversely, changes to ident-with-escape results show that we still have problems where our handling of escape sequences differs from true JSON, which will matter down the road if we allow arbitrary default string values for optional parameters (but for now is not too bad, as we currently can avoid unicode escaping as we don't need to represent anything beyond C identifier material). While valid .json files won't trigger any of these cases, we might as well be nicer to developers that make a typo while trying to add new QAPI code. Signed-off-by: Eric Blake Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- scripts/qapi.py | 44 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 7 deletions(-) (limited to 'scripts') diff --git a/scripts/qapi.py b/scripts/qapi.py index 05c38c5b00..868f08b593 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -348,11 +348,6 @@ def check_alternate(expr, expr_info): values = { 'MAX': '(automatic)' } types_seen = {} - if expr.get('base') is not None: - raise QAPIExprError(expr_info, - "Alternate '%s' must not have a base" - % name) - # Check every branch for (key, value) in members.items(): # Check for conflicts in the generated enum @@ -414,6 +409,26 @@ def check_exprs(schema): elif expr.has_key('event'): check_event(expr, info) +def check_keys(expr_elem, meta, required, optional=[]): + expr = expr_elem['expr'] + info = expr_elem['info'] + name = expr[meta] + if not isinstance(name, str): + raise QAPIExprError(info, + "'%s' key must have a string value" % meta) + required = required + [ meta ] + for (key, value) in expr.items(): + if not key in required and not key in optional: + raise QAPIExprError(info, + "Unknown key '%s' in %s '%s'" + % (key, meta, name)) + for key in required: + if not expr.has_key(key): + raise QAPIExprError(info, + "Key '%s' is missing from %s '%s'" + % (key, meta, name)) + + def parse_schema(input_file): # First pass: read entire file into memory try: @@ -425,15 +440,30 @@ def parse_schema(input_file): exprs = [] try: - # Next pass: learn the types. + # Next pass: learn the types and check for valid expression keys. At + # this point, top-level 'include' has already been flattened. for expr_elem in schema.exprs: expr = expr_elem['expr'] if expr.has_key('enum'): - add_enum(expr['enum'], expr.get('data')) + check_keys(expr_elem, 'enum', ['data']) + add_enum(expr['enum'], expr['data']) elif expr.has_key('union'): + check_keys(expr_elem, 'union', ['data'], + ['base', 'discriminator']) add_union(expr) + elif expr.has_key('alternate'): + check_keys(expr_elem, 'alternate', ['data']) elif expr.has_key('type'): + check_keys(expr_elem, 'type', ['data'], ['base']) add_struct(expr) + elif expr.has_key('command'): + check_keys(expr_elem, 'command', [], + ['data', 'returns', 'gen', 'success-response']) + elif expr.has_key('event'): + check_keys(expr_elem, 'event', [], ['data']) + else: + raise QAPIExprError(expr_elem['info'], + "Expression is missing metatype") exprs.append(expr) # Try again for hidden UnionKind enum -- cgit v1.2.1