]>
Commit | Line | Data |
---|---|---|
cc61c64b XL |
1 | // Copyright 2017 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
11 | use hir::def_id::{DefId, DefIndex, CRATE_DEF_INDEX}; | |
12 | use hir::{self, intravisit, HirId, ItemLocalId}; | |
13 | use syntax::ast::NodeId; | |
14 | use hir::itemlikevisit::ItemLikeVisitor; | |
15 | use rustc_data_structures::fx::FxHashMap; | |
16 | ||
17 | pub fn check_crate<'hir>(hir_map: &hir::map::Map<'hir>) { | |
18 | let mut outer_visitor = OuterVisitor { | |
041b39d2 | 19 | hir_map, |
cc61c64b XL |
20 | errors: vec![], |
21 | }; | |
22 | ||
2c00a5a8 XL |
23 | hir_map.dep_graph.assert_ignored(); |
24 | ||
25 | hir_map.krate().visit_all_item_likes(&mut outer_visitor); | |
26 | if !outer_visitor.errors.is_empty() { | |
27 | let message = outer_visitor | |
28 | .errors | |
29 | .iter() | |
30 | .fold(String::new(), |s1, s2| s1 + "\n" + s2); | |
31 | bug!("{}", message); | |
32 | } | |
cc61c64b XL |
33 | } |
34 | ||
35 | struct HirIdValidator<'a, 'hir: 'a> { | |
36 | hir_map: &'a hir::map::Map<'hir>, | |
37 | owner_def_index: Option<DefIndex>, | |
38 | hir_ids_seen: FxHashMap<ItemLocalId, NodeId>, | |
39 | errors: Vec<String>, | |
40 | } | |
41 | ||
42 | struct OuterVisitor<'a, 'hir: 'a> { | |
43 | hir_map: &'a hir::map::Map<'hir>, | |
44 | errors: Vec<String>, | |
45 | } | |
46 | ||
47 | impl<'a, 'hir: 'a> OuterVisitor<'a, 'hir> { | |
48 | fn new_inner_visitor(&self, | |
49 | hir_map: &'a hir::map::Map<'hir>) | |
50 | -> HirIdValidator<'a, 'hir> { | |
51 | HirIdValidator { | |
041b39d2 | 52 | hir_map, |
cc61c64b | 53 | owner_def_index: None, |
0bf4aa26 | 54 | hir_ids_seen: Default::default(), |
cc61c64b XL |
55 | errors: Vec::new(), |
56 | } | |
57 | } | |
58 | } | |
59 | ||
60 | impl<'a, 'hir: 'a> ItemLikeVisitor<'hir> for OuterVisitor<'a, 'hir> { | |
61 | fn visit_item(&mut self, i: &'hir hir::Item) { | |
62 | let mut inner_visitor = self.new_inner_visitor(self.hir_map); | |
63 | inner_visitor.check(i.id, |this| intravisit::walk_item(this, i)); | |
64 | self.errors.extend(inner_visitor.errors.drain(..)); | |
65 | } | |
66 | ||
67 | fn visit_trait_item(&mut self, i: &'hir hir::TraitItem) { | |
68 | let mut inner_visitor = self.new_inner_visitor(self.hir_map); | |
69 | inner_visitor.check(i.id, |this| intravisit::walk_trait_item(this, i)); | |
70 | self.errors.extend(inner_visitor.errors.drain(..)); | |
71 | } | |
72 | ||
73 | fn visit_impl_item(&mut self, i: &'hir hir::ImplItem) { | |
74 | let mut inner_visitor = self.new_inner_visitor(self.hir_map); | |
75 | inner_visitor.check(i.id, |this| intravisit::walk_impl_item(this, i)); | |
76 | self.errors.extend(inner_visitor.errors.drain(..)); | |
77 | } | |
78 | } | |
79 | ||
80 | impl<'a, 'hir: 'a> HirIdValidator<'a, 'hir> { | |
81 | ||
82 | fn check<F: FnOnce(&mut HirIdValidator<'a, 'hir>)>(&mut self, | |
83 | node_id: NodeId, | |
84 | walk: F) { | |
85 | assert!(self.owner_def_index.is_none()); | |
86 | let owner_def_index = self.hir_map.local_def_id(node_id).index; | |
87 | self.owner_def_index = Some(owner_def_index); | |
88 | walk(self); | |
89 | ||
90 | if owner_def_index == CRATE_DEF_INDEX { | |
91 | return | |
92 | } | |
93 | ||
94 | // There's always at least one entry for the owning item itself | |
95 | let max = self.hir_ids_seen | |
96 | .keys() | |
97 | .map(|local_id| local_id.as_usize()) | |
98 | .max() | |
94b46f34 | 99 | .expect("owning item has no entry"); |
cc61c64b XL |
100 | |
101 | if max != self.hir_ids_seen.len() - 1 { | |
102 | // Collect the missing ItemLocalIds | |
b7449926 XL |
103 | let missing: Vec<_> = (0 .. max as u32 + 1) |
104 | .filter(|&i| !self.hir_ids_seen.contains_key(&ItemLocalId(i))) | |
cc61c64b XL |
105 | .collect(); |
106 | ||
107 | // Try to map those to something more useful | |
8faf50e0 | 108 | let mut missing_items = Vec::with_capacity(missing.len()); |
cc61c64b XL |
109 | |
110 | for local_id in missing { | |
111 | let hir_id = HirId { | |
112 | owner: owner_def_index, | |
113 | local_id: ItemLocalId(local_id as u32), | |
114 | }; | |
115 | ||
94b46f34 XL |
116 | trace!("missing hir id {:#?}", hir_id); |
117 | ||
cc61c64b XL |
118 | // We are already in ICE mode here, so doing a linear search |
119 | // should be fine. | |
120 | let (node_id, _) = self.hir_map | |
121 | .definitions() | |
122 | .node_to_hir_id | |
123 | .iter() | |
124 | .enumerate() | |
125 | .find(|&(_, &entry)| hir_id == entry) | |
94b46f34 | 126 | .expect("no node_to_hir_id entry"); |
cc61c64b XL |
127 | let node_id = NodeId::new(node_id); |
128 | missing_items.push(format!("[local_id: {}, node:{}]", | |
129 | local_id, | |
130 | self.hir_map.node_to_string(node_id))); | |
131 | } | |
132 | ||
133 | self.errors.push(format!( | |
134 | "ItemLocalIds not assigned densely in {}. \ | |
135 | Max ItemLocalId = {}, missing IDs = {:?}", | |
136 | self.hir_map.def_path(DefId::local(owner_def_index)).to_string_no_crate(), | |
137 | max, | |
138 | missing_items)); | |
139 | } | |
140 | } | |
141 | } | |
142 | ||
143 | impl<'a, 'hir: 'a> intravisit::Visitor<'hir> for HirIdValidator<'a, 'hir> { | |
144 | ||
145 | fn nested_visit_map<'this>(&'this mut self) | |
146 | -> intravisit::NestedVisitorMap<'this, 'hir> { | |
147 | intravisit::NestedVisitorMap::OnlyBodies(self.hir_map) | |
148 | } | |
149 | ||
150 | fn visit_id(&mut self, node_id: NodeId) { | |
94b46f34 | 151 | let owner = self.owner_def_index.expect("no owner_def_index"); |
cc61c64b XL |
152 | let stable_id = self.hir_map.definitions().node_to_hir_id[node_id]; |
153 | ||
154 | if stable_id == hir::DUMMY_HIR_ID { | |
155 | self.errors.push(format!("HirIdValidator: No HirId assigned for NodeId {}: {:?}", | |
156 | node_id, | |
157 | self.hir_map.node_to_string(node_id))); | |
158 | } | |
159 | ||
160 | if owner != stable_id.owner { | |
161 | self.errors.push(format!( | |
162 | "HirIdValidator: The recorded owner of {} is {} instead of {}", | |
163 | self.hir_map.node_to_string(node_id), | |
164 | self.hir_map.def_path(DefId::local(stable_id.owner)).to_string_no_crate(), | |
165 | self.hir_map.def_path(DefId::local(owner)).to_string_no_crate())); | |
166 | } | |
167 | ||
168 | if let Some(prev) = self.hir_ids_seen.insert(stable_id.local_id, node_id) { | |
169 | if prev != node_id { | |
170 | self.errors.push(format!( | |
171 | "HirIdValidator: Same HirId {}/{} assigned for nodes {} and {}", | |
172 | self.hir_map.def_path(DefId::local(stable_id.owner)).to_string_no_crate(), | |
173 | stable_id.local_id.as_usize(), | |
174 | self.hir_map.node_to_string(prev), | |
175 | self.hir_map.node_to_string(node_id))); | |
176 | } | |
177 | } | |
178 | } | |
179 | ||
180 | fn visit_impl_item_ref(&mut self, _: &'hir hir::ImplItemRef) { | |
181 | // Explicitly do nothing here. ImplItemRefs contain hir::Visibility | |
8faf50e0 | 182 | // values that actually belong to an ImplItem instead of the ItemKind::Impl |
cc61c64b XL |
183 | // we are currently in. So for those it's correct that they have a |
184 | // different owner. | |
185 | } | |
186 | } |