Message ID | 1443497249-15361-6-git-send-email-eblake@redhat.com |
---|---|
State | New |
Headers | show |
Eric Blake <eblake@redhat.com> writes: > Expose some weaknesses in the generator: we don't always forbid > the generation of structs that contain multiple members that map > to the same C name. C or QMP name, perhaps? > This has already been marked FIXME in > qapi.py in commit d90675f, but having more tests will make sure > future patches produce desired behavior. This patch focuses on > C struct members, and does not consider collisions between commands > and events (affecting C function names), or even on collisions > between generated C type names with user type names (for things > like automatic FOOList struct representing array types). > > There are two types of struct collisions we want to catch: > 1) Collision that would require the QMP user to specify the > same key more than once inside a single JSON {} object (we > already prevent duplicate keys within a single type, so this > is triggered when merging multiple types into a single JSON > object via base classes or flat unions) I understand what you want to say here, but find it a bit awkward. Perhaps: 1) Collision between two keys in a JSON object. qapi.py should prevent that, but currently fails to do so for collisions between a type's members, its base type's members, and its flat union variant members. Could add pointers to the tests covering this type of collision. > 2) Collision between two members of the C struct that is > generated for a given qapi type: We generally spell it QAPI. > a) a collision occurs from two qapi names mapping to the > same C name > b) the collision is with a C name used for another purpose > (we already fixed 'kind' in commit 0f61af3e, and one use of > 'base' in commit 1e6c1616 for unions, but we have another > use of 'base' in structs, and still collide with 'data' in > unions, not to mention the tag 'type'/'kind' and the various > tag values). I'm not sure this makes sense to readers who aren't already familiar with our name clashing issues. Perhaps: 2) Collision between members of the C struct that is generated for a given qapi type. Two cases: a) Multiple QAPI names map to the same C name. b) A QAPI name maps to a C name that is used for another purpose. We already fixed such cases in commit 0f61af3e and commit 1e6c1616, but more cases are left. Avoids getting bogged down in details prematurely. Could add pointers to the tests covering each type of collision. > Oddly enough, while commit 1e6c1616 fixed a type 2b collision > with 'base', What collided with 'base' before 1e6c1616 and not after? > it introduced a different collision that was not > previously possible: now that the base members are no longer > boxed behind 'base', they can collide with the enum tag members > (used as member names within the embedded C union). > > Ultimately, if we need to have a flat union where an enum > value clashes with a base member name, we could change the Suggest "an enum tag member", because this is confusing enough without using multiple names for the same thing :) > generator to use something like '_tag_value' instead of > 'value' for the C name of union members; Or wrap the base in a struct, or give the union member a name. > but until such a > need arises, it will probably be easier to just forbid these > collisions. Again, I'm not sure this makes sense to readers who aren't already familiar with our name clashing issues. Do you fix any of this later in your series? If yes, you could punt detailed bug descriptions to the patches that fixes them. > Some of these collisions are already caught in the old-style > parser checks, but ultimately we want all collisions to be > caught in the new-style QAPISchema*.check() methods. > > Some of these negative tests will be deleted later, and positive > tests added to qapi-schema-test.json in their place, when the > generator code is reworked so that a particular collision no > longer causes failed code generation. > > [git rename detection may be a bit confused by the actions of > this commit, since it both creates and renames files that are > equally small and similar in content] Why square brackets? > Signed-off-by: Eric Blake <eblake@redhat.com> > > --- > v6: rework commit message, alter test names and comments to be > more descriptive, repurpose flat-union-cycle to better match its > name (and not duplicate what the renamed flat-union-clash-branch > tests), add new tests (union-clash-type, flat-union-clash-type) > that detect an assertion failures > --- > tests/Makefile | 10 +++++++++- > .../{flat-union-branch-clash.out => args-name-clash.err} | 0 > tests/qapi-schema/args-name-clash.exit | 1 + > tests/qapi-schema/args-name-clash.json | 2 ++ > tests/qapi-schema/args-name-clash.out | 6 ++++++ > tests/qapi-schema/flat-union-branch-clash.err | 1 - > tests/qapi-schema/flat-union-clash-branch.err | 0 > tests/qapi-schema/flat-union-clash-branch.exit | 1 + > tests/qapi-schema/flat-union-clash-branch.json | 15 +++++++++++++++ > tests/qapi-schema/flat-union-clash-branch.out | 14 ++++++++++++++ > tests/qapi-schema/flat-union-clash-member.err | 1 + > ...on-branch-clash.exit => flat-union-clash-member.exit} | 0 > ...on-branch-clash.json => flat-union-clash-member.json} | 2 +- > tests/qapi-schema/flat-union-clash-member.out | 0 > tests/qapi-schema/flat-union-clash-type.err | 16 ++++++++++++++++ > tests/qapi-schema/flat-union-clash-type.exit | 1 + > tests/qapi-schema/flat-union-clash-type.json | 11 +++++++++++ > tests/qapi-schema/flat-union-clash-type.out | 0 > tests/qapi-schema/flat-union-cycle.err | 1 + > tests/qapi-schema/flat-union-cycle.exit | 1 + > tests/qapi-schema/flat-union-cycle.json | 7 +++++++ > tests/qapi-schema/flat-union-cycle.out | 0 > tests/qapi-schema/qapi-schema-test.json | 7 +++++-- > tests/qapi-schema/qapi-schema-test.out | 2 ++ > tests/qapi-schema/struct-base-clash-base.err | 0 > tests/qapi-schema/struct-base-clash-base.exit | 1 + > tests/qapi-schema/struct-base-clash-base.json | 6 ++++++ > tests/qapi-schema/struct-base-clash-base.out | 5 +++++ > tests/qapi-schema/union-clash-data.err | 0 > tests/qapi-schema/union-clash-data.exit | 1 + > tests/qapi-schema/union-clash-data.json | 4 ++++ > tests/qapi-schema/union-clash-data.out | 6 ++++++ > tests/qapi-schema/union-clash-members.err | 1 + > tests/qapi-schema/union-clash-members.exit | 1 + > tests/qapi-schema/union-clash-members.json | 3 +++ > tests/qapi-schema/union-clash-members.out | 0 > tests/qapi-schema/union-clash-type.err | 16 ++++++++++++++++ > tests/qapi-schema/union-clash-type.exit | 1 + > tests/qapi-schema/union-clash-type.json | 3 +++ > tests/qapi-schema/union-clash-type.out | 0 > 40 files changed, 142 insertions(+), 5 deletions(-) > rename tests/qapi-schema/{flat-union-branch-clash.out => args-name-clash.err} (100%) > create mode 100644 tests/qapi-schema/args-name-clash.exit > create mode 100644 tests/qapi-schema/args-name-clash.json > create mode 100644 tests/qapi-schema/args-name-clash.out > delete mode 100644 tests/qapi-schema/flat-union-branch-clash.err > create mode 100644 tests/qapi-schema/flat-union-clash-branch.err > create mode 100644 tests/qapi-schema/flat-union-clash-branch.exit > create mode 100644 tests/qapi-schema/flat-union-clash-branch.json > create mode 100644 tests/qapi-schema/flat-union-clash-branch.out > create mode 100644 tests/qapi-schema/flat-union-clash-member.err > rename tests/qapi-schema/{flat-union-branch-clash.exit => flat-union-clash-member.exit} (100%) > rename tests/qapi-schema/{flat-union-branch-clash.json => flat-union-clash-member.json} (85%) > create mode 100644 tests/qapi-schema/flat-union-clash-member.out > create mode 100644 tests/qapi-schema/flat-union-clash-type.err > create mode 100644 tests/qapi-schema/flat-union-clash-type.exit > create mode 100644 tests/qapi-schema/flat-union-clash-type.json > create mode 100644 tests/qapi-schema/flat-union-clash-type.out > create mode 100644 tests/qapi-schema/flat-union-cycle.err > create mode 100644 tests/qapi-schema/flat-union-cycle.exit > create mode 100644 tests/qapi-schema/flat-union-cycle.json > create mode 100644 tests/qapi-schema/flat-union-cycle.out > create mode 100644 tests/qapi-schema/struct-base-clash-base.err > create mode 100644 tests/qapi-schema/struct-base-clash-base.exit > create mode 100644 tests/qapi-schema/struct-base-clash-base.json > create mode 100644 tests/qapi-schema/struct-base-clash-base.out > create mode 100644 tests/qapi-schema/union-clash-data.err > create mode 100644 tests/qapi-schema/union-clash-data.exit > create mode 100644 tests/qapi-schema/union-clash-data.json > create mode 100644 tests/qapi-schema/union-clash-data.out > create mode 100644 tests/qapi-schema/union-clash-members.err > create mode 100644 tests/qapi-schema/union-clash-members.exit > create mode 100644 tests/qapi-schema/union-clash-members.json > create mode 100644 tests/qapi-schema/union-clash-members.out > create mode 100644 tests/qapi-schema/union-clash-type.err > create mode 100644 tests/qapi-schema/union-clash-type.exit > create mode 100644 tests/qapi-schema/union-clash-type.json > create mode 100644 tests/qapi-schema/union-clash-type.out > > diff --git a/tests/Makefile b/tests/Makefile > index 6fd5885..d43b8e9 100644 > --- a/tests/Makefile > +++ b/tests/Makefile > @@ -238,6 +238,7 @@ qapi-schema += args-invalid.json > qapi-schema += args-member-array-bad.json > qapi-schema += args-member-array.json > qapi-schema += args-member-unknown.json > +qapi-schema += args-name-clash.json > qapi-schema += args-union.json > qapi-schema += args-unknown.json > qapi-schema += bad-base.json > @@ -273,7 +274,10 @@ qapi-schema += flat-union-bad-base.json > qapi-schema += flat-union-bad-discriminator.json > qapi-schema += flat-union-base-any.json > qapi-schema += flat-union-base-union.json > -qapi-schema += flat-union-branch-clash.json > +qapi-schema += flat-union-clash-branch.json > +qapi-schema += flat-union-clash-member.json > +qapi-schema += flat-union-clash-type.json > +qapi-schema += flat-union-cycle.json > qapi-schema += flat-union-inline.json > qapi-schema += flat-union-int-branch.json > qapi-schema += flat-union-invalid-branch-key.json > @@ -315,6 +319,7 @@ qapi-schema += returns-dict.json > qapi-schema += returns-int.json > qapi-schema += returns-unknown.json > qapi-schema += returns-whitelist.json > +qapi-schema += struct-base-clash-base.json > qapi-schema += struct-base-clash-deep.json > qapi-schema += struct-base-clash.json > qapi-schema += struct-data-invalid.json > @@ -328,6 +333,9 @@ qapi-schema += unclosed-string.json > qapi-schema += unicode-str.json > qapi-schema += union-bad-branch.json > qapi-schema += union-base-no-discriminator.json > +qapi-schema += union-clash-data.json > +qapi-schema += union-clash-members.json > +qapi-schema += union-clash-type.json > qapi-schema += union-invalid-base.json > qapi-schema += union-max.json > qapi-schema += union-optional-branch.json > diff --git a/tests/qapi-schema/flat-union-branch-clash.out b/tests/qapi-schema/args-name-clash.err > similarity index 100% > rename from tests/qapi-schema/flat-union-branch-clash.out > rename to tests/qapi-schema/args-name-clash.err > diff --git a/tests/qapi-schema/args-name-clash.exit b/tests/qapi-schema/args-name-clash.exit > new file mode 100644 > index 0000000..573541a > --- /dev/null > +++ b/tests/qapi-schema/args-name-clash.exit > @@ -0,0 +1 @@ > +0 > diff --git a/tests/qapi-schema/args-name-clash.json b/tests/qapi-schema/args-name-clash.json > new file mode 100644 > index 0000000..19bf792 > --- /dev/null > +++ b/tests/qapi-schema/args-name-clash.json > @@ -0,0 +1,2 @@ > +# FIXME - we should reject data with members that clash when mapped to C names > +{ 'command': 'oops', 'data': { 'a-b': 'str', 'a_b': 'str' } } Here, the collision is fairly obvious. We could spell it out anyway: # Multiple members mapping to the same C identifier: both 'a-b' and # 'a_b' map to a_b' # FIXME they clash in generated C, should reject them cleanly instead. Unfortunately, the test doesn't actually make the clash visible. Same for all the other tests that produce clashes in C or QMP without actually upsetting the generator. Oh well, good enough. > diff --git a/tests/qapi-schema/args-name-clash.out b/tests/qapi-schema/args-name-clash.out > new file mode 100644 > index 0000000..9b2f6e4 > --- /dev/null > +++ b/tests/qapi-schema/args-name-clash.out > @@ -0,0 +1,6 @@ > +object :empty > +object :obj-oops-arg > + member a-b: str optional=False > + member a_b: str optional=False > +command oops :obj-oops-arg -> None > + gen=True success_response=True > diff --git a/tests/qapi-schema/flat-union-branch-clash.err b/tests/qapi-schema/flat-union-branch-clash.err > deleted file mode 100644 > index f112766..0000000 > --- a/tests/qapi-schema/flat-union-branch-clash.err > +++ /dev/null > @@ -1 +0,0 @@ > -tests/qapi-schema/flat-union-branch-clash.json:10: Member name 'name' of branch 'value1' clashes with base 'Base' > diff --git a/tests/qapi-schema/flat-union-clash-branch.err b/tests/qapi-schema/flat-union-clash-branch.err > new file mode 100644 > index 0000000..e69de29 > diff --git a/tests/qapi-schema/flat-union-clash-branch.exit b/tests/qapi-schema/flat-union-clash-branch.exit > new file mode 100644 > index 0000000..573541a > --- /dev/null > +++ b/tests/qapi-schema/flat-union-clash-branch.exit > @@ -0,0 +1 @@ > +0 > diff --git a/tests/qapi-schema/flat-union-clash-branch.json b/tests/qapi-schema/flat-union-clash-branch.json > new file mode 100644 > index 0000000..cda3af5 > --- /dev/null > +++ b/tests/qapi-schema/flat-union-clash-branch.json > @@ -0,0 +1,15 @@ > +# FIXME: this parses, but then fails to compile due to a duplicate 'c_d' > +# we should either detect the collision or allow this to compile Here, the collision is less obvious, mostly because it gets lost in the scaffolding. Let's spell it out: # Base members and tag enum members mapping to the same C identifier: # both 'c-d' and 'c_d' map to c_d' # FIXME they clash in generated C, should reject them cleanly instead. > +{ 'enum': 'TestEnum', > + 'data': [ 'base', 'c-d' ] } > +{ 'struct': 'Base', > + 'data': { 'enum1': 'TestEnum', '*c_d': 'str' } } > +{ 'struct': 'Branch1', > + 'data': { 'string': 'str' } } > +{ 'struct': 'Branch2', > + 'data': { 'value': 'int' } } > +{ 'union': 'TestUnion', > + 'base': 'Base', > + 'discriminator': 'enum1', > + 'data': { 'base': 'Branch1', > + 'c-d': 'Branch2' } } > diff --git a/tests/qapi-schema/flat-union-clash-branch.out b/tests/qapi-schema/flat-union-clash-branch.out > new file mode 100644 > index 0000000..8e0da73 > --- /dev/null > +++ b/tests/qapi-schema/flat-union-clash-branch.out > @@ -0,0 +1,14 @@ > +object :empty > +object Base > + member enum1: TestEnum optional=False > + member c_d: str optional=True > +object Branch1 > + member string: str optional=False > +object Branch2 > + member value: int optional=False > +enum TestEnum ['base', 'c-d'] > +object TestUnion > + base Base > + tag enum1 > + case base: Branch1 > + case c-d: Branch2 > diff --git a/tests/qapi-schema/flat-union-clash-member.err b/tests/qapi-schema/flat-union-clash-member.err > new file mode 100644 > index 0000000..152d402 > --- /dev/null > +++ b/tests/qapi-schema/flat-union-clash-member.err > @@ -0,0 +1 @@ > +tests/qapi-schema/flat-union-clash-member.json:10: Member name 'name' of branch 'value1' clashes with base 'Base' > diff --git a/tests/qapi-schema/flat-union-branch-clash.exit b/tests/qapi-schema/flat-union-clash-member.exit > similarity index 100% > rename from tests/qapi-schema/flat-union-branch-clash.exit > rename to tests/qapi-schema/flat-union-clash-member.exit > diff --git a/tests/qapi-schema/flat-union-branch-clash.json b/tests/qapi-schema/flat-union-clash-member.json > similarity index 85% > rename from tests/qapi-schema/flat-union-branch-clash.json > rename to tests/qapi-schema/flat-union-clash-member.json > index 8fb054f..3d7e6c6 100644 > --- a/tests/qapi-schema/flat-union-branch-clash.json > +++ b/tests/qapi-schema/flat-union-clash-member.json > @@ -1,4 +1,4 @@ > -# we check for no duplicate keys between branches and base > +# we check for no duplicate keys between branch members and base Spelling out the clashing members wouldn't hurt. > { 'enum': 'TestEnum', > 'data': [ 'value1', 'value2' ] } > { 'struct': 'Base', > diff --git a/tests/qapi-schema/flat-union-clash-member.out b/tests/qapi-schema/flat-union-clash-member.out > new file mode 100644 > index 0000000..e69de29 > diff --git a/tests/qapi-schema/flat-union-clash-type.err b/tests/qapi-schema/flat-union-clash-type.err > new file mode 100644 > index 0000000..6e64d1d > --- /dev/null > +++ b/tests/qapi-schema/flat-union-clash-type.err > @@ -0,0 +1,16 @@ > +Traceback (most recent call last): > + File "tests/qapi-schema/test-qapi.py", line 55, in <module> > + schema = QAPISchema(sys.argv[1]) > + File "scripts/qapi.py", line 1116, in __init__ > + self.check() > + File "scripts/qapi.py", line 1299, in check > + ent.check(self) > + File "scripts/qapi.py", line 962, in check > + self.variants.check(schema, members, seen) > + File "scripts/qapi.py", line 1024, in check > + v.check(schema, self.tag_member.type, vseen) > + File "scripts/qapi.py", line 1032, in check > + QAPISchemaObjectTypeMember.check(self, schema, [], seen) > + File "scripts/qapi.py", line 994, in check > + assert self.name not in seen > +AssertionError > diff --git a/tests/qapi-schema/flat-union-clash-type.exit b/tests/qapi-schema/flat-union-clash-type.exit > new file mode 100644 > index 0000000..d00491f > --- /dev/null > +++ b/tests/qapi-schema/flat-union-clash-type.exit > @@ -0,0 +1 @@ > +1 > diff --git a/tests/qapi-schema/flat-union-clash-type.json b/tests/qapi-schema/flat-union-clash-type.json > new file mode 100644 > index 0000000..87e5fd8 > --- /dev/null > +++ b/tests/qapi-schema/flat-union-clash-type.json > @@ -0,0 +1,11 @@ > +# FIXME: detect this collision, rather than triggering an assertion failure Here, the collision is unobvious. Could you spell it out, please? > +{ 'enum': 'TestEnum', > + 'data': [ 'type' ] } > +{ 'struct': 'Base', > + 'data': { 'type': 'TestEnum' } } > +{ 'struct': 'Branch1', > + 'data': { 'string': 'str' } } > +{ 'union': 'TestUnion', > + 'base': 'Base', > + 'discriminator': 'type', > + 'data': { 'type': 'Branch1' } } > diff --git a/tests/qapi-schema/flat-union-clash-type.out b/tests/qapi-schema/flat-union-clash-type.out > new file mode 100644 > index 0000000..e69de29 > diff --git a/tests/qapi-schema/flat-union-cycle.err b/tests/qapi-schema/flat-union-cycle.err > new file mode 100644 > index 0000000..d4f2a09 > --- /dev/null > +++ b/tests/qapi-schema/flat-union-cycle.err > @@ -0,0 +1 @@ > +tests/qapi-schema/flat-union-cycle.json:6: Base 'Union' is not a valid struct > diff --git a/tests/qapi-schema/flat-union-cycle.exit b/tests/qapi-schema/flat-union-cycle.exit > new file mode 100644 > index 0000000..d00491f > --- /dev/null > +++ b/tests/qapi-schema/flat-union-cycle.exit > @@ -0,0 +1 @@ > +1 > diff --git a/tests/qapi-schema/flat-union-cycle.json b/tests/qapi-schema/flat-union-cycle.json > new file mode 100644 > index 0000000..7414277 > --- /dev/null > +++ b/tests/qapi-schema/flat-union-cycle.json > @@ -0,0 +1,7 @@ > +# ensure that we have a sane error message for attempts at self-inheritance > +# (this test currently fails because we don't permit a union base, but > +# even if we relax things to allow that in the future, this should still fail) Humor me: start sentences with a capital letter and end them with punctuation. Headlines don't end with punctuation, but still start with a capital letter. > +{ 'enum': 'Enum', 'data': [ 'okay' ] } > +{ 'struct': 'Okay', 'data': { 'int': 'int' } } > +{ 'union': 'Union', 'base': 'Union', 'discriminator': 'switch', > + 'data': { 'okay': 'Okay' } } Currently tests the exact same thing as flat-union-base-union.json, doesn't it? > diff --git a/tests/qapi-schema/flat-union-cycle.out b/tests/qapi-schema/flat-union-cycle.out > new file mode 100644 > index 0000000..e69de29 > diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json > index 6897a6e..c511338 100644 > --- a/tests/qapi-schema/qapi-schema-test.json > +++ b/tests/qapi-schema/qapi-schema-test.json > @@ -32,11 +32,14 @@ > 'dict1': 'UserDefTwoDict' } } > > # for testing unions > +# Among other things, test that a name collision between branches does > +# not cause any problems (since only one branch can be in use at a time), > +# by intentionally using two branches that both have a C member 'a_b' > { 'struct': 'UserDefA', > - 'data': { 'boolean': 'bool' } } > + 'data': { 'boolean': 'bool', '*a_b': 'int' } } > > { 'struct': 'UserDefB', > - 'data': { 'intb': 'int' } } > + 'data': { 'intb': 'int', '*a-b': 'bool' } } > > { 'union': 'UserDefFlatUnion', > 'base': 'UserDefUnionBase', # intentional forward reference > diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out > index 1f6e858..28a0b3c 100644 > --- a/tests/qapi-schema/qapi-schema-test.out > +++ b/tests/qapi-schema/qapi-schema-test.out > @@ -71,6 +71,7 @@ enum QEnumTwo ['value1', 'value2'] > prefix QENUM_TWO > object UserDefA > member boolean: bool optional=False > + member a_b: int optional=True > alternate UserDefAlternate > case uda: UserDefA > case s: str > @@ -78,6 +79,7 @@ alternate UserDefAlternate > enum UserDefAlternateKind ['uda', 's', 'i'] > object UserDefB > member intb: int optional=False > + member a-b: bool optional=True > object UserDefC > member string1: str optional=False > member string2: str optional=False > diff --git a/tests/qapi-schema/struct-base-clash-base.err b/tests/qapi-schema/struct-base-clash-base.err > new file mode 100644 > index 0000000..e69de29 > diff --git a/tests/qapi-schema/struct-base-clash-base.exit b/tests/qapi-schema/struct-base-clash-base.exit > new file mode 100644 > index 0000000..573541a > --- /dev/null > +++ b/tests/qapi-schema/struct-base-clash-base.exit > @@ -0,0 +1 @@ > +0 > diff --git a/tests/qapi-schema/struct-base-clash-base.json b/tests/qapi-schema/struct-base-clash-base.json > new file mode 100644 > index 0000000..fb25128 > --- /dev/null > +++ b/tests/qapi-schema/struct-base-clash-base.json > @@ -0,0 +1,6 @@ > +# FIXME: this parses, but then fails to compile due to a duplicate 'base' > +# we should either detect the collision or allow this to compile Suggest: # Struct type member 'base' # FIXME clashes in generated C, should either reject 'base' cleanly or # make it work. > +{ 'struct': 'Base', 'data': {} } > +{ 'struct': 'Sub', > + 'base': 'Base', > + 'data': { 'base': 'str' } } > diff --git a/tests/qapi-schema/struct-base-clash-base.out b/tests/qapi-schema/struct-base-clash-base.out > new file mode 100644 > index 0000000..e69a416 > --- /dev/null > +++ b/tests/qapi-schema/struct-base-clash-base.out > @@ -0,0 +1,5 @@ > +object :empty > +object Base > +object Sub > + base Base > + member base: str optional=False > diff --git a/tests/qapi-schema/union-clash-data.err b/tests/qapi-schema/union-clash-data.err > new file mode 100644 > index 0000000..e69de29 > diff --git a/tests/qapi-schema/union-clash-data.exit b/tests/qapi-schema/union-clash-data.exit > new file mode 100644 > index 0000000..573541a > --- /dev/null > +++ b/tests/qapi-schema/union-clash-data.exit > @@ -0,0 +1 @@ > +0 > diff --git a/tests/qapi-schema/union-clash-data.json b/tests/qapi-schema/union-clash-data.json > new file mode 100644 > index 0000000..322f61b > --- /dev/null > +++ b/tests/qapi-schema/union-clash-data.json > @@ -0,0 +1,4 @@ > +# FIXME: this parses, but then fails to compile due to a duplicate 'data' > +# we should either detect the collision or allow this to compile Suggest: # Union tag member 'data' # FIXME clashes in generated C, should either reject 'data' cleanly or # make it work. > +{ 'union': 'TestUnion', > + 'data': { 'data': 'int' } } > diff --git a/tests/qapi-schema/union-clash-data.out b/tests/qapi-schema/union-clash-data.out > new file mode 100644 > index 0000000..6277239 > --- /dev/null > +++ b/tests/qapi-schema/union-clash-data.out > @@ -0,0 +1,6 @@ > +object :empty > +object :obj-int-wrapper > + member data: int optional=False > +object TestUnion > + case data: :obj-int-wrapper > +enum TestUnionKind ['data'] > diff --git a/tests/qapi-schema/union-clash-members.err b/tests/qapi-schema/union-clash-members.err > new file mode 100644 > index 0000000..b77aeb8 > --- /dev/null > +++ b/tests/qapi-schema/union-clash-members.err > @@ -0,0 +1 @@ > +tests/qapi-schema/union-clash-members.json:2: Union 'TestUnion' member 'a_b' clashes with 'a-b' > diff --git a/tests/qapi-schema/union-clash-members.exit b/tests/qapi-schema/union-clash-members.exit > new file mode 100644 > index 0000000..d00491f > --- /dev/null > +++ b/tests/qapi-schema/union-clash-members.exit > @@ -0,0 +1 @@ > +1 > diff --git a/tests/qapi-schema/union-clash-members.json b/tests/qapi-schema/union-clash-members.json > new file mode 100644 > index 0000000..0393ed8 > --- /dev/null > +++ b/tests/qapi-schema/union-clash-members.json > @@ -0,0 +1,3 @@ > +# we reject unions where branch names clash when mapped to C > +{ 'union': 'TestUnion', > + 'data': { 'a-b': 'int', 'a_b': 'str' } } > diff --git a/tests/qapi-schema/union-clash-members.out b/tests/qapi-schema/union-clash-members.out > new file mode 100644 > index 0000000..e69de29 > diff --git a/tests/qapi-schema/union-clash-type.err b/tests/qapi-schema/union-clash-type.err > new file mode 100644 > index 0000000..6e64d1d > --- /dev/null > +++ b/tests/qapi-schema/union-clash-type.err > @@ -0,0 +1,16 @@ > +Traceback (most recent call last): > + File "tests/qapi-schema/test-qapi.py", line 55, in <module> > + schema = QAPISchema(sys.argv[1]) > + File "scripts/qapi.py", line 1116, in __init__ > + self.check() > + File "scripts/qapi.py", line 1299, in check > + ent.check(self) > + File "scripts/qapi.py", line 962, in check > + self.variants.check(schema, members, seen) > + File "scripts/qapi.py", line 1024, in check > + v.check(schema, self.tag_member.type, vseen) > + File "scripts/qapi.py", line 1032, in check > + QAPISchemaObjectTypeMember.check(self, schema, [], seen) > + File "scripts/qapi.py", line 994, in check > + assert self.name not in seen > +AssertionError > diff --git a/tests/qapi-schema/union-clash-type.exit b/tests/qapi-schema/union-clash-type.exit > new file mode 100644 > index 0000000..d00491f > --- /dev/null > +++ b/tests/qapi-schema/union-clash-type.exit > @@ -0,0 +1 @@ > +1 > diff --git a/tests/qapi-schema/union-clash-type.json b/tests/qapi-schema/union-clash-type.json > new file mode 100644 > index 0000000..2294f4f > --- /dev/null > +++ b/tests/qapi-schema/union-clash-type.json > @@ -0,0 +1,3 @@ > +# FIXME: detect this collision, rather than triggering an assertion failure Could you spell out the collision? > +{ 'union': 'TestUnion', > + 'data': { 'kind': 'int', 'type': 'str' } } > diff --git a/tests/qapi-schema/union-clash-type.out b/tests/qapi-schema/union-clash-type.out > new file mode 100644 > index 0000000..e69de29
On 09/29/2015 06:33 AM, Markus Armbruster wrote: > Eric Blake <eblake@redhat.com> writes: > >> Expose some weaknesses in the generator: we don't always forbid >> the generation of structs that contain multiple members that map >> to the same C name. > > C or QMP name, perhaps? Lots of good advice for an improved commit message and test comments; looks like I'll do a v7 respin to address them. > >> Oddly enough, while commit 1e6c1616 fixed a type 2b collision >> with 'base', > > What collided with 'base' before 1e6c1616 and not after? Uggh. I wrote that comment late at night, and it shows. I was mixing up struct bases (which are boxed behind a 'FOO *base' member) and flat unions; but flat union base classes have always been expressed on a per-member basis. I'll either drop this paragraph entirely, or come up with a real example (about the only one that might be left is when we dropped the mis-use of 'kind', we were then introducing the possibility of collision with the 'discriminator':'name' - and that's one of the two tests added in this commit where we assert). > >> it introduced a different collision that was not >> previously possible: now that the base members are no longer >> boxed behind 'base', they can collide with the enum tag members >> (used as member names within the embedded C union). >> >> Ultimately, if we need to have a flat union where an enum >> value clashes with a base member name, we could change the > > Suggest "an enum tag member", because this is confusing enough without > using multiple names for the same thing :) > >> generator to use something like '_tag_value' instead of >> 'value' for the C name of union members; > > Or wrap the base in a struct, or give the union member a name. Giving the union member a name might be the least amount of typing, but would still touch quite a bit of code if we have to do it. > >> but until such a >> need arises, it will probably be easier to just forbid these >> collisions. > > Again, I'm not sure this makes sense to readers who aren't already > familiar with our name clashing issues. > > Do you fix any of this later in your series? If yes, you could punt > detailed bug descriptions to the patches that fixes them. Yes, I fix a number of the issues later on (although only the two assertion failures get fixed in subset A; the rest of the fixups are in the tail end of v5 which will become subset B). > >> Some of these collisions are already caught in the old-style >> parser checks, but ultimately we want all collisions to be >> caught in the new-style QAPISchema*.check() methods. >> >> Some of these negative tests will be deleted later, and positive >> tests added to qapi-schema-test.json in their place, when the >> generator code is reworked so that a particular collision no >> longer causes failed code generation. >> >> [git rename detection may be a bit confused by the actions of >> this commit, since it both creates and renames files that are >> equally small and similar in content] > > Why square brackets? > To delineate that this paragraph is is unrelated to the contents of the patch, but instead guides a reader to how to interpret the git diff of the patch - if rename detection is enabled, git botches the rename detection, doing things like: >> --- >> tests/Makefile | 10 +++++++++- >> .../{flat-union-branch-clash.out => args-name-clash.err} | 0 claiming this file is a rename (in reality, I renamed flat-union-branch-clash.out to flat-union-clash-branch.out; and .out files never really rename to .err files other than the fact that both happened to be empty). We could drop it if desired, but I think it makes sense to keep the comment in qemu.git (because 'git diff' will botch the rename detection no matter how much in the future you are inspecting this patch). >> +++ b/tests/qapi-schema/args-name-clash.json >> @@ -0,0 +1,2 @@ >> +# FIXME - we should reject data with members that clash when mapped to C names >> +{ 'command': 'oops', 'data': { 'a-b': 'str', 'a_b': 'str' } } > > Here, the collision is fairly obvious. We could spell it out anyway: > > # Multiple members mapping to the same C identifier: both 'a-b' and > # 'a_b' map to a_b' > # FIXME they clash in generated C, should reject them cleanly instead. > > Unfortunately, the test doesn't actually make the clash visible. Same > for all the other tests that produce clashes in C or QMP without > actually upsetting the generator. Oh well, good enough. And for every FIXME I add here, a later patch in the v5 series resolves it (either by making it a parse error, or by removing the generated collision).
diff --git a/tests/Makefile b/tests/Makefile index 6fd5885..d43b8e9 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -238,6 +238,7 @@ qapi-schema += args-invalid.json qapi-schema += args-member-array-bad.json qapi-schema += args-member-array.json qapi-schema += args-member-unknown.json +qapi-schema += args-name-clash.json qapi-schema += args-union.json qapi-schema += args-unknown.json qapi-schema += bad-base.json @@ -273,7 +274,10 @@ qapi-schema += flat-union-bad-base.json qapi-schema += flat-union-bad-discriminator.json qapi-schema += flat-union-base-any.json qapi-schema += flat-union-base-union.json -qapi-schema += flat-union-branch-clash.json +qapi-schema += flat-union-clash-branch.json +qapi-schema += flat-union-clash-member.json +qapi-schema += flat-union-clash-type.json +qapi-schema += flat-union-cycle.json qapi-schema += flat-union-inline.json qapi-schema += flat-union-int-branch.json qapi-schema += flat-union-invalid-branch-key.json @@ -315,6 +319,7 @@ qapi-schema += returns-dict.json qapi-schema += returns-int.json qapi-schema += returns-unknown.json qapi-schema += returns-whitelist.json +qapi-schema += struct-base-clash-base.json qapi-schema += struct-base-clash-deep.json qapi-schema += struct-base-clash.json qapi-schema += struct-data-invalid.json @@ -328,6 +333,9 @@ qapi-schema += unclosed-string.json qapi-schema += unicode-str.json qapi-schema += union-bad-branch.json qapi-schema += union-base-no-discriminator.json +qapi-schema += union-clash-data.json +qapi-schema += union-clash-members.json +qapi-schema += union-clash-type.json qapi-schema += union-invalid-base.json qapi-schema += union-max.json qapi-schema += union-optional-branch.json diff --git a/tests/qapi-schema/flat-union-branch-clash.out b/tests/qapi-schema/args-name-clash.err similarity index 100% rename from tests/qapi-schema/flat-union-branch-clash.out rename to tests/qapi-schema/args-name-clash.err diff --git a/tests/qapi-schema/args-name-clash.exit b/tests/qapi-schema/args-name-clash.exit new file mode 100644 index 0000000..573541a --- /dev/null +++ b/tests/qapi-schema/args-name-clash.exit @@ -0,0 +1 @@ +0 diff --git a/tests/qapi-schema/args-name-clash.json b/tests/qapi-schema/args-name-clash.json new file mode 100644 index 0000000..19bf792 --- /dev/null +++ b/tests/qapi-schema/args-name-clash.json @@ -0,0 +1,2 @@ +# FIXME - we should reject data with members that clash when mapped to C names +{ 'command': 'oops', 'data': { 'a-b': 'str', 'a_b': 'str' } } diff --git a/tests/qapi-schema/args-name-clash.out b/tests/qapi-schema/args-name-clash.out new file mode 100644 index 0000000..9b2f6e4 --- /dev/null +++ b/tests/qapi-schema/args-name-clash.out @@ -0,0 +1,6 @@ +object :empty +object :obj-oops-arg + member a-b: str optional=False + member a_b: str optional=False +command oops :obj-oops-arg -> None + gen=True success_response=True diff --git a/tests/qapi-schema/flat-union-branch-clash.err b/tests/qapi-schema/flat-union-branch-clash.err deleted file mode 100644 index f112766..0000000 --- a/tests/qapi-schema/flat-union-branch-clash.err +++ /dev/null @@ -1 +0,0 @@ -tests/qapi-schema/flat-union-branch-clash.json:10: Member name 'name' of branch 'value1' clashes with base 'Base' diff --git a/tests/qapi-schema/flat-union-clash-branch.err b/tests/qapi-schema/flat-union-clash-branch.err new file mode 100644 index 0000000..e69de29 diff --git a/tests/qapi-schema/flat-union-clash-branch.exit b/tests/qapi-schema/flat-union-clash-branch.exit new file mode 100644 index 0000000..573541a --- /dev/null +++ b/tests/qapi-schema/flat-union-clash-branch.exit @@ -0,0 +1 @@ +0 diff --git a/tests/qapi-schema/flat-union-clash-branch.json b/tests/qapi-schema/flat-union-clash-branch.json new file mode 100644 index 0000000..cda3af5 --- /dev/null +++ b/tests/qapi-schema/flat-union-clash-branch.json @@ -0,0 +1,15 @@ +# FIXME: this parses, but then fails to compile due to a duplicate 'c_d' +# we should either detect the collision or allow this to compile +{ 'enum': 'TestEnum', + 'data': [ 'base', 'c-d' ] } +{ 'struct': 'Base', + 'data': { 'enum1': 'TestEnum', '*c_d': 'str' } } +{ 'struct': 'Branch1', + 'data': { 'string': 'str' } } +{ 'struct': 'Branch2', + 'data': { 'value': 'int' } } +{ 'union': 'TestUnion', + 'base': 'Base', + 'discriminator': 'enum1', + 'data': { 'base': 'Branch1', + 'c-d': 'Branch2' } } diff --git a/tests/qapi-schema/flat-union-clash-branch.out b/tests/qapi-schema/flat-union-clash-branch.out new file mode 100644 index 0000000..8e0da73 --- /dev/null +++ b/tests/qapi-schema/flat-union-clash-branch.out @@ -0,0 +1,14 @@ +object :empty +object Base + member enum1: TestEnum optional=False + member c_d: str optional=True +object Branch1 + member string: str optional=False +object Branch2 + member value: int optional=False +enum TestEnum ['base', 'c-d'] +object TestUnion + base Base + tag enum1 + case base: Branch1 + case c-d: Branch2 diff --git a/tests/qapi-schema/flat-union-clash-member.err b/tests/qapi-schema/flat-union-clash-member.err new file mode 100644 index 0000000..152d402 --- /dev/null +++ b/tests/qapi-schema/flat-union-clash-member.err @@ -0,0 +1 @@ +tests/qapi-schema/flat-union-clash-member.json:10: Member name 'name' of branch 'value1' clashes with base 'Base' diff --git a/tests/qapi-schema/flat-union-branch-clash.exit b/tests/qapi-schema/flat-union-clash-member.exit similarity index 100% rename from tests/qapi-schema/flat-union-branch-clash.exit rename to tests/qapi-schema/flat-union-clash-member.exit diff --git a/tests/qapi-schema/flat-union-branch-clash.json b/tests/qapi-schema/flat-union-clash-member.json similarity index 85% rename from tests/qapi-schema/flat-union-branch-clash.json rename to tests/qapi-schema/flat-union-clash-member.json index 8fb054f..3d7e6c6 100644 --- a/tests/qapi-schema/flat-union-branch-clash.json +++ b/tests/qapi-schema/flat-union-clash-member.json @@ -1,4 +1,4 @@ -# we check for no duplicate keys between branches and base +# we check for no duplicate keys between branch members and base { 'enum': 'TestEnum', 'data': [ 'value1', 'value2' ] } { 'struct': 'Base', diff --git a/tests/qapi-schema/flat-union-clash-member.out b/tests/qapi-schema/flat-union-clash-member.out new file mode 100644 index 0000000..e69de29 diff --git a/tests/qapi-schema/flat-union-clash-type.err b/tests/qapi-schema/flat-union-clash-type.err new file mode 100644 index 0000000..6e64d1d --- /dev/null +++ b/tests/qapi-schema/flat-union-clash-type.err @@ -0,0 +1,16 @@ +Traceback (most recent call last): + File "tests/qapi-schema/test-qapi.py", line 55, in <module> + schema = QAPISchema(sys.argv[1]) + File "scripts/qapi.py", line 1116, in __init__ + self.check() + File "scripts/qapi.py", line 1299, in check + ent.check(self) + File "scripts/qapi.py", line 962, in check + self.variants.check(schema, members, seen) + File "scripts/qapi.py", line 1024, in check + v.check(schema, self.tag_member.type, vseen) + File "scripts/qapi.py", line 1032, in check + QAPISchemaObjectTypeMember.check(self, schema, [], seen) + File "scripts/qapi.py", line 994, in check + assert self.name not in seen +AssertionError diff --git a/tests/qapi-schema/flat-union-clash-type.exit b/tests/qapi-schema/flat-union-clash-type.exit new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/tests/qapi-schema/flat-union-clash-type.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/flat-union-clash-type.json b/tests/qapi-schema/flat-union-clash-type.json new file mode 100644 index 0000000..87e5fd8 --- /dev/null +++ b/tests/qapi-schema/flat-union-clash-type.json @@ -0,0 +1,11 @@ +# FIXME: detect this collision, rather than triggering an assertion failure +{ 'enum': 'TestEnum', + 'data': [ 'type' ] } +{ 'struct': 'Base', + 'data': { 'type': 'TestEnum' } } +{ 'struct': 'Branch1', + 'data': { 'string': 'str' } } +{ 'union': 'TestUnion', + 'base': 'Base', + 'discriminator': 'type', + 'data': { 'type': 'Branch1' } } diff --git a/tests/qapi-schema/flat-union-clash-type.out b/tests/qapi-schema/flat-union-clash-type.out new file mode 100644 index 0000000..e69de29 diff --git a/tests/qapi-schema/flat-union-cycle.err b/tests/qapi-schema/flat-union-cycle.err new file mode 100644 index 0000000..d4f2a09 --- /dev/null +++ b/tests/qapi-schema/flat-union-cycle.err @@ -0,0 +1 @@ +tests/qapi-schema/flat-union-cycle.json:6: Base 'Union' is not a valid struct diff --git a/tests/qapi-schema/flat-union-cycle.exit b/tests/qapi-schema/flat-union-cycle.exit new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/tests/qapi-schema/flat-union-cycle.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/flat-union-cycle.json b/tests/qapi-schema/flat-union-cycle.json new file mode 100644 index 0000000..7414277 --- /dev/null +++ b/tests/qapi-schema/flat-union-cycle.json @@ -0,0 +1,7 @@ +# ensure that we have a sane error message for attempts at self-inheritance +# (this test currently fails because we don't permit a union base, but +# even if we relax things to allow that in the future, this should still fail) +{ 'enum': 'Enum', 'data': [ 'okay' ] } +{ 'struct': 'Okay', 'data': { 'int': 'int' } } +{ 'union': 'Union', 'base': 'Union', 'discriminator': 'switch', + 'data': { 'okay': 'Okay' } } diff --git a/tests/qapi-schema/flat-union-cycle.out b/tests/qapi-schema/flat-union-cycle.out new file mode 100644 index 0000000..e69de29 diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json index 6897a6e..c511338 100644 --- a/tests/qapi-schema/qapi-schema-test.json +++ b/tests/qapi-schema/qapi-schema-test.json @@ -32,11 +32,14 @@ 'dict1': 'UserDefTwoDict' } } # for testing unions +# Among other things, test that a name collision between branches does +# not cause any problems (since only one branch can be in use at a time), +# by intentionally using two branches that both have a C member 'a_b' { 'struct': 'UserDefA', - 'data': { 'boolean': 'bool' } } + 'data': { 'boolean': 'bool', '*a_b': 'int' } } { 'struct': 'UserDefB', - 'data': { 'intb': 'int' } } + 'data': { 'intb': 'int', '*a-b': 'bool' } } { 'union': 'UserDefFlatUnion', 'base': 'UserDefUnionBase', # intentional forward reference diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out index 1f6e858..28a0b3c 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -71,6 +71,7 @@ enum QEnumTwo ['value1', 'value2'] prefix QENUM_TWO object UserDefA member boolean: bool optional=False + member a_b: int optional=True alternate UserDefAlternate case uda: UserDefA case s: str @@ -78,6 +79,7 @@ alternate UserDefAlternate enum UserDefAlternateKind ['uda', 's', 'i'] object UserDefB member intb: int optional=False + member a-b: bool optional=True object UserDefC member string1: str optional=False member string2: str optional=False diff --git a/tests/qapi-schema/struct-base-clash-base.err b/tests/qapi-schema/struct-base-clash-base.err new file mode 100644 index 0000000..e69de29 diff --git a/tests/qapi-schema/struct-base-clash-base.exit b/tests/qapi-schema/struct-base-clash-base.exit new file mode 100644 index 0000000..573541a --- /dev/null +++ b/tests/qapi-schema/struct-base-clash-base.exit @@ -0,0 +1 @@ +0 diff --git a/tests/qapi-schema/struct-base-clash-base.json b/tests/qapi-schema/struct-base-clash-base.json new file mode 100644 index 0000000..fb25128 --- /dev/null +++ b/tests/qapi-schema/struct-base-clash-base.json @@ -0,0 +1,6 @@ +# FIXME: this parses, but then fails to compile due to a duplicate 'base' +# we should either detect the collision or allow this to compile +{ 'struct': 'Base', 'data': {} } +{ 'struct': 'Sub', + 'base': 'Base', + 'data': { 'base': 'str' } } diff --git a/tests/qapi-schema/struct-base-clash-base.out b/tests/qapi-schema/struct-base-clash-base.out new file mode 100644 index 0000000..e69a416 --- /dev/null +++ b/tests/qapi-schema/struct-base-clash-base.out @@ -0,0 +1,5 @@ +object :empty +object Base +object Sub + base Base + member base: str optional=False diff --git a/tests/qapi-schema/union-clash-data.err b/tests/qapi-schema/union-clash-data.err new file mode 100644 index 0000000..e69de29 diff --git a/tests/qapi-schema/union-clash-data.exit b/tests/qapi-schema/union-clash-data.exit new file mode 100644 index 0000000..573541a --- /dev/null +++ b/tests/qapi-schema/union-clash-data.exit @@ -0,0 +1 @@ +0 diff --git a/tests/qapi-schema/union-clash-data.json b/tests/qapi-schema/union-clash-data.json new file mode 100644 index 0000000..322f61b --- /dev/null +++ b/tests/qapi-schema/union-clash-data.json @@ -0,0 +1,4 @@ +# FIXME: this parses, but then fails to compile due to a duplicate 'data' +# we should either detect the collision or allow this to compile +{ 'union': 'TestUnion', + 'data': { 'data': 'int' } } diff --git a/tests/qapi-schema/union-clash-data.out b/tests/qapi-schema/union-clash-data.out new file mode 100644 index 0000000..6277239 --- /dev/null +++ b/tests/qapi-schema/union-clash-data.out @@ -0,0 +1,6 @@ +object :empty +object :obj-int-wrapper + member data: int optional=False +object TestUnion + case data: :obj-int-wrapper +enum TestUnionKind ['data'] diff --git a/tests/qapi-schema/union-clash-members.err b/tests/qapi-schema/union-clash-members.err new file mode 100644 index 0000000..b77aeb8 --- /dev/null +++ b/tests/qapi-schema/union-clash-members.err @@ -0,0 +1 @@ +tests/qapi-schema/union-clash-members.json:2: Union 'TestUnion' member 'a_b' clashes with 'a-b' diff --git a/tests/qapi-schema/union-clash-members.exit b/tests/qapi-schema/union-clash-members.exit new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/tests/qapi-schema/union-clash-members.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/union-clash-members.json b/tests/qapi-schema/union-clash-members.json new file mode 100644 index 0000000..0393ed8 --- /dev/null +++ b/tests/qapi-schema/union-clash-members.json @@ -0,0 +1,3 @@ +# we reject unions where branch names clash when mapped to C +{ 'union': 'TestUnion', + 'data': { 'a-b': 'int', 'a_b': 'str' } } diff --git a/tests/qapi-schema/union-clash-members.out b/tests/qapi-schema/union-clash-members.out new file mode 100644 index 0000000..e69de29 diff --git a/tests/qapi-schema/union-clash-type.err b/tests/qapi-schema/union-clash-type.err new file mode 100644 index 0000000..6e64d1d --- /dev/null +++ b/tests/qapi-schema/union-clash-type.err @@ -0,0 +1,16 @@ +Traceback (most recent call last): + File "tests/qapi-schema/test-qapi.py", line 55, in <module> + schema = QAPISchema(sys.argv[1]) + File "scripts/qapi.py", line 1116, in __init__ + self.check() + File "scripts/qapi.py", line 1299, in check + ent.check(self) + File "scripts/qapi.py", line 962, in check + self.variants.check(schema, members, seen) + File "scripts/qapi.py", line 1024, in check + v.check(schema, self.tag_member.type, vseen) + File "scripts/qapi.py", line 1032, in check + QAPISchemaObjectTypeMember.check(self, schema, [], seen) + File "scripts/qapi.py", line 994, in check + assert self.name not in seen +AssertionError diff --git a/tests/qapi-schema/union-clash-type.exit b/tests/qapi-schema/union-clash-type.exit new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/tests/qapi-schema/union-clash-type.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/union-clash-type.json b/tests/qapi-schema/union-clash-type.json new file mode 100644 index 0000000..2294f4f --- /dev/null +++ b/tests/qapi-schema/union-clash-type.json @@ -0,0 +1,3 @@ +# FIXME: detect this collision, rather than triggering an assertion failure +{ 'union': 'TestUnion', + 'data': { 'kind': 'int', 'type': 'str' } }
Expose some weaknesses in the generator: we don't always forbid the generation of structs that contain multiple members that map to the same C name. This has already been marked FIXME in qapi.py in commit d90675f, but having more tests will make sure future patches produce desired behavior. This patch focuses on C struct members, and does not consider collisions between commands and events (affecting C function names), or even on collisions between generated C type names with user type names (for things like automatic FOOList struct representing array types). There are two types of struct collisions we want to catch: 1) Collision that would require the QMP user to specify the same key more than once inside a single JSON {} object (we already prevent duplicate keys within a single type, so this is triggered when merging multiple types into a single JSON object via base classes or flat unions) 2) Collision between two members of the C struct that is generated for a given qapi type: a) a collision occurs from two qapi names mapping to the same C name b) the collision is with a C name used for another purpose (we already fixed 'kind' in commit 0f61af3e, and one use of 'base' in commit 1e6c1616 for unions, but we have another use of 'base' in structs, and still collide with 'data' in unions, not to mention the tag 'type'/'kind' and the various tag values). Oddly enough, while commit 1e6c1616 fixed a type 2b collision with 'base', it introduced a different collision that was not previously possible: now that the base members are no longer boxed behind 'base', they can collide with the enum tag members (used as member names within the embedded C union). Ultimately, if we need to have a flat union where an enum value clashes with a base member name, we could change the generator to use something like '_tag_value' instead of 'value' for the C name of union members; but until such a need arises, it will probably be easier to just forbid these collisions. Some of these collisions are already caught in the old-style parser checks, but ultimately we want all collisions to be caught in the new-style QAPISchema*.check() methods. Some of these negative tests will be deleted later, and positive tests added to qapi-schema-test.json in their place, when the generator code is reworked so that a particular collision no longer causes failed code generation. [git rename detection may be a bit confused by the actions of this commit, since it both creates and renames files that are equally small and similar in content] Signed-off-by: Eric Blake <eblake@redhat.com> --- v6: rework commit message, alter test names and comments to be more descriptive, repurpose flat-union-cycle to better match its name (and not duplicate what the renamed flat-union-clash-branch tests), add new tests (union-clash-type, flat-union-clash-type) that detect an assertion failures --- tests/Makefile | 10 +++++++++- .../{flat-union-branch-clash.out => args-name-clash.err} | 0 tests/qapi-schema/args-name-clash.exit | 1 + tests/qapi-schema/args-name-clash.json | 2 ++ tests/qapi-schema/args-name-clash.out | 6 ++++++ tests/qapi-schema/flat-union-branch-clash.err | 1 - tests/qapi-schema/flat-union-clash-branch.err | 0 tests/qapi-schema/flat-union-clash-branch.exit | 1 + tests/qapi-schema/flat-union-clash-branch.json | 15 +++++++++++++++ tests/qapi-schema/flat-union-clash-branch.out | 14 ++++++++++++++ tests/qapi-schema/flat-union-clash-member.err | 1 + ...on-branch-clash.exit => flat-union-clash-member.exit} | 0 ...on-branch-clash.json => flat-union-clash-member.json} | 2 +- tests/qapi-schema/flat-union-clash-member.out | 0 tests/qapi-schema/flat-union-clash-type.err | 16 ++++++++++++++++ tests/qapi-schema/flat-union-clash-type.exit | 1 + tests/qapi-schema/flat-union-clash-type.json | 11 +++++++++++ tests/qapi-schema/flat-union-clash-type.out | 0 tests/qapi-schema/flat-union-cycle.err | 1 + tests/qapi-schema/flat-union-cycle.exit | 1 + tests/qapi-schema/flat-union-cycle.json | 7 +++++++ tests/qapi-schema/flat-union-cycle.out | 0 tests/qapi-schema/qapi-schema-test.json | 7 +++++-- tests/qapi-schema/qapi-schema-test.out | 2 ++ tests/qapi-schema/struct-base-clash-base.err | 0 tests/qapi-schema/struct-base-clash-base.exit | 1 + tests/qapi-schema/struct-base-clash-base.json | 6 ++++++ tests/qapi-schema/struct-base-clash-base.out | 5 +++++ tests/qapi-schema/union-clash-data.err | 0 tests/qapi-schema/union-clash-data.exit | 1 + tests/qapi-schema/union-clash-data.json | 4 ++++ tests/qapi-schema/union-clash-data.out | 6 ++++++ tests/qapi-schema/union-clash-members.err | 1 + tests/qapi-schema/union-clash-members.exit | 1 + tests/qapi-schema/union-clash-members.json | 3 +++ tests/qapi-schema/union-clash-members.out | 0 tests/qapi-schema/union-clash-type.err | 16 ++++++++++++++++ tests/qapi-schema/union-clash-type.exit | 1 + tests/qapi-schema/union-clash-type.json | 3 +++ tests/qapi-schema/union-clash-type.out | 0 40 files changed, 142 insertions(+), 5 deletions(-) rename tests/qapi-schema/{flat-union-branch-clash.out => args-name-clash.err} (100%) create mode 100644 tests/qapi-schema/args-name-clash.exit create mode 100644 tests/qapi-schema/args-name-clash.json create mode 100644 tests/qapi-schema/args-name-clash.out delete mode 100644 tests/qapi-schema/flat-union-branch-clash.err create mode 100644 tests/qapi-schema/flat-union-clash-branch.err create mode 100644 tests/qapi-schema/flat-union-clash-branch.exit create mode 100644 tests/qapi-schema/flat-union-clash-branch.json create mode 100644 tests/qapi-schema/flat-union-clash-branch.out create mode 100644 tests/qapi-schema/flat-union-clash-member.err rename tests/qapi-schema/{flat-union-branch-clash.exit => flat-union-clash-member.exit} (100%) rename tests/qapi-schema/{flat-union-branch-clash.json => flat-union-clash-member.json} (85%) create mode 100644 tests/qapi-schema/flat-union-clash-member.out create mode 100644 tests/qapi-schema/flat-union-clash-type.err create mode 100644 tests/qapi-schema/flat-union-clash-type.exit create mode 100644 tests/qapi-schema/flat-union-clash-type.json create mode 100644 tests/qapi-schema/flat-union-clash-type.out create mode 100644 tests/qapi-schema/flat-union-cycle.err create mode 100644 tests/qapi-schema/flat-union-cycle.exit create mode 100644 tests/qapi-schema/flat-union-cycle.json create mode 100644 tests/qapi-schema/flat-union-cycle.out create mode 100644 tests/qapi-schema/struct-base-clash-base.err create mode 100644 tests/qapi-schema/struct-base-clash-base.exit create mode 100644 tests/qapi-schema/struct-base-clash-base.json create mode 100644 tests/qapi-schema/struct-base-clash-base.out create mode 100644 tests/qapi-schema/union-clash-data.err create mode 100644 tests/qapi-schema/union-clash-data.exit create mode 100644 tests/qapi-schema/union-clash-data.json create mode 100644 tests/qapi-schema/union-clash-data.out create mode 100644 tests/qapi-schema/union-clash-members.err create mode 100644 tests/qapi-schema/union-clash-members.exit create mode 100644 tests/qapi-schema/union-clash-members.json create mode 100644 tests/qapi-schema/union-clash-members.out create mode 100644 tests/qapi-schema/union-clash-type.err create mode 100644 tests/qapi-schema/union-clash-type.exit create mode 100644 tests/qapi-schema/union-clash-type.json create mode 100644 tests/qapi-schema/union-clash-type.out diff --git a/tests/qapi-schema/union-clash-type.out b/tests/qapi-schema/union-clash-type.out new file mode 100644 index 0000000..e69de29