]> git.lizzy.rs Git - rust.git/blob - src/etc/check_missing_items.py
Rollup merge of #101422 - mkroening:hermit-file-time, r=joshtriplett
[rust.git] / src / etc / check_missing_items.py
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
7 # FIXME: Better error output
8
9 import sys
10 import json
11
12 crate = json.load(open(sys.argv[1], encoding="utf-8"))
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"]
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"])
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     elif "const" in param["kind"]:
53         check_type(param["kind"]["const"])
54
55
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"])
61
62
63 def check_decl(decl):
64     for (_name, ty) in decl["inputs"]:
65         check_type(ty)
66     if decl["output"]:
67         check_type(decl["output"])
68
69 def check_path(path):
70     args = path["args"]
71     if args:
72         if "angle_bracketed" in args:
73             for arg in args["angle_bracketed"]["args"]:
74                 if "type" in arg:
75                     check_type(arg["type"])
76                 elif "const" in arg:
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"]:
88                 check_type(input_ty)
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"])
93         sys.exit(1)
94
95 def check_type(ty):
96     if ty["kind"] == "resolved_path":
97         check_path(ty["inner"])
98     elif ty["kind"] == "tuple":
99         for ty in ty["inner"]:
100             check_type(ty)
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"])
115
116
117 work_list = set([crate["root"]])
118 visited = work_list.copy()
119
120 while work_list:
121     current = work_list.pop()
122     visited.add(current)
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)
128
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"])
135         work_list |= set(item["inner"]["impls"]) - visited
136         if "tuple" in item["inner"]["kind"]:
137             work_list |= set(filter(None, item["inner"]["kind"]["tuple"])) - visited
138         elif "plain" in item["inner"]["kind"]:
139             work_list |= set(item["inner"]["kind"]["plain"]["fields"]) - 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 field_id in filter(None, item["inner"]["variant_inner"]):
150                 work_list.add(field_id)
151         elif item["inner"]["variant_kind"] == "struct":
152             work_list |= set(item["inner"]["variant_inner"]["fields"]) - 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 |= (
174             set(item["inner"]["items"]) | set(item["inner"]["implementations"])
175         ) - visited
176     elif item["kind"] == "impl":
177         check_generics(item["inner"]["generics"])
178         if item["inner"]["trait"]:
179             check_path(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"])
192     elif item["kind"] == "import":
193         if item["inner"]["id"]:
194             inner_id = item["inner"]["id"]
195             assert valid_id(inner_id)
196             if inner_id in crate["index"] and inner_id not in visited:
197                 work_list.add(inner_id)