diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt index c4264a819b..4d8c2fcf02 100644 --- a/docs/qapi-code-gen.txt +++ b/docs/qapi-code-gen.txt @@ -112,7 +112,9 @@ and field names within a type, should be all lower case with words separated by a hyphen. However, some existing older commands and complex types use underscore; when extending such expressions, consistency is preferred over blindly avoiding underscore. Event -names should be ALL_CAPS with words separated by underscore. +names should be ALL_CAPS with words separated by underscore. Field +names cannot start with 'has-' or 'has_', as this is reserved for +tracking optional fields. Any name (command, event, type, field, or enum value) beginning with "x-" is marked experimental, and may be withdrawn or changed @@ -123,9 +125,10 @@ vendor), even if the rest of the name uses dash (example: __com.redhat_drive-mirror). Other than downstream extensions (with leading underscore and the use of dots), all names should begin with a letter, and contain only ASCII letters, digits, dash, and underscore. -It is okay to reuse names that match C keywords; the generator will -rename a field named "default" in the QAPI to "q_default" in the -generated C code. +Names beginning with 'q_' are reserved for the generator: QMP names +that resemble C keywords or other problematic strings will be munged +in C to use this prefix. For example, a field named "default" in +qapi becomes "q_default" in the generated C code. In the rest of this document, usage lines are given for each expression type, with literal strings written in lower case and diff --git a/scripts/qapi.py b/scripts/qapi.py index d53b5c4b45..3ff7b11e61 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -376,7 +376,9 @@ def check_name(expr_info, source, name, allow_optional=False, # code always prefixes it with the enum name if enum_member: membername = '_' + membername - if not valid_name.match(membername): + # Reserve the entire 'q_' namespace for c_name() + if not valid_name.match(membername) or \ + c_name(membername, False).startswith('q_'): raise QAPIExprError(expr_info, "%s uses invalid name '%s'" % (source, name)) @@ -488,6 +490,10 @@ def check_type(expr_info, source, value, allow_array=False, for (key, arg) in value.items(): check_name(expr_info, "Member of %s" % source, key, allow_optional=allow_optional) + if c_name(key, False).startswith('has_'): + raise QAPIExprError(expr_info, + "Member of %s uses reserved name '%s'" + % (source, key)) # Todo: allow dictionaries to represent default values of # an optional argument. check_type(expr_info, "Member '%s' of %s" % (key, source), arg, diff --git a/tests/qapi-schema/reserved-command-q.err b/tests/qapi-schema/reserved-command-q.err index e69de29bb2..f939e044eb 100644 --- a/tests/qapi-schema/reserved-command-q.err +++ b/tests/qapi-schema/reserved-command-q.err @@ -0,0 +1 @@ +tests/qapi-schema/reserved-command-q.json:5: 'command' uses invalid name 'q-unix' diff --git a/tests/qapi-schema/reserved-command-q.exit b/tests/qapi-schema/reserved-command-q.exit index 573541ac97..d00491fd7e 100644 --- a/tests/qapi-schema/reserved-command-q.exit +++ b/tests/qapi-schema/reserved-command-q.exit @@ -1 +1 @@ -0 +1 diff --git a/tests/qapi-schema/reserved-command-q.json b/tests/qapi-schema/reserved-command-q.json index be9944c68a..99f8aae314 100644 --- a/tests/qapi-schema/reserved-command-q.json +++ b/tests/qapi-schema/reserved-command-q.json @@ -1,7 +1,5 @@ # C entity name collision -# FIXME - This parses, but fails to compile, because it attempts to declare -# two 'qmp_q_unix' functions (one for 'q-unix', the other because c_name() -# munges 'unix' to 'q_unix' to avoid reserved word collisions). We should -# reject attempts to explicitly use 'q_' names, to reserve it for qapi. +# We reject names like 'q-unix', because they can collide with the mangled +# name for 'unix' in generated C. { 'command': 'unix' } { 'command': 'q-unix' } diff --git a/tests/qapi-schema/reserved-command-q.out b/tests/qapi-schema/reserved-command-q.out index b31b38ff0d..e69de29bb2 100644 --- a/tests/qapi-schema/reserved-command-q.out +++ b/tests/qapi-schema/reserved-command-q.out @@ -1,5 +0,0 @@ -object :empty -command q-unix None -> None - gen=True success_response=True -command unix None -> None - gen=True success_response=True diff --git a/tests/qapi-schema/reserved-member-has.err b/tests/qapi-schema/reserved-member-has.err index e69de29bb2..e755771446 100644 --- a/tests/qapi-schema/reserved-member-has.err +++ b/tests/qapi-schema/reserved-member-has.err @@ -0,0 +1 @@ +tests/qapi-schema/reserved-member-has.json:5: Member of 'data' for command 'oops' uses reserved name 'has-a' diff --git a/tests/qapi-schema/reserved-member-has.exit b/tests/qapi-schema/reserved-member-has.exit index 573541ac97..d00491fd7e 100644 --- a/tests/qapi-schema/reserved-member-has.exit +++ b/tests/qapi-schema/reserved-member-has.exit @@ -1 +1 @@ -0 +1 diff --git a/tests/qapi-schema/reserved-member-has.json b/tests/qapi-schema/reserved-member-has.json index a2197de6b5..45b9109bdc 100644 --- a/tests/qapi-schema/reserved-member-has.json +++ b/tests/qapi-schema/reserved-member-has.json @@ -1,6 +1,5 @@ # C member name collision -# FIXME - This parses, but fails to compile, because the C struct is given -# two 'has_a' members, one from the flag for optional 'a', and the other -# from member 'has-a'. Either reject this at parse time, or munge the C -# names to avoid the collision. +# We reject names like 'has-a', because they can collide with the flag +# for an optional 'a' in generated C. +# TODO we could munge the optional flag name to avoid the collision. { 'command': 'oops', 'data': { '*a': 'str', 'has-a': 'str' } } diff --git a/tests/qapi-schema/reserved-member-has.out b/tests/qapi-schema/reserved-member-has.out index 5a18b6be8c..e69de29bb2 100644 --- a/tests/qapi-schema/reserved-member-has.out +++ b/tests/qapi-schema/reserved-member-has.out @@ -1,6 +0,0 @@ -object :empty -object :obj-oops-arg - member a: str optional=True - member has-a: str optional=False -command oops :obj-oops-arg -> None - gen=True success_response=True diff --git a/tests/qapi-schema/reserved-member-q.err b/tests/qapi-schema/reserved-member-q.err index e69de29bb2..f3d5dd7818 100644 --- a/tests/qapi-schema/reserved-member-q.err +++ b/tests/qapi-schema/reserved-member-q.err @@ -0,0 +1 @@ +tests/qapi-schema/reserved-member-q.json:4: Member of 'data' for struct 'Foo' uses invalid name 'q-unix' diff --git a/tests/qapi-schema/reserved-member-q.exit b/tests/qapi-schema/reserved-member-q.exit index 573541ac97..d00491fd7e 100644 --- a/tests/qapi-schema/reserved-member-q.exit +++ b/tests/qapi-schema/reserved-member-q.exit @@ -1 +1 @@ -0 +1 diff --git a/tests/qapi-schema/reserved-member-q.json b/tests/qapi-schema/reserved-member-q.json index 1602ed3281..62fed8fddf 100644 --- a/tests/qapi-schema/reserved-member-q.json +++ b/tests/qapi-schema/reserved-member-q.json @@ -1,6 +1,4 @@ # C member name collision -# FIXME - This parses, but fails to compile, because it attempts to declare -# two 'q_unix' members (one for 'q-unix', the other because c_name() -# munges 'unix' to 'q_unix' to avoid reserved word collisions). We should -# reject attempts to explicitly use 'q_' names, to reserve it for qapi. +# We reject names like 'q-unix', because they can collide with the mangled +# name for 'unix' in generated C. { 'struct': 'Foo', 'data': { 'unix':'int', 'q-unix':'bool' } } diff --git a/tests/qapi-schema/reserved-member-q.out b/tests/qapi-schema/reserved-member-q.out index 0d8685aeb0..e69de29bb2 100644 --- a/tests/qapi-schema/reserved-member-q.out +++ b/tests/qapi-schema/reserved-member-q.out @@ -1,4 +0,0 @@ -object :empty -object Foo - member unix: int optional=False - member q-unix: bool optional=False