From: Markus Armbruster <armbru@redhat.com>
To: Eric Blake <eblake@redhat.com>
Cc: marcandre.lureau@redhat.com, qemu-devel@nongnu.org,
ehabkost@redhat.com, Michael Roth <mdroth@linux.vnet.ibm.com>
Subject: Re: [Qemu-devel] [PATCH v6 07/12] qapi: Detect collisions in C member names
Date: Fri, 02 Oct 2015 15:19:27 +0200 [thread overview]
Message-ID: <87bnch76hs.fsf@blackfin.pond.sub.org> (raw)
In-Reply-To: <1443760312-656-8-git-send-email-eblake@redhat.com> (Eric Blake's message of "Thu, 1 Oct 2015 22:31:47 -0600")
Eric Blake <eblake@redhat.com> writes:
> Detect attempts to declare two object members that would result
> in the same C member name, by keying the 'seen' dictionary off
> of the C name rather than the qapi name. Doing this was made
> easier by adding a member.c_name() helper function.
This gains protection against colliding C names. It keeps protection
against colliding QMP names as long as QMP name collision implies C name
collision. I think that's the case, but it's definitely worth spelling
out in the code, and possibly in the commit message.
> As this is the first error raised within the QAPISchema*.check()
> methods, we also have to pass 'info' through the call stack, and
> fix the overall 'try' to display errors detected during the
> check() phase.
Could also be a separate patch, if the parts are easier to review.
> This fixes a couple of previously-broken tests, and the
Just two.
> resulting error messages demonstrate the utility of the
> .describe() method added previously. No change to generated
> code.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v6: rebase to earlier testsuite and info improvements
> ---
> scripts/qapi.py | 46 ++++++++++++++++----------
> tests/qapi-schema/args-name-clash.err | 1 +
> tests/qapi-schema/args-name-clash.exit | 2 +-
> tests/qapi-schema/args-name-clash.json | 6 ++--
> tests/qapi-schema/args-name-clash.out | 6 ----
> tests/qapi-schema/flat-union-clash-branch.err | 1 +
> tests/qapi-schema/flat-union-clash-branch.exit | 2 +-
> tests/qapi-schema/flat-union-clash-branch.json | 9 ++---
> tests/qapi-schema/flat-union-clash-branch.out | 14 --------
> 9 files changed, 40 insertions(+), 47 deletions(-)
>
> diff --git a/scripts/qapi.py b/scripts/qapi.py
> index 880de94..1acf02b 100644
> --- a/scripts/qapi.py
> +++ b/scripts/qapi.py
> @@ -103,6 +103,7 @@ class QAPISchemaError(Exception):
> class QAPIExprError(Exception):
> def __init__(self, expr_info, msg):
> Exception.__init__(self)
> + assert expr_info
> self.info = expr_info
> self.msg = msg
>
Assertion should hold before the patch. Could therefore be added in a
separate patch before this one. Only if we have enough material for
a separate patch, or it makes this one materially easier to review.
> @@ -964,11 +965,12 @@ class QAPISchemaObjectType(QAPISchemaType):
> members = []
> seen = {}
> for m in members:
> - seen[m.name] = m
> + assert m.c_name() not in seen
> + seen[m.c_name()] = m
Assertion should hold before the patch.
> for m in self.local_members:
> - m.check(schema, members, seen)
> + m.check(schema, self.info, members, seen)
> if self.variants:
> - self.variants.check(schema, members, seen)
> + self.variants.check(schema, self.info, members, seen)
> self.members = members
>
> def is_implicit(self):
> @@ -1004,12 +1006,19 @@ class QAPISchemaObjectTypeMember(object):
> self.optional = optional
> self._owner = owner
>
> - def check(self, schema, all_members, seen):
> - assert self.name not in seen
> + def check(self, schema, info, all_members, seen):
> + if self.c_name() in seen:
> + raise QAPIExprError(info,
> + "%s collides with %s"
> + % (self.describe(),
> + seen[self.c_name()].describe()))
> self.type = schema.lookup_type(self._type_name)
> assert self.type
> all_members.append(self)
> - seen[self.name] = self
> + seen[self.c_name()] = self
> +
> + def c_name(self):
> + return c_name(self.name)
Why wrap function c_name() in a method? Why not simply call the
function?
It's method in QAPISchemaEntity only because this lets us add special
cases in a neat way.
>
> def describe(self):
> return "'%s' (member of %s)" % (self.name, self._owner)
> @@ -1028,23 +1037,24 @@ class QAPISchemaObjectTypeVariants(object):
> self.tag_member = tag_member
> self.variants = variants
>
> - def check(self, schema, members, seen):
> + def check(self, schema, info, members, seen):
> if self.tag_name:
> - self.tag_member = seen[self.tag_name]
> + self.tag_member = seen[c_name(self.tag_name)]
> + assert self.tag_name == self.tag_member.name
Assertion should hold before the patch, but it's kind of trivial then.
> else:
> - self.tag_member.check(schema, members, seen)
> + self.tag_member.check(schema, info, members, seen)
> assert isinstance(self.tag_member.type, QAPISchemaEnumType)
> for v in self.variants:
> vseen = dict(seen)
> - v.check(schema, self.tag_member.type, vseen)
> + v.check(schema, info, self.tag_member.type, vseen)
>
>
> class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
> def __init__(self, name, typ, owner):
> QAPISchemaObjectTypeMember.__init__(self, name, typ, False, owner)
>
> - def check(self, schema, tag_type, seen):
> - QAPISchemaObjectTypeMember.check(self, schema, [], seen)
> + def check(self, schema, info, tag_type, seen):
> + QAPISchemaObjectTypeMember.check(self, schema, info, [], seen)
> assert self.name in tag_type.values
>
> def describe(self):
> @@ -1069,7 +1079,7 @@ class QAPISchemaAlternateType(QAPISchemaType):
> self.variants = variants
>
> def check(self, schema):
> - self.variants.check(schema, [], {})
> + self.variants.check(schema, self.info, [], {})
>
> def json_type(self):
> return 'value'
> @@ -1128,14 +1138,14 @@ class QAPISchema(object):
> def __init__(self, fname):
> try:
> self.exprs = check_exprs(QAPISchemaParser(open(fname, "r")).exprs)
> + self._entity_dict = {}
> + self._def_predefineds()
> + QAPISchema.predefined_initialized = True
> + self._def_exprs()
> + self.check()
> except (QAPISchemaError, QAPIExprError), err:
> print >>sys.stderr, err
> exit(1)
> - self._entity_dict = {}
> - self._def_predefineds()
> - QAPISchema.predefined_initialized = True
> - self._def_exprs()
> - self.check()
Moving code into the try wholesale is okay because we catch only our own
exceptions.
>
> def _def_entity(self, ent):
> assert ent.name not in self._entity_dict
> diff --git a/tests/qapi-schema/args-name-clash.err b/tests/qapi-schema/args-name-clash.err
> index e69de29..66f609c 100644
> --- a/tests/qapi-schema/args-name-clash.err
> +++ b/tests/qapi-schema/args-name-clash.err
> @@ -0,0 +1 @@
> +tests/qapi-schema/args-name-clash.json:5: 'a_b' (member of oops arguments) collides with 'a-b' (member of oops arguments)
> diff --git a/tests/qapi-schema/args-name-clash.exit b/tests/qapi-schema/args-name-clash.exit
> index 573541a..d00491f 100644
> --- a/tests/qapi-schema/args-name-clash.exit
> +++ b/tests/qapi-schema/args-name-clash.exit
> @@ -1 +1 @@
> -0
> +1
> diff --git a/tests/qapi-schema/args-name-clash.json b/tests/qapi-schema/args-name-clash.json
> index 9e8f889..3fe4ea5 100644
> --- a/tests/qapi-schema/args-name-clash.json
> +++ b/tests/qapi-schema/args-name-clash.json
> @@ -1,5 +1,5 @@
> # C member name collision
> -# FIXME - This parses, but fails to compile, because the C struct is given
> -# two 'a_b' members. Either reject this at parse time, or munge the C names
> -# to avoid the collision.
> +# Reject members that clash when mapped to C names (we would have two 'a_b'
> +# members). It would also be possible to munge the C names to avoid the
> +# collision, but unlikely to be worth the effort.
> { '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
> index 9b2f6e4..e69de29 100644
> --- a/tests/qapi-schema/args-name-clash.out
> +++ b/tests/qapi-schema/args-name-clash.out
> @@ -1,6 +0,0 @@
> -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-clash-branch.err b/tests/qapi-schema/flat-union-clash-branch.err
> index e69de29..0190d79 100644
> --- a/tests/qapi-schema/flat-union-clash-branch.err
> +++ b/tests/qapi-schema/flat-union-clash-branch.err
> @@ -0,0 +1 @@
> +tests/qapi-schema/flat-union-clash-branch.json:15: 'c-d' (branch of TestUnion) collides with 'c_d' (member of Base)
> diff --git a/tests/qapi-schema/flat-union-clash-branch.exit b/tests/qapi-schema/flat-union-clash-branch.exit
> index 573541a..d00491f 100644
> --- a/tests/qapi-schema/flat-union-clash-branch.exit
> +++ b/tests/qapi-schema/flat-union-clash-branch.exit
> @@ -1 +1 @@
> -0
> +1
> diff --git a/tests/qapi-schema/flat-union-clash-branch.json b/tests/qapi-schema/flat-union-clash-branch.json
> index e593336..a6c302f 100644
> --- a/tests/qapi-schema/flat-union-clash-branch.json
> +++ b/tests/qapi-schema/flat-union-clash-branch.json
> @@ -1,8 +1,9 @@
> # Flat union branch name collision
> -# FIXME: this parses, but then fails to compile due to a duplicate 'c_d'
> -# (one from the base member, the other from the branch name). We should
> -# either reject the collision at parse time, or munge the generated branch
> -# name to allow this to compile.
> +# Reject attempts to use a branch name that would clash with a non-variant
> +# member, when mapped to C names (here, we would have two 'c_d' members,
> +# one from the base member, the other from the branch name).
> +# TODO: We could munge the generated branch name to avoid the collision and
> +# allow this to compile.
> { 'enum': 'TestEnum',
> 'data': [ 'base', 'c-d' ] }
> { 'struct': 'Base',
> diff --git a/tests/qapi-schema/flat-union-clash-branch.out b/tests/qapi-schema/flat-union-clash-branch.out
> index 8e0da73..e69de29 100644
> --- a/tests/qapi-schema/flat-union-clash-branch.out
> +++ b/tests/qapi-schema/flat-union-clash-branch.out
> @@ -1,14 +0,0 @@
> -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
I'm fine with not splitting this patch. I'd be also fine with splitting
it up. Your choice.
next prev parent reply other threads:[~2015-10-02 13:19 UTC|newest]
Thread overview: 46+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-10-02 4:31 [Qemu-devel] [PATCH v6 00/12] post-introspection cleanups, subset B Eric Blake
2015-10-02 4:31 ` [Qemu-devel] [PATCH v6 01/12] qapi: Use predicate callback to determine visit filtering Eric Blake
2015-10-02 6:47 ` Markus Armbruster
2015-10-02 12:16 ` Eric Blake
2015-10-02 4:31 ` [Qemu-devel] [PATCH v6 02/12] qapi: Don't use info as witness of implicit object type Eric Blake
2015-10-02 7:02 ` Markus Armbruster
2015-10-02 12:54 ` Eric Blake
2015-10-02 14:12 ` Markus Armbruster
2015-10-02 14:33 ` Eric Blake
2015-10-02 16:48 ` Markus Armbruster
2015-10-02 16:57 ` Eric Blake
2015-10-02 22:40 ` Eric Blake
2015-10-06 8:32 ` Markus Armbruster
2015-10-06 11:56 ` Eric Blake
2015-10-06 13:31 ` Markus Armbruster
2015-10-02 4:31 ` [Qemu-devel] [PATCH v6 03/12] qapi: Lazy creation of array types Eric Blake
2015-10-02 8:06 ` Markus Armbruster
2015-10-02 13:05 ` Eric Blake
2015-10-06 8:35 ` Markus Armbruster
2015-10-02 4:31 ` [Qemu-devel] [PATCH v6 04/12] qapi: Create simple union type member earlier Eric Blake
2015-10-02 8:34 ` Markus Armbruster
2015-10-02 13:08 ` Eric Blake
2015-10-02 4:31 ` [Qemu-devel] [PATCH v6 05/12] qapi: Track location that created an implicit type Eric Blake
2015-10-02 8:54 ` Markus Armbruster
2015-10-02 14:07 ` Eric Blake
2015-10-02 16:07 ` Markus Armbruster
2015-10-02 16:13 ` Eric Blake
2015-10-02 4:31 ` [Qemu-devel] [PATCH v6 06/12] qapi: Track owner of each object member Eric Blake
2015-10-02 9:50 ` Markus Armbruster
2015-10-02 14:48 ` Eric Blake
2015-10-02 17:05 ` Markus Armbruster
2015-10-02 22:35 ` Eric Blake
2015-10-02 4:31 ` [Qemu-devel] [PATCH v6 07/12] qapi: Detect collisions in C member names Eric Blake
2015-10-02 13:19 ` Markus Armbruster [this message]
2015-10-02 15:12 ` Eric Blake
2015-10-02 17:11 ` Markus Armbruster
2015-10-03 1:01 ` Eric Blake
2015-10-06 8:41 ` Markus Armbruster
2015-10-02 4:31 ` [Qemu-devel] [PATCH v6 08/12] qapi: Defer duplicate member checks to schema check() Eric Blake
2015-10-02 14:00 ` Markus Armbruster
2015-10-02 15:52 ` Eric Blake
2015-10-02 4:31 ` [Qemu-devel] [PATCH v6 09/12] qapi: Defer duplicate enum value " Eric Blake
2015-10-02 4:31 ` [Qemu-devel] [PATCH v6 10/12] qapi: Correct error for union branch 'kind' clash Eric Blake
2015-10-03 17:56 ` Eric Blake
2015-10-02 4:31 ` [Qemu-devel] [PATCH v6 11/12] qapi: Detect base class loops Eric Blake
2015-10-02 4:31 ` [Qemu-devel] [PATCH v6 12/12] RFC: qapi: Hide _info member 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=87bnch76hs.fsf@blackfin.pond.sub.org \
--to=armbru@redhat.com \
--cc=eblake@redhat.com \
--cc=ehabkost@redhat.com \
--cc=marcandre.lureau@redhat.com \
--cc=mdroth@linux.vnet.ibm.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.