qapi: Tighten the regex on valid names

We already documented that qapi names should match specific
patterns (such as starting with a letter unless it was an enum
value or a downstream extension).  Tighten that from a suggestion
into a hard requirement, which frees up names beginning with a
single underscore for qapi internal usage.

The tighter regex doesn't forbid everything insane that a user
could provide (for example, a user could name a type 'Foo-lookup'
to collide with the generated 'Foo_lookup[]' for an enum 'Foo'),
but does a good job at protecting the most obvious uses, and
also happens to reserve single leading underscore for later use.

The handling of enum values starting with a digit is tricky:
commit 9fb081e introduced a subtle bug by using c_name() on
a munged value, which would allow an enum to include the
member 'q-int' in spite of our reservation.  Furthermore,
munging with a leading '_' would fail our tighter regex.  So
fix it by only munging for leading digits (which are never
ticklish in c_name()) and by using a different prefix (I
picked 'D', although any letter should do).

Add new tests, reserved-member-underscore and reserved-enum-q,
to demonstrate the tighter checking.

Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1447836791-369-22-git-send-email-eblake@redhat.com>
Message-Id: <1447883135-18020-1-git-send-email-eblake@redhat.com>
[Eric's fixup squashed in]
Signed-off-by: Markus Armbruster <armbru@redhat.com>
This commit is contained in:
Eric Blake 2015-11-18 01:52:56 -07:00 committed by Markus Armbruster
parent 5be5b7764f
commit 59a92feedc
11 changed files with 32 additions and 16 deletions

View File

@ -118,17 +118,17 @@ tracking optional fields.
Any name (command, event, type, field, or enum value) beginning with Any name (command, event, type, field, or enum value) beginning with
"x-" is marked experimental, and may be withdrawn or changed "x-" is marked experimental, and may be withdrawn or changed
incompatibly in a future release. Downstream vendors may add incompatibly in a future release. All names must begin with a letter,
extensions; such extensions should begin with a prefix matching and contain only ASCII letters, digits, dash, and underscore. There
"__RFQDN_" (for the reverse-fully-qualified-domain-name of the are two exceptions: enum values may start with a digit, and any
vendor), even if the rest of the name uses dash (example: extensions added by downstream vendors should start with a prefix
__com.redhat_drive-mirror). Other than downstream extensions (with matching "__RFQDN_" (for the reverse-fully-qualified-domain-name of
leading underscore and the use of dots), all names should begin with a the vendor), even if the rest of the name uses dash (example:
letter, and contain only ASCII letters, digits, dash, and underscore. __com.redhat_drive-mirror). Names beginning with 'q_' are reserved
Names beginning with 'q_' are reserved for the generator: QMP names for the generator: QMP names that resemble C keywords or other
that resemble C keywords or other problematic strings will be munged problematic strings will be munged in C to use this prefix. For
in C to use this prefix. For example, a field named "default" in example, a field named "default" in qapi becomes "q_default" in the
qapi becomes "q_default" in the generated C code. generated C code.
In the rest of this document, usage lines are given for each In the rest of this document, usage lines are given for each
expression type, with literal strings written in lower case and expression type, with literal strings written in lower case and

View File

@ -353,9 +353,11 @@ def discriminator_find_enum_define(expr):
return find_enum(discriminator_type) return find_enum(discriminator_type)
# FIXME should enforce "other than downstream extensions [...], all # Names must be letters, numbers, -, and _. They must start with letter,
# names should begin with a letter". # except for downstream extensions which must start with __RFQDN_.
valid_name = re.compile('^[a-zA-Z_][a-zA-Z0-9_.-]*$') # Dots are only valid in the downstream extension prefix.
valid_name = re.compile('^(__[a-zA-Z0-9.-]+_)?'
'[a-zA-Z][a-zA-Z0-9_-]*$')
def check_name(expr_info, source, name, allow_optional=False, def check_name(expr_info, source, name, allow_optional=False,
@ -374,8 +376,8 @@ def check_name(expr_info, source, name, allow_optional=False,
% (source, name)) % (source, name))
# Enum members can start with a digit, because the generated C # Enum members can start with a digit, because the generated C
# code always prefixes it with the enum name # code always prefixes it with the enum name
if enum_member: if enum_member and membername[0].isdigit():
membername = '_' + membername membername = 'D' + membername
# Reserve the entire 'q_' namespace for c_name() # Reserve the entire 'q_' namespace for c_name()
if not valid_name.match(membername) or \ if not valid_name.match(membername) or \
c_name(membername, False).startswith('q_'): c_name(membername, False).startswith('q_'):

View File

@ -318,9 +318,11 @@ qapi-schema += redefined-command.json
qapi-schema += redefined-event.json qapi-schema += redefined-event.json
qapi-schema += redefined-type.json qapi-schema += redefined-type.json
qapi-schema += reserved-command-q.json qapi-schema += reserved-command-q.json
qapi-schema += reserved-enum-q.json
qapi-schema += reserved-member-has.json qapi-schema += reserved-member-has.json
qapi-schema += reserved-member-q.json qapi-schema += reserved-member-q.json
qapi-schema += reserved-member-u.json qapi-schema += reserved-member-u.json
qapi-schema += reserved-member-underscore.json
qapi-schema += reserved-type-kind.json qapi-schema += reserved-type-kind.json
qapi-schema += reserved-type-list.json qapi-schema += reserved-type-list.json
qapi-schema += returns-alternate.json qapi-schema += returns-alternate.json

View File

@ -0,0 +1 @@
tests/qapi-schema/reserved-enum-q.json:4: Member of enum 'Foo' uses invalid name 'q-Unix'

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,4 @@
# C entity name collision
# We reject names like 'q-unix', because they can collide with the mangled
# name for 'unix' in generated C.
{ 'enum': 'Foo', 'data': [ 'unix', 'q-Unix' ] }

View File

View File

@ -0,0 +1 @@
tests/qapi-schema/reserved-member-underscore.json:4: Member of 'data' for struct 'Oops' uses invalid name '_oops'

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,4 @@
# C member name collision
# We reject use of a single leading underscore in all names (names must
# begin with a letter or a downstream extension double-underscore prefix).
{ 'struct': 'Oops', 'data': { '_oops': 'str' } }