aboutsummaryrefslogtreecommitdiff
path: root/scripts/qapi/expr.py
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2021-03-23 22:28:58 +0000
committerPeter Maydell <peter.maydell@linaro.org>2021-03-23 22:28:58 +0000
commit266469947161aa10b1d36843580d369d5aa38589 (patch)
treea7c31491b7f1bd3540822c7350308ad44ece7ad9 /scripts/qapi/expr.py
parent1a4d83b5643e8e965cbc16950f78066a7cd27cb4 (diff)
parentbdabafc6836edc0f34732cac473899cb4e77a296 (diff)
downloadqemu-266469947161aa10b1d36843580d369d5aa38589.zip
qemu-266469947161aa10b1d36843580d369d5aa38589.tar.gz
qemu-266469947161aa10b1d36843580d369d5aa38589.tar.bz2
Merge remote-tracking branch 'remotes/armbru/tags/pull-qapi-2021-03-23' into staging
QAPI patches patches for 2021-03-23 # gpg: Signature made Tue 23 Mar 2021 21:37:53 GMT # 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-2021-03-23: (29 commits) block: Remove monitor command block_passwd qapi: Enforce union and alternate branch naming rules qapi: Enforce enum member naming rules qapi: Enforce struct member naming rules tests/qapi-schema: Switch member name clash test to struct qapi: Enforce command naming rules qapi: Enforce feature naming rules qapi: Prepare for rejecting underscore in command and member names tests-qmp-cmds: Drop unused and incorrect qmp_TestIfCmd() qapi/pragma: Streamline comments on member-name-exceptions qapi: Rename pragma *-whitelist to *-exceptions tests/qapi-schema: Rename returns-whitelist to returns-bad-type tests/qapi-schema: Rename pragma-*-crap to pragma-value-not-* qapi: Factor out QAPISchemaParser._check_pragma_list_of_str() tests/qapi-schema: Rename redefined-builtin to redefined-predefined qapi: Enforce type naming rules qapi: Enforce event naming rules qapi: Consistently permit any case in downstream prefixes qapi: Move uppercase rejection to check_name_lower() qapi: Rework name checking in preparation of stricter checking ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'scripts/qapi/expr.py')
-rw-r--r--scripts/qapi/expr.py94
1 files changed, 61 insertions, 33 deletions
diff --git a/scripts/qapi/expr.py b/scripts/qapi/expr.py
index 2fcaaa2..540b398 100644
--- a/scripts/qapi/expr.py
+++ b/scripts/qapi/expr.py
@@ -21,11 +21,12 @@ from .common import c_name
from .error import QAPISemError
-# 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.
-valid_name = re.compile(r'^(__[a-zA-Z0-9.-]+_)?'
- '[a-zA-Z][a-zA-Z0-9_-]*$')
+# Names consist of letters, digits, -, and _, starting with a letter.
+# An experimental name is prefixed with x-. A name of a downstream
+# extension is prefixed with __RFQDN_. The latter prefix goes first.
+valid_name = re.compile(r'(__[a-z0-9.-]+_)?'
+ r'(x-)?'
+ r'([a-z][a-z0-9_-]*)$', re.IGNORECASE)
def check_name_is_str(name, info, source):
@@ -33,30 +34,47 @@ def check_name_is_str(name, info, source):
raise QAPISemError(info, "%s requires a string name" % source)
-def check_name_str(name, info, source,
- allow_optional=False, enum_member=False,
- permit_upper=False):
- membername = name
-
- if allow_optional and name.startswith('*'):
- membername = name[1:]
- # Enum members can start with a digit, because the generated C
- # code always prefixes it with the enum name
- if enum_member and membername[0].isdigit():
- membername = 'D' + membername
+def check_name_str(name, info, source):
# Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
# and 'q_obj_*' implicit type names.
- if not valid_name.match(membername) or \
- c_name(membername, False).startswith('q_'):
+ match = valid_name.match(name)
+ if not match or c_name(name, False).startswith('q_'):
raise QAPISemError(info, "%s has an invalid name" % source)
- if not permit_upper and name.lower() != name:
+ return match.group(3)
+
+
+def check_name_upper(name, info, source):
+ stem = check_name_str(name, info, source)
+ if re.search(r'[a-z-]', stem):
raise QAPISemError(
- info, "%s uses uppercase in name" % source)
- assert not membername.startswith('*')
+ info, "name of %s must not use lowercase or '-'" % source)
+
+
+def check_name_lower(name, info, source,
+ permit_upper=False,
+ permit_underscore=False):
+ stem = check_name_str(name, info, source)
+ if ((not permit_upper and re.search(r'[A-Z]', stem))
+ or (not permit_underscore and '_' in stem)):
+ raise QAPISemError(
+ info, "name of %s must not use uppercase or '_'" % source)
+
+
+def check_name_camel(name, info, source):
+ stem = check_name_str(name, info, source)
+ if not re.match(r'[A-Z][A-Za-z0-9]*[a-z][A-Za-z0-9]*$', stem):
+ raise QAPISemError(info, "name of %s must use CamelCase" % source)
def check_defn_name_str(name, info, meta):
- check_name_str(name, info, meta, permit_upper=True)
+ if meta == 'event':
+ check_name_upper(name, info, meta)
+ elif meta == 'command':
+ check_name_lower(
+ name, info, meta,
+ permit_underscore=name in info.pragma.command_name_exceptions)
+ else:
+ check_name_camel(name, info, meta)
if name.endswith('Kind') or name.endswith('List'):
raise QAPISemError(
info, "%s name should not end in '%s'" % (meta, name[-4:]))
@@ -166,13 +184,16 @@ def check_type(value, info, source,
raise QAPISemError(info,
"%s should be an object or type name" % source)
- permit_upper = allow_dict in info.pragma.name_case_whitelist
+ permissive = allow_dict in info.pragma.member_name_exceptions
# value is a dictionary, check that each member is okay
for (key, arg) in value.items():
key_source = "%s member '%s'" % (source, key)
- check_name_str(key, info, key_source,
- allow_optional=True, permit_upper=permit_upper)
+ if key.startswith('*'):
+ key = key[1:]
+ check_name_lower(key, info, key_source,
+ permit_upper=permissive,
+ permit_underscore=permissive)
if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
raise QAPISemError(info, "%s uses reserved name" % key_source)
check_keys(arg, info, key_source, ['type'], ['if', 'features'])
@@ -194,7 +215,7 @@ def check_features(features, info):
check_keys(f, info, source, ['name'], ['if'])
check_name_is_str(f['name'], info, source)
source = "%s '%s'" % (source, f['name'])
- check_name_str(f['name'], info, source)
+ check_name_lower(f['name'], info, source)
check_if(f, info, source)
@@ -208,17 +229,22 @@ def check_enum(expr, info):
if prefix is not None and not isinstance(prefix, str):
raise QAPISemError(info, "'prefix' must be a string")
- permit_upper = name in info.pragma.name_case_whitelist
+ permissive = name in info.pragma.member_name_exceptions
members[:] = [m if isinstance(m, dict) else {'name': m}
for m in members]
for member in members:
source = "'data' member"
+ member_name = member['name']
check_keys(member, info, source, ['name'], ['if'])
- check_name_is_str(member['name'], info, source)
- source = "%s '%s'" % (source, member['name'])
- check_name_str(member['name'], info, source,
- enum_member=True, permit_upper=permit_upper)
+ check_name_is_str(member_name, info, source)
+ source = "%s '%s'" % (source, member_name)
+ # Enum members may start with a digit
+ if member_name[0].isdigit():
+ member_name = 'd' + member_name # Hack: hide the digit
+ check_name_lower(member_name, info, source,
+ permit_upper=permissive,
+ permit_underscore=permissive)
check_if(member, info, source)
@@ -247,7 +273,9 @@ def check_union(expr, info):
for (key, value) in members.items():
source = "'data' member '%s'" % key
- check_name_str(key, info, source)
+ if discriminator is None:
+ check_name_lower(key, info, source)
+ # else: name is in discriminator enum, which gets checked
check_keys(value, info, source, ['type'], ['if'])
check_if(value, info, source)
check_type(value['type'], info, source, allow_array=not base)
@@ -260,7 +288,7 @@ def check_alternate(expr, info):
raise QAPISemError(info, "'data' must not be empty")
for (key, value) in members.items():
source = "'data' member '%s'" % key
- check_name_str(key, info, source)
+ check_name_lower(key, info, source)
check_keys(value, info, source, ['type'], ['if'])
check_if(value, info, source)
check_type(value['type'], info, source)