]> git.lizzy.rs Git - rust.git/blob - src/etc/check_missing_items.py
Add 'src/tools/rust-analyzer/' from commit '977e12a0bdc3e329af179ef3a9d466af9eb613bb'
[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_type(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
70 def check_type(ty):
71     if ty["kind"] == "resolved_path":
72         for bound in ty["inner"]["param_names"]:
73             check_generic_bound(bound)
74         args = ty["inner"]["args"]
75         if args:
76             if "angle_bracketed" in args:
77                 for arg in args["angle_bracketed"]["args"]:
78                     if "type" in arg:
79                         check_type(arg["type"])
80                     elif "const" in arg:
81                         check_type(arg["const"]["type"])
82                 for binding in args["angle_bracketed"]["bindings"]:
83                     if "equality" in binding["binding"]:
84                         term = binding["binding"]["equality"]
85                         if "type" in term: check_type(term["type"])
86                         elif "const" in term: check_type(term["const"])
87                     elif "constraint" in binding["binding"]:
88                         for bound in binding["binding"]["constraint"]:
89                             check_generic_bound(bound)
90             elif "parenthesized" in args:
91                 for ty in args["parenthesized"]["inputs"]:
92                     check_type(ty)
93                 if args["parenthesized"]["output"]:
94                     check_type(args["parenthesized"]["output"])
95         if not valid_id(ty["inner"]["id"]):
96             print("Type contained an invalid ID:", ty["inner"]["id"])
97             sys.exit(1)
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_type(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 |= (
136             set(item["inner"]["fields"]) | set(item["inner"]["impls"])
137         ) - visited
138     elif item["kind"] == "struct_field":
139         check_type(item["inner"])
140     elif item["kind"] == "enum":
141         check_generics(item["inner"]["generics"])
142         work_list |= (
143             set(item["inner"]["variants"]) | set(item["inner"]["impls"])
144         ) - visited
145     elif item["kind"] == "variant":
146         if item["inner"]["variant_kind"] == "tuple":
147             for ty in item["inner"]["variant_inner"]:
148                 check_type(ty)
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)
171         work_list |= (
172             set(item["inner"]["items"]) | set(item["inner"]["implementations"])
173         ) - visited
174     elif item["kind"] == "impl":
175         check_generics(item["inner"]["generics"])
176         if item["inner"]["trait"]:
177             check_type(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)
184                 sys.exit(1)
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"])