diff options
author | Aleksey Kladov <[email protected]> | 2019-03-16 14:17:50 +0000 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2019-03-17 09:53:22 +0000 |
commit | 967a4b64af2f09e11108d28f11565fa0f3e583a5 (patch) | |
tree | 985072c007ce020de3c5205f0f976d872e44c41a /crates/ra_hir/src/nameres/collector.rs | |
parent | d4449945a069d26035afe9d8627414f6dfc8bf0a (diff) |
Reorganize name resolution
Diffstat (limited to 'crates/ra_hir/src/nameres/collector.rs')
-rw-r--r-- | crates/ra_hir/src/nameres/collector.rs | 547 |
1 files changed, 547 insertions, 0 deletions
diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs new file mode 100644 index 000000000..cbe850ba4 --- /dev/null +++ b/crates/ra_hir/src/nameres/collector.rs | |||
@@ -0,0 +1,547 @@ | |||
1 | use arrayvec::ArrayVec; | ||
2 | use rustc_hash::FxHashMap; | ||
3 | use relative_path::RelativePathBuf; | ||
4 | use test_utils::tested_by; | ||
5 | use ra_db::FileId; | ||
6 | |||
7 | use crate::{ | ||
8 | Function, Module, Struct, Enum, Const, Static, Trait, TypeAlias, | ||
9 | PersistentHirDatabase, HirFileId, Name, Path, Problem, | ||
10 | KnownName, | ||
11 | nameres::{Resolution, PerNs, ModuleDef, ReachedFixedPoint, ResolveMode, raw}, | ||
12 | ids::{AstItemDef, LocationCtx, MacroCallLoc, SourceItemId, MacroCallId}, | ||
13 | }; | ||
14 | |||
15 | use super::{CrateDefMap, ModuleId, ModuleData}; | ||
16 | |||
17 | pub(super) fn collect_defs( | ||
18 | db: &impl PersistentHirDatabase, | ||
19 | mut def_map: CrateDefMap, | ||
20 | ) -> CrateDefMap { | ||
21 | // populate external prelude | ||
22 | for dep in def_map.krate.dependencies(db) { | ||
23 | log::debug!("crate dep {:?} -> {:?}", dep.name, dep.krate); | ||
24 | if let Some(module) = dep.krate.root_module(db) { | ||
25 | def_map.extern_prelude.insert(dep.name.clone(), module.into()); | ||
26 | } | ||
27 | // look for the prelude | ||
28 | if def_map.prelude.is_none() { | ||
29 | let map = db.crate_def_map(dep.krate); | ||
30 | if map.prelude.is_some() { | ||
31 | def_map.prelude = map.prelude; | ||
32 | } | ||
33 | } | ||
34 | } | ||
35 | |||
36 | let mut collector = DefCollector { | ||
37 | db, | ||
38 | def_map, | ||
39 | glob_imports: FxHashMap::default(), | ||
40 | unresolved_imports: Vec::new(), | ||
41 | unexpanded_macros: Vec::new(), | ||
42 | global_macro_scope: FxHashMap::default(), | ||
43 | }; | ||
44 | collector.collect(); | ||
45 | collector.finish() | ||
46 | } | ||
47 | |||
48 | /// Walks the tree of module recursively | ||
49 | struct DefCollector<DB> { | ||
50 | db: DB, | ||
51 | def_map: CrateDefMap, | ||
52 | glob_imports: FxHashMap<ModuleId, Vec<(ModuleId, raw::ImportId)>>, | ||
53 | unresolved_imports: Vec<(ModuleId, raw::ImportId, raw::ImportData)>, | ||
54 | unexpanded_macros: Vec<(ModuleId, MacroCallId, Path, tt::Subtree)>, | ||
55 | global_macro_scope: FxHashMap<Name, mbe::MacroRules>, | ||
56 | } | ||
57 | |||
58 | impl<'a, DB> DefCollector<&'a DB> | ||
59 | where | ||
60 | DB: PersistentHirDatabase, | ||
61 | { | ||
62 | fn collect(&mut self) { | ||
63 | let crate_graph = self.db.crate_graph(); | ||
64 | let file_id = crate_graph.crate_root(self.def_map.krate.crate_id()); | ||
65 | let raw_items = self.db.raw_items(file_id); | ||
66 | let module_id = self.def_map.root; | ||
67 | self.def_map.modules[module_id].definition = Some(file_id); | ||
68 | ModCollector { | ||
69 | def_collector: &mut *self, | ||
70 | module_id, | ||
71 | file_id: file_id.into(), | ||
72 | raw_items: &raw_items, | ||
73 | } | ||
74 | .collect(raw_items.items()); | ||
75 | |||
76 | // main name resolution fixed-point loop. | ||
77 | let mut i = 0; | ||
78 | loop { | ||
79 | match (self.resolve_imports(), self.resolve_macros()) { | ||
80 | (ReachedFixedPoint::Yes, ReachedFixedPoint::Yes) => break, | ||
81 | _ => i += 1, | ||
82 | } | ||
83 | if i == 1000 { | ||
84 | log::error!("diverging name resolution"); | ||
85 | break; | ||
86 | } | ||
87 | } | ||
88 | |||
89 | let unresolved_imports = std::mem::replace(&mut self.unresolved_imports, Vec::new()); | ||
90 | // show unresolved imports in completion, etc | ||
91 | for (module_id, import, import_data) in unresolved_imports { | ||
92 | self.record_resolved_import(module_id, PerNs::none(), import, &import_data) | ||
93 | } | ||
94 | } | ||
95 | |||
96 | fn define_macro(&mut self, name: Name, tt: &tt::Subtree, export: bool) { | ||
97 | if let Ok(rules) = mbe::MacroRules::parse(tt) { | ||
98 | if export { | ||
99 | self.def_map.public_macros.insert(name.clone(), rules.clone()); | ||
100 | } | ||
101 | self.global_macro_scope.insert(name, rules); | ||
102 | } | ||
103 | } | ||
104 | |||
105 | fn resolve_imports(&mut self) -> ReachedFixedPoint { | ||
106 | let mut imports = std::mem::replace(&mut self.unresolved_imports, Vec::new()); | ||
107 | let mut resolved = Vec::new(); | ||
108 | imports.retain(|(module_id, import, import_data)| { | ||
109 | let (def, fp) = self.resolve_import(*module_id, import_data); | ||
110 | if fp == ReachedFixedPoint::Yes { | ||
111 | resolved.push((*module_id, def, *import, import_data.clone())) | ||
112 | } | ||
113 | fp == ReachedFixedPoint::No | ||
114 | }); | ||
115 | self.unresolved_imports = imports; | ||
116 | // Resolves imports, filling-in module scopes | ||
117 | let result = | ||
118 | if resolved.is_empty() { ReachedFixedPoint::Yes } else { ReachedFixedPoint::No }; | ||
119 | for (module_id, def, import, import_data) in resolved { | ||
120 | self.record_resolved_import(module_id, def, import, &import_data) | ||
121 | } | ||
122 | result | ||
123 | } | ||
124 | |||
125 | fn resolve_import( | ||
126 | &mut self, | ||
127 | module_id: ModuleId, | ||
128 | import: &raw::ImportData, | ||
129 | ) -> (PerNs<ModuleDef>, ReachedFixedPoint) { | ||
130 | log::debug!("resolving import: {:?} ({:?})", import, self.def_map.edition); | ||
131 | if import.is_extern_crate { | ||
132 | let res = self.def_map.resolve_name_in_extern_prelude( | ||
133 | &import | ||
134 | .path | ||
135 | .as_ident() | ||
136 | .expect("extern crate should have been desugared to one-element path"), | ||
137 | ); | ||
138 | // FIXME: why do we return No here? | ||
139 | (res, if res.is_none() { ReachedFixedPoint::No } else { ReachedFixedPoint::Yes }) | ||
140 | } else { | ||
141 | let res = | ||
142 | self.def_map.resolve_path_fp(self.db, ResolveMode::Import, module_id, &import.path); | ||
143 | |||
144 | (res.resolved_def, res.reached_fixedpoint) | ||
145 | } | ||
146 | } | ||
147 | |||
148 | fn record_resolved_import( | ||
149 | &mut self, | ||
150 | module_id: ModuleId, | ||
151 | def: PerNs<ModuleDef>, | ||
152 | import_id: raw::ImportId, | ||
153 | import: &raw::ImportData, | ||
154 | ) { | ||
155 | if import.is_glob { | ||
156 | log::debug!("glob import: {:?}", import); | ||
157 | match def.take_types() { | ||
158 | Some(ModuleDef::Module(m)) => { | ||
159 | if import.is_prelude { | ||
160 | tested_by!(std_prelude); | ||
161 | self.def_map.prelude = Some(m); | ||
162 | } else if m.krate != self.def_map.krate { | ||
163 | tested_by!(glob_across_crates); | ||
164 | // glob import from other crate => we can just import everything once | ||
165 | let item_map = self.db.crate_def_map(m.krate); | ||
166 | let scope = &item_map[m.module_id].scope; | ||
167 | let items = scope | ||
168 | .items | ||
169 | .iter() | ||
170 | .map(|(name, res)| (name.clone(), res.clone())) | ||
171 | .collect::<Vec<_>>(); | ||
172 | self.update(module_id, Some(import_id), &items); | ||
173 | } else { | ||
174 | // glob import from same crate => we do an initial | ||
175 | // import, and then need to propagate any further | ||
176 | // additions | ||
177 | let scope = &self.def_map[m.module_id].scope; | ||
178 | let items = scope | ||
179 | .items | ||
180 | .iter() | ||
181 | .map(|(name, res)| (name.clone(), res.clone())) | ||
182 | .collect::<Vec<_>>(); | ||
183 | self.update(module_id, Some(import_id), &items); | ||
184 | // record the glob import in case we add further items | ||
185 | self.glob_imports | ||
186 | .entry(m.module_id) | ||
187 | .or_default() | ||
188 | .push((module_id, import_id)); | ||
189 | } | ||
190 | } | ||
191 | Some(ModuleDef::Enum(e)) => { | ||
192 | tested_by!(glob_enum); | ||
193 | // glob import from enum => just import all the variants | ||
194 | let variants = e.variants(self.db); | ||
195 | let resolutions = variants | ||
196 | .into_iter() | ||
197 | .filter_map(|variant| { | ||
198 | let res = Resolution { | ||
199 | def: PerNs::both(variant.into(), variant.into()), | ||
200 | import: Some(import_id), | ||
201 | }; | ||
202 | let name = variant.name(self.db)?; | ||
203 | Some((name, res)) | ||
204 | }) | ||
205 | .collect::<Vec<_>>(); | ||
206 | self.update(module_id, Some(import_id), &resolutions); | ||
207 | } | ||
208 | Some(d) => { | ||
209 | log::debug!("glob import {:?} from non-module/enum {:?}", import, d); | ||
210 | } | ||
211 | None => { | ||
212 | log::debug!("glob import {:?} didn't resolve as type", import); | ||
213 | } | ||
214 | } | ||
215 | } else { | ||
216 | let last_segment = import.path.segments.last().unwrap(); | ||
217 | let name = import.alias.clone().unwrap_or_else(|| last_segment.name.clone()); | ||
218 | log::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def); | ||
219 | |||
220 | // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658 | ||
221 | if import.is_extern_crate && module_id == self.def_map.root { | ||
222 | if let Some(def) = def.take_types() { | ||
223 | self.def_map.extern_prelude.insert(name.clone(), def); | ||
224 | } | ||
225 | } | ||
226 | let resolution = Resolution { def, import: Some(import_id) }; | ||
227 | self.update(module_id, Some(import_id), &[(name, resolution)]); | ||
228 | } | ||
229 | } | ||
230 | |||
231 | fn update( | ||
232 | &mut self, | ||
233 | module_id: ModuleId, | ||
234 | import: Option<raw::ImportId>, | ||
235 | resolutions: &[(Name, Resolution)], | ||
236 | ) { | ||
237 | self.update_recursive(module_id, import, resolutions, 0) | ||
238 | } | ||
239 | |||
240 | fn update_recursive( | ||
241 | &mut self, | ||
242 | module_id: ModuleId, | ||
243 | import: Option<raw::ImportId>, | ||
244 | resolutions: &[(Name, Resolution)], | ||
245 | depth: usize, | ||
246 | ) { | ||
247 | if depth > 100 { | ||
248 | // prevent stack overflows (but this shouldn't be possible) | ||
249 | panic!("infinite recursion in glob imports!"); | ||
250 | } | ||
251 | let module_items = &mut self.def_map.modules[module_id].scope; | ||
252 | let mut changed = false; | ||
253 | for (name, res) in resolutions { | ||
254 | let existing = module_items.items.entry(name.clone()).or_default(); | ||
255 | if existing.def.types.is_none() && res.def.types.is_some() { | ||
256 | existing.def.types = res.def.types; | ||
257 | existing.import = import.or(res.import); | ||
258 | changed = true; | ||
259 | } | ||
260 | if existing.def.values.is_none() && res.def.values.is_some() { | ||
261 | existing.def.values = res.def.values; | ||
262 | existing.import = import.or(res.import); | ||
263 | changed = true; | ||
264 | } | ||
265 | if existing.def.is_none() | ||
266 | && res.def.is_none() | ||
267 | && existing.import.is_none() | ||
268 | && res.import.is_some() | ||
269 | { | ||
270 | existing.import = res.import; | ||
271 | } | ||
272 | } | ||
273 | if !changed { | ||
274 | return; | ||
275 | } | ||
276 | let glob_imports = self | ||
277 | .glob_imports | ||
278 | .get(&module_id) | ||
279 | .into_iter() | ||
280 | .flat_map(|v| v.iter()) | ||
281 | .cloned() | ||
282 | .collect::<Vec<_>>(); | ||
283 | for (glob_importing_module, glob_import) in glob_imports { | ||
284 | // We pass the glob import so that the tracked import in those modules is that glob import | ||
285 | self.update_recursive(glob_importing_module, Some(glob_import), resolutions, depth + 1); | ||
286 | } | ||
287 | } | ||
288 | |||
289 | // XXX: this is just a pile of hacks now, because `PerNs` does not handle | ||
290 | // macro namespace. | ||
291 | fn resolve_macros(&mut self) -> ReachedFixedPoint { | ||
292 | let mut macros = std::mem::replace(&mut self.unexpanded_macros, Vec::new()); | ||
293 | let mut resolved = Vec::new(); | ||
294 | macros.retain(|(module_id, call_id, path, tt)| { | ||
295 | if path.segments.len() != 2 { | ||
296 | return true; | ||
297 | } | ||
298 | let crate_name = &path.segments[0].name; | ||
299 | let krate = match self.def_map.resolve_name_in_extern_prelude(crate_name).take_types() { | ||
300 | Some(ModuleDef::Module(m)) => m.krate(self.db), | ||
301 | _ => return true, | ||
302 | }; | ||
303 | let krate = match krate { | ||
304 | Some(it) => it, | ||
305 | _ => return true, | ||
306 | }; | ||
307 | let def_map = self.db.crate_def_map(krate); | ||
308 | let rules = def_map.public_macros.get(&path.segments[1].name).cloned(); | ||
309 | resolved.push((*module_id, *call_id, rules, tt.clone())); | ||
310 | false | ||
311 | }); | ||
312 | let res = if resolved.is_empty() { ReachedFixedPoint::Yes } else { ReachedFixedPoint::No }; | ||
313 | |||
314 | for (module_id, macro_call_id, rules, arg) in resolved { | ||
315 | if let Some(rules) = rules { | ||
316 | if let Ok(tt) = rules.expand(&arg) { | ||
317 | self.collect_macro_expansion(module_id, macro_call_id, tt); | ||
318 | } | ||
319 | } | ||
320 | } | ||
321 | res | ||
322 | } | ||
323 | |||
324 | fn collect_macro_expansion( | ||
325 | &mut self, | ||
326 | module_id: ModuleId, | ||
327 | macro_call_id: MacroCallId, | ||
328 | expansion: tt::Subtree, | ||
329 | ) { | ||
330 | // XXX: this **does not** go through a database, because we can't | ||
331 | // identify macro_call without adding the whole state of name resolution | ||
332 | // as a parameter to the query. | ||
333 | // | ||
334 | // So, we run the queries "manually" and we must ensure that | ||
335 | // `db.hir_parse(macro_call_id)` returns the same source_file. | ||
336 | let file_id: HirFileId = macro_call_id.into(); | ||
337 | let source_file = mbe::token_tree_to_ast_item_list(&expansion); | ||
338 | |||
339 | let raw_items = raw::RawItems::from_source_file(&source_file, file_id); | ||
340 | ModCollector { def_collector: &mut *self, file_id, module_id, raw_items: &raw_items } | ||
341 | .collect(raw_items.items()) | ||
342 | } | ||
343 | |||
344 | fn finish(self) -> CrateDefMap { | ||
345 | self.def_map | ||
346 | } | ||
347 | } | ||
348 | |||
349 | /// Walks a single module, populating defs, imports and macros | ||
350 | struct ModCollector<'a, D> { | ||
351 | def_collector: D, | ||
352 | module_id: ModuleId, | ||
353 | file_id: HirFileId, | ||
354 | raw_items: &'a raw::RawItems, | ||
355 | } | ||
356 | |||
357 | impl<DB> ModCollector<'_, &'_ mut DefCollector<&'_ DB>> | ||
358 | where | ||
359 | DB: PersistentHirDatabase, | ||
360 | { | ||
361 | fn collect(&mut self, items: &[raw::RawItem]) { | ||
362 | for item in items { | ||
363 | match *item { | ||
364 | raw::RawItem::Module(m) => self.collect_module(&self.raw_items[m]), | ||
365 | raw::RawItem::Import(import) => self.def_collector.unresolved_imports.push(( | ||
366 | self.module_id, | ||
367 | import, | ||
368 | self.raw_items[import].clone(), | ||
369 | )), | ||
370 | raw::RawItem::Def(def) => self.define_def(&self.raw_items[def]), | ||
371 | raw::RawItem::Macro(mac) => self.collect_macro(&self.raw_items[mac]), | ||
372 | } | ||
373 | } | ||
374 | } | ||
375 | |||
376 | fn collect_module(&mut self, module: &raw::ModuleData) { | ||
377 | match module { | ||
378 | // inline module, just recurse | ||
379 | raw::ModuleData::Definition { name, items, source_item_id } => { | ||
380 | let module_id = self.push_child_module( | ||
381 | name.clone(), | ||
382 | source_item_id.with_file_id(self.file_id), | ||
383 | None, | ||
384 | ); | ||
385 | ModCollector { | ||
386 | def_collector: &mut *self.def_collector, | ||
387 | module_id, | ||
388 | file_id: self.file_id, | ||
389 | raw_items: self.raw_items, | ||
390 | } | ||
391 | .collect(&*items); | ||
392 | } | ||
393 | // out of line module, resovle, parse and recurse | ||
394 | raw::ModuleData::Declaration { name, source_item_id } => { | ||
395 | let source_item_id = source_item_id.with_file_id(self.file_id); | ||
396 | let is_root = self.def_collector.def_map.modules[self.module_id].parent.is_none(); | ||
397 | let (file_ids, problem) = | ||
398 | resolve_submodule(self.def_collector.db, self.file_id, name, is_root); | ||
399 | |||
400 | if let Some(problem) = problem { | ||
401 | self.def_collector.def_map.problems.add(source_item_id, problem) | ||
402 | } | ||
403 | |||
404 | if let Some(&file_id) = file_ids.first() { | ||
405 | let module_id = | ||
406 | self.push_child_module(name.clone(), source_item_id, Some(file_id)); | ||
407 | let raw_items = self.def_collector.db.raw_items(file_id); | ||
408 | ModCollector { | ||
409 | def_collector: &mut *self.def_collector, | ||
410 | module_id, | ||
411 | file_id: file_id.into(), | ||
412 | raw_items: &raw_items, | ||
413 | } | ||
414 | .collect(raw_items.items()) | ||
415 | } | ||
416 | } | ||
417 | } | ||
418 | } | ||
419 | |||
420 | fn push_child_module( | ||
421 | &mut self, | ||
422 | name: Name, | ||
423 | declaration: SourceItemId, | ||
424 | definition: Option<FileId>, | ||
425 | ) -> ModuleId { | ||
426 | let modules = &mut self.def_collector.def_map.modules; | ||
427 | let res = modules.alloc(ModuleData::default()); | ||
428 | modules[res].parent = Some(self.module_id); | ||
429 | modules[res].declaration = Some(declaration); | ||
430 | modules[res].definition = definition; | ||
431 | modules[self.module_id].children.insert(name.clone(), res); | ||
432 | let resolution = Resolution { | ||
433 | def: PerNs::types( | ||
434 | Module { krate: self.def_collector.def_map.krate, module_id: res }.into(), | ||
435 | ), | ||
436 | import: None, | ||
437 | }; | ||
438 | self.def_collector.update(self.module_id, None, &[(name, resolution)]); | ||
439 | res | ||
440 | } | ||
441 | |||
442 | fn define_def(&mut self, def: &raw::DefData) { | ||
443 | let module = Module { krate: self.def_collector.def_map.krate, module_id: self.module_id }; | ||
444 | let ctx = LocationCtx::new(self.def_collector.db, module, self.file_id.into()); | ||
445 | macro_rules! id { | ||
446 | () => { | ||
447 | AstItemDef::from_source_item_id_unchecked(ctx, def.source_item_id) | ||
448 | }; | ||
449 | } | ||
450 | let name = def.name.clone(); | ||
451 | let def: PerNs<ModuleDef> = match def.kind { | ||
452 | raw::DefKind::Function => PerNs::values(Function { id: id!() }.into()), | ||
453 | raw::DefKind::Struct => { | ||
454 | let s = Struct { id: id!() }.into(); | ||
455 | PerNs::both(s, s) | ||
456 | } | ||
457 | raw::DefKind::Enum => PerNs::types(Enum { id: id!() }.into()), | ||
458 | raw::DefKind::Const => PerNs::values(Const { id: id!() }.into()), | ||
459 | raw::DefKind::Static => PerNs::values(Static { id: id!() }.into()), | ||
460 | raw::DefKind::Trait => PerNs::types(Trait { id: id!() }.into()), | ||
461 | raw::DefKind::TypeAlias => PerNs::types(TypeAlias { id: id!() }.into()), | ||
462 | }; | ||
463 | let resolution = Resolution { def, import: None }; | ||
464 | self.def_collector.update(self.module_id, None, &[(name, resolution)]) | ||
465 | } | ||
466 | |||
467 | fn collect_macro(&mut self, mac: &raw::MacroData) { | ||
468 | // Case 1: macro rules, define a macro in crate-global mutable scope | ||
469 | if is_macro_rules(&mac.path) { | ||
470 | if let Some(name) = &mac.name { | ||
471 | self.def_collector.define_macro(name.clone(), &mac.arg, mac.export) | ||
472 | } | ||
473 | return; | ||
474 | } | ||
475 | |||
476 | let source_item_id = SourceItemId { file_id: self.file_id, item_id: mac.source_item_id }; | ||
477 | let macro_call_id = MacroCallLoc { | ||
478 | module: Module { krate: self.def_collector.def_map.krate, module_id: self.module_id }, | ||
479 | source_item_id, | ||
480 | } | ||
481 | .id(self.def_collector.db); | ||
482 | |||
483 | // Case 2: try to expand macro_rules from this crate, triggering | ||
484 | // recursive item collection. | ||
485 | if let Some(rules) = | ||
486 | mac.path.as_ident().and_then(|name| self.def_collector.global_macro_scope.get(name)) | ||
487 | { | ||
488 | if let Ok(tt) = rules.expand(&mac.arg) { | ||
489 | self.def_collector.collect_macro_expansion(self.module_id, macro_call_id, tt); | ||
490 | } | ||
491 | return; | ||
492 | } | ||
493 | |||
494 | // Case 3: path to a macro from another crate, expand during name resolution | ||
495 | self.def_collector.unexpanded_macros.push(( | ||
496 | self.module_id, | ||
497 | macro_call_id, | ||
498 | mac.path.clone(), | ||
499 | mac.arg.clone(), | ||
500 | )) | ||
501 | } | ||
502 | } | ||
503 | |||
504 | fn is_macro_rules(path: &Path) -> bool { | ||
505 | path.as_ident().and_then(Name::as_known_name) == Some(KnownName::MacroRules) | ||
506 | } | ||
507 | |||
508 | fn resolve_submodule( | ||
509 | db: &impl PersistentHirDatabase, | ||
510 | file_id: HirFileId, | ||
511 | name: &Name, | ||
512 | is_root: bool, | ||
513 | ) -> (Vec<FileId>, Option<Problem>) { | ||
514 | // FIXME: handle submodules of inline modules properly | ||
515 | let file_id = file_id.original_file(db); | ||
516 | let source_root_id = db.file_source_root(file_id); | ||
517 | let path = db.file_relative_path(file_id); | ||
518 | let root = RelativePathBuf::default(); | ||
519 | let dir_path = path.parent().unwrap_or(&root); | ||
520 | let mod_name = path.file_stem().unwrap_or("unknown"); | ||
521 | let is_dir_owner = is_root || mod_name == "mod"; | ||
522 | |||
523 | let file_mod = dir_path.join(format!("{}.rs", name)); | ||
524 | let dir_mod = dir_path.join(format!("{}/mod.rs", name)); | ||
525 | let file_dir_mod = dir_path.join(format!("{}/{}.rs", mod_name, name)); | ||
526 | let mut candidates = ArrayVec::<[_; 2]>::new(); | ||
527 | if is_dir_owner { | ||
528 | candidates.push(file_mod.clone()); | ||
529 | candidates.push(dir_mod); | ||
530 | } else { | ||
531 | candidates.push(file_dir_mod.clone()); | ||
532 | }; | ||
533 | let sr = db.source_root(source_root_id); | ||
534 | let points_to = candidates | ||
535 | .into_iter() | ||
536 | .filter_map(|path| sr.files.get(&path)) | ||
537 | .map(|&it| it) | ||
538 | .collect::<Vec<_>>(); | ||
539 | let problem = if points_to.is_empty() { | ||
540 | Some(Problem::UnresolvedModule { | ||
541 | candidate: if is_dir_owner { file_mod } else { file_dir_mod }, | ||
542 | }) | ||
543 | } else { | ||
544 | None | ||
545 | }; | ||
546 | (points_to, problem) | ||
547 | } | ||