]>
Commit | Line | Data |
---|---|---|
fc512014 XL |
1 | #!/usr/bin/env python |
2 | ||
3 | # This test ensures that every ID in the produced json actually resolves to an item either in | |
4 | # `index` or `paths`. It DOES NOT check that the structure of the produced json is actually in | |
5 | # any way correct, for example an empty map would pass. | |
6 | ||
5869c6ff XL |
7 | # FIXME: Better error output |
8 | ||
fc512014 XL |
9 | import sys |
10 | import json | |
11 | ||
c295e0f8 | 12 | crate = json.load(open(sys.argv[1], encoding="utf-8")) |
fc512014 XL |
13 | |
14 | ||
15 | def get_local_item(item_id): | |
16 | if item_id in crate["index"]: | |
17 | return crate["index"][item_id] | |
18 | print("Missing local ID:", item_id) | |
19 | sys.exit(1) | |
20 | ||
21 | ||
22 | # local IDs have to be in `index`, external ones can sometimes be in `index` but otherwise have | |
23 | # to be in `paths` | |
24 | def valid_id(item_id): | |
25 | return item_id in crate["index"] or item_id[0] != "0" and item_id in crate["paths"] | |
26 | ||
27 | ||
28 | def check_generics(generics): | |
29 | for param in generics["params"]: | |
30 | check_generic_param(param) | |
31 | for where_predicate in generics["where_predicates"]: | |
32 | if "bound_predicate" in where_predicate: | |
33 | pred = where_predicate["bound_predicate"] | |
ee023bcb | 34 | check_type(pred["type"]) |
fc512014 XL |
35 | for bound in pred["bounds"]: |
36 | check_generic_bound(bound) | |
37 | elif "region_predicate" in where_predicate: | |
38 | pred = where_predicate["region_predicate"] | |
39 | for bound in pred["bounds"]: | |
40 | check_generic_bound(bound) | |
41 | elif "eq_predicate" in where_predicate: | |
42 | pred = where_predicate["eq_predicate"] | |
43 | check_type(pred["rhs"]) | |
44 | check_type(pred["lhs"]) | |
45 | ||
46 | ||
47 | def check_generic_param(param): | |
48 | if "type" in param["kind"]: | |
49 | ty = param["kind"]["type"] | |
50 | if ty["default"]: | |
51 | check_type(ty["default"]) | |
52 | for bound in ty["bounds"]: | |
53 | check_generic_bound(bound) | |
54 | elif "const" in param["kind"]: | |
55 | check_type(param["kind"]["const"]) | |
56 | ||
57 | ||
58 | def check_generic_bound(bound): | |
59 | if "trait_bound" in bound: | |
60 | for param in bound["trait_bound"]["generic_params"]: | |
61 | check_generic_param(param) | |
62 | check_type(bound["trait_bound"]["trait"]) | |
63 | ||
64 | ||
65 | def check_decl(decl): | |
66 | for (_name, ty) in decl["inputs"]: | |
67 | check_type(ty) | |
68 | if decl["output"]: | |
69 | check_type(decl["output"]) | |
70 | ||
71 | ||
72 | def check_type(ty): | |
73 | if ty["kind"] == "resolved_path": | |
74 | for bound in ty["inner"]["param_names"]: | |
75 | check_generic_bound(bound) | |
76 | args = ty["inner"]["args"] | |
77 | if args: | |
78 | if "angle_bracketed" in args: | |
79 | for arg in args["angle_bracketed"]["args"]: | |
80 | if "type" in arg: | |
81 | check_type(arg["type"]) | |
82 | elif "const" in arg: | |
83 | check_type(arg["const"]["type"]) | |
84 | for binding in args["angle_bracketed"]["bindings"]: | |
85 | if "equality" in binding["binding"]: | |
5099ac24 FG |
86 | term = binding["binding"]["equality"] |
87 | if "type" in term: check_type(term["type"]) | |
88 | elif "const" in term: check_type(term["const"]) | |
fc512014 XL |
89 | elif "constraint" in binding["binding"]: |
90 | for bound in binding["binding"]["constraint"]: | |
91 | check_generic_bound(bound) | |
92 | elif "parenthesized" in args: | |
93 | for ty in args["parenthesized"]["inputs"]: | |
94 | check_type(ty) | |
95 | if args["parenthesized"]["output"]: | |
96 | check_type(args["parenthesized"]["output"]) | |
97 | if not valid_id(ty["inner"]["id"]): | |
98 | print("Type contained an invalid ID:", ty["inner"]["id"]) | |
99 | sys.exit(1) | |
100 | elif ty["kind"] == "tuple": | |
101 | for ty in ty["inner"]: | |
102 | check_type(ty) | |
103 | elif ty["kind"] == "slice": | |
104 | check_type(ty["inner"]) | |
105 | elif ty["kind"] == "impl_trait": | |
106 | for bound in ty["inner"]: | |
107 | check_generic_bound(bound) | |
108 | elif ty["kind"] in ("raw_pointer", "borrowed_ref", "array"): | |
109 | check_type(ty["inner"]["type"]) | |
110 | elif ty["kind"] == "function_pointer": | |
111 | for param in ty["inner"]["generic_params"]: | |
112 | check_generic_param(param) | |
6a06907d | 113 | check_decl(ty["inner"]["decl"]) |
fc512014 XL |
114 | elif ty["kind"] == "qualified_path": |
115 | check_type(ty["inner"]["self_type"]) | |
116 | check_type(ty["inner"]["trait"]) | |
117 | ||
118 | ||
119 | work_list = set([crate["root"]]) | |
120 | visited = work_list.copy() | |
121 | ||
122 | while work_list: | |
123 | current = work_list.pop() | |
124 | visited.add(current) | |
125 | item = get_local_item(current) | |
126 | # check intradoc links | |
127 | for (_name, link) in item["links"].items(): | |
128 | if not valid_id(link): | |
129 | print("Intra-doc link contains invalid ID:", link) | |
130 | ||
131 | # check all fields that reference types such as generics as well as nested items | |
132 | # (modules, structs, traits, and enums) | |
133 | if item["kind"] == "module": | |
134 | work_list |= set(item["inner"]["items"]) - visited | |
135 | elif item["kind"] == "struct": | |
136 | check_generics(item["inner"]["generics"]) | |
137 | work_list |= ( | |
138 | set(item["inner"]["fields"]) | set(item["inner"]["impls"]) | |
139 | ) - visited | |
140 | elif item["kind"] == "struct_field": | |
141 | check_type(item["inner"]) | |
142 | elif item["kind"] == "enum": | |
143 | check_generics(item["inner"]["generics"]) | |
144 | work_list |= ( | |
145 | set(item["inner"]["variants"]) | set(item["inner"]["impls"]) | |
146 | ) - visited | |
147 | elif item["kind"] == "variant": | |
148 | if item["inner"]["variant_kind"] == "tuple": | |
149 | for ty in item["inner"]["variant_inner"]: | |
150 | check_type(ty) | |
151 | elif item["inner"]["variant_kind"] == "struct": | |
152 | work_list |= set(item["inner"]["variant_inner"]) - visited | |
153 | elif item["kind"] in ("function", "method"): | |
154 | check_generics(item["inner"]["generics"]) | |
155 | check_decl(item["inner"]["decl"]) | |
156 | elif item["kind"] in ("static", "constant", "assoc_const"): | |
157 | check_type(item["inner"]["type"]) | |
158 | elif item["kind"] == "typedef": | |
159 | check_type(item["inner"]["type"]) | |
160 | check_generics(item["inner"]["generics"]) | |
161 | elif item["kind"] == "opaque_ty": | |
162 | check_generics(item["inner"]["generics"]) | |
163 | for bound in item["inner"]["bounds"]: | |
164 | check_generic_bound(bound) | |
165 | elif item["kind"] == "trait_alias": | |
166 | check_generics(item["inner"]["params"]) | |
167 | for bound in item["inner"]["bounds"]: | |
168 | check_generic_bound(bound) | |
169 | elif item["kind"] == "trait": | |
170 | check_generics(item["inner"]["generics"]) | |
171 | for bound in item["inner"]["bounds"]: | |
172 | check_generic_bound(bound) | |
173 | work_list |= ( | |
ee023bcb | 174 | set(item["inner"]["items"]) | set(item["inner"]["implementations"]) |
fc512014 XL |
175 | ) - visited |
176 | elif item["kind"] == "impl": | |
177 | check_generics(item["inner"]["generics"]) | |
178 | if item["inner"]["trait"]: | |
179 | check_type(item["inner"]["trait"]) | |
180 | if item["inner"]["blanket_impl"]: | |
181 | check_type(item["inner"]["blanket_impl"]) | |
182 | check_type(item["inner"]["for"]) | |
183 | for assoc_item in item["inner"]["items"]: | |
184 | if not valid_id(assoc_item): | |
185 | print("Impl block referenced a missing ID:", assoc_item) | |
186 | sys.exit(1) | |
187 | elif item["kind"] == "assoc_type": | |
188 | for bound in item["inner"]["bounds"]: | |
189 | check_generic_bound(bound) | |
190 | if item["inner"]["default"]: | |
191 | check_type(item["inner"]["default"]) |