qemu with hax to log dma reads & writes jcs.org/2018/11/12/vfio

qapi: Move context-sensitive checking to the proper place

When we introduced the QAPISchema intermediate representation (commit
ac88219a6c7), we took a shortcut: we left check_exprs() & friends
alone instead of moving semantic checks into the
QAPISchemaFOO.check(). The .check() assert check_exprs() did its job.

Time to finish the conversion job. Move exactly the context-sensitive
checks to the .check(). They replace assertions there. Context-free
checks stay put.

Fixes the misleading optional tag error demonstrated by test
flat-union-optional-discriminator.

A few other error message improve.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-Id: <20190927134639.4284-17-armbru@redhat.com>

+230 -270
+193 -231
scripts/qapi/common.py
··· 21 21 import sys 22 22 from collections import OrderedDict 23 23 24 - builtin_types = { 25 - 'null': 'QTYPE_QNULL', 26 - 'str': 'QTYPE_QSTRING', 27 - 'int': 'QTYPE_QNUM', 28 - 'number': 'QTYPE_QNUM', 29 - 'bool': 'QTYPE_QBOOL', 30 - 'int8': 'QTYPE_QNUM', 31 - 'int16': 'QTYPE_QNUM', 32 - 'int32': 'QTYPE_QNUM', 33 - 'int64': 'QTYPE_QNUM', 34 - 'uint8': 'QTYPE_QNUM', 35 - 'uint16': 'QTYPE_QNUM', 36 - 'uint32': 'QTYPE_QNUM', 37 - 'uint64': 'QTYPE_QNUM', 38 - 'size': 'QTYPE_QNUM', 39 - 'any': None, # any QType possible, actually 40 - 'QType': 'QTYPE_QSTRING', 41 - } 42 - 43 24 # Are documentation comments required? 44 25 doc_required = False 45 26 ··· 48 29 49 30 # Whitelist of entities allowed to violate case conventions 50 31 name_case_whitelist = [] 51 - 52 - enum_types = {} 53 - struct_types = {} 54 - union_types = {} 55 - all_names = {} 56 32 57 33 58 34 # ··· 671 647 672 648 673 649 # 674 - # Semantic analysis of schema expressions 675 - # TODO fold into QAPISchema 676 - # TODO catching name collisions in generated code would be nice 650 + # Check (context-free) schema expression structure 677 651 # 678 652 679 - 680 - def find_base_members(base): 681 - if isinstance(base, dict): 682 - return base 683 - base_struct_define = struct_types.get(base) 684 - if not base_struct_define: 685 - return None 686 - return base_struct_define['data'] 687 - 688 - 689 - # Return the qtype of an alternate branch, or None on error. 690 - def find_alternate_member_qtype(qapi_type): 691 - if qapi_type in builtin_types: 692 - return builtin_types[qapi_type] 693 - elif qapi_type in struct_types: 694 - return 'QTYPE_QDICT' 695 - elif qapi_type in enum_types: 696 - return 'QTYPE_QSTRING' 697 - elif qapi_type in union_types: 698 - return 'QTYPE_QDICT' 699 - return None 700 - 701 - 702 653 # Names must be letters, numbers, -, and _. They must start with letter, 703 654 # except for downstream extensions which must start with __RFQDN_. 704 655 # Dots are only valid in the downstream extension prefix. ··· 748 699 info, "%s '%s' should not end in '%s'" % (meta, name, name[-4:])) 749 700 750 701 751 - def add_name(name, info, meta): 752 - global all_names 753 - # FIXME should reject names that differ only in '_' vs. '.' 754 - # vs. '-', because they're liable to clash in generated C. 755 - if name in all_names: 756 - raise QAPISemError(info, "%s '%s' is already defined" 757 - % (all_names[name], name)) 758 - all_names[name] = meta 759 - 760 - 761 702 def check_if(expr, info): 762 703 763 704 def check_if_str(ifcond, info): ··· 781 722 782 723 783 724 def check_type(value, info, source, 784 - allow_array=False, allow_dict=False, allow_metas=[]): 785 - global all_names 786 - 725 + allow_array=False, allow_dict=False): 787 726 if value is None: 788 727 return 789 728 790 - # Check if array type for value is okay 729 + # Array type 791 730 if isinstance(value, list): 792 731 if not allow_array: 793 732 raise QAPISemError(info, "%s cannot be an array" % source) ··· 795 734 raise QAPISemError(info, 796 735 "%s: array type must contain single type name" % 797 736 source) 798 - check_type(value[0], info, source, allow_metas=allow_metas) 799 737 return 800 738 801 - # Check if type name for value is okay 739 + # Type name 802 740 if isinstance(value, str): 803 - if value not in all_names: 804 - raise QAPISemError(info, "%s uses unknown type '%s'" 805 - % (source, value)) 806 - if not all_names[value] in allow_metas: 807 - raise QAPISemError(info, "%s cannot use %s type '%s'" % 808 - (source, all_names[value], value)) 809 741 return 742 + 743 + # Anonymous type 810 744 811 745 if not allow_dict: 812 746 raise QAPISemError(info, "%s should be a type name" % source) ··· 829 763 check_if(arg, info) 830 764 normalize_if(arg) 831 765 check_type(arg['type'], info, "member '%s' of %s" % (key, source), 832 - allow_array=True, 833 - allow_metas=['built-in', 'union', 'alternate', 'struct', 834 - 'enum']) 766 + allow_array=True) 835 767 836 768 837 769 def check_command(expr, info): 838 770 name = expr['command'] 839 771 boxed = expr.get('boxed', False) 840 772 841 - args_meta = ['struct'] 842 - if boxed: 843 - args_meta += ['union'] 844 773 check_type(expr.get('data'), info, 845 774 "'data' for command '%s'" % name, 846 - allow_dict=not boxed, allow_metas=args_meta) 847 - returns_meta = ['union', 'struct'] 848 - if name in returns_whitelist: 849 - returns_meta += ['built-in', 'alternate', 'enum'] 775 + allow_dict=not boxed) 850 776 check_type(expr.get('returns'), info, 851 777 "'returns' for command '%s'" % name, 852 - allow_array=True, allow_metas=returns_meta) 778 + allow_array=True) 853 779 854 780 855 781 def check_event(expr, info): 856 782 name = expr['event'] 857 783 boxed = expr.get('boxed', False) 858 784 859 - meta = ['struct'] 860 - if boxed: 861 - meta += ['union'] 862 785 check_type(expr.get('data'), info, 863 786 "'data' for event '%s'" % name, 864 - allow_dict=not boxed, allow_metas=meta) 865 - 866 - 867 - def enum_get_names(expr): 868 - return [e['name'] for e in expr['data']] 787 + allow_dict=not boxed) 869 788 870 789 871 790 def check_union(expr, info): ··· 874 793 discriminator = expr.get('discriminator') 875 794 members = expr['data'] 876 795 877 - # Two types of unions, determined by discriminator. 878 - 879 - # With no discriminator it is a simple union. 880 - if discriminator is None: 881 - enum_values = members.keys() 882 - allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum'] 796 + if discriminator is None: # simple union 883 797 if base is not None: 884 798 raise QAPISemError( 885 799 info, "simple union '%s' must not have a base" % name) 886 - 887 - # Else, it's a flat union. 888 - else: 889 - # The object must have a string or dictionary 'base'. 800 + else: # flat union 890 801 check_type(base, info, "'base' for union '%s'" % name, 891 - allow_dict=name, allow_metas=['struct']) 802 + allow_dict=name) 892 803 if not base: 893 804 raise QAPISemError( 894 805 info, "flat union '%s' must have a base" % name) 895 - base_members = find_base_members(base) 896 - assert base_members is not None 897 - 898 - # The value of member 'discriminator' must name a non-optional 899 - # member of the base struct. 900 806 check_name_is_str(discriminator, info, 901 807 "discriminator of flat union '%s'" % name) 902 - check_name_str(discriminator, info, 903 - "discriminator of flat union '%s'" % name) 904 - discriminator_value = base_members.get(discriminator) 905 - if not discriminator_value: 906 - raise QAPISemError(info, 907 - "discriminator '%s' is not a member of 'base'" 908 - % discriminator) 909 - if discriminator_value.get('if'): 910 - raise QAPISemError( 911 - info, 912 - "the discriminator '%s' for union %s must not be conditional" 913 - % (discriminator, name)) 914 - enum_define = enum_types.get(discriminator_value['type']) 915 - # Do not allow string discriminator 916 - if not enum_define: 917 - raise QAPISemError( 918 - info, 919 - "discriminator '%s' must be of enumeration type" 920 - % discriminator) 921 - enum_values = enum_get_names(enum_define) 922 - allow_metas = ['struct'] 923 - 924 - if (len(enum_values) == 0): 925 - raise QAPISemError(info, "union '%s' has no branches" % name) 926 808 927 809 for (key, value) in members.items(): 928 810 check_name_str(key, info, "member of union '%s'" % name) ··· 931 813 ['type'], ['if']) 932 814 check_if(value, info) 933 815 normalize_if(value) 934 - # Each value must name a known type 935 816 check_type(value['type'], info, 936 817 "member '%s' of union '%s'" % (key, name), 937 - allow_array=not base, allow_metas=allow_metas) 938 - 939 - # If the discriminator names an enum type, then all members 940 - # of 'data' must also be members of the enum type. 941 - if discriminator is not None: 942 - if key not in enum_values: 943 - raise QAPISemError( 944 - info, 945 - "discriminator value '%s' is not found in enum '%s'" 946 - % (key, enum_define['enum'])) 818 + allow_array=not base) 947 819 948 820 949 821 def check_alternate(expr, info): 950 822 name = expr['alternate'] 951 823 members = expr['data'] 952 - types_seen = {} 953 824 954 825 if len(members) == 0: 955 826 raise QAPISemError(info, ··· 961 832 ['type'], ['if']) 962 833 check_if(value, info) 963 834 normalize_if(value) 964 - typ = value['type'] 965 - 966 - # Ensure alternates have no type conflicts. 967 - check_type(typ, info, "member '%s' of alternate '%s'" % (key, name), 968 - allow_metas=['built-in', 'union', 'struct', 'enum']) 969 - qtype = find_alternate_member_qtype(typ) 970 - if not qtype: 971 - raise QAPISemError( 972 - info, 973 - "alternate '%s' member '%s' cannot use type '%s'" 974 - % (name, key, typ)) 975 - conflicting = set([qtype]) 976 - if qtype == 'QTYPE_QSTRING': 977 - enum_expr = enum_types.get(typ) 978 - if enum_expr: 979 - for v in enum_get_names(enum_expr): 980 - if v in ['on', 'off']: 981 - conflicting.add('QTYPE_QBOOL') 982 - if re.match(r'[-+0-9.]', v): # lazy, could be tightened 983 - conflicting.add('QTYPE_QNUM') 984 - else: 985 - conflicting.add('QTYPE_QNUM') 986 - conflicting.add('QTYPE_QBOOL') 987 - for qt in conflicting: 988 - if qt in types_seen: 989 - raise QAPISemError( 990 - info, 991 - "alternate '%s' member '%s' can't be distinguished " 992 - "from member '%s'" 993 - % (name, key, types_seen[qt])) 994 - types_seen[qt] = key 835 + check_type(value['type'], info, 836 + "member '%s' of alternate '%s'" % (key, name)) 995 837 996 838 997 839 def check_enum(expr, info): ··· 1024 866 1025 867 check_type(members, info, "'data' for struct '%s'" % name, 1026 868 allow_dict=name) 1027 - check_type(expr.get('base'), info, "'base' for struct '%s'" % name, 1028 - allow_metas=['struct']) 869 + check_type(expr.get('base'), info, "'base' for struct '%s'" % name) 1029 870 1030 871 if features: 1031 872 if not isinstance(features, list): ··· 1111 952 1112 953 1113 954 def check_exprs(exprs): 1114 - global all_names 1115 - 1116 - # Populate name table with names of built-in types 1117 - for builtin in builtin_types.keys(): 1118 - all_names[builtin] = 'built-in' 1119 - 1120 - # Learn the types and check for valid expression keys 1121 955 for expr_elem in exprs: 1122 956 expr = expr_elem['expr'] 1123 957 info = expr_elem['info'] ··· 1134 968 meta = 'enum' 1135 969 check_keys(expr, info, 'enum', ['data'], ['if', 'prefix']) 1136 970 normalize_enum(expr) 1137 - enum_types[expr[meta]] = expr 1138 971 elif 'union' in expr: 1139 972 meta = 'union' 1140 973 check_keys(expr, info, 'union', ['data'], 1141 974 ['base', 'discriminator', 'if']) 1142 975 normalize_members(expr.get('base')) 1143 976 normalize_members(expr['data']) 1144 - union_types[expr[meta]] = expr 1145 977 elif 'alternate' in expr: 1146 978 meta = 'alternate' 1147 979 check_keys(expr, info, 'alternate', ['data'], ['if']) ··· 1152 984 ['base', 'if', 'features']) 1153 985 normalize_members(expr['data']) 1154 986 normalize_features(expr.get('features')) 1155 - struct_types[expr[meta]] = expr 1156 987 elif 'command' in expr: 1157 988 meta = 'command' 1158 989 check_keys(expr, info, 'command', [], ··· 1166 997 else: 1167 998 raise QAPISemError(info, "expression is missing metatype") 1168 999 normalize_if(expr) 1000 + 1169 1001 name = expr[meta] 1170 1002 check_name_is_str(name, info, "'%s'" % meta) 1171 1003 info.set_defn(meta, name) 1172 1004 check_defn_name_str(name, info, meta) 1173 - add_name(name, info, meta) 1005 + 1174 1006 if doc and doc.symbol != name: 1175 1007 raise QAPISemError( 1176 1008 info, 1177 1009 "definition of '%s' follows documentation for '%s'" 1178 1010 % (name, doc.symbol)) 1179 1011 1180 - # Validate that exprs make sense 1181 - for expr_elem in exprs: 1182 - expr = expr_elem['expr'] 1183 - info = expr_elem['info'] 1184 - doc = expr_elem.get('doc') 1185 - 1186 - if 'include' in expr: 1187 - continue 1188 - if 'enum' in expr: 1012 + if meta == 'enum': 1189 1013 check_enum(expr, info) 1190 - elif 'union' in expr: 1014 + elif meta == 'union': 1191 1015 check_union(expr, info) 1192 - elif 'alternate' in expr: 1016 + elif meta == 'alternate': 1193 1017 check_alternate(expr, info) 1194 - elif 'struct' in expr: 1018 + elif meta == 'struct': 1195 1019 check_struct(expr, info) 1196 - elif 'command' in expr: 1020 + elif meta == 'command': 1197 1021 check_command(expr, info) 1198 - elif 'event' in expr: 1022 + elif meta == 'event': 1199 1023 check_event(expr, info) 1200 1024 else: 1201 1025 assert False, 'unexpected meta type' ··· 1208 1032 1209 1033 # 1210 1034 # Schema compiler frontend 1035 + # TODO catching name collisions in generated code would be nice 1211 1036 # 1212 1037 1213 1038 class QAPISchemaEntity(object): 1039 + meta = None 1040 + 1214 1041 def __init__(self, name, info, doc, ifcond=None): 1215 1042 assert name is None or isinstance(name, str) 1216 1043 self.name = name ··· 1250 1077 1251 1078 def visit(self, visitor): 1252 1079 assert self._checked 1080 + 1081 + def describe(self): 1082 + assert self.meta 1083 + return "%s '%s'" % (self.meta, self.name) 1253 1084 1254 1085 1255 1086 class QAPISchemaVisitor(object): ··· 1341 1172 return None 1342 1173 return self.name 1343 1174 1175 + def describe(self): 1176 + assert self.meta 1177 + return "%s type '%s'" % (self.meta, self.name) 1178 + 1344 1179 1345 1180 class QAPISchemaBuiltinType(QAPISchemaType): 1181 + meta = 'built-in' 1182 + 1346 1183 def __init__(self, name, json_type, c_type): 1347 1184 QAPISchemaType.__init__(self, name, None, None) 1348 1185 assert not c_type or isinstance(c_type, str) ··· 1374 1211 1375 1212 1376 1213 class QAPISchemaEnumType(QAPISchemaType): 1214 + meta = 'enum' 1215 + 1377 1216 def __init__(self, name, info, doc, ifcond, members, prefix): 1378 1217 QAPISchemaType.__init__(self, name, info, doc, ifcond) 1379 1218 for m in members: ··· 1411 1250 1412 1251 1413 1252 class QAPISchemaArrayType(QAPISchemaType): 1253 + meta = 'array' 1254 + 1414 1255 def __init__(self, name, info, element_type): 1415 1256 QAPISchemaType.__init__(self, name, info, None, None) 1416 1257 assert isinstance(element_type, str) ··· 1419 1260 1420 1261 def check(self, schema): 1421 1262 QAPISchemaType.check(self, schema) 1422 - self.element_type = schema.lookup_type(self._element_type_name) 1423 - assert self.element_type 1263 + self.element_type = schema.resolve_type( 1264 + self._element_type_name, self.info, 1265 + self.info and self.info.defn_meta) 1424 1266 assert not isinstance(self.element_type, QAPISchemaArrayType) 1425 1267 1426 1268 @property ··· 1452 1294 QAPISchemaType.visit(self, visitor) 1453 1295 visitor.visit_array_type(self.name, self.info, self.ifcond, 1454 1296 self.element_type) 1297 + 1298 + def describe(self): 1299 + assert self.meta 1300 + return "%s type ['%s']" % (self.meta, self._element_type_name) 1455 1301 1456 1302 1457 1303 class QAPISchemaObjectType(QAPISchemaType): ··· 1461 1307 # flat union has base, variants, and no local_members 1462 1308 # simple union has local_members, variants, and no base 1463 1309 QAPISchemaType.__init__(self, name, info, doc, ifcond) 1310 + self.meta = 'union' if variants else 'struct' 1464 1311 assert base is None or isinstance(base, str) 1465 1312 for m in local_members: 1466 1313 assert isinstance(m, QAPISchemaObjectTypeMember) ··· 1495 1342 1496 1343 seen = OrderedDict() 1497 1344 if self._base_name: 1498 - self.base = schema.lookup_type(self._base_name) 1499 - assert isinstance(self.base, QAPISchemaObjectType) 1345 + self.base = schema.resolve_type(self._base_name, self.info, 1346 + "'base'") 1347 + if (not isinstance(self.base, QAPISchemaObjectType) 1348 + or self.base.variants): 1349 + raise QAPISemError( 1350 + self.info, 1351 + "'base' requires a struct type, %s isn't" 1352 + % self.base.describe()) 1500 1353 self.base.check(schema) 1501 1354 self.base.check_clash(self.info, seen) 1502 1355 for m in self.local_members: ··· 1508 1361 1509 1362 if self.variants: 1510 1363 self.variants.check(schema, seen) 1511 - assert self.variants.tag_member in members 1512 1364 self.variants.check_clash(self.info, seen) 1513 1365 1514 1366 # Features are in a name space separate from members ··· 1646 1498 1647 1499 def check(self, schema): 1648 1500 assert self.defined_in 1649 - self.type = schema.lookup_type(self._type_name) 1650 - assert self.type 1501 + self.type = schema.resolve_type(self._type_name, self.info, 1502 + self.describe) 1651 1503 1652 1504 1653 1505 class QAPISchemaObjectTypeVariants(object): ··· 1671 1523 v.set_defined_in(name) 1672 1524 1673 1525 def check(self, schema, seen): 1674 - if not self.tag_member: # flat union 1675 - self.tag_member = seen[c_name(self._tag_name)] 1676 - assert self._tag_name == self.tag_member.name 1677 - assert isinstance(self.tag_member.type, QAPISchemaEnumType) 1678 - assert not self.tag_member.optional 1679 - assert self.tag_member.ifcond == [] 1526 + if not self.tag_member: # flat union 1527 + self.tag_member = seen.get(c_name(self._tag_name)) 1528 + base = "'base'" 1529 + # Pointing to the base type when not implicit would be 1530 + # nice, but we don't know it here 1531 + if not self.tag_member or self._tag_name != self.tag_member.name: 1532 + raise QAPISemError( 1533 + self.info, 1534 + "discriminator '%s' is not a member of %s" 1535 + % (self._tag_name, base)) 1536 + # Here we do: 1537 + base_type = schema.lookup_type(self.tag_member.defined_in) 1538 + assert base_type 1539 + if not base_type.is_implicit(): 1540 + base = "base type '%s'" % self.tag_member.defined_in 1541 + if not isinstance(self.tag_member.type, QAPISchemaEnumType): 1542 + raise QAPISemError( 1543 + self.info, 1544 + "discriminator member '%s' of %s must be of enum type" 1545 + % (self._tag_name, base)) 1546 + if self.tag_member.optional: 1547 + raise QAPISemError( 1548 + self.info, 1549 + "discriminator member '%s' of %s must not be optional" 1550 + % (self._tag_name, base)) 1551 + if self.tag_member.ifcond: 1552 + raise QAPISemError( 1553 + self.info, 1554 + "discriminator member '%s' of %s must not be conditional" 1555 + % (self._tag_name, base)) 1556 + else: # simple union 1557 + assert isinstance(self.tag_member.type, QAPISchemaEnumType) 1558 + assert not self.tag_member.optional 1559 + assert self.tag_member.ifcond == [] 1680 1560 if self._tag_name: # flat union 1681 1561 # branches that are not explicitly covered get an empty type 1682 1562 cases = set([v.name for v in self.variants]) ··· 1686 1566 'q_empty', m.ifcond) 1687 1567 v.set_defined_in(self.tag_member.defined_in) 1688 1568 self.variants.append(v) 1689 - assert self.variants 1569 + if not self.variants: 1570 + raise QAPISemError(self.info, "union has no branches") 1690 1571 for v in self.variants: 1691 1572 v.check(schema) 1692 1573 # Union names must match enum values; alternate names are 1693 1574 # checked separately. Use 'seen' to tell the two apart. 1694 1575 if seen: 1695 - assert v.name in self.tag_member.type.member_names() 1696 - assert (isinstance(v.type, QAPISchemaObjectType) 1697 - and not v.type.variants) 1576 + if v.name not in self.tag_member.type.member_names(): 1577 + raise QAPISemError( 1578 + self.info, 1579 + "branch '%s' is not a value of %s" 1580 + % (v.name, self.tag_member.type.describe())) 1581 + if (not isinstance(v.type, QAPISchemaObjectType) 1582 + or v.type.variants): 1583 + raise QAPISemError( 1584 + self.info, 1585 + "%s cannot use %s" 1586 + % (v.describe(self.info), v.type.describe())) 1698 1587 v.type.check(schema) 1699 1588 1700 1589 def check_clash(self, info, seen): ··· 1713 1602 1714 1603 1715 1604 class QAPISchemaAlternateType(QAPISchemaType): 1605 + meta = 'alternate' 1606 + 1716 1607 def __init__(self, name, info, doc, ifcond, variants): 1717 1608 QAPISchemaType.__init__(self, name, info, doc, ifcond) 1718 1609 assert isinstance(variants, QAPISchemaObjectTypeVariants) ··· 1730 1621 # Alternate branch names have no relation to the tag enum values; 1731 1622 # so we have to check for potential name collisions ourselves. 1732 1623 seen = {} 1624 + types_seen = {} 1733 1625 for v in self.variants.variants: 1734 1626 v.check_clash(self.info, seen) 1735 - # TODO check conflicting qtypes 1627 + qtype = v.type.alternate_qtype() 1628 + if not qtype: 1629 + raise QAPISemError( 1630 + self.info, 1631 + "%s cannot use %s" 1632 + % (v.describe(self.info), v.type.describe())) 1633 + conflicting = set([qtype]) 1634 + if qtype == 'QTYPE_QSTRING': 1635 + if isinstance(v.type, QAPISchemaEnumType): 1636 + for m in v.type.members: 1637 + if m.name in ['on', 'off']: 1638 + conflicting.add('QTYPE_QBOOL') 1639 + if re.match(r'[-+0-9.]', m.name): 1640 + # lazy, could be tightened 1641 + conflicting.add('QTYPE_QNUM') 1642 + else: 1643 + conflicting.add('QTYPE_QNUM') 1644 + conflicting.add('QTYPE_QBOOL') 1645 + for qt in conflicting: 1646 + if qt in types_seen: 1647 + raise QAPISemError( 1648 + self.info, 1649 + "%s can't be distinguished from '%s'" 1650 + % (v.describe(self.info), types_seen[qt])) 1651 + types_seen[qt] = v.name 1736 1652 if self.doc: 1737 1653 self.doc.connect_member(v) 1738 1654 if self.doc: ··· 1751 1667 1752 1668 1753 1669 class QAPISchemaCommand(QAPISchemaEntity): 1670 + meta = 'command' 1671 + 1754 1672 def __init__(self, name, info, doc, ifcond, arg_type, ret_type, 1755 1673 gen, success_response, boxed, allow_oob, allow_preconfig): 1756 1674 QAPISchemaEntity.__init__(self, name, info, doc, ifcond) ··· 1769 1687 def check(self, schema): 1770 1688 QAPISchemaEntity.check(self, schema) 1771 1689 if self._arg_type_name: 1772 - self.arg_type = schema.lookup_type(self._arg_type_name) 1773 - assert isinstance(self.arg_type, QAPISchemaObjectType) 1774 - assert not self.arg_type.variants or self.boxed 1690 + self.arg_type = schema.resolve_type( 1691 + self._arg_type_name, self.info, "command's 'data'") 1692 + if not isinstance(self.arg_type, QAPISchemaObjectType): 1693 + raise QAPISemError( 1694 + self.info, 1695 + "command's 'data' cannot take %s" 1696 + % self.arg_type.describe()) 1697 + if self.arg_type.variants and not self.boxed: 1698 + raise QAPISemError( 1699 + self.info, 1700 + "command's 'data' can take %s only with 'boxed': true" 1701 + % self.arg_type.describe()) 1775 1702 elif self.boxed: 1776 1703 raise QAPISemError(self.info, "use of 'boxed' requires 'data'") 1777 1704 if self._ret_type_name: 1778 - self.ret_type = schema.lookup_type(self._ret_type_name) 1779 - assert isinstance(self.ret_type, QAPISchemaType) 1705 + self.ret_type = schema.resolve_type( 1706 + self._ret_type_name, self.info, "command's 'returns'") 1707 + if self.name not in returns_whitelist: 1708 + if not (isinstance(self.ret_type, QAPISchemaObjectType) 1709 + or (isinstance(self.ret_type, QAPISchemaArrayType) 1710 + and isinstance(self.ret_type.element_type, 1711 + QAPISchemaObjectType))): 1712 + raise QAPISemError( 1713 + self.info, 1714 + "command's 'returns' cannot take %s" 1715 + % self.ret_type.describe()) 1780 1716 1781 1717 def visit(self, visitor): 1782 1718 QAPISchemaEntity.visit(self, visitor) ··· 1788 1724 1789 1725 1790 1726 class QAPISchemaEvent(QAPISchemaEntity): 1727 + meta = 'event' 1728 + 1791 1729 def __init__(self, name, info, doc, ifcond, arg_type, boxed): 1792 1730 QAPISchemaEntity.__init__(self, name, info, doc, ifcond) 1793 1731 assert not arg_type or isinstance(arg_type, str) ··· 1798 1736 def check(self, schema): 1799 1737 QAPISchemaEntity.check(self, schema) 1800 1738 if self._arg_type_name: 1801 - self.arg_type = schema.lookup_type(self._arg_type_name) 1802 - assert isinstance(self.arg_type, QAPISchemaObjectType) 1803 - assert not self.arg_type.variants or self.boxed 1739 + self.arg_type = schema.resolve_type( 1740 + self._arg_type_name, self.info, "event's 'data'") 1741 + if not isinstance(self.arg_type, QAPISchemaObjectType): 1742 + raise QAPISemError( 1743 + self.info, 1744 + "event's 'data' cannot take %s" 1745 + % self.arg_type.describe()) 1746 + if self.arg_type.variants and not self.boxed: 1747 + raise QAPISemError( 1748 + self.info, 1749 + "event's 'data' can take %s only with 'boxed': true" 1750 + % self.arg_type.describe()) 1804 1751 elif self.boxed: 1805 1752 raise QAPISemError(self.info, "use of 'boxed' requires 'data'") 1806 1753 ··· 1831 1778 def _def_entity(self, ent): 1832 1779 # Only the predefined types are allowed to not have info 1833 1780 assert ent.info or self._predefining 1834 - assert ent.name is None or ent.name not in self._entity_dict 1835 1781 self._entity_list.append(ent) 1836 - if ent.name is not None: 1837 - self._entity_dict[ent.name] = ent 1782 + if ent.name is None: 1783 + return 1784 + # TODO reject names that differ only in '_' vs. '.' vs. '-', 1785 + # because they're liable to clash in generated C. 1786 + other_ent = self._entity_dict.get(ent.name) 1787 + if other_ent: 1788 + raise QAPISemError( 1789 + ent.info, "%s is already defined" % other_ent.describe()) 1790 + self._entity_dict[ent.name] = ent 1838 1791 1839 1792 def lookup_entity(self, name, typ=None): 1840 1793 ent = self._entity_dict.get(name) ··· 1844 1797 1845 1798 def lookup_type(self, name): 1846 1799 return self.lookup_entity(name, QAPISchemaType) 1800 + 1801 + def resolve_type(self, name, info, what): 1802 + typ = self.lookup_type(name) 1803 + if not typ: 1804 + if callable(what): 1805 + what = what(info) 1806 + raise QAPISemError( 1807 + info, "%s uses unknown type '%s'" % (what, name)) 1808 + return typ 1847 1809 1848 1810 def _def_include(self, expr, info, doc): 1849 1811 include = expr['include'] ··· 1930 1892 # But it's not tight: the disjunction need not imply it. We 1931 1893 # may end up compiling useless wrapper types. 1932 1894 # TODO kill simple unions or implement the disjunction 1933 - assert ifcond == typ._ifcond # pylint: disable=protected-access 1895 + assert (ifcond or []) == typ._ifcond # pylint: disable=protected-access 1934 1896 else: 1935 1897 self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, 1936 1898 None, members, None, []))
+1 -1
tests/qapi-schema/alternate-any.err
··· 1 1 tests/qapi-schema/alternate-any.json: In alternate 'Alt': 2 - tests/qapi-schema/alternate-any.json:2: alternate 'Alt' member 'one' cannot use type 'any' 2 + tests/qapi-schema/alternate-any.json:2: branch 'one' cannot use built-in type 'any'
+1 -1
tests/qapi-schema/alternate-conflict-bool-string.err
··· 1 1 tests/qapi-schema/alternate-conflict-bool-string.json: In alternate 'Alt': 2 - tests/qapi-schema/alternate-conflict-bool-string.json:2: alternate 'Alt' member 'two' can't be distinguished from member 'one' 2 + tests/qapi-schema/alternate-conflict-bool-string.json:2: branch 'two' can't be distinguished from 'one'
+1 -1
tests/qapi-schema/alternate-conflict-dict.err
··· 1 1 tests/qapi-schema/alternate-conflict-dict.json: In alternate 'Alt': 2 - tests/qapi-schema/alternate-conflict-dict.json:6: alternate 'Alt' member 'two' can't be distinguished from member 'one' 2 + tests/qapi-schema/alternate-conflict-dict.json:6: branch 'two' can't be distinguished from 'one'
+1 -1
tests/qapi-schema/alternate-conflict-enum-bool.err
··· 1 1 tests/qapi-schema/alternate-conflict-enum-bool.json: In alternate 'Alt': 2 - tests/qapi-schema/alternate-conflict-enum-bool.json:4: alternate 'Alt' member 'two' can't be distinguished from member 'one' 2 + tests/qapi-schema/alternate-conflict-enum-bool.json:4: branch 'two' can't be distinguished from 'one'
+1 -1
tests/qapi-schema/alternate-conflict-enum-int.err
··· 1 1 tests/qapi-schema/alternate-conflict-enum-int.json: In alternate 'Alt': 2 - tests/qapi-schema/alternate-conflict-enum-int.json:4: alternate 'Alt' member 'two' can't be distinguished from member 'one' 2 + tests/qapi-schema/alternate-conflict-enum-int.json:4: branch 'two' can't be distinguished from 'one'
+1 -1
tests/qapi-schema/alternate-conflict-num-string.err
··· 1 1 tests/qapi-schema/alternate-conflict-num-string.json: In alternate 'Alt': 2 - tests/qapi-schema/alternate-conflict-num-string.json:2: alternate 'Alt' member 'two' can't be distinguished from member 'one' 2 + tests/qapi-schema/alternate-conflict-num-string.json:2: branch 'two' can't be distinguished from 'one'
+1 -1
tests/qapi-schema/alternate-conflict-string.err
··· 1 1 tests/qapi-schema/alternate-conflict-string.json: In alternate 'Alt': 2 - tests/qapi-schema/alternate-conflict-string.json:2: alternate 'Alt' member 'two' can't be distinguished from member 'one' 2 + tests/qapi-schema/alternate-conflict-string.json:2: branch 'two' can't be distinguished from 'one'
+1 -1
tests/qapi-schema/alternate-nested.err
··· 1 1 tests/qapi-schema/alternate-nested.json: In alternate 'Alt2': 2 - tests/qapi-schema/alternate-nested.json:4: member 'nested' of alternate 'Alt2' cannot use alternate type 'Alt1' 2 + tests/qapi-schema/alternate-nested.json:4: branch 'nested' cannot use alternate type 'Alt1'
+1 -1
tests/qapi-schema/alternate-unknown.err
··· 1 1 tests/qapi-schema/alternate-unknown.json: In alternate 'Alt': 2 - tests/qapi-schema/alternate-unknown.json:2: member 'unknown' of alternate 'Alt' uses unknown type 'MissingType' 2 + tests/qapi-schema/alternate-unknown.json:2: branch 'unknown' uses unknown type 'MissingType'
+1 -1
tests/qapi-schema/args-alternate.err
··· 1 1 tests/qapi-schema/args-alternate.json: In command 'oops': 2 - tests/qapi-schema/args-alternate.json:3: 'data' for command 'oops' cannot use alternate type 'Alt' 2 + tests/qapi-schema/args-alternate.json:3: command's 'data' cannot take alternate type 'Alt'
+1 -1
tests/qapi-schema/args-any.err
··· 1 1 tests/qapi-schema/args-any.json: In command 'oops': 2 - tests/qapi-schema/args-any.json:2: 'data' for command 'oops' cannot use built-in type 'any' 2 + tests/qapi-schema/args-any.json:2: command's 'data' cannot take built-in type 'any'
+1 -1
tests/qapi-schema/args-array-unknown.err
··· 1 1 tests/qapi-schema/args-array-unknown.json: In command 'oops': 2 - tests/qapi-schema/args-array-unknown.json:2: member 'array' of 'data' for command 'oops' uses unknown type 'NoSuchType' 2 + tests/qapi-schema/args-array-unknown.json:2: command uses unknown type 'NoSuchType'
+1 -1
tests/qapi-schema/args-boxed-string.err
··· 1 1 tests/qapi-schema/args-boxed-string.json: In command 'foo': 2 - tests/qapi-schema/args-boxed-string.json:2: 'data' for command 'foo' cannot use built-in type 'str' 2 + tests/qapi-schema/args-boxed-string.json:2: command's 'data' cannot take built-in type 'str'
+1 -1
tests/qapi-schema/args-int.err
··· 1 1 tests/qapi-schema/args-int.json: In command 'oops': 2 - tests/qapi-schema/args-int.json:2: 'data' for command 'oops' cannot use built-in type 'int' 2 + tests/qapi-schema/args-int.json:2: command's 'data' cannot take built-in type 'int'
+1 -1
tests/qapi-schema/args-member-unknown.err
··· 1 1 tests/qapi-schema/args-member-unknown.json: In command 'oops': 2 - tests/qapi-schema/args-member-unknown.json:2: member 'member' of 'data' for command 'oops' uses unknown type 'NoSuchType' 2 + tests/qapi-schema/args-member-unknown.json:2: parameter 'member' uses unknown type 'NoSuchType'
+1 -1
tests/qapi-schema/args-union.err
··· 1 1 tests/qapi-schema/args-union.json: In command 'oops': 2 - tests/qapi-schema/args-union.json:3: 'data' for command 'oops' cannot use union type 'Uni' 2 + tests/qapi-schema/args-union.json:3: command's 'data' can take union type 'Uni' only with 'boxed': true
+1 -1
tests/qapi-schema/args-unknown.err
··· 1 1 tests/qapi-schema/args-unknown.json: In command 'oops': 2 - tests/qapi-schema/args-unknown.json:2: 'data' for command 'oops' uses unknown type 'NoSuchType' 2 + tests/qapi-schema/args-unknown.json:2: command's 'data' uses unknown type 'NoSuchType'
+1 -1
tests/qapi-schema/bad-base.err
··· 1 1 tests/qapi-schema/bad-base.json: In struct 'MyType': 2 - tests/qapi-schema/bad-base.json:3: 'base' for struct 'MyType' cannot use union type 'Union' 2 + tests/qapi-schema/bad-base.json:3: 'base' requires a struct type, union type 'Union' isn't
+1 -1
tests/qapi-schema/command-int.err
··· 1 1 tests/qapi-schema/command-int.json: In command 'int': 2 - tests/qapi-schema/command-int.json:2: built-in 'int' is already defined 2 + tests/qapi-schema/command-int.json:2: built-in type 'int' is already defined
+1 -1
tests/qapi-schema/flat-union-base-any.err
··· 1 1 tests/qapi-schema/flat-union-base-any.json: In union 'TestUnion': 2 - tests/qapi-schema/flat-union-base-any.json:8: 'base' for union 'TestUnion' cannot use built-in type 'any' 2 + tests/qapi-schema/flat-union-base-any.json:8: 'base' requires a struct type, built-in type 'any' isn't
+1 -1
tests/qapi-schema/flat-union-base-union.err
··· 1 1 tests/qapi-schema/flat-union-base-union.json: In union 'TestUnion': 2 - tests/qapi-schema/flat-union-base-union.json:14: 'base' for union 'TestUnion' cannot use union type 'UnionBase' 2 + tests/qapi-schema/flat-union-base-union.json:14: 'base' requires a struct type, union type 'UnionBase' isn't
+1 -1
tests/qapi-schema/flat-union-discriminator-bad-name.err
··· 1 1 tests/qapi-schema/flat-union-discriminator-bad-name.json: In union 'MyUnion': 2 - tests/qapi-schema/flat-union-discriminator-bad-name.json:7: discriminator of flat union 'MyUnion' uses invalid name '*switch' 2 + tests/qapi-schema/flat-union-discriminator-bad-name.json:6: discriminator '*switch' is not a member of 'base'
-1
tests/qapi-schema/flat-union-discriminator-bad-name.json
··· 1 1 # discriminator '*switch' isn't a member of base, 'switch' is 2 - # reports "uses invalid name", which is good enough 3 2 { 'enum': 'Enum', 'data': [ 'one', 'two' ] } 4 3 { 'struct': 'Base', 5 4 'data': { '*switch': 'Enum' } }
+1 -1
tests/qapi-schema/flat-union-empty.err
··· 1 1 tests/qapi-schema/flat-union-empty.json: In union 'Union': 2 - tests/qapi-schema/flat-union-empty.json:4: union 'Union' has no branches 2 + tests/qapi-schema/flat-union-empty.json:4: union has no branches
+1 -1
tests/qapi-schema/flat-union-int-branch.err
··· 1 1 tests/qapi-schema/flat-union-int-branch.json: In union 'TestUnion': 2 - tests/qapi-schema/flat-union-int-branch.json:8: member 'value1' of union 'TestUnion' cannot use built-in type 'int' 2 + tests/qapi-schema/flat-union-int-branch.json:8: branch 'value1' cannot use built-in type 'int'
+1 -1
tests/qapi-schema/flat-union-invalid-branch-key.err
··· 1 1 tests/qapi-schema/flat-union-invalid-branch-key.json: In union 'TestUnion': 2 - tests/qapi-schema/flat-union-invalid-branch-key.json:13: discriminator value 'value_wrong' is not found in enum 'TestEnum' 2 + tests/qapi-schema/flat-union-invalid-branch-key.json:13: branch 'value_wrong' is not a value of enum type 'TestEnum'
+1 -1
tests/qapi-schema/flat-union-invalid-if-discriminator.err
··· 1 1 tests/qapi-schema/flat-union-invalid-if-discriminator.json: In union 'TestUnion': 2 - tests/qapi-schema/flat-union-invalid-if-discriminator.json:10: the discriminator 'enum1' for union TestUnion must not be conditional 2 + tests/qapi-schema/flat-union-invalid-if-discriminator.json:10: discriminator member 'enum1' of 'base' must not be conditional
+1 -1
tests/qapi-schema/flat-union-optional-discriminator.err
··· 1 1 tests/qapi-schema/flat-union-optional-discriminator.json: In union 'MyUnion': 2 - tests/qapi-schema/flat-union-optional-discriminator.json:7: discriminator 'switch' is not a member of 'base' 2 + tests/qapi-schema/flat-union-optional-discriminator.json:6: discriminator member 'switch' of base type 'Base' must not be optional
-1
tests/qapi-schema/flat-union-optional-discriminator.json
··· 1 1 # we require the discriminator to be non-optional 2 - # FIXME reports "discriminator 'switch' is not a member of base struct 'Base'" 3 2 { 'enum': 'Enum', 'data': [ 'one', 'two' ] } 4 3 { 'struct': 'Base', 5 4 'data': { '*switch': 'Enum' } }
+1 -1
tests/qapi-schema/flat-union-string-discriminator.err
··· 1 1 tests/qapi-schema/flat-union-string-discriminator.json: In union 'TestUnion': 2 - tests/qapi-schema/flat-union-string-discriminator.json:13: discriminator 'kind' must be of enumeration type 2 + tests/qapi-schema/flat-union-string-discriminator.json:13: discriminator member 'kind' of base type 'TestBase' must be of enum type
+1 -1
tests/qapi-schema/redefined-builtin.err
··· 1 1 tests/qapi-schema/redefined-builtin.json: In struct 'size': 2 - tests/qapi-schema/redefined-builtin.json:2: built-in 'size' is already defined 2 + tests/qapi-schema/redefined-builtin.json:2: built-in type 'size' is already defined
+1 -1
tests/qapi-schema/redefined-type.err
··· 1 1 tests/qapi-schema/redefined-type.json: In enum 'foo': 2 - tests/qapi-schema/redefined-type.json:3: struct 'foo' is already defined 2 + tests/qapi-schema/redefined-type.json:3: struct type 'foo' is already defined
+1 -1
tests/qapi-schema/returns-alternate.err
··· 1 1 tests/qapi-schema/returns-alternate.json: In command 'oops': 2 - tests/qapi-schema/returns-alternate.json:3: 'returns' for command 'oops' cannot use alternate type 'Alt' 2 + tests/qapi-schema/returns-alternate.json:3: command's 'returns' cannot take alternate type 'Alt'
+1 -1
tests/qapi-schema/returns-unknown.err
··· 1 1 tests/qapi-schema/returns-unknown.json: In command 'oops': 2 - tests/qapi-schema/returns-unknown.json:2: 'returns' for command 'oops' uses unknown type 'NoSuchType' 2 + tests/qapi-schema/returns-unknown.json:2: command's 'returns' uses unknown type 'NoSuchType'
+1 -1
tests/qapi-schema/returns-whitelist.err
··· 1 1 tests/qapi-schema/returns-whitelist.json: In command 'no-way-this-will-get-whitelisted': 2 - tests/qapi-schema/returns-whitelist.json:14: 'returns' for command 'no-way-this-will-get-whitelisted' cannot use built-in type 'int' 2 + tests/qapi-schema/returns-whitelist.json:14: command's 'returns' cannot take array type ['int']
+1 -1
tests/qapi-schema/union-empty.err
··· 1 1 tests/qapi-schema/union-empty.json: In union 'Union': 2 - tests/qapi-schema/union-empty.json:2: union 'Union' has no branches 2 + tests/qapi-schema/union-empty.json:2: union has no branches
+1 -1
tests/qapi-schema/union-invalid-base.err
··· 1 1 tests/qapi-schema/union-invalid-base.json: In union 'TestUnion': 2 - tests/qapi-schema/union-invalid-base.json:8: 'base' for union 'TestUnion' cannot use built-in type 'int' 2 + tests/qapi-schema/union-invalid-base.json:8: 'base' requires a struct type, built-in type 'int' isn't
+1 -1
tests/qapi-schema/union-unknown.err
··· 1 1 tests/qapi-schema/union-unknown.json: In union 'Union': 2 - tests/qapi-schema/union-unknown.json:2: member 'unknown' of union 'Union' uses unknown type 'MissingType' 2 + tests/qapi-schema/union-unknown.json:2: union uses unknown type 'MissingType'
+1 -1
tests/qapi-schema/union-unknown.json
··· 1 1 # we reject a union with unknown type in branch 2 2 { 'union': 'Union', 3 - 'data': { 'unknown': 'MissingType' } } 3 + 'data': { 'unknown': ['MissingType'] } }