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.
7 # FIXME: Better error output
12 crate = json.load(open(sys.argv[1], encoding="utf-8"))
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)
22 # local IDs have to be in `index`, external ones can sometimes be in `index` but otherwise have
24 def valid_id(item_id):
25 return item_id in crate["index"] or item_id[0] != "0" and item_id in crate["paths"]
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"]
34 check_type(pred["type"])
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"])
47 def check_generic_param(param):
48 if "type" in param["kind"]:
49 ty = param["kind"]["type"]
51 check_type(ty["default"])
52 elif "const" in param["kind"]:
53 check_type(param["kind"]["const"])
56 def check_generic_bound(bound):
57 if "trait_bound" in bound:
58 for param in bound["trait_bound"]["generic_params"]:
59 check_generic_param(param)
60 check_path(bound["trait_bound"]["trait"])
64 for (_name, ty) in decl["inputs"]:
67 check_type(decl["output"])
72 if "angle_bracketed" in args:
73 for arg in args["angle_bracketed"]["args"]:
75 check_type(arg["type"])
77 check_type(arg["const"]["type"])
78 for binding in args["angle_bracketed"]["bindings"]:
79 if "equality" in binding["binding"]:
80 term = binding["binding"]["equality"]
81 if "type" in term: check_type(term["type"])
82 elif "const" in term: check_type(term["const"])
83 elif "constraint" in binding["binding"]:
84 for bound in binding["binding"]["constraint"]:
85 check_generic_bound(bound)
86 elif "parenthesized" in args:
87 for input_ty in args["parenthesized"]["inputs"]:
89 if args["parenthesized"]["output"]:
90 check_type(args["parenthesized"]["output"])
91 if not valid_id(path["id"]):
92 print("Type contained an invalid ID:", path["id"])
96 if ty["kind"] == "resolved_path":
97 check_path(ty["inner"])
98 elif ty["kind"] == "tuple":
99 for ty in ty["inner"]:
101 elif ty["kind"] == "slice":
102 check_type(ty["inner"])
103 elif ty["kind"] == "impl_trait":
104 for bound in ty["inner"]:
105 check_generic_bound(bound)
106 elif ty["kind"] in ("raw_pointer", "borrowed_ref", "array"):
107 check_type(ty["inner"]["type"])
108 elif ty["kind"] == "function_pointer":
109 for param in ty["inner"]["generic_params"]:
110 check_generic_param(param)
111 check_decl(ty["inner"]["decl"])
112 elif ty["kind"] == "qualified_path":
113 check_type(ty["inner"]["self_type"])
114 check_path(ty["inner"]["trait"])
117 work_list = set([crate["root"]])
118 visited = work_list.copy()
121 current = work_list.pop()
123 item = get_local_item(current)
124 # check intradoc links
125 for (_name, link) in item["links"].items():
126 if not valid_id(link):
127 print("Intra-doc link contains invalid ID:", link)
129 # check all fields that reference types such as generics as well as nested items
130 # (modules, structs, traits, and enums)
131 if item["kind"] == "module":
132 work_list |= set(item["inner"]["items"]) - visited
133 elif item["kind"] == "struct":
134 check_generics(item["inner"]["generics"])
136 set(item["inner"]["fields"]) | set(item["inner"]["impls"])
138 elif item["kind"] == "struct_field":
139 check_type(item["inner"])
140 elif item["kind"] == "enum":
141 check_generics(item["inner"]["generics"])
143 set(item["inner"]["variants"]) | set(item["inner"]["impls"])
145 elif item["kind"] == "variant":
146 if item["inner"]["variant_kind"] == "tuple":
147 for ty in item["inner"]["variant_inner"]:
149 elif item["inner"]["variant_kind"] == "struct":
150 work_list |= set(item["inner"]["variant_inner"]) - visited
151 elif item["kind"] in ("function", "method"):
152 check_generics(item["inner"]["generics"])
153 check_decl(item["inner"]["decl"])
154 elif item["kind"] in ("static", "constant", "assoc_const"):
155 check_type(item["inner"]["type"])
156 elif item["kind"] == "typedef":
157 check_type(item["inner"]["type"])
158 check_generics(item["inner"]["generics"])
159 elif item["kind"] == "opaque_ty":
160 check_generics(item["inner"]["generics"])
161 for bound in item["inner"]["bounds"]:
162 check_generic_bound(bound)
163 elif item["kind"] == "trait_alias":
164 check_generics(item["inner"]["params"])
165 for bound in item["inner"]["bounds"]:
166 check_generic_bound(bound)
167 elif item["kind"] == "trait":
168 check_generics(item["inner"]["generics"])
169 for bound in item["inner"]["bounds"]:
170 check_generic_bound(bound)
172 set(item["inner"]["items"]) | set(item["inner"]["implementations"])
174 elif item["kind"] == "impl":
175 check_generics(item["inner"]["generics"])
176 if item["inner"]["trait"]:
177 check_path(item["inner"]["trait"])
178 if item["inner"]["blanket_impl"]:
179 check_type(item["inner"]["blanket_impl"])
180 check_type(item["inner"]["for"])
181 for assoc_item in item["inner"]["items"]:
182 if not valid_id(assoc_item):
183 print("Impl block referenced a missing ID:", assoc_item)
185 elif item["kind"] == "assoc_type":
186 for bound in item["inner"]["bounds"]:
187 check_generic_bound(bound)
188 if item["inner"]["default"]:
189 check_type(item["inner"]["default"])
190 elif item["kind"] == "import":
191 if item["inner"]["id"]:
192 inner_id = item["inner"]["id"]
193 assert valid_id(inner_id)
194 if inner_id in crate["index"] and inner_id not in visited:
195 work_list.add(inner_id)