From patchwork Tue May 5 16:47:26 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Markus Armbruster X-Patchwork-Id: 468236 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id AFEA41402AB for ; Wed, 6 May 2015 03:09:24 +1000 (AEST) Received: from localhost ([::1]:40876 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YpgLS-00052H-KY for incoming@patchwork.ozlabs.org; Tue, 05 May 2015 13:09:22 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:52709) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Ypg0g-0001n4-H7 for qemu-devel@nongnu.org; Tue, 05 May 2015 12:47:58 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Ypg0c-0006LB-1K for qemu-devel@nongnu.org; Tue, 05 May 2015 12:47:54 -0400 Received: from mx1.redhat.com ([209.132.183.28]:60552) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Ypg0b-0006KR-NW for qemu-devel@nongnu.org; Tue, 05 May 2015 12:47:49 -0400 Received: from int-mx13.intmail.prod.int.phx2.redhat.com (int-mx13.intmail.prod.int.phx2.redhat.com [10.5.11.26]) by mx1.redhat.com (Postfix) with ESMTPS id 413C596C5 for ; Tue, 5 May 2015 16:47:49 +0000 (UTC) Received: from blackfin.pond.sub.org (ovpn-116-60.ams2.redhat.com [10.36.116.60]) by int-mx13.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id t45Glhw3017957 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=NO); Tue, 5 May 2015 12:47:47 -0400 Received: by blackfin.pond.sub.org (Postfix, from userid 1000) id 00AC4304390D; Tue, 5 May 2015 18:47:28 +0200 (CEST) From: Markus Armbruster To: qemu-devel@nongnu.org Date: Tue, 5 May 2015 18:47:26 +0200 Message-Id: <1430844446-12491-41-git-send-email-armbru@redhat.com> In-Reply-To: <1430844446-12491-1-git-send-email-armbru@redhat.com> References: <1430844446-12491-1-git-send-email-armbru@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.26 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PULL 40/40] qapi: Check for member name conflicts with a base class X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: Eric Blake Our type inheritance for both 'struct' and for flat 'union' merges key/value pairs from the base class with those from the type in question. Although the C code currently boxes things so that there is a distinction between which member is referred to, the QMP wire format does not allow passing a key more than once in a single object. Besides, if we ever change the generated C code to not be quite so boxy, we'd want to avoid duplicate member names there, too. Fix a testsuite entry added in an earlier patch, as well as adding a couple more tests to ensure we have appropriate coverage. Ensure that collisions are detected, regardless of whether there is a difference in opinion on whether the member name is optional. Signed-off-by: Eric Blake Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- scripts/qapi.py | 23 ++++++++++++++++++++++- tests/Makefile | 3 ++- tests/qapi-schema/flat-union-branch-clash.err | 1 + tests/qapi-schema/flat-union-branch-clash.exit | 2 +- tests/qapi-schema/flat-union-branch-clash.json | 4 ++-- tests/qapi-schema/flat-union-branch-clash.out | 9 --------- tests/qapi-schema/struct-base-clash-deep.err | 1 + tests/qapi-schema/struct-base-clash-deep.exit | 1 + tests/qapi-schema/struct-base-clash-deep.json | 9 +++++++++ tests/qapi-schema/struct-base-clash-deep.out | 0 tests/qapi-schema/struct-base-clash.err | 1 + tests/qapi-schema/struct-base-clash.exit | 1 + tests/qapi-schema/struct-base-clash.json | 6 ++++++ tests/qapi-schema/struct-base-clash.out | 0 14 files changed, 47 insertions(+), 14 deletions(-) create mode 100644 tests/qapi-schema/struct-base-clash-deep.err create mode 100644 tests/qapi-schema/struct-base-clash-deep.exit create mode 100644 tests/qapi-schema/struct-base-clash-deep.json create mode 100644 tests/qapi-schema/struct-base-clash-deep.out create mode 100644 tests/qapi-schema/struct-base-clash.err create mode 100644 tests/qapi-schema/struct-base-clash.exit create mode 100644 tests/qapi-schema/struct-base-clash.json create mode 100644 tests/qapi-schema/struct-base-clash.out diff --git a/tests/qapi-schema/struct-base-clash.out b/tests/qapi-schema/struct-base-clash.out new file mode 100644 index 0000000..e69de29 diff --git a/scripts/qapi.py b/scripts/qapi.py index 6a9aa24..166b74f 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -414,6 +414,20 @@ def check_type(expr_info, source, value, allow_array = False, allow_metas=['built-in', 'union', 'alternate', 'struct', 'enum']) +def check_member_clash(expr_info, base_name, data, source = ""): + base = find_struct(base_name) + assert base + base_members = base['data'] + for key in data.keys(): + if key.startswith('*'): + key = key[1:] + if key in base_members or "*" + key in base_members: + raise QAPIExprError(expr_info, + "Member name '%s'%s clashes with base '%s'" + % (key, source, base_name)) + if base.get('base'): + check_member_clash(expr_info, base['base'], data, source) + def check_command(expr, expr_info): name = expr['command'] allow_star = expr.has_key('gen') @@ -503,9 +517,14 @@ def check_union(expr, expr_info): check_name(expr_info, "Member of union '%s'" % name, key) # Each value must name a known type; furthermore, in flat unions, - # branches must be a struct + # branches must be a struct with no overlapping member names check_type(expr_info, "Member '%s' of union '%s'" % (key, name), value, allow_array=True, allow_metas=allow_metas) + if base: + branch_struct = find_struct(value) + assert branch_struct + check_member_clash(expr_info, base, branch_struct['data'], + " of branch '%s'" % key) # If the discriminator names an enum type, then all members # of 'data' must also be members of the enum type. @@ -582,6 +601,8 @@ def check_struct(expr, expr_info): allow_dict=True, allow_optional=True) check_type(expr_info, "'base' for struct '%s'" % name, expr.get('base'), allow_metas=['struct']) + if expr.get('base'): + check_member_clash(expr_info, expr['base'], expr['data']) def check_exprs(schema): for expr_elem in schema.exprs: diff --git a/tests/Makefile b/tests/Makefile index 547a249..666aee2 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -243,7 +243,8 @@ check-qapi-schema-y := $(addprefix tests/qapi-schema/, \ include-simple.json include-relpath.json include-format-err.json \ include-non-file.json include-no-file.json include-before-err.json \ include-nested-err.json include-self-cycle.json include-cycle.json \ - include-repetition.json event-nest-struct.json event-case.json) + include-repetition.json event-nest-struct.json event-case.json \ + struct-base-clash.json struct-base-clash-deep.json ) GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h \ tests/test-qmp-commands.h tests/test-qapi-event.h diff --git a/tests/qapi-schema/flat-union-branch-clash.err b/tests/qapi-schema/flat-union-branch-clash.err index e69de29..f112766 100644 --- a/tests/qapi-schema/flat-union-branch-clash.err +++ b/tests/qapi-schema/flat-union-branch-clash.err @@ -0,0 +1 @@ +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-branch-clash.exit b/tests/qapi-schema/flat-union-branch-clash.exit index 573541a..d00491f 100644 --- a/tests/qapi-schema/flat-union-branch-clash.exit +++ b/tests/qapi-schema/flat-union-branch-clash.exit @@ -1 +1 @@ -0 +1 diff --git a/tests/qapi-schema/flat-union-branch-clash.json b/tests/qapi-schema/flat-union-branch-clash.json index 8b0b807..8fb054f 100644 --- a/tests/qapi-schema/flat-union-branch-clash.json +++ b/tests/qapi-schema/flat-union-branch-clash.json @@ -1,8 +1,8 @@ -# FIXME: we should check for no duplicate keys between branches and base +# we check for no duplicate keys between branches and base { 'enum': 'TestEnum', 'data': [ 'value1', 'value2' ] } { 'struct': 'Base', - 'data': { 'enum1': 'TestEnum', 'name': 'str' } } + 'data': { 'enum1': 'TestEnum', '*name': 'str' } } { 'struct': 'Branch1', 'data': { 'name': 'str' } } { 'struct': 'Branch2', diff --git a/tests/qapi-schema/flat-union-branch-clash.out b/tests/qapi-schema/flat-union-branch-clash.out index 04c2395..e69de29 100644 --- a/tests/qapi-schema/flat-union-branch-clash.out +++ b/tests/qapi-schema/flat-union-branch-clash.out @@ -1,9 +0,0 @@ -[OrderedDict([('enum', 'TestEnum'), ('data', ['value1', 'value2'])]), - OrderedDict([('struct', 'Base'), ('data', OrderedDict([('enum1', 'TestEnum'), ('name', 'str')]))]), - OrderedDict([('struct', 'Branch1'), ('data', OrderedDict([('name', 'str')]))]), - OrderedDict([('struct', 'Branch2'), ('data', OrderedDict([('value', 'int')]))]), - OrderedDict([('union', 'TestUnion'), ('base', 'Base'), ('discriminator', 'enum1'), ('data', OrderedDict([('value1', 'Branch1'), ('value2', 'Branch2')]))])] -[{'enum_name': 'TestEnum', 'enum_values': ['value1', 'value2']}] -[OrderedDict([('struct', 'Base'), ('data', OrderedDict([('enum1', 'TestEnum'), ('name', 'str')]))]), - OrderedDict([('struct', 'Branch1'), ('data', OrderedDict([('name', 'str')]))]), - OrderedDict([('struct', 'Branch2'), ('data', OrderedDict([('value', 'int')]))])] diff --git a/tests/qapi-schema/struct-base-clash-deep.err b/tests/qapi-schema/struct-base-clash-deep.err new file mode 100644 index 0000000..e3e9f8d --- /dev/null +++ b/tests/qapi-schema/struct-base-clash-deep.err @@ -0,0 +1 @@ +tests/qapi-schema/struct-base-clash-deep.json:7: Member name 'name' clashes with base 'Base' diff --git a/tests/qapi-schema/struct-base-clash-deep.exit b/tests/qapi-schema/struct-base-clash-deep.exit new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/tests/qapi-schema/struct-base-clash-deep.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/struct-base-clash-deep.json b/tests/qapi-schema/struct-base-clash-deep.json new file mode 100644 index 0000000..552fe94 --- /dev/null +++ b/tests/qapi-schema/struct-base-clash-deep.json @@ -0,0 +1,9 @@ +# we check for no duplicate keys with indirect base +{ 'struct': 'Base', + 'data': { 'name': 'str' } } +{ 'struct': 'Mid', + 'base': 'Base', + 'data': { 'value': 'int' } } +{ 'struct': 'Sub', + 'base': 'Mid', + 'data': { '*name': 'str' } } diff --git a/tests/qapi-schema/struct-base-clash-deep.out b/tests/qapi-schema/struct-base-clash-deep.out new file mode 100644 index 0000000..e69de29 diff --git a/tests/qapi-schema/struct-base-clash.err b/tests/qapi-schema/struct-base-clash.err new file mode 100644 index 0000000..3ac37fb --- /dev/null +++ b/tests/qapi-schema/struct-base-clash.err @@ -0,0 +1 @@ +tests/qapi-schema/struct-base-clash.json:4: Member name 'name' clashes with base 'Base' diff --git a/tests/qapi-schema/struct-base-clash.exit b/tests/qapi-schema/struct-base-clash.exit new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/tests/qapi-schema/struct-base-clash.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/struct-base-clash.json b/tests/qapi-schema/struct-base-clash.json new file mode 100644 index 0000000..f2afc9b --- /dev/null +++ b/tests/qapi-schema/struct-base-clash.json @@ -0,0 +1,6 @@ +# we check for no duplicate keys with base +{ 'struct': 'Base', + 'data': { 'name': 'str' } } +{ 'struct': 'Sub', + 'base': 'Base', + 'data': { 'name': 'str' } }