Hi On Thu, Dec 6, 2018 at 7:56 PM Markus Armbruster <arm...@redhat.com> wrote: > > Marc-André Lureau <marcandre.lur...@redhat.com> writes: > > > Wherever a struct/union/alternate/command/event member with NAME: TYPE > > form is accepted, desugar it to a NAME: { 'type': TYPE } form. > > > > This will allow to add new member details, such as 'if' in the > > following patch to introduce conditionals, or 'default' for default > > values etc. > > > > Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> > > --- > > scripts/qapi/common.py | 56 +++++++++++++------ > > tests/Makefile.include | 3 + > > tests/qapi-schema/alternate-invalid-dict.err | 1 + > > tests/qapi-schema/alternate-invalid-dict.exit | 1 + > > tests/qapi-schema/alternate-invalid-dict.json | 4 ++ > > tests/qapi-schema/alternate-invalid-dict.out | 0 > > tests/qapi-schema/event-nest-struct.err | 2 +- > > tests/qapi-schema/flat-union-inline.err | 2 +- > > tests/qapi-schema/nested-struct-data.err | 2 +- > > tests/qapi-schema/qapi-schema-test.json | 10 ++-- > > .../struct-member-invalid-dict.err | 1 + > > .../struct-member-invalid-dict.exit | 1 + > > .../struct-member-invalid-dict.json | 3 + > > .../struct-member-invalid-dict.out | 0 > > .../qapi-schema/union-branch-invalid-dict.err | 1 + > > .../union-branch-invalid-dict.exit | 1 + > > .../union-branch-invalid-dict.json | 4 ++ > > .../qapi-schema/union-branch-invalid-dict.out | 0 > > 18 files changed, 66 insertions(+), 26 deletions(-) > > create mode 100644 tests/qapi-schema/alternate-invalid-dict.err > > create mode 100644 tests/qapi-schema/alternate-invalid-dict.exit > > create mode 100644 tests/qapi-schema/alternate-invalid-dict.json > > create mode 100644 tests/qapi-schema/alternate-invalid-dict.out > > create mode 100644 tests/qapi-schema/struct-member-invalid-dict.err > > create mode 100644 tests/qapi-schema/struct-member-invalid-dict.exit > > create mode 100644 tests/qapi-schema/struct-member-invalid-dict.json > > create mode 100644 tests/qapi-schema/struct-member-invalid-dict.out > > create mode 100644 tests/qapi-schema/union-branch-invalid-dict.err > > create mode 100644 tests/qapi-schema/union-branch-invalid-dict.exit > > create mode 100644 tests/qapi-schema/union-branch-invalid-dict.json > > create mode 100644 tests/qapi-schema/union-branch-invalid-dict.out > > > > diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py > > index d83fa1900e..fc68bb472e 100644 > > --- a/scripts/qapi/common.py > > +++ b/scripts/qapi/common.py > > @@ -588,11 +588,11 @@ def discriminator_find_enum_define(expr): > > if not base_members: > > return None > > > > - discriminator_type = base_members.get(discriminator) > > - if not discriminator_type: > > + discriminator_member = base_members.get(discriminator) > > + if not discriminator_member: > > return None > > > > - return enum_types.get(discriminator_type) > > + return enum_types.get(discriminator_member['type']) > > > > > > The name discriminator_member is confusing. It suggests its value is > the (desugared) member definition, i.e. something like > > {'enum1': {'type': 'EnumOne'}} > > it's actually only the definition's value part, i.e. something like > > {'type': 'EnumOne'} > > Naming is hard... discriminator_value? discriminator_def_rhs? > > Same in check_union() below. >
discriminator_value, ok > > # Names must be letters, numbers, -, and _. They must start with letter, > > @@ -660,6 +660,15 @@ def check_if(expr, info): > > check_if_str(ifcond, info) > > > > > > +def normalize_members(expr, field): > > + members = expr.get(field) > > + if isinstance(members, OrderedDict): > > + for key, arg in members.items(): > > + if isinstance(arg, dict): > > + continue > > + members[key] = {'type': arg} > > + > > + > > PATCH 12's normalize_enum() conflates normalization and checking. This > one doesn't. Better. I fixed patch 12 > > > def check_type(info, source, value, allow_array=False, > > allow_implicit=False, allow_optional=False, > > allow_metas=[]): > > @@ -704,8 +713,10 @@ def check_type(info, source, value, allow_array=False, > > % (source, key)) > > # Todo: allow dictionaries to represent default values of > > # an optional argument. > > - check_type(info, "Member '%s' of %s" % (key, source), arg, > > - allow_array=True, > > + check_known_keys(info, "member '%s' of %s" % (key, source), > > + arg, ['type'], []) > > + check_type(info, "Member '%s' of %s" % (key, source), > > + arg['type'], allow_array=True, > > allow_metas=['built-in', 'union', 'alternate', 'struct', > > 'enum']) > > > > @@ -776,13 +787,13 @@ def check_union(expr, info): > > # member of the base struct. > > check_name(info, "Discriminator of flat union '%s'" % name, > > discriminator) > > - discriminator_type = base_members.get(discriminator) > > - if not discriminator_type: > > + discriminator_member = base_members.get(discriminator) > > + if not discriminator_member: > > raise QAPISemError(info, > > "Discriminator '%s' is not a member of base > > " > > "struct '%s'" > > % (discriminator, base)) > > - enum_define = enum_types.get(discriminator_type) > > + enum_define = enum_types.get(discriminator_member['type']) > > allow_metas = ['struct'] > > # Do not allow string discriminator > > if not enum_define: > > @@ -795,10 +806,13 @@ def check_union(expr, info): > > raise QAPISemError(info, "Union '%s' cannot have empty 'data'" % > > name) > > for (key, value) in members.items(): > > check_name(info, "Member of union '%s'" % name, key) > > + source = "member '%s' of union '%s'" % (key, name) > > + check_known_keys(info, source, value, ['type'], []) > > Elsewhere, you do it like > > check_known_keys(info, "member '%s' of union '%s'" % (key, name), > value, ['type'], []) > > Let's stick to that. ok > > > + typ = value['type'] > > > > # Each value must name a known type > > check_type(info, "Member '%s' of union '%s'" % (key, name), > > - value, allow_array=not base, allow_metas=allow_metas) > > + typ, allow_array=not base, allow_metas=allow_metas) > > > > # If the discriminator names an enum type, then all members > > # of 'data' must also be members of the enum type. > > @@ -822,18 +836,20 @@ def check_alternate(expr, info): > > "in 'data'" % name) > > for (key, value) in members.items(): > > check_name(info, "Member of alternate '%s'" % name, key) > > + source = "member '%s' of alternate '%s'" % (key, name) > > + check_known_keys(info, source, value, ['type'], []) > > Likewise. > > > + typ = value['type'] > > > > # Ensure alternates have no type conflicts. > > - check_type(info, "Member '%s' of alternate '%s'" % (key, name), > > - value, > > + check_type(info, "Member '%s' of alternate '%s'" % (key, name), > > typ, > > allow_metas=['built-in', 'union', 'struct', 'enum']) > > - qtype = find_alternate_member_qtype(value) > > + qtype = find_alternate_member_qtype(typ) > > if not qtype: > > raise QAPISemError(info, "Alternate '%s' member '%s' cannot > > use " > > - "type '%s'" % (name, key, value)) > > + "type '%s'" % (name, key, typ)) > > conflicting = set([qtype]) > > if qtype == 'QTYPE_QSTRING': > > - enum_expr = enum_types.get(value) > > + enum_expr = enum_types.get(typ) > > if enum_expr: > > for v in enum_get_names(enum_expr): > > if v in ['on', 'off']: > > @@ -947,6 +963,10 @@ def check_exprs(exprs): > > info = expr_elem['info'] > > if 'enum' in expr: > > normalize_enum(expr, info) > > + elif 'union' in expr: > > + normalize_members(expr, 'base') > > + if {'union', 'alternate', 'struct', 'command', 'event'} & > > set(expr): > > + normalize_members(expr, 'data') > > > > # Learn the types and check for valid expression keys > > for expr_elem in exprs: > > PATCH 12 had an issue in this loop, and the obvious fix was to fold it > into the next loop. I think this can be done here too. moved to end-of-check > > > @@ -1735,7 +1755,7 @@ class QAPISchema(object): > > return QAPISchemaObjectTypeMember(name, typ, optional) > > > > def _make_members(self, data, info): > > - return [self._make_member(key, value, info) > > + return [self._make_member(key, value['type'], info) > > for (key, value) in data.items()] > > > > def _def_struct_type(self, expr, info, doc): > > @@ -1771,11 +1791,11 @@ class QAPISchema(object): > > name, info, doc, ifcond, > > 'base', self._make_members(base, info)) > > if tag_name: > > - variants = [self._make_variant(key, value) > > + variants = [self._make_variant(key, value['type']) > > for (key, value) in data.items()] > > members = [] > > else: > > - variants = [self._make_simple_variant(key, value, info) > > + variants = [self._make_simple_variant(key, value['type'], info) > > for (key, value) in data.items()] > > typ = self._make_implicit_enum_type(name, info, ifcond, > > [v.name for v in variants]) > > @@ -1791,7 +1811,7 @@ class QAPISchema(object): > > name = expr['alternate'] > > data = expr['data'] > > ifcond = expr.get('if') > > - variants = [self._make_variant(key, value) > > + variants = [self._make_variant(key, value['type']) > > for (key, value) in data.items()] > > tag_member = QAPISchemaObjectTypeMember('type', 'QType', False) > > self._def_entity( > > diff --git a/tests/Makefile.include b/tests/Makefile.include > > index 3ba9097892..43e100a6cd 100644 > > --- a/tests/Makefile.include > > +++ b/tests/Makefile.include > > @@ -421,6 +421,7 @@ qapi-schema += alternate-conflict-string.json > > qapi-schema += alternate-conflict-bool-string.json > > qapi-schema += alternate-conflict-num-string.json > > qapi-schema += alternate-empty.json > > +qapi-schema += alternate-invalid-dict.json > > qapi-schema += alternate-nested.json > > qapi-schema += alternate-unknown.json > > qapi-schema += args-alternate.json > > @@ -563,6 +564,7 @@ qapi-schema += returns-whitelist.json > > qapi-schema += struct-base-clash-deep.json > > qapi-schema += struct-base-clash.json > > qapi-schema += struct-data-invalid.json > > +qapi-schema += struct-member-invalid-dict.json > > qapi-schema += struct-member-invalid.json > > qapi-schema += trailing-comma-list.json > > qapi-schema += trailing-comma-object.json > > @@ -574,6 +576,7 @@ qapi-schema += unicode-str.json > > qapi-schema += union-base-empty.json > > qapi-schema += union-base-no-discriminator.json > > qapi-schema += union-branch-case.json > > +qapi-schema += union-branch-invalid-dict.json > > qapi-schema += union-clash-branches.json > > qapi-schema += union-empty.json > > qapi-schema += union-invalid-base.json > > diff --git a/tests/qapi-schema/alternate-invalid-dict.err > > b/tests/qapi-schema/alternate-invalid-dict.err > > new file mode 100644 > > index 0000000000..631d46628e > > --- /dev/null > > +++ b/tests/qapi-schema/alternate-invalid-dict.err > > @@ -0,0 +1 @@ > > +tests/qapi-schema/alternate-invalid-dict.json:2: Key 'type' is missing > > from member 'two' of alternate 'Alt' > > diff --git a/tests/qapi-schema/alternate-invalid-dict.exit > > b/tests/qapi-schema/alternate-invalid-dict.exit > > new file mode 100644 > > index 0000000000..d00491fd7e > > --- /dev/null > > +++ b/tests/qapi-schema/alternate-invalid-dict.exit > > @@ -0,0 +1 @@ > > +1 > > diff --git a/tests/qapi-schema/alternate-invalid-dict.json > > b/tests/qapi-schema/alternate-invalid-dict.json > > new file mode 100644 > > index 0000000000..45f2c8ebef > > --- /dev/null > > +++ b/tests/qapi-schema/alternate-invalid-dict.json > > @@ -0,0 +1,4 @@ > > +# invalid field dictionnary, missing type > > dictionary argh.. > > Suggest a grep over the whole series. > > Even better, reuse struct-member-invalid-dict.json's comment here. ok > > > +{ 'alternate': 'Alt', > > + 'data': { 'one': 'str', > > + 'two': { 'if': 'foo' } } } > > diff --git a/tests/qapi-schema/alternate-invalid-dict.out > > b/tests/qapi-schema/alternate-invalid-dict.out > > new file mode 100644 > > index 0000000000..e69de29bb2 > > diff --git a/tests/qapi-schema/event-nest-struct.err > > b/tests/qapi-schema/event-nest-struct.err > > index 5a42701b8f..ea1be65d89 100644 > > --- a/tests/qapi-schema/event-nest-struct.err > > +++ b/tests/qapi-schema/event-nest-struct.err > > @@ -1 +1 @@ > > -tests/qapi-schema/event-nest-struct.json:1: Member 'a' of 'data' for event > > 'EVENT_A' should be a type name > > +tests/qapi-schema/event-nest-struct.json:1: Key 'type' is missing from > > member 'a' of 'data' for event 'EVENT_A' > > We lose the test of the "should be a type name" error. Let's copy this > test to event-member-invalid-dict.json to cover "Key 'type' is missing", > and update the original to keep covering "should be a type name". > ok > > > diff --git a/tests/qapi-schema/flat-union-inline.err > > b/tests/qapi-schema/flat-union-inline.err > > index 2333358d28..3d29433bfb 100644 > > --- a/tests/qapi-schema/flat-union-inline.err > > +++ b/tests/qapi-schema/flat-union-inline.err > > @@ -1 +1 @@ > > -tests/qapi-schema/flat-union-inline.json:7: Member 'value1' of union > > 'TestUnion' should be a type name > > +tests/qapi-schema/flat-union-inline.json:7: Key 'type' is missing from > > member 'value1' of union 'TestUnion' > > Likewise. > > > diff --git a/tests/qapi-schema/nested-struct-data.err > > b/tests/qapi-schema/nested-struct-data.err > > index da767bade2..48355adcbe 100644 > > --- a/tests/qapi-schema/nested-struct-data.err > > +++ b/tests/qapi-schema/nested-struct-data.err > > @@ -1 +1 @@ > > -tests/qapi-schema/nested-struct-data.json:2: Member 'a' of 'data' for > > command 'foo' should be a type name > > +tests/qapi-schema/nested-struct-data.json:2: Key 'type' is missing from > > member 'a' of 'data' for command 'foo' > > Likewise. > > > diff --git a/tests/qapi-schema/qapi-schema-test.json > > b/tests/qapi-schema/qapi-schema-test.json > > index 35ca94d991..30f635d484 100644 > > --- a/tests/qapi-schema/qapi-schema-test.json > > +++ b/tests/qapi-schema/qapi-schema-test.json > > Positive tests for ... > > > @@ -11,7 +11,7 @@ > > 'guest-sync' ] } } > > > > { 'struct': 'TestStruct', > > - 'data': { 'integer': 'int', 'boolean': 'bool', 'string': 'str' } } > > + 'data': { 'integer': {'type': 'int'}, 'boolean': 'bool', 'string': 'str' > > } } > > ... struct member, ... > > > > > # for testing enums > > { 'struct': 'NestedEnumsOne', > > @@ -77,7 +77,7 @@ > > { 'union': 'UserDefFlatUnion', > > 'base': 'UserDefUnionBase', # intentional forward reference > > 'discriminator': 'enum1', > > - 'data': { 'value1' : 'UserDefA', > > + 'data': { 'value1' : {'type': 'UserDefA'}, > > 'value2' : 'UserDefB', > > 'value3' : 'UserDefB' > > # 'value4' defaults to empty > > ... flat union member, ... > > > @@ -98,7 +98,7 @@ > > { 'struct': 'WrapAlternate', > > 'data': { 'alt': 'UserDefAlternate' } } > > { 'alternate': 'UserDefAlternate', > > - 'data': { 'udfu': 'UserDefFlatUnion', 'e': 'EnumOne', 'i': 'int', > > + 'data': { 'udfu': {'type': 'UserDefFlatUnion'}, 'e': 'EnumOne', 'i': > > 'int', > > 'n': 'null' } } > > > > ... alternate member, ... > > > { 'struct': 'UserDefC', > > @@ -134,7 +134,7 @@ > > { 'command': 'user_def_cmd', 'data': {} } > > { 'command': 'user_def_cmd1', 'data': {'ud1a': 'UserDefOne'} } > > { 'command': 'user_def_cmd2', > > - 'data': {'ud1a': 'UserDefOne', '*ud1b': 'UserDefOne'}, > > + 'data': {'ud1a': {'type': 'UserDefOne'}, '*ud1b': 'UserDefOne'}, > > 'returns': 'UserDefTwo' } > > > > ... command argument, ... > > > # Returning a non-dictionary requires a name from the whitelist > > @@ -164,7 +164,7 @@ > > > > # testing event > > { 'struct': 'EventStructOne', > > - 'data': { 'struct1': 'UserDefOne', 'string': 'str', '*enum2': 'EnumOne' > > } } > > + 'data': { 'struct1': {'type': 'UserDefOne'}, 'string': 'str', '*enum2': > > 'EnumOne' } } > > > > { 'event': 'EVENT_A' } > > { 'event': 'EVENT_B', > > ... and event argument. > > Missing: simple union. Okay because the desugaring is oblivious of the > difference between flat and simple unions. > > > diff --git a/tests/qapi-schema/struct-member-invalid-dict.err > > b/tests/qapi-schema/struct-member-invalid-dict.err > > new file mode 100644 > > index 0000000000..6a765bc668 > > --- /dev/null > > +++ b/tests/qapi-schema/struct-member-invalid-dict.err > > @@ -0,0 +1 @@ > > +tests/qapi-schema/struct-member-invalid-dict.json:2: Key 'type' is missing > > from member '*a' of 'data' for struct 'foo' > > diff --git a/tests/qapi-schema/struct-member-invalid-dict.exit > > b/tests/qapi-schema/struct-member-invalid-dict.exit > > new file mode 100644 > > index 0000000000..d00491fd7e > > --- /dev/null > > +++ b/tests/qapi-schema/struct-member-invalid-dict.exit > > @@ -0,0 +1 @@ > > +1 > > diff --git a/tests/qapi-schema/struct-member-invalid-dict.json > > b/tests/qapi-schema/struct-member-invalid-dict.json > > new file mode 100644 > > index 0000000000..ebd9733b49 > > --- /dev/null > > +++ b/tests/qapi-schema/struct-member-invalid-dict.json > > @@ -0,0 +1,3 @@ > > +# exploded member form must have a 'type' > > Suggest > > # Long form of member must have a value member 'type' > ok > or simply > > # Missing type > > > +{ 'struct': 'foo', > > + 'data': { '*a': { 'case': 'foo' } } } > > diff --git a/tests/qapi-schema/struct-member-invalid-dict.out > > b/tests/qapi-schema/struct-member-invalid-dict.out > > new file mode 100644 > > index 0000000000..e69de29bb2 > > diff --git a/tests/qapi-schema/union-branch-invalid-dict.err > > b/tests/qapi-schema/union-branch-invalid-dict.err > > new file mode 100644 > > index 0000000000..89f9b36791 > > --- /dev/null > > +++ b/tests/qapi-schema/union-branch-invalid-dict.err > > @@ -0,0 +1 @@ > > +tests/qapi-schema/union-branch-invalid-dict.json:2: Key 'type' is missing > > from member 'integer' of union 'UnionInvalidBranch' > > diff --git a/tests/qapi-schema/union-branch-invalid-dict.exit > > b/tests/qapi-schema/union-branch-invalid-dict.exit > > new file mode 100644 > > index 0000000000..d00491fd7e > > --- /dev/null > > +++ b/tests/qapi-schema/union-branch-invalid-dict.exit > > @@ -0,0 +1 @@ > > +1 > > diff --git a/tests/qapi-schema/union-branch-invalid-dict.json > > b/tests/qapi-schema/union-branch-invalid-dict.json > > new file mode 100644 > > index 0000000000..19c5d9cacd > > --- /dev/null > > +++ b/tests/qapi-schema/union-branch-invalid-dict.json > > @@ -0,0 +1,4 @@ > > +# exploded member form must have a 'type' > > Likewise. > > > +{ 'union': 'UnionInvalidBranch', > > + 'data': { 'integer': { 'if': 'foo'}, > > + 's8': 'int8' } } > > I avoid simple unions like the plague. That said, having one here is > tolerable; we'll just have to replace it when we abolish simple unions. > > > diff --git a/tests/qapi-schema/union-branch-invalid-dict.out > > b/tests/qapi-schema/union-branch-invalid-dict.out > > new file mode 100644 > > index 0000000000..e69de29bb2