diff options
Diffstat (limited to 'crates/ra_hir/src/module_tree.rs')
-rw-r--r-- | crates/ra_hir/src/module_tree.rs | 246 |
1 files changed, 112 insertions, 134 deletions
diff --git a/crates/ra_hir/src/module_tree.rs b/crates/ra_hir/src/module_tree.rs index 47c14af35..b201bf69b 100644 --- a/crates/ra_hir/src/module_tree.rs +++ b/crates/ra_hir/src/module_tree.rs | |||
@@ -62,14 +62,26 @@ impl Submodule { | |||
62 | file_items: &SourceFileItems, | 62 | file_items: &SourceFileItems, |
63 | root: &impl ast::ModuleItemOwner, | 63 | root: &impl ast::ModuleItemOwner, |
64 | ) -> Vec<Submodule> { | 64 | ) -> Vec<Submodule> { |
65 | modules(root) | 65 | root.items() |
66 | .map(|(name, m)| Submodule { | 66 | .filter_map(|item| match item.kind() { |
67 | name, | 67 | ast::ModuleItemKind::Module(m) => Some(m), |
68 | is_declaration: m.has_semi(), | 68 | _ => None, |
69 | source: SourceItemId { | 69 | }) |
70 | file_id, | 70 | .filter_map(|module| { |
71 | item_id: Some(file_items.id_of(file_id, m.syntax())), | 71 | let name = module.name()?.as_name(); |
72 | }, | 72 | if !module.has_semi() && module.item_list().is_none() { |
73 | tested_by!(name_res_works_for_broken_modules); | ||
74 | return None; | ||
75 | } | ||
76 | let sub = Submodule { | ||
77 | name, | ||
78 | is_declaration: module.has_semi(), | ||
79 | source: SourceItemId { | ||
80 | file_id, | ||
81 | item_id: Some(file_items.id_of(file_id, module.syntax())), | ||
82 | }, | ||
83 | }; | ||
84 | Some(sub) | ||
73 | }) | 85 | }) |
74 | .collect() | 86 | .collect() |
75 | } | 87 | } |
@@ -119,7 +131,8 @@ impl ModuleTree { | |||
119 | source_root: SourceRootId, | 131 | source_root: SourceRootId, |
120 | ) -> Arc<ModuleTree> { | 132 | ) -> Arc<ModuleTree> { |
121 | db.check_canceled(); | 133 | db.check_canceled(); |
122 | let res = create_module_tree(db, source_root); | 134 | let mut res = ModuleTree::default(); |
135 | res.init(db, source_root); | ||
123 | Arc::new(res) | 136 | Arc::new(res) |
124 | } | 137 | } |
125 | 138 | ||
@@ -131,6 +144,96 @@ impl ModuleTree { | |||
131 | let (res, _) = self.mods.iter().find(|(_, m)| m.source == source)?; | 144 | let (res, _) = self.mods.iter().find(|(_, m)| m.source == source)?; |
132 | Some(res) | 145 | Some(res) |
133 | } | 146 | } |
147 | |||
148 | fn init(&mut self, db: &impl HirDatabase, source_root: SourceRootId) { | ||
149 | let mut roots = FxHashMap::default(); | ||
150 | let mut visited = FxHashSet::default(); | ||
151 | |||
152 | let source_root = db.source_root(source_root); | ||
153 | for &file_id in source_root.files.values() { | ||
154 | let source = SourceItemId { | ||
155 | file_id: file_id.into(), | ||
156 | item_id: None, | ||
157 | }; | ||
158 | if visited.contains(&source) { | ||
159 | continue; // TODO: use explicit crate_roots here | ||
160 | } | ||
161 | assert!(!roots.contains_key(&file_id)); | ||
162 | let module_id = | ||
163 | self.init_subtree(db, &source_root, &mut visited, &mut roots, None, source); | ||
164 | roots.insert(file_id, module_id); | ||
165 | } | ||
166 | } | ||
167 | |||
168 | fn init_subtree( | ||
169 | &mut self, | ||
170 | db: &impl HirDatabase, | ||
171 | source_root: &SourceRoot, | ||
172 | visited: &mut FxHashSet<SourceItemId>, | ||
173 | roots: &mut FxHashMap<FileId, ModuleId>, | ||
174 | parent: Option<LinkId>, | ||
175 | source: SourceItemId, | ||
176 | ) -> ModuleId { | ||
177 | visited.insert(source); | ||
178 | let id = self.alloc_mod(ModuleData { | ||
179 | source, | ||
180 | parent, | ||
181 | children: Vec::new(), | ||
182 | }); | ||
183 | for sub in db.submodules(source).iter() { | ||
184 | let link = self.alloc_link(LinkData { | ||
185 | source: sub.source, | ||
186 | name: sub.name.clone(), | ||
187 | owner: id, | ||
188 | points_to: Vec::new(), | ||
189 | problem: None, | ||
190 | }); | ||
191 | |||
192 | let (points_to, problem) = if sub.is_declaration { | ||
193 | let (points_to, problem) = resolve_submodule(db, source.file_id, &sub.name); | ||
194 | let points_to = points_to | ||
195 | .into_iter() | ||
196 | .map(|file_id| match roots.remove(&file_id) { | ||
197 | Some(module_id) => { | ||
198 | self.mods[module_id].parent = Some(link); | ||
199 | module_id | ||
200 | } | ||
201 | None => self.init_subtree( | ||
202 | db, | ||
203 | source_root, | ||
204 | visited, | ||
205 | roots, | ||
206 | Some(link), | ||
207 | SourceItemId { | ||
208 | file_id: file_id.into(), | ||
209 | item_id: None, | ||
210 | }, | ||
211 | ), | ||
212 | }) | ||
213 | .collect::<Vec<_>>(); | ||
214 | (points_to, problem) | ||
215 | } else { | ||
216 | let points_to = | ||
217 | self.init_subtree(db, source_root, visited, roots, Some(link), sub.source); | ||
218 | (vec![points_to], None) | ||
219 | }; | ||
220 | |||
221 | self.links[link].points_to = points_to; | ||
222 | self.links[link].problem = problem; | ||
223 | } | ||
224 | id | ||
225 | } | ||
226 | |||
227 | fn alloc_mod(&mut self, data: ModuleData) -> ModuleId { | ||
228 | self.mods.alloc(data) | ||
229 | } | ||
230 | |||
231 | fn alloc_link(&mut self, data: LinkData) -> LinkId { | ||
232 | let owner = data.owner; | ||
233 | let id = self.links.alloc(data); | ||
234 | self.mods[owner].children.push(id); | ||
235 | id | ||
236 | } | ||
134 | } | 237 | } |
135 | 238 | ||
136 | impl ModuleId { | 239 | impl ModuleId { |
@@ -198,131 +301,6 @@ impl LinkId { | |||
198 | } | 301 | } |
199 | } | 302 | } |
200 | 303 | ||
201 | impl ModuleTree { | ||
202 | fn push_mod(&mut self, data: ModuleData) -> ModuleId { | ||
203 | self.mods.alloc(data) | ||
204 | } | ||
205 | fn push_link(&mut self, data: LinkData) -> LinkId { | ||
206 | let owner = data.owner; | ||
207 | let id = self.links.alloc(data); | ||
208 | self.mods[owner].children.push(id); | ||
209 | id | ||
210 | } | ||
211 | } | ||
212 | |||
213 | fn modules(root: &impl ast::ModuleItemOwner) -> impl Iterator<Item = (Name, &ast::Module)> { | ||
214 | root.items() | ||
215 | .filter_map(|item| match item.kind() { | ||
216 | ast::ModuleItemKind::Module(m) => Some(m), | ||
217 | _ => None, | ||
218 | }) | ||
219 | .filter_map(|module| { | ||
220 | let name = module.name()?.as_name(); | ||
221 | if !module.has_semi() && module.item_list().is_none() { | ||
222 | tested_by!(name_res_works_for_broken_modules); | ||
223 | return None; | ||
224 | } | ||
225 | Some((name, module)) | ||
226 | }) | ||
227 | } | ||
228 | |||
229 | fn create_module_tree<'a>(db: &impl HirDatabase, source_root: SourceRootId) -> ModuleTree { | ||
230 | let mut tree = ModuleTree::default(); | ||
231 | |||
232 | let mut roots = FxHashMap::default(); | ||
233 | let mut visited = FxHashSet::default(); | ||
234 | |||
235 | let source_root = db.source_root(source_root); | ||
236 | for &file_id in source_root.files.values() { | ||
237 | let source = SourceItemId { | ||
238 | file_id: file_id.into(), | ||
239 | item_id: None, | ||
240 | }; | ||
241 | if visited.contains(&source) { | ||
242 | continue; // TODO: use explicit crate_roots here | ||
243 | } | ||
244 | assert!(!roots.contains_key(&file_id)); | ||
245 | let module_id = build_subtree( | ||
246 | db, | ||
247 | &source_root, | ||
248 | &mut tree, | ||
249 | &mut visited, | ||
250 | &mut roots, | ||
251 | None, | ||
252 | source, | ||
253 | ); | ||
254 | roots.insert(file_id, module_id); | ||
255 | } | ||
256 | tree | ||
257 | } | ||
258 | |||
259 | fn build_subtree( | ||
260 | db: &impl HirDatabase, | ||
261 | source_root: &SourceRoot, | ||
262 | tree: &mut ModuleTree, | ||
263 | visited: &mut FxHashSet<SourceItemId>, | ||
264 | roots: &mut FxHashMap<FileId, ModuleId>, | ||
265 | parent: Option<LinkId>, | ||
266 | source: SourceItemId, | ||
267 | ) -> ModuleId { | ||
268 | visited.insert(source); | ||
269 | let id = tree.push_mod(ModuleData { | ||
270 | source, | ||
271 | parent, | ||
272 | children: Vec::new(), | ||
273 | }); | ||
274 | for sub in db.submodules(source).iter() { | ||
275 | let link = tree.push_link(LinkData { | ||
276 | source: sub.source, | ||
277 | name: sub.name.clone(), | ||
278 | owner: id, | ||
279 | points_to: Vec::new(), | ||
280 | problem: None, | ||
281 | }); | ||
282 | |||
283 | let (points_to, problem) = if sub.is_declaration { | ||
284 | let (points_to, problem) = resolve_submodule(db, source.file_id, &sub.name); | ||
285 | let points_to = points_to | ||
286 | .into_iter() | ||
287 | .map(|file_id| match roots.remove(&file_id) { | ||
288 | Some(module_id) => { | ||
289 | tree.mods[module_id].parent = Some(link); | ||
290 | module_id | ||
291 | } | ||
292 | None => build_subtree( | ||
293 | db, | ||
294 | source_root, | ||
295 | tree, | ||
296 | visited, | ||
297 | roots, | ||
298 | Some(link), | ||
299 | SourceItemId { | ||
300 | file_id: file_id.into(), | ||
301 | item_id: None, | ||
302 | }, | ||
303 | ), | ||
304 | }) | ||
305 | .collect::<Vec<_>>(); | ||
306 | (points_to, problem) | ||
307 | } else { | ||
308 | let points_to = build_subtree( | ||
309 | db, | ||
310 | source_root, | ||
311 | tree, | ||
312 | visited, | ||
313 | roots, | ||
314 | Some(link), | ||
315 | sub.source, | ||
316 | ); | ||
317 | (vec![points_to], None) | ||
318 | }; | ||
319 | |||
320 | tree.links[link].points_to = points_to; | ||
321 | tree.links[link].problem = problem; | ||
322 | } | ||
323 | id | ||
324 | } | ||
325 | |||
326 | fn resolve_submodule( | 304 | fn resolve_submodule( |
327 | db: &impl HirDatabase, | 305 | db: &impl HirDatabase, |
328 | file_id: HirFileId, | 306 | file_id: HirFileId, |