From: Eric Blake <eblake@redhat.com>
To: qemu-devel@nongnu.org
Cc: kwolf@redhat.com, berto@igalia.com, armbru@redhat.com
Subject: [Qemu-devel] [PATCH v7 25/39] qapi: Require valid names
Date: Wed, 29 Apr 2015 07:06:40 -0600 [thread overview]
Message-ID: <1430312814-19706-26-git-send-email-eblake@redhat.com> (raw)
In-Reply-To: <1430312814-19706-1-git-send-email-eblake@redhat.com>
Previous commits demonstrated that the generator overlooked various
bad naming situations:
- types, commands, and events need a valid name
- enum members must be valid names, when combined with prefix
- union and alternate branches cannot be marked optional
Valid upstream names match [a-zA-Z][a-zA-Z0-9_-]*; valid downstream
names match __[a-zA-Z][a-zA-Z0-9._-]*. Enumerations match the
weaker [a-zA-Z0-9._-]+ (in part thanks to QKeyCode picking an enum
that starts with a digit, which we can't change now due to
backwards compatibility). Rather than call out three separate
regex, this patch just uses a broader combination that allows both
upstream and downstream names, as well as a small hack that
realizes that any enum name is merely a suffix to an already valid
name prefix (that is, any enum name is valid if prepending _ fits
the normal rules).
We could reject new enumeration names beginning with a digit by
whitelisting existing exceptions. We could also be stricter
about the distinction between upstream names (no leading
underscore, no use of dot) and downstream (mandatory leading
double underscore), but it is probably not worth the bother.
Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
---
v7: improve commit message
---
scripts/qapi.py | 63 ++++++++++++++++------
tests/qapi-schema/bad-ident.err | 1 +
tests/qapi-schema/bad-ident.exit | 2 +-
tests/qapi-schema/bad-ident.json | 2 +-
tests/qapi-schema/bad-ident.out | 3 --
tests/qapi-schema/enum-bad-name.err | 1 +
tests/qapi-schema/enum-bad-name.exit | 2 +-
tests/qapi-schema/enum-bad-name.json | 2 +-
tests/qapi-schema/enum-bad-name.out | 3 --
tests/qapi-schema/enum-dict-member.err | 2 +-
tests/qapi-schema/flat-union-bad-discriminator.err | 2 +-
.../flat-union-optional-discriminator.err | 1 +
.../flat-union-optional-discriminator.exit | 2 +-
.../flat-union-optional-discriminator.json | 2 +-
.../flat-union-optional-discriminator.out | 7 ---
tests/qapi-schema/union-optional-branch.err | 1 +
tests/qapi-schema/union-optional-branch.exit | 2 +-
tests/qapi-schema/union-optional-branch.json | 2 +-
tests/qapi-schema/union-optional-branch.out | 3 --
19 files changed, 60 insertions(+), 43 deletions(-)
diff --git a/scripts/qapi.py b/scripts/qapi.py
index 61e1c43..c6ab8f3 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -276,8 +276,31 @@ def discriminator_find_enum_define(expr):
return find_enum(discriminator_type)
+valid_name = re.compile('^[a-zA-Z_][a-zA-Z0-9_.-]*$')
+def check_name(expr_info, source, name, allow_optional = False,
+ enum_member = False):
+ global valid_name
+ membername = name
+
+ if not isinstance(name, str):
+ raise QAPIExprError(expr_info,
+ "%s requires a string name" % source)
+ if name.startswith('*'):
+ membername = name[1:]
+ if not allow_optional:
+ raise QAPIExprError(expr_info,
+ "%s does not allow optional name '%s'"
+ % (source, name))
+ # Enum members can start with a digit, because the generated C
+ # code always prefixes it with the enum name
+ if enum_member:
+ membername = "_%s" %membername
+ if not valid_name.match(membername):
+ raise QAPIExprError(expr_info,
+ "%s uses invalid name '%s'" % (source, name))
+
def check_type(expr_info, source, value, allow_array = False,
- allow_dict = False, allow_metas = []):
+ allow_dict = False, allow_optional = False, allow_metas = []):
global all_names
orig_value = value
@@ -319,18 +342,21 @@ def check_type(expr_info, source, value, allow_array = False,
raise QAPIExprError(expr_info,
"%s should be a type name" % source)
for (key, arg) in value.items():
+ check_name(expr_info, "Member of %s" % source, key,
+ allow_optional=allow_optional)
check_type(expr_info, "Member '%s' of %s" % (key, source), arg,
- allow_array=True, allow_dict=True,
+ allow_array=True, allow_dict=True, allow_optional=True,
allow_metas=['built-in', 'union', 'alternate', 'struct',
'enum'])
def check_command(expr, expr_info):
name = expr['command']
check_type(expr_info, "'data' for command '%s'" % name,
- expr.get('data'), allow_dict=True,
+ expr.get('data'), allow_dict=True, allow_optional=True,
allow_metas=['union', 'struct'])
check_type(expr_info, "'returns' for command '%s'" % name,
expr.get('returns'), allow_array=True, allow_dict=True,
+ allow_optional=True,
allow_metas=['built-in', 'union', 'alternate', 'struct',
'enum'])
@@ -343,7 +369,7 @@ def check_event(expr, expr_info):
raise QAPIExprError(expr_info, "Event name 'MAX' cannot be created")
events.append(name)
check_type(expr_info, "'data' for event '%s'" % name,
- expr.get('data'), allow_dict=True,
+ expr.get('data'), allow_dict=True, allow_optional=True,
allow_metas=['union', 'struct'])
if params:
for argname, argentry, optional, structured in parse_args(params):
@@ -392,12 +418,10 @@ def check_union(expr, expr_info):
"Base '%s' is not a valid type"
% base)
- # The value of member 'discriminator' must name a member of the
- # base type.
- if not isinstance(discriminator, str):
- raise QAPIExprError(expr_info,
- "Flat union '%s' discriminator must be a string"
- % name)
+ # The value of member 'discriminator' must name a non-optional
+ # member of the base type.
+ check_name(expr_info, "Discriminator of flat union '%s'" % name,
+ discriminator)
discriminator_type = base_fields.get(discriminator)
if not discriminator_type:
raise QAPIExprError(expr_info,
@@ -414,6 +438,8 @@ def check_union(expr, expr_info):
# Check every branch
for (key, value) in members.items():
+ check_name(expr_info, "Member of union '%s'" % name, key)
+
# Each value must name a known type; furthermore, in flat unions,
# branches must be a struct
check_type(expr_info, "Member '%s' of union '%s'" % (key, name),
@@ -445,6 +471,8 @@ def check_alternate(expr, expr_info):
# Check every branch
for (key, value) in members.items():
+ check_name(expr_info, "Member of alternate '%s'" % name, key)
+
# Check for conflicts in the generated enum
c_key = _generate_enum_string(key)
if c_key in values:
@@ -475,10 +503,8 @@ def check_enum(expr, expr_info):
raise QAPIExprError(expr_info,
"Enum '%s' requires an array for 'data'" % name)
for member in members:
- if not isinstance(member, str):
- raise QAPIExprError(expr_info,
- "Enum '%s' member '%s' is not a string"
- % (name, member))
+ check_name(expr_info, "Member of enum '%s'" %name, member,
+ enum_member=True)
key = _generate_enum_string(member)
if key in values:
raise QAPIExprError(expr_info,
@@ -491,7 +517,7 @@ def check_struct(expr, expr_info):
members = expr['data']
check_type(expr_info, "'data' for type '%s'" % name, members,
- allow_dict=True)
+ allow_dict=True, allow_optional=True)
check_type(expr_info, "'base' for type '%s'" % name, expr.get('base'),
allow_metas=['struct'])
@@ -682,8 +708,11 @@ def type_name(name):
return c_list_type(name[0])
return name
-def add_name(name, info, meta, implicit = False):
+def add_name(name, info, meta, implicit = False, source = None):
global all_names
+ if not source:
+ source = "'%s'" % meta
+ check_name(info, source, name)
if name in all_names:
raise QAPIExprError(info,
"%s '%s' is already defined"
@@ -697,7 +726,7 @@ def add_name(name, info, meta, implicit = False):
def add_struct(definition, info):
global struct_types
name = definition['type']
- add_name(name, info, 'struct')
+ add_name(name, info, 'struct', source="'type'")
struct_types.append(definition)
def find_struct(name):
diff --git a/tests/qapi-schema/bad-ident.err b/tests/qapi-schema/bad-ident.err
index e69de29..42b490c 100644
--- a/tests/qapi-schema/bad-ident.err
+++ b/tests/qapi-schema/bad-ident.err
@@ -0,0 +1 @@
+tests/qapi-schema/bad-ident.json:2: 'type' does not allow optional name '*oops'
diff --git a/tests/qapi-schema/bad-ident.exit b/tests/qapi-schema/bad-ident.exit
index 573541a..d00491f 100644
--- a/tests/qapi-schema/bad-ident.exit
+++ b/tests/qapi-schema/bad-ident.exit
@@ -1 +1 @@
-0
+1
diff --git a/tests/qapi-schema/bad-ident.json b/tests/qapi-schema/bad-ident.json
index f139110..da949e8 100644
--- a/tests/qapi-schema/bad-ident.json
+++ b/tests/qapi-schema/bad-ident.json
@@ -1,2 +1,2 @@
-# FIXME: we should reject creating a type name with bad name
+# we reject creating a type name with bad name
{ 'type': '*oops', 'data': { 'i': 'int' } }
diff --git a/tests/qapi-schema/bad-ident.out b/tests/qapi-schema/bad-ident.out
index 165e346..e69de29 100644
--- a/tests/qapi-schema/bad-ident.out
+++ b/tests/qapi-schema/bad-ident.out
@@ -1,3 +0,0 @@
-[OrderedDict([('type', '*oops'), ('data', OrderedDict([('i', 'int')]))])]
-[]
-[OrderedDict([('type', '*oops'), ('data', OrderedDict([('i', 'int')]))])]
diff --git a/tests/qapi-schema/enum-bad-name.err b/tests/qapi-schema/enum-bad-name.err
index e69de29..9c3c100 100644
--- a/tests/qapi-schema/enum-bad-name.err
+++ b/tests/qapi-schema/enum-bad-name.err
@@ -0,0 +1 @@
+tests/qapi-schema/enum-bad-name.json:2: Member of enum 'MyEnum' uses invalid name 'not^possible'
diff --git a/tests/qapi-schema/enum-bad-name.exit b/tests/qapi-schema/enum-bad-name.exit
index 573541a..d00491f 100644
--- a/tests/qapi-schema/enum-bad-name.exit
+++ b/tests/qapi-schema/enum-bad-name.exit
@@ -1 +1 @@
-0
+1
diff --git a/tests/qapi-schema/enum-bad-name.json b/tests/qapi-schema/enum-bad-name.json
index 0c32448..8506562 100644
--- a/tests/qapi-schema/enum-bad-name.json
+++ b/tests/qapi-schema/enum-bad-name.json
@@ -1,2 +1,2 @@
-# FIXME: we should ensure all enum names can map to C
+# we ensure all enum names can map to C
{ 'enum': 'MyEnum', 'data': [ 'not^possible' ] }
diff --git a/tests/qapi-schema/enum-bad-name.out b/tests/qapi-schema/enum-bad-name.out
index d24ea49..e69de29 100644
--- a/tests/qapi-schema/enum-bad-name.out
+++ b/tests/qapi-schema/enum-bad-name.out
@@ -1,3 +0,0 @@
-[OrderedDict([('enum', 'MyEnum'), ('data', ['not^possible'])])]
-[{'enum_name': 'MyEnum', 'enum_values': ['not^possible']}]
-[]
diff --git a/tests/qapi-schema/enum-dict-member.err b/tests/qapi-schema/enum-dict-member.err
index 7e966a8..8ca146e 100644
--- a/tests/qapi-schema/enum-dict-member.err
+++ b/tests/qapi-schema/enum-dict-member.err
@@ -1 +1 @@
-tests/qapi-schema/enum-dict-member.json:2: Enum 'MyEnum' member 'OrderedDict([('value', 'str')])' is not a string
+tests/qapi-schema/enum-dict-member.json:2: Member of enum 'MyEnum' requires a string name
diff --git a/tests/qapi-schema/flat-union-bad-discriminator.err b/tests/qapi-schema/flat-union-bad-discriminator.err
index 507e2ba..c38cc8e 100644
--- a/tests/qapi-schema/flat-union-bad-discriminator.err
+++ b/tests/qapi-schema/flat-union-bad-discriminator.err
@@ -1 +1 @@
-tests/qapi-schema/flat-union-bad-discriminator.json:11: Flat union 'TestUnion' discriminator must be a string
+tests/qapi-schema/flat-union-bad-discriminator.json:11: Discriminator of flat union 'TestUnion' requires a string name
diff --git a/tests/qapi-schema/flat-union-optional-discriminator.err b/tests/qapi-schema/flat-union-optional-discriminator.err
index e69de29..aaabedb 100644
--- a/tests/qapi-schema/flat-union-optional-discriminator.err
+++ b/tests/qapi-schema/flat-union-optional-discriminator.err
@@ -0,0 +1 @@
+tests/qapi-schema/flat-union-optional-discriminator.json:6: Discriminator of flat union 'MyUnion' does not allow optional name '*switch'
diff --git a/tests/qapi-schema/flat-union-optional-discriminator.exit b/tests/qapi-schema/flat-union-optional-discriminator.exit
index 573541a..d00491f 100644
--- a/tests/qapi-schema/flat-union-optional-discriminator.exit
+++ b/tests/qapi-schema/flat-union-optional-discriminator.exit
@@ -1 +1 @@
-0
+1
diff --git a/tests/qapi-schema/flat-union-optional-discriminator.json b/tests/qapi-schema/flat-union-optional-discriminator.json
index ece0d31..25ce0e6 100644
--- a/tests/qapi-schema/flat-union-optional-discriminator.json
+++ b/tests/qapi-schema/flat-union-optional-discriminator.json
@@ -1,4 +1,4 @@
-# FIXME: we should require the discriminator to be non-optional
+# we require the discriminator to be non-optional
{ 'enum': 'Enum', 'data': [ 'one', 'two' ] }
{ 'type': 'Base',
'data': { '*switch': 'Enum' } }
diff --git a/tests/qapi-schema/flat-union-optional-discriminator.out b/tests/qapi-schema/flat-union-optional-discriminator.out
index bb7db00..e69de29 100644
--- a/tests/qapi-schema/flat-union-optional-discriminator.out
+++ b/tests/qapi-schema/flat-union-optional-discriminator.out
@@ -1,7 +0,0 @@
-[OrderedDict([('enum', 'Enum'), ('data', ['one', 'two'])]),
- OrderedDict([('type', 'Base'), ('data', OrderedDict([('*switch', 'Enum')]))]),
- OrderedDict([('type', 'Branch'), ('data', OrderedDict([('name', 'str')]))]),
- OrderedDict([('union', 'MyUnion'), ('base', 'Base'), ('discriminator', '*switch'), ('data', OrderedDict([('one', 'Branch'), ('two', 'Branch')]))])]
-[{'enum_name': 'Enum', 'enum_values': ['one', 'two']}]
-[OrderedDict([('type', 'Base'), ('data', OrderedDict([('*switch', 'Enum')]))]),
- OrderedDict([('type', 'Branch'), ('data', OrderedDict([('name', 'str')]))])]
diff --git a/tests/qapi-schema/union-optional-branch.err b/tests/qapi-schema/union-optional-branch.err
index e69de29..3ada133 100644
--- a/tests/qapi-schema/union-optional-branch.err
+++ b/tests/qapi-schema/union-optional-branch.err
@@ -0,0 +1 @@
+tests/qapi-schema/union-optional-branch.json:2: Member of union 'Union' does not allow optional name '*a'
diff --git a/tests/qapi-schema/union-optional-branch.exit b/tests/qapi-schema/union-optional-branch.exit
index 573541a..d00491f 100644
--- a/tests/qapi-schema/union-optional-branch.exit
+++ b/tests/qapi-schema/union-optional-branch.exit
@@ -1 +1 @@
-0
+1
diff --git a/tests/qapi-schema/union-optional-branch.json b/tests/qapi-schema/union-optional-branch.json
index c513db7..591615f 100644
--- a/tests/qapi-schema/union-optional-branch.json
+++ b/tests/qapi-schema/union-optional-branch.json
@@ -1,2 +1,2 @@
-# FIXME: union branches cannot be optional
+# union branches cannot be optional
{ 'union': 'Union', 'data': { '*a': 'int', 'b': 'str' } }
diff --git a/tests/qapi-schema/union-optional-branch.out b/tests/qapi-schema/union-optional-branch.out
index b03b5d2..e69de29 100644
--- a/tests/qapi-schema/union-optional-branch.out
+++ b/tests/qapi-schema/union-optional-branch.out
@@ -1,3 +0,0 @@
-[OrderedDict([('union', 'Union'), ('data', OrderedDict([('*a', 'int'), ('b', 'str')]))])]
-[{'enum_name': 'UnionKind', 'enum_values': None}]
-[]
--
2.1.0
next prev parent reply other threads:[~2015-04-29 13:07 UTC|newest]
Thread overview: 49+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-04-29 13:06 [Qemu-devel] [PATCH v7 00/39] drop qapi nested structs Eric Blake
2015-04-29 13:06 ` [Qemu-devel] [PATCH v7 01/39] qapi: Add copyright declaration on docs Eric Blake
2015-04-29 13:06 ` [Qemu-devel] [PATCH v7 02/39] qapi: Document type-safety considerations Eric Blake
2015-04-29 13:06 ` [Qemu-devel] [PATCH v7 03/39] qapi: Simplify builtin type handling Eric Blake
2015-04-29 13:06 ` [Qemu-devel] [PATCH v7 04/39] qapi: Fix generation of 'size' builtin type Eric Blake
2015-04-29 13:06 ` [Qemu-devel] [PATCH v7 05/39] qapi: Require ASCII in schema Eric Blake
2015-04-29 13:06 ` [Qemu-devel] [PATCH v7 06/39] qapi: Add some enum tests Eric Blake
2015-04-29 13:06 ` [Qemu-devel] [PATCH v7 07/39] qapi: Better error messages for bad enums Eric Blake
2015-04-29 13:06 ` [Qemu-devel] [PATCH v7 08/39] qapi: Add some union tests Eric Blake
2015-04-29 13:06 ` [Qemu-devel] [PATCH v7 09/39] qapi: Clean up test coverage of simple unions Eric Blake
2015-04-29 13:06 ` [Qemu-devel] [PATCH v7 10/39] qapi: Forbid base without discriminator in unions Eric Blake
2015-04-29 13:06 ` [Qemu-devel] [PATCH v7 11/39] qapi: Tighten checking of unions Eric Blake
2015-04-29 13:06 ` [Qemu-devel] [PATCH v7 12/39] qapi: Prepare for catching more semantic parse errors Eric Blake
2015-04-29 13:06 ` [Qemu-devel] [PATCH v7 13/39] qapi: Segregate anonymous unions into alternates in generator Eric Blake
2015-04-29 13:06 ` [Qemu-devel] [PATCH v7 14/39] qapi: Rename anonymous union type in test Eric Blake
2015-04-29 13:06 ` [Qemu-devel] [PATCH v7 15/39] qapi: Document new 'alternate' meta-type Eric Blake
2015-04-29 13:06 ` [Qemu-devel] [PATCH v7 16/39] qapi: Use 'alternate' to replace anonymous union Eric Blake
2015-04-29 13:06 ` [Qemu-devel] [PATCH v7 17/39] qapi: Add some expr tests Eric Blake
2015-04-29 13:06 ` [Qemu-devel] [PATCH v7 18/39] qapi: Better error messages for bad expressions Eric Blake
2015-04-29 13:06 ` [Qemu-devel] [PATCH v7 19/39] qapi: Add tests of redefined expressions Eric Blake
2015-04-29 13:06 ` [Qemu-devel] [PATCH v7 20/39] qapi: Better error messages for duplicated expressions Eric Blake
2015-04-29 13:06 ` [Qemu-devel] [PATCH v7 21/39] qapi: Allow true, false and null in schema json Eric Blake
2015-04-29 13:06 ` [Qemu-devel] [PATCH v7 22/39] qapi: Unify type bypass and add tests Eric Blake
2015-05-01 19:55 ` Eric Blake
2015-05-01 20:10 ` [Qemu-devel] [PATCHv7 22a/39] squash: " Eric Blake
2015-04-29 13:06 ` [Qemu-devel] [PATCH v7 23/39] qapi: Add some type check tests Eric Blake
2015-04-29 13:06 ` [Qemu-devel] [PATCH v7 24/39] qapi: More rigourous checking of types Eric Blake
2015-04-29 13:06 ` Eric Blake [this message]
2015-05-02 20:51 ` [Qemu-devel] [PATCH v7 25/39] qapi: Require valid names Eric Blake
2015-05-04 7:26 ` Markus Armbruster
2015-04-29 13:06 ` [Qemu-devel] [PATCH v7 26/39] qapi: Whitelist commands that don't return dictionary Eric Blake
2015-04-29 13:06 ` [Qemu-devel] [PATCH v7 27/39] qapi: More rigorous checking for type safety bypass Eric Blake
2015-04-29 13:06 ` [Qemu-devel] [PATCH v7 28/39] qapi: Prefer 'struct' over 'type' in generator Eric Blake
2015-04-29 13:06 ` [Qemu-devel] [PATCH v7 29/39] qapi: Document 'struct' metatype Eric Blake
2015-04-29 13:06 ` [Qemu-devel] [PATCH v7 30/39] qapi: Use 'struct' instead of 'type' in schema Eric Blake
2015-04-29 13:06 ` [Qemu-devel] [PATCH v7 31/39] qapi: Forbid " Eric Blake
2015-04-29 13:06 ` [Qemu-devel] [PATCH v7 32/39] qapi: Merge UserDefTwo and UserDefNested in tests Eric Blake
2015-04-29 13:06 ` [Qemu-devel] [PATCH v7 33/39] qapi: Drop tests for inline nested structs Eric Blake
2015-04-29 13:06 ` [Qemu-devel] [PATCH v7 34/39] qapi: Drop inline nested struct in query-version Eric Blake
2015-04-29 13:06 ` [Qemu-devel] [PATCH v7 35/39] qapi: Drop inline nested structs in query-pci Eric Blake
2015-04-29 13:06 ` [Qemu-devel] [PATCH v7 36/39] qapi: Drop support for inline nested types Eric Blake
2015-05-01 20:20 ` Eric Blake
2015-05-04 7:28 ` Markus Armbruster
2015-05-01 20:29 ` [Qemu-devel] [PATCH v7 36a/39] squash: qapi: Drop dead visitor code related to nested structs Eric Blake
2015-04-29 13:06 ` [Qemu-devel] [PATCH v7 37/39] qapi: Tweak doc references to QMP when QGA is also meant Eric Blake
2015-04-29 13:06 ` [Qemu-devel] [PATCH v7 38/39] qapi: Support (subset of) \u escapes in strings Eric Blake
2015-04-29 13:06 ` [Qemu-devel] [PATCH v7 39/39] qapi: Check for member name conflicts with a base class Eric Blake
2015-05-01 20:22 ` Eric Blake
2015-05-01 20:27 ` [Qemu-devel] [PATCH v7 39a/39] squash: " Eric Blake
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1430312814-19706-26-git-send-email-eblake@redhat.com \
--to=eblake@redhat.com \
--cc=armbru@redhat.com \
--cc=berto@igalia.com \
--cc=kwolf@redhat.com \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).