diff options
Diffstat (limited to 'crates/ra_hir/src/nameres.rs')
-rw-r--r-- | crates/ra_hir/src/nameres.rs | 743 |
1 files changed, 252 insertions, 491 deletions
diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index 73919ee37..edd2f25f7 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs | |||
@@ -1,60 +1,148 @@ | |||
1 | //! Name resolution algorithm. The end result of the algorithm is an `ItemMap`: | 1 | /// This module implements import-resolution/macro expansion algorithm. |
2 | //! a map which maps each module to its scope: the set of items visible in the | 2 | /// |
3 | //! module. That is, we only resolve imports here, name resolution of item | 3 | /// The result of this module is `CrateDefMap`: a datastructure which contains: |
4 | //! bodies will be done in a separate step. | 4 | /// |
5 | //! | 5 | /// * a tree of modules for the crate |
6 | //! Like Rustc, we use an interactive per-crate algorithm: we start with scopes | 6 | /// * for each module, a set of items visible in the module (directly declared |
7 | //! containing only directly defined items, and then iteratively resolve | 7 | /// or imported) |
8 | //! imports. | 8 | /// |
9 | //! | 9 | /// Note that `CrateDefMap` contains fully macro expanded code. |
10 | //! To make this work nicely in the IDE scenario, we place `InputModuleItems` | 10 | /// |
11 | //! in between raw syntax and name resolution. `InputModuleItems` are computed | 11 | /// Computing `CrateDefMap` can be partitioned into several logically |
12 | //! using only the module's syntax, and it is all directly defined items plus | 12 | /// independent "phases". The phases are mutually recursive though, there's no |
13 | //! imports. The plan is to make `InputModuleItems` independent of local | 13 | /// strict ordering. |
14 | //! modifications (that is, typing inside a function should not change IMIs), | 14 | /// |
15 | //! so that the results of name resolution can be preserved unless the module | 15 | /// ## Collecting RawItems |
16 | //! structure itself is modified. | 16 | /// |
17 | pub(crate) mod lower; | 17 | /// This happens in the `raw` module, which parses a single source file into a |
18 | 18 | /// set of top-level items. Nested imports are desugared to flat imports in | |
19 | use std::{time, sync::Arc}; | 19 | /// this phase. Macro calls are represented as a triple of (Path, Option<Name>, |
20 | 20 | /// TokenTree). | |
21 | use rustc_hash::{FxHashMap, FxHashSet}; | 21 | /// |
22 | 22 | /// ## Collecting Modules | |
23 | use ra_arena::map::ArenaMap; | 23 | /// |
24 | use ra_db::Edition; | 24 | /// This happens in the `collector` module. In this phase, we recursively walk |
25 | /// tree of modules, collect raw items from submodules, populate module scopes | ||
26 | /// with defined items (so, we assign item ids in this phase) and record the set | ||
27 | /// of unresolved imports and macros. | ||
28 | /// | ||
29 | /// While we walk tree of modules, we also record macro_rules definitions and | ||
30 | /// expand calls to macro_rules defined macros. | ||
31 | /// | ||
32 | /// ## Resolving Imports | ||
33 | /// | ||
34 | /// We maintain a list of currently unresolved imports. On every iteration, we | ||
35 | /// try to resolve some imports from this list. If the import is resolved, we | ||
36 | /// record it, by adding an item to current module scope and, if necessary, by | ||
37 | /// recursively populating glob imports. | ||
38 | /// | ||
39 | /// ## Resolving Macros | ||
40 | /// | ||
41 | /// macro_rules from the same crate use a global mutable namespace. We expand | ||
42 | /// them immediately, when we collect modules. | ||
43 | /// | ||
44 | /// Macros from other crates (including proc-macros) can be used with | ||
45 | /// `foo::bar!` syntax. We handle them similarly to imports. There's a list of | ||
46 | /// unexpanded macros. On every iteration, we try to resolve each macro call | ||
47 | /// path and, upon success, we run macro expansion and "collect module" phase | ||
48 | /// on the result | ||
49 | |||
50 | mod per_ns; | ||
51 | mod raw; | ||
52 | mod collector; | ||
53 | #[cfg(test)] | ||
54 | mod tests; | ||
55 | |||
56 | use std::sync::Arc; | ||
57 | |||
58 | use rustc_hash::FxHashMap; | ||
59 | use ra_arena::{Arena, RawId, impl_arena_id}; | ||
60 | use ra_db::{FileId, Edition}; | ||
25 | use test_utils::tested_by; | 61 | use test_utils::tested_by; |
26 | 62 | ||
27 | use crate::{ | 63 | use crate::{ |
28 | Module, ModuleDef, | 64 | ModuleDef, Name, Crate, Module, Problem, |
29 | Path, PathKind, PersistentHirDatabase, | 65 | PersistentHirDatabase, Path, PathKind, HirFileId, |
30 | Crate, Name, | 66 | ids::{SourceItemId, SourceFileItemId, MacroCallId}, |
31 | module_tree::{ModuleId, ModuleTree}, | ||
32 | nameres::lower::{ImportId, LoweredModule, ImportData}, | ||
33 | }; | 67 | }; |
34 | 68 | ||
35 | /// `ItemMap` is the result of module name resolution. It contains, for each | 69 | pub(crate) use self::raw::{RawItems, ImportId, ImportSourceMap}; |
36 | /// module, the set of visible items. | 70 | |
71 | pub use self::per_ns::{PerNs, Namespace}; | ||
72 | |||
73 | /// Contans all top-level defs from a macro-expanded crate | ||
37 | #[derive(Debug, PartialEq, Eq)] | 74 | #[derive(Debug, PartialEq, Eq)] |
38 | pub struct ItemMap { | 75 | pub struct CrateDefMap { |
76 | krate: Crate, | ||
39 | edition: Edition, | 77 | edition: Edition, |
40 | /// The prelude module for this crate. This either comes from an import | 78 | /// The prelude module for this crate. This either comes from an import |
41 | /// marked with the `prelude_import` attribute, or (in the normal case) from | 79 | /// marked with the `prelude_import` attribute, or (in the normal case) from |
42 | /// a dependency (`std` or `core`). | 80 | /// a dependency (`std` or `core`). |
43 | pub(crate) prelude: Option<Module>, | 81 | prelude: Option<Module>, |
44 | pub(crate) extern_prelude: FxHashMap<Name, ModuleDef>, | 82 | extern_prelude: FxHashMap<Name, ModuleDef>, |
45 | per_module: ArenaMap<ModuleId, ModuleScope>, | 83 | root: CrateModuleId, |
84 | modules: Arena<CrateModuleId, ModuleData>, | ||
85 | macros: Arena<CrateMacroId, mbe::MacroRules>, | ||
86 | public_macros: FxHashMap<Name, CrateMacroId>, | ||
87 | macro_resolutions: FxHashMap<MacroCallId, (Crate, CrateMacroId)>, | ||
88 | problems: CrateDefMapProblems, | ||
89 | } | ||
90 | |||
91 | impl std::ops::Index<CrateModuleId> for CrateDefMap { | ||
92 | type Output = ModuleData; | ||
93 | fn index(&self, id: CrateModuleId) -> &ModuleData { | ||
94 | &self.modules[id] | ||
95 | } | ||
96 | } | ||
97 | |||
98 | impl std::ops::Index<CrateMacroId> for CrateDefMap { | ||
99 | type Output = mbe::MacroRules; | ||
100 | fn index(&self, id: CrateMacroId) -> &mbe::MacroRules { | ||
101 | &self.macros[id] | ||
102 | } | ||
103 | } | ||
104 | |||
105 | /// An ID of a macro, **local** to a specific crate | ||
106 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
107 | pub(crate) struct CrateMacroId(RawId); | ||
108 | impl_arena_id!(CrateMacroId); | ||
109 | |||
110 | /// An ID of a module, **local** to a specific crate | ||
111 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
112 | pub(crate) struct CrateModuleId(RawId); | ||
113 | impl_arena_id!(CrateModuleId); | ||
114 | |||
115 | #[derive(Default, Debug, PartialEq, Eq)] | ||
116 | pub(crate) struct ModuleData { | ||
117 | pub(crate) parent: Option<CrateModuleId>, | ||
118 | pub(crate) children: FxHashMap<Name, CrateModuleId>, | ||
119 | pub(crate) scope: ModuleScope, | ||
120 | /// None for root | ||
121 | pub(crate) declaration: Option<SourceItemId>, | ||
122 | /// None for inline modules. | ||
123 | /// | ||
124 | /// Note that non-inline modules, by definition, live inside non-macro file. | ||
125 | pub(crate) definition: Option<FileId>, | ||
126 | } | ||
127 | |||
128 | #[derive(Default, Debug, PartialEq, Eq)] | ||
129 | pub(crate) struct CrateDefMapProblems { | ||
130 | problems: Vec<(SourceItemId, Problem)>, | ||
46 | } | 131 | } |
47 | 132 | ||
48 | impl std::ops::Index<ModuleId> for ItemMap { | 133 | impl CrateDefMapProblems { |
49 | type Output = ModuleScope; | 134 | fn add(&mut self, source_item_id: SourceItemId, problem: Problem) { |
50 | fn index(&self, id: ModuleId) -> &ModuleScope { | 135 | self.problems.push((source_item_id, problem)) |
51 | &self.per_module[id] | 136 | } |
137 | |||
138 | pub(crate) fn iter<'a>(&'a self) -> impl Iterator<Item = (&'a SourceItemId, &'a Problem)> + 'a { | ||
139 | self.problems.iter().map(|(s, p)| (s, p)) | ||
52 | } | 140 | } |
53 | } | 141 | } |
54 | 142 | ||
55 | #[derive(Debug, Default, PartialEq, Eq, Clone)] | 143 | #[derive(Debug, Default, PartialEq, Eq, Clone)] |
56 | pub struct ModuleScope { | 144 | pub struct ModuleScope { |
57 | pub(crate) items: FxHashMap<Name, Resolution>, | 145 | items: FxHashMap<Name, Resolution>, |
58 | } | 146 | } |
59 | 147 | ||
60 | impl ModuleScope { | 148 | impl ModuleScope { |
@@ -66,8 +154,6 @@ impl ModuleScope { | |||
66 | } | 154 | } |
67 | } | 155 | } |
68 | 156 | ||
69 | /// `Resolution` is basically `DefId` atm, but it should account for stuff like | ||
70 | /// multiple namespaces, ambiguity and errors. | ||
71 | #[derive(Debug, Clone, PartialEq, Eq, Default)] | 157 | #[derive(Debug, Clone, PartialEq, Eq, Default)] |
72 | pub struct Resolution { | 158 | pub struct Resolution { |
73 | /// None for unresolved | 159 | /// None for unresolved |
@@ -76,372 +162,6 @@ pub struct Resolution { | |||
76 | pub import: Option<ImportId>, | 162 | pub import: Option<ImportId>, |
77 | } | 163 | } |
78 | 164 | ||
79 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
80 | pub enum Namespace { | ||
81 | Types, | ||
82 | Values, | ||
83 | } | ||
84 | |||
85 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] | ||
86 | pub struct PerNs<T> { | ||
87 | pub types: Option<T>, | ||
88 | pub values: Option<T>, | ||
89 | } | ||
90 | |||
91 | impl<T> Default for PerNs<T> { | ||
92 | fn default() -> Self { | ||
93 | PerNs { types: None, values: None } | ||
94 | } | ||
95 | } | ||
96 | |||
97 | impl<T> PerNs<T> { | ||
98 | pub fn none() -> PerNs<T> { | ||
99 | PerNs { types: None, values: None } | ||
100 | } | ||
101 | |||
102 | pub fn values(t: T) -> PerNs<T> { | ||
103 | PerNs { types: None, values: Some(t) } | ||
104 | } | ||
105 | |||
106 | pub fn types(t: T) -> PerNs<T> { | ||
107 | PerNs { types: Some(t), values: None } | ||
108 | } | ||
109 | |||
110 | pub fn both(types: T, values: T) -> PerNs<T> { | ||
111 | PerNs { types: Some(types), values: Some(values) } | ||
112 | } | ||
113 | |||
114 | pub fn is_none(&self) -> bool { | ||
115 | self.types.is_none() && self.values.is_none() | ||
116 | } | ||
117 | |||
118 | pub fn is_both(&self) -> bool { | ||
119 | self.types.is_some() && self.values.is_some() | ||
120 | } | ||
121 | |||
122 | pub fn take(self, namespace: Namespace) -> Option<T> { | ||
123 | match namespace { | ||
124 | Namespace::Types => self.types, | ||
125 | Namespace::Values => self.values, | ||
126 | } | ||
127 | } | ||
128 | |||
129 | pub fn take_types(self) -> Option<T> { | ||
130 | self.take(Namespace::Types) | ||
131 | } | ||
132 | |||
133 | pub fn take_values(self) -> Option<T> { | ||
134 | self.take(Namespace::Values) | ||
135 | } | ||
136 | |||
137 | pub fn get(&self, namespace: Namespace) -> Option<&T> { | ||
138 | self.as_ref().take(namespace) | ||
139 | } | ||
140 | |||
141 | pub fn as_ref(&self) -> PerNs<&T> { | ||
142 | PerNs { types: self.types.as_ref(), values: self.values.as_ref() } | ||
143 | } | ||
144 | |||
145 | pub fn or(self, other: PerNs<T>) -> PerNs<T> { | ||
146 | PerNs { types: self.types.or(other.types), values: self.values.or(other.values) } | ||
147 | } | ||
148 | |||
149 | pub fn and_then<U>(self, f: impl Fn(T) -> Option<U>) -> PerNs<U> { | ||
150 | PerNs { types: self.types.and_then(&f), values: self.values.and_then(&f) } | ||
151 | } | ||
152 | |||
153 | pub fn map<U>(self, f: impl Fn(T) -> U) -> PerNs<U> { | ||
154 | PerNs { types: self.types.map(&f), values: self.values.map(&f) } | ||
155 | } | ||
156 | } | ||
157 | |||
158 | struct Resolver<'a, DB> { | ||
159 | db: &'a DB, | ||
160 | input: &'a FxHashMap<ModuleId, Arc<LoweredModule>>, | ||
161 | krate: Crate, | ||
162 | module_tree: Arc<ModuleTree>, | ||
163 | processed_imports: FxHashSet<(ModuleId, ImportId)>, | ||
164 | /// If module `a` has `use b::*`, then this contains the mapping b -> a (and the import) | ||
165 | glob_imports: FxHashMap<ModuleId, Vec<(ModuleId, ImportId)>>, | ||
166 | result: ItemMap, | ||
167 | } | ||
168 | |||
169 | impl<'a, DB> Resolver<'a, DB> | ||
170 | where | ||
171 | DB: PersistentHirDatabase, | ||
172 | { | ||
173 | fn new( | ||
174 | db: &'a DB, | ||
175 | input: &'a FxHashMap<ModuleId, Arc<LoweredModule>>, | ||
176 | krate: Crate, | ||
177 | ) -> Resolver<'a, DB> { | ||
178 | let module_tree = db.module_tree(krate); | ||
179 | Resolver { | ||
180 | db, | ||
181 | input, | ||
182 | krate, | ||
183 | module_tree, | ||
184 | processed_imports: FxHashSet::default(), | ||
185 | glob_imports: FxHashMap::default(), | ||
186 | result: ItemMap { | ||
187 | edition: krate.edition(db), | ||
188 | prelude: None, | ||
189 | extern_prelude: FxHashMap::default(), | ||
190 | per_module: ArenaMap::default(), | ||
191 | }, | ||
192 | } | ||
193 | } | ||
194 | |||
195 | pub(crate) fn resolve(mut self) -> ItemMap { | ||
196 | self.populate_extern_prelude(); | ||
197 | for (&module_id, items) in self.input.iter() { | ||
198 | self.populate_module(module_id, Arc::clone(items)); | ||
199 | } | ||
200 | |||
201 | let mut iter = 0; | ||
202 | loop { | ||
203 | iter += 1; | ||
204 | if iter > 1000 { | ||
205 | panic!("failed to reach fixedpoint after 1000 iters") | ||
206 | } | ||
207 | let processed_imports_count = self.processed_imports.len(); | ||
208 | for &module_id in self.input.keys() { | ||
209 | self.db.check_canceled(); | ||
210 | self.resolve_imports(module_id); | ||
211 | } | ||
212 | if processed_imports_count == self.processed_imports.len() { | ||
213 | // no new imports resolved | ||
214 | break; | ||
215 | } | ||
216 | } | ||
217 | self.result | ||
218 | } | ||
219 | |||
220 | fn populate_extern_prelude(&mut self) { | ||
221 | for dep in self.krate.dependencies(self.db) { | ||
222 | log::debug!("crate dep {:?} -> {:?}", dep.name, dep.krate); | ||
223 | if let Some(module) = dep.krate.root_module(self.db) { | ||
224 | self.result.extern_prelude.insert(dep.name.clone(), module.into()); | ||
225 | } | ||
226 | // look for the prelude | ||
227 | if self.result.prelude.is_none() { | ||
228 | let item_map = self.db.item_map(dep.krate); | ||
229 | if item_map.prelude.is_some() { | ||
230 | self.result.prelude = item_map.prelude; | ||
231 | } | ||
232 | } | ||
233 | } | ||
234 | } | ||
235 | |||
236 | fn populate_module(&mut self, module_id: ModuleId, input: Arc<LoweredModule>) { | ||
237 | let mut module_items = ModuleScope::default(); | ||
238 | for (import_id, import_data) in input.imports.iter() { | ||
239 | if let Some(last_segment) = import_data.path.segments.iter().last() { | ||
240 | if !import_data.is_glob { | ||
241 | let name = | ||
242 | import_data.alias.clone().unwrap_or_else(|| last_segment.name.clone()); | ||
243 | module_items | ||
244 | .items | ||
245 | .insert(name, Resolution { def: PerNs::none(), import: Some(import_id) }); | ||
246 | } | ||
247 | } | ||
248 | } | ||
249 | // Populate explicitly declared items, except modules | ||
250 | for (name, &def) in input.declarations.iter() { | ||
251 | let resolution = Resolution { def, import: None }; | ||
252 | module_items.items.insert(name.clone(), resolution); | ||
253 | } | ||
254 | |||
255 | // Populate modules | ||
256 | for (name, module_id) in module_id.children(&self.module_tree) { | ||
257 | let module = Module { module_id, krate: self.krate }; | ||
258 | self.add_module_item(&mut module_items, name, PerNs::types(module.into())); | ||
259 | } | ||
260 | |||
261 | self.result.per_module.insert(module_id, module_items); | ||
262 | } | ||
263 | |||
264 | fn add_module_item(&self, module_items: &mut ModuleScope, name: Name, def: PerNs<ModuleDef>) { | ||
265 | let resolution = Resolution { def, import: None }; | ||
266 | module_items.items.insert(name, resolution); | ||
267 | } | ||
268 | |||
269 | fn resolve_imports(&mut self, module_id: ModuleId) { | ||
270 | for (import_id, import_data) in self.input[&module_id].imports.iter() { | ||
271 | if self.processed_imports.contains(&(module_id, import_id)) { | ||
272 | // already done | ||
273 | continue; | ||
274 | } | ||
275 | if self.resolve_import(module_id, import_id, import_data) == ReachedFixedPoint::Yes { | ||
276 | log::debug!("import {:?} resolved (or definite error)", import_id); | ||
277 | self.processed_imports.insert((module_id, import_id)); | ||
278 | } | ||
279 | } | ||
280 | } | ||
281 | |||
282 | fn resolve_import( | ||
283 | &mut self, | ||
284 | module_id: ModuleId, | ||
285 | import_id: ImportId, | ||
286 | import: &ImportData, | ||
287 | ) -> ReachedFixedPoint { | ||
288 | log::debug!("resolving import: {:?} ({:?})", import, self.result.edition); | ||
289 | let original_module = Module { krate: self.krate, module_id }; | ||
290 | |||
291 | let (def, reached_fixedpoint) = if import.is_extern_crate { | ||
292 | let res = self.result.resolve_name_in_extern_prelude( | ||
293 | &import | ||
294 | .path | ||
295 | .as_ident() | ||
296 | .expect("extern crate should have been desugared to one-element path"), | ||
297 | ); | ||
298 | (res, if res.is_none() { ReachedFixedPoint::No } else { ReachedFixedPoint::Yes }) | ||
299 | } else { | ||
300 | let res = self.result.resolve_path_fp( | ||
301 | self.db, | ||
302 | ResolveMode::Import, | ||
303 | original_module, | ||
304 | &import.path, | ||
305 | ); | ||
306 | |||
307 | (res.resolved_def, res.reached_fixedpoint) | ||
308 | }; | ||
309 | |||
310 | if reached_fixedpoint != ReachedFixedPoint::Yes { | ||
311 | return reached_fixedpoint; | ||
312 | } | ||
313 | |||
314 | if import.is_glob { | ||
315 | log::debug!("glob import: {:?}", import); | ||
316 | match def.take_types() { | ||
317 | Some(ModuleDef::Module(m)) => { | ||
318 | if import.is_prelude { | ||
319 | tested_by!(std_prelude); | ||
320 | self.result.prelude = Some(m); | ||
321 | } else if m.krate != self.krate { | ||
322 | tested_by!(glob_across_crates); | ||
323 | // glob import from other crate => we can just import everything once | ||
324 | let item_map = self.db.item_map(m.krate); | ||
325 | let scope = &item_map[m.module_id]; | ||
326 | let items = scope | ||
327 | .items | ||
328 | .iter() | ||
329 | .map(|(name, res)| (name.clone(), res.clone())) | ||
330 | .collect::<Vec<_>>(); | ||
331 | self.update(module_id, Some(import_id), &items); | ||
332 | } else { | ||
333 | // glob import from same crate => we do an initial | ||
334 | // import, and then need to propagate any further | ||
335 | // additions | ||
336 | let scope = &self.result[m.module_id]; | ||
337 | let items = scope | ||
338 | .items | ||
339 | .iter() | ||
340 | .map(|(name, res)| (name.clone(), res.clone())) | ||
341 | .collect::<Vec<_>>(); | ||
342 | self.update(module_id, Some(import_id), &items); | ||
343 | // record the glob import in case we add further items | ||
344 | self.glob_imports | ||
345 | .entry(m.module_id) | ||
346 | .or_default() | ||
347 | .push((module_id, import_id)); | ||
348 | } | ||
349 | } | ||
350 | Some(ModuleDef::Enum(e)) => { | ||
351 | tested_by!(glob_enum); | ||
352 | // glob import from enum => just import all the variants | ||
353 | let variants = e.variants(self.db); | ||
354 | let resolutions = variants | ||
355 | .into_iter() | ||
356 | .filter_map(|variant| { | ||
357 | let res = Resolution { | ||
358 | def: PerNs::both(variant.into(), variant.into()), | ||
359 | import: Some(import_id), | ||
360 | }; | ||
361 | let name = variant.name(self.db)?; | ||
362 | Some((name, res)) | ||
363 | }) | ||
364 | .collect::<Vec<_>>(); | ||
365 | self.update(module_id, Some(import_id), &resolutions); | ||
366 | } | ||
367 | Some(d) => { | ||
368 | log::debug!("glob import {:?} from non-module/enum {:?}", import, d); | ||
369 | } | ||
370 | None => { | ||
371 | log::debug!("glob import {:?} didn't resolve as type", import); | ||
372 | } | ||
373 | } | ||
374 | } else { | ||
375 | let last_segment = import.path.segments.last().unwrap(); | ||
376 | let name = import.alias.clone().unwrap_or_else(|| last_segment.name.clone()); | ||
377 | log::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def); | ||
378 | |||
379 | // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658 | ||
380 | if let Some(root_module) = self.krate.root_module(self.db) { | ||
381 | if import.is_extern_crate && module_id == root_module.module_id { | ||
382 | if let Some(def) = def.take_types() { | ||
383 | self.result.extern_prelude.insert(name.clone(), def); | ||
384 | } | ||
385 | } | ||
386 | } | ||
387 | let resolution = Resolution { def, import: Some(import_id) }; | ||
388 | self.update(module_id, None, &[(name, resolution)]); | ||
389 | } | ||
390 | reached_fixedpoint | ||
391 | } | ||
392 | |||
393 | fn update( | ||
394 | &mut self, | ||
395 | module_id: ModuleId, | ||
396 | import: Option<ImportId>, | ||
397 | resolutions: &[(Name, Resolution)], | ||
398 | ) { | ||
399 | self.update_recursive(module_id, import, resolutions, 0) | ||
400 | } | ||
401 | |||
402 | fn update_recursive( | ||
403 | &mut self, | ||
404 | module_id: ModuleId, | ||
405 | import: Option<ImportId>, | ||
406 | resolutions: &[(Name, Resolution)], | ||
407 | depth: usize, | ||
408 | ) { | ||
409 | if depth > 100 { | ||
410 | // prevent stack overflows (but this shouldn't be possible) | ||
411 | panic!("infinite recursion in glob imports!"); | ||
412 | } | ||
413 | let module_items = self.result.per_module.get_mut(module_id).unwrap(); | ||
414 | let mut changed = false; | ||
415 | for (name, res) in resolutions { | ||
416 | let existing = module_items.items.entry(name.clone()).or_default(); | ||
417 | if existing.def.types.is_none() && res.def.types.is_some() { | ||
418 | existing.def.types = res.def.types; | ||
419 | existing.import = import.or(res.import); | ||
420 | changed = true; | ||
421 | } | ||
422 | if existing.def.values.is_none() && res.def.values.is_some() { | ||
423 | existing.def.values = res.def.values; | ||
424 | existing.import = import.or(res.import); | ||
425 | changed = true; | ||
426 | } | ||
427 | } | ||
428 | if !changed { | ||
429 | return; | ||
430 | } | ||
431 | let glob_imports = self | ||
432 | .glob_imports | ||
433 | .get(&module_id) | ||
434 | .into_iter() | ||
435 | .flat_map(|v| v.iter()) | ||
436 | .cloned() | ||
437 | .collect::<Vec<_>>(); | ||
438 | for (glob_importing_module, glob_import) in glob_imports { | ||
439 | // We pass the glob import so that the tracked import in those modules is that glob import | ||
440 | self.update_recursive(glob_importing_module, Some(glob_import), resolutions, depth + 1); | ||
441 | } | ||
442 | } | ||
443 | } | ||
444 | |||
445 | #[derive(Debug, Clone)] | 165 | #[derive(Debug, Clone)] |
446 | struct ResolvePathResult { | 166 | struct ResolvePathResult { |
447 | resolved_def: PerNs<ModuleDef>, | 167 | resolved_def: PerNs<ModuleDef>, |
@@ -475,84 +195,85 @@ enum ReachedFixedPoint { | |||
475 | No, | 195 | No, |
476 | } | 196 | } |
477 | 197 | ||
478 | impl ItemMap { | 198 | impl CrateDefMap { |
479 | pub(crate) fn item_map_query(db: &impl PersistentHirDatabase, krate: Crate) -> Arc<ItemMap> { | 199 | pub(crate) fn crate_def_map_query( |
480 | let start = time::Instant::now(); | 200 | db: &impl PersistentHirDatabase, |
481 | let module_tree = db.module_tree(krate); | 201 | krate: Crate, |
482 | let input = module_tree | 202 | ) -> Arc<CrateDefMap> { |
483 | .modules() | 203 | let start = std::time::Instant::now(); |
484 | .map(|module_id| (module_id, db.lower_module(Module { krate, module_id }))) | 204 | let def_map = { |
485 | .collect::<FxHashMap<_, _>>(); | 205 | let edition = krate.edition(db); |
486 | 206 | let mut modules: Arena<CrateModuleId, ModuleData> = Arena::default(); | |
487 | let resolver = Resolver::new(db, &input, krate); | 207 | let root = modules.alloc(ModuleData::default()); |
488 | let res = resolver.resolve(); | 208 | CrateDefMap { |
489 | let elapsed = start.elapsed(); | 209 | krate, |
490 | log::info!("item_map: {:?}", elapsed); | 210 | edition, |
491 | Arc::new(res) | 211 | extern_prelude: FxHashMap::default(), |
212 | prelude: None, | ||
213 | root, | ||
214 | modules, | ||
215 | macros: Arena::default(), | ||
216 | public_macros: FxHashMap::default(), | ||
217 | macro_resolutions: FxHashMap::default(), | ||
218 | problems: CrateDefMapProblems::default(), | ||
219 | } | ||
220 | }; | ||
221 | let def_map = collector::collect_defs(db, def_map); | ||
222 | log::info!("crate_def_map_query: {:?}", start.elapsed()); | ||
223 | Arc::new(def_map) | ||
492 | } | 224 | } |
493 | 225 | ||
494 | pub(crate) fn resolve_path( | 226 | pub(crate) fn root(&self) -> CrateModuleId { |
495 | &self, | 227 | self.root |
496 | db: &impl PersistentHirDatabase, | ||
497 | original_module: Module, | ||
498 | path: &Path, | ||
499 | ) -> (PerNs<ModuleDef>, Option<usize>) { | ||
500 | let res = self.resolve_path_fp(db, ResolveMode::Other, original_module, path); | ||
501 | (res.resolved_def, res.segment_index) | ||
502 | } | 228 | } |
503 | 229 | ||
504 | fn resolve_in_prelude( | 230 | pub(crate) fn problems(&self) -> &CrateDefMapProblems { |
505 | &self, | 231 | &self.problems |
506 | db: &impl PersistentHirDatabase, | ||
507 | original_module: Module, | ||
508 | name: &Name, | ||
509 | ) -> PerNs<ModuleDef> { | ||
510 | if let Some(prelude) = self.prelude { | ||
511 | let resolution = if prelude.krate == original_module.krate { | ||
512 | self[prelude.module_id].items.get(name).cloned() | ||
513 | } else { | ||
514 | db.item_map(prelude.krate)[prelude.module_id].items.get(name).cloned() | ||
515 | }; | ||
516 | resolution.map(|r| r.def).unwrap_or_else(PerNs::none) | ||
517 | } else { | ||
518 | PerNs::none() | ||
519 | } | ||
520 | } | 232 | } |
521 | 233 | ||
522 | pub(crate) fn resolve_name_in_module( | 234 | pub(crate) fn mk_module(&self, module_id: CrateModuleId) -> Module { |
523 | &self, | 235 | Module { krate: self.krate, module_id } |
524 | db: &impl PersistentHirDatabase, | 236 | } |
525 | module: Module, | ||
526 | name: &Name, | ||
527 | ) -> PerNs<ModuleDef> { | ||
528 | // Resolve in: | ||
529 | // - current module / scope | ||
530 | // - extern prelude | ||
531 | // - std prelude | ||
532 | let from_scope = self[module.module_id].items.get(name).map_or(PerNs::none(), |it| it.def); | ||
533 | let from_extern_prelude = | ||
534 | self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it)); | ||
535 | let from_prelude = self.resolve_in_prelude(db, module, name); | ||
536 | 237 | ||
537 | from_scope.or(from_extern_prelude).or(from_prelude) | 238 | pub(crate) fn prelude(&self) -> Option<Module> { |
239 | self.prelude | ||
538 | } | 240 | } |
539 | 241 | ||
540 | fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs<ModuleDef> { | 242 | pub(crate) fn extern_prelude(&self) -> &FxHashMap<Name, ModuleDef> { |
541 | self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it)) | 243 | &self.extern_prelude |
542 | } | 244 | } |
543 | 245 | ||
544 | fn resolve_name_in_crate_root_or_extern_prelude( | 246 | pub(crate) fn resolve_macro( |
545 | &self, | 247 | &self, |
546 | db: &impl PersistentHirDatabase, | 248 | macro_call_id: MacroCallId, |
547 | module: Module, | 249 | ) -> Option<(Crate, CrateMacroId)> { |
548 | name: &Name, | 250 | self.macro_resolutions.get(¯o_call_id).map(|&it| it) |
549 | ) -> PerNs<ModuleDef> { | 251 | } |
550 | let crate_root = module.crate_root(db); | ||
551 | let from_crate_root = | ||
552 | self[crate_root.module_id].items.get(name).map_or(PerNs::none(), |it| it.def); | ||
553 | let from_extern_prelude = self.resolve_name_in_extern_prelude(name); | ||
554 | 252 | ||
555 | from_crate_root.or(from_extern_prelude) | 253 | pub(crate) fn find_module_by_source( |
254 | &self, | ||
255 | file_id: HirFileId, | ||
256 | decl_id: Option<SourceFileItemId>, | ||
257 | ) -> Option<CrateModuleId> { | ||
258 | let decl_id = decl_id.map(|it| it.with_file_id(file_id)); | ||
259 | let (module_id, _module_data) = self.modules.iter().find(|(_module_id, module_data)| { | ||
260 | if decl_id.is_some() { | ||
261 | module_data.declaration == decl_id | ||
262 | } else { | ||
263 | module_data.definition.map(|it| it.into()) == Some(file_id) | ||
264 | } | ||
265 | })?; | ||
266 | Some(module_id) | ||
267 | } | ||
268 | |||
269 | pub(crate) fn resolve_path( | ||
270 | &self, | ||
271 | db: &impl PersistentHirDatabase, | ||
272 | original_module: CrateModuleId, | ||
273 | path: &Path, | ||
274 | ) -> (PerNs<ModuleDef>, Option<usize>) { | ||
275 | let res = self.resolve_path_fp(db, ResolveMode::Other, original_module, path); | ||
276 | (res.resolved_def, res.segment_index) | ||
556 | } | 277 | } |
557 | 278 | ||
558 | // Returns Yes if we are sure that additions to `ItemMap` wouldn't change | 279 | // Returns Yes if we are sure that additions to `ItemMap` wouldn't change |
@@ -561,13 +282,17 @@ impl ItemMap { | |||
561 | &self, | 282 | &self, |
562 | db: &impl PersistentHirDatabase, | 283 | db: &impl PersistentHirDatabase, |
563 | mode: ResolveMode, | 284 | mode: ResolveMode, |
564 | original_module: Module, | 285 | original_module: CrateModuleId, |
565 | path: &Path, | 286 | path: &Path, |
566 | ) -> ResolvePathResult { | 287 | ) -> ResolvePathResult { |
567 | let mut segments = path.segments.iter().enumerate(); | 288 | let mut segments = path.segments.iter().enumerate(); |
568 | let mut curr_per_ns: PerNs<ModuleDef> = match path.kind { | 289 | let mut curr_per_ns: PerNs<ModuleDef> = match path.kind { |
569 | PathKind::Crate => PerNs::types(original_module.crate_root(db).into()), | 290 | PathKind::Crate => { |
570 | PathKind::Self_ => PerNs::types(original_module.into()), | 291 | PerNs::types(Module { krate: self.krate, module_id: self.root }.into()) |
292 | } | ||
293 | PathKind::Self_ => { | ||
294 | PerNs::types(Module { krate: self.krate, module_id: original_module }.into()) | ||
295 | } | ||
571 | // plain import or absolute path in 2015: crate-relative with | 296 | // plain import or absolute path in 2015: crate-relative with |
572 | // fallback to extern prelude (with the simplification in | 297 | // fallback to extern prelude (with the simplification in |
573 | // rust-lang/rust#57745) | 298 | // rust-lang/rust#57745) |
@@ -581,11 +306,7 @@ impl ItemMap { | |||
581 | None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), | 306 | None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), |
582 | }; | 307 | }; |
583 | log::debug!("resolving {:?} in crate root (+ extern prelude)", segment); | 308 | log::debug!("resolving {:?} in crate root (+ extern prelude)", segment); |
584 | self.resolve_name_in_crate_root_or_extern_prelude( | 309 | self.resolve_name_in_crate_root_or_extern_prelude(&segment.name) |
585 | db, | ||
586 | original_module, | ||
587 | &segment.name, | ||
588 | ) | ||
589 | } | 310 | } |
590 | PathKind::Plain => { | 311 | PathKind::Plain => { |
591 | let segment = match segments.next() { | 312 | let segment = match segments.next() { |
@@ -596,8 +317,8 @@ impl ItemMap { | |||
596 | self.resolve_name_in_module(db, original_module, &segment.name) | 317 | self.resolve_name_in_module(db, original_module, &segment.name) |
597 | } | 318 | } |
598 | PathKind::Super => { | 319 | PathKind::Super => { |
599 | if let Some(p) = original_module.parent(db) { | 320 | if let Some(p) = self.modules[original_module].parent { |
600 | PerNs::types(p.into()) | 321 | PerNs::types(Module { krate: self.krate, module_id: p }.into()) |
601 | } else { | 322 | } else { |
602 | log::debug!("super path in root module"); | 323 | log::debug!("super path in root module"); |
603 | return ResolvePathResult::empty(ReachedFixedPoint::Yes); | 324 | return ResolvePathResult::empty(ReachedFixedPoint::Yes); |
@@ -634,14 +355,14 @@ impl ItemMap { | |||
634 | 355 | ||
635 | curr_per_ns = match curr { | 356 | curr_per_ns = match curr { |
636 | ModuleDef::Module(module) => { | 357 | ModuleDef::Module(module) => { |
637 | if module.krate != original_module.krate { | 358 | if module.krate != self.krate { |
638 | let path = Path { | 359 | let path = Path { |
639 | segments: path.segments[i..].iter().cloned().collect(), | 360 | segments: path.segments[i..].iter().cloned().collect(), |
640 | kind: PathKind::Self_, | 361 | kind: PathKind::Self_, |
641 | }; | 362 | }; |
642 | log::debug!("resolving {:?} in other crate", path); | 363 | log::debug!("resolving {:?} in other crate", path); |
643 | let item_map = db.item_map(module.krate); | 364 | let defp_map = db.crate_def_map(module.krate); |
644 | let (def, s) = item_map.resolve_path(db, *module, &path); | 365 | let (def, s) = defp_map.resolve_path(db, module.module_id, &path); |
645 | return ResolvePathResult::with( | 366 | return ResolvePathResult::with( |
646 | def, | 367 | def, |
647 | ReachedFixedPoint::Yes, | 368 | ReachedFixedPoint::Yes, |
@@ -649,7 +370,7 @@ impl ItemMap { | |||
649 | ); | 370 | ); |
650 | } | 371 | } |
651 | 372 | ||
652 | match self[module.module_id].items.get(&segment.name) { | 373 | match self[module.module_id].scope.items.get(&segment.name) { |
653 | Some(res) if !res.def.is_none() => res.def, | 374 | Some(res) if !res.def.is_none() => res.def, |
654 | _ => { | 375 | _ => { |
655 | log::debug!("path segment {:?} not found", segment.name); | 376 | log::debug!("path segment {:?} not found", segment.name); |
@@ -659,7 +380,7 @@ impl ItemMap { | |||
659 | } | 380 | } |
660 | ModuleDef::Enum(e) => { | 381 | ModuleDef::Enum(e) => { |
661 | // enum variant | 382 | // enum variant |
662 | tested_by!(item_map_enum_importing); | 383 | tested_by!(can_import_enum_variant); |
663 | match e.variant(db, &segment.name) { | 384 | match e.variant(db, &segment.name) { |
664 | Some(variant) => PerNs::both(variant.into(), variant.into()), | 385 | Some(variant) => PerNs::both(variant.into(), variant.into()), |
665 | None => { | 386 | None => { |
@@ -690,7 +411,47 @@ impl ItemMap { | |||
690 | } | 411 | } |
691 | ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None) | 412 | ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None) |
692 | } | 413 | } |
693 | } | ||
694 | 414 | ||
695 | #[cfg(test)] | 415 | fn resolve_name_in_crate_root_or_extern_prelude(&self, name: &Name) -> PerNs<ModuleDef> { |
696 | mod tests; | 416 | let from_crate_root = |
417 | self[self.root].scope.items.get(name).map_or(PerNs::none(), |it| it.def); | ||
418 | let from_extern_prelude = self.resolve_name_in_extern_prelude(name); | ||
419 | |||
420 | from_crate_root.or(from_extern_prelude) | ||
421 | } | ||
422 | |||
423 | pub(crate) fn resolve_name_in_module( | ||
424 | &self, | ||
425 | db: &impl PersistentHirDatabase, | ||
426 | module: CrateModuleId, | ||
427 | name: &Name, | ||
428 | ) -> PerNs<ModuleDef> { | ||
429 | // Resolve in: | ||
430 | // - current module / scope | ||
431 | // - extern prelude | ||
432 | // - std prelude | ||
433 | let from_scope = self[module].scope.items.get(name).map_or(PerNs::none(), |it| it.def); | ||
434 | let from_extern_prelude = | ||
435 | self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it)); | ||
436 | let from_prelude = self.resolve_in_prelude(db, name); | ||
437 | |||
438 | from_scope.or(from_extern_prelude).or(from_prelude) | ||
439 | } | ||
440 | |||
441 | fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs<ModuleDef> { | ||
442 | self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it)) | ||
443 | } | ||
444 | |||
445 | fn resolve_in_prelude(&self, db: &impl PersistentHirDatabase, name: &Name) -> PerNs<ModuleDef> { | ||
446 | if let Some(prelude) = self.prelude { | ||
447 | let resolution = if prelude.krate == self.krate { | ||
448 | self[prelude.module_id].scope.items.get(name).cloned() | ||
449 | } else { | ||
450 | db.crate_def_map(prelude.krate)[prelude.module_id].scope.items.get(name).cloned() | ||
451 | }; | ||
452 | resolution.map(|r| r.def).unwrap_or_else(PerNs::none) | ||
453 | } else { | ||
454 | PerNs::none() | ||
455 | } | ||
456 | } | ||
457 | } | ||