diff options
Diffstat (limited to 'crates/ra_hir/src/nameres/crate_def_map.rs')
-rw-r--r-- | crates/ra_hir/src/nameres/crate_def_map.rs | 368 |
1 files changed, 0 insertions, 368 deletions
diff --git a/crates/ra_hir/src/nameres/crate_def_map.rs b/crates/ra_hir/src/nameres/crate_def_map.rs deleted file mode 100644 index cc4955053..000000000 --- a/crates/ra_hir/src/nameres/crate_def_map.rs +++ /dev/null | |||
@@ -1,368 +0,0 @@ | |||
1 | /// This module implements new import-resolution/macro expansion algorithm. | ||
2 | /// | ||
3 | /// The result of this module is `CrateDefMap`: a datastructure which contains: | ||
4 | /// | ||
5 | /// * a tree of modules for the crate | ||
6 | /// * for each module, a set of items visible in the module (directly declared | ||
7 | /// or imported) | ||
8 | /// | ||
9 | /// Note that `CrateDefMap` contains fully macro expanded code. | ||
10 | /// | ||
11 | /// Computing `CrateDefMap` can be partitioned into several logically | ||
12 | /// independent "phases". The phases are mutually recursive though, there's no | ||
13 | /// stric ordering. | ||
14 | /// | ||
15 | /// ## Collecting RawItems | ||
16 | /// | ||
17 | /// This happens in the `raw` module, which parses a single source file into a | ||
18 | /// set of top-level items. Nested importa are desugared to flat imports in | ||
19 | /// this phase. Macro calls are represented as a triple of (Path, Option<Name>, | ||
20 | /// TokenTree). | ||
21 | /// | ||
22 | /// ## Collecting Modules | ||
23 | /// | ||
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 unresovled imports and macros. | ||
28 | /// | ||
29 | /// While we walk tree of modules, we also record macro_rules defenitions and | ||
30 | /// expand calls to macro_rules defined macros. | ||
31 | /// | ||
32 | /// ## Resolving Imports | ||
33 | /// | ||
34 | /// TBD | ||
35 | /// | ||
36 | /// ## Resolving Macros | ||
37 | /// | ||
38 | /// While macro_rules from the same crate use a global mutable namespace, macros | ||
39 | /// from other crates (including proc-macros) can be used with `foo::bar!` | ||
40 | /// syntax. | ||
41 | /// | ||
42 | /// TBD; | ||
43 | |||
44 | mod raw; | ||
45 | mod collector; | ||
46 | #[cfg(test)] | ||
47 | mod tests; | ||
48 | |||
49 | use rustc_hash::FxHashMap; | ||
50 | use test_utils::tested_by; | ||
51 | use ra_arena::{Arena, RawId, impl_arena_id}; | ||
52 | use ra_db::FileId; | ||
53 | |||
54 | use std::sync::Arc; | ||
55 | |||
56 | use crate::{ | ||
57 | Name, Module, Path, PathKind, ModuleDef, Crate, Problem, HirFileId, | ||
58 | PersistentHirDatabase, | ||
59 | nameres::{ModuleScope, ResolveMode, ResolvePathResult, PerNs, Edition, ReachedFixedPoint}, | ||
60 | ids::{SourceItemId, SourceFileItemId}, | ||
61 | }; | ||
62 | |||
63 | pub(crate) use self::raw::RawItems; | ||
64 | |||
65 | #[derive(Default, Debug, PartialEq, Eq)] | ||
66 | pub(crate) struct ModuleData { | ||
67 | pub(crate) parent: Option<ModuleId>, | ||
68 | pub(crate) children: FxHashMap<Name, ModuleId>, | ||
69 | pub(crate) scope: ModuleScope, | ||
70 | /// None for root | ||
71 | pub(crate) declaration: Option<SourceItemId>, | ||
72 | /// None for inline modules. | ||
73 | /// | ||
74 | /// Note that non-inline modules, by definition, live inside non-macro file. | ||
75 | pub(crate) definition: Option<FileId>, | ||
76 | } | ||
77 | |||
78 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
79 | pub(crate) struct ModuleId(RawId); | ||
80 | impl_arena_id!(ModuleId); | ||
81 | |||
82 | /// Contans all top-level defs from a macro-expanded crate | ||
83 | #[derive(Debug, PartialEq, Eq)] | ||
84 | pub struct CrateDefMap { | ||
85 | krate: Crate, | ||
86 | edition: Edition, | ||
87 | /// The prelude module for this crate. This either comes from an import | ||
88 | /// marked with the `prelude_import` attribute, or (in the normal case) from | ||
89 | /// a dependency (`std` or `core`). | ||
90 | prelude: Option<Module>, | ||
91 | extern_prelude: FxHashMap<Name, ModuleDef>, | ||
92 | root: ModuleId, | ||
93 | modules: Arena<ModuleId, ModuleData>, | ||
94 | public_macros: FxHashMap<Name, mbe::MacroRules>, | ||
95 | problems: CrateDefMapProblems, | ||
96 | } | ||
97 | |||
98 | #[derive(Default, Debug, PartialEq, Eq)] | ||
99 | pub(crate) struct CrateDefMapProblems { | ||
100 | problems: Vec<(SourceItemId, Problem)>, | ||
101 | } | ||
102 | |||
103 | impl CrateDefMapProblems { | ||
104 | fn add(&mut self, source_item_id: SourceItemId, problem: Problem) { | ||
105 | self.problems.push((source_item_id, problem)) | ||
106 | } | ||
107 | |||
108 | pub(crate) fn iter<'a>(&'a self) -> impl Iterator<Item = (&'a SourceItemId, &'a Problem)> + 'a { | ||
109 | self.problems.iter().map(|(s, p)| (s, p)) | ||
110 | } | ||
111 | } | ||
112 | |||
113 | impl std::ops::Index<ModuleId> for CrateDefMap { | ||
114 | type Output = ModuleData; | ||
115 | fn index(&self, id: ModuleId) -> &ModuleData { | ||
116 | &self.modules[id] | ||
117 | } | ||
118 | } | ||
119 | |||
120 | impl CrateDefMap { | ||
121 | pub(crate) fn crate_def_map_query( | ||
122 | db: &impl PersistentHirDatabase, | ||
123 | krate: Crate, | ||
124 | ) -> Arc<CrateDefMap> { | ||
125 | let def_map = { | ||
126 | let edition = krate.edition(db); | ||
127 | let mut modules: Arena<ModuleId, ModuleData> = Arena::default(); | ||
128 | let root = modules.alloc(ModuleData::default()); | ||
129 | CrateDefMap { | ||
130 | krate, | ||
131 | edition, | ||
132 | extern_prelude: FxHashMap::default(), | ||
133 | prelude: None, | ||
134 | root, | ||
135 | modules, | ||
136 | public_macros: FxHashMap::default(), | ||
137 | problems: CrateDefMapProblems::default(), | ||
138 | } | ||
139 | }; | ||
140 | let def_map = collector::collect_defs(db, def_map); | ||
141 | Arc::new(def_map) | ||
142 | } | ||
143 | |||
144 | pub(crate) fn root(&self) -> ModuleId { | ||
145 | self.root | ||
146 | } | ||
147 | |||
148 | pub(crate) fn problems(&self) -> &CrateDefMapProblems { | ||
149 | &self.problems | ||
150 | } | ||
151 | |||
152 | pub(crate) fn mk_module(&self, module_id: ModuleId) -> Module { | ||
153 | Module { krate: self.krate, module_id } | ||
154 | } | ||
155 | |||
156 | pub(crate) fn prelude(&self) -> Option<Module> { | ||
157 | self.prelude | ||
158 | } | ||
159 | |||
160 | pub(crate) fn extern_prelude(&self) -> &FxHashMap<Name, ModuleDef> { | ||
161 | &self.extern_prelude | ||
162 | } | ||
163 | |||
164 | pub(crate) fn find_module_by_source( | ||
165 | &self, | ||
166 | file_id: HirFileId, | ||
167 | decl_id: Option<SourceFileItemId>, | ||
168 | ) -> Option<ModuleId> { | ||
169 | let decl_id = decl_id.map(|it| it.with_file_id(file_id)); | ||
170 | let (module_id, _module_data) = self.modules.iter().find(|(_module_id, module_data)| { | ||
171 | if decl_id.is_some() { | ||
172 | module_data.declaration == decl_id | ||
173 | } else { | ||
174 | module_data.definition.map(|it| it.into()) == Some(file_id) | ||
175 | } | ||
176 | })?; | ||
177 | Some(module_id) | ||
178 | } | ||
179 | |||
180 | pub(crate) fn resolve_path( | ||
181 | &self, | ||
182 | db: &impl PersistentHirDatabase, | ||
183 | original_module: ModuleId, | ||
184 | path: &Path, | ||
185 | ) -> (PerNs<ModuleDef>, Option<usize>) { | ||
186 | let res = self.resolve_path_fp(db, ResolveMode::Other, original_module, path); | ||
187 | (res.resolved_def, res.segment_index) | ||
188 | } | ||
189 | |||
190 | // Returns Yes if we are sure that additions to `ItemMap` wouldn't change | ||
191 | // the result. | ||
192 | fn resolve_path_fp( | ||
193 | &self, | ||
194 | db: &impl PersistentHirDatabase, | ||
195 | mode: ResolveMode, | ||
196 | original_module: ModuleId, | ||
197 | path: &Path, | ||
198 | ) -> ResolvePathResult { | ||
199 | let mut segments = path.segments.iter().enumerate(); | ||
200 | let mut curr_per_ns: PerNs<ModuleDef> = match path.kind { | ||
201 | PathKind::Crate => { | ||
202 | PerNs::types(Module { krate: self.krate, module_id: self.root }.into()) | ||
203 | } | ||
204 | PathKind::Self_ => { | ||
205 | PerNs::types(Module { krate: self.krate, module_id: original_module }.into()) | ||
206 | } | ||
207 | // plain import or absolute path in 2015: crate-relative with | ||
208 | // fallback to extern prelude (with the simplification in | ||
209 | // rust-lang/rust#57745) | ||
210 | // TODO there must be a nicer way to write this condition | ||
211 | PathKind::Plain | PathKind::Abs | ||
212 | if self.edition == Edition::Edition2015 | ||
213 | && (path.kind == PathKind::Abs || mode == ResolveMode::Import) => | ||
214 | { | ||
215 | let segment = match segments.next() { | ||
216 | Some((_, segment)) => segment, | ||
217 | None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), | ||
218 | }; | ||
219 | log::debug!("resolving {:?} in crate root (+ extern prelude)", segment); | ||
220 | self.resolve_name_in_crate_root_or_extern_prelude(&segment.name) | ||
221 | } | ||
222 | PathKind::Plain => { | ||
223 | let segment = match segments.next() { | ||
224 | Some((_, segment)) => segment, | ||
225 | None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), | ||
226 | }; | ||
227 | log::debug!("resolving {:?} in module", segment); | ||
228 | self.resolve_name_in_module(db, original_module, &segment.name) | ||
229 | } | ||
230 | PathKind::Super => { | ||
231 | if let Some(p) = self.modules[original_module].parent { | ||
232 | PerNs::types(Module { krate: self.krate, module_id: p }.into()) | ||
233 | } else { | ||
234 | log::debug!("super path in root module"); | ||
235 | return ResolvePathResult::empty(ReachedFixedPoint::Yes); | ||
236 | } | ||
237 | } | ||
238 | PathKind::Abs => { | ||
239 | // 2018-style absolute path -- only extern prelude | ||
240 | let segment = match segments.next() { | ||
241 | Some((_, segment)) => segment, | ||
242 | None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), | ||
243 | }; | ||
244 | if let Some(def) = self.extern_prelude.get(&segment.name) { | ||
245 | log::debug!("absolute path {:?} resolved to crate {:?}", path, def); | ||
246 | PerNs::types(*def) | ||
247 | } else { | ||
248 | return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude | ||
249 | } | ||
250 | } | ||
251 | }; | ||
252 | |||
253 | for (i, segment) in segments { | ||
254 | let curr = match curr_per_ns.as_ref().take_types() { | ||
255 | Some(r) => r, | ||
256 | None => { | ||
257 | // we still have path segments left, but the path so far | ||
258 | // didn't resolve in the types namespace => no resolution | ||
259 | // (don't break here because `curr_per_ns` might contain | ||
260 | // something in the value namespace, and it would be wrong | ||
261 | // to return that) | ||
262 | return ResolvePathResult::empty(ReachedFixedPoint::No); | ||
263 | } | ||
264 | }; | ||
265 | // resolve segment in curr | ||
266 | |||
267 | curr_per_ns = match curr { | ||
268 | ModuleDef::Module(module) => { | ||
269 | if module.krate != self.krate { | ||
270 | let path = Path { | ||
271 | segments: path.segments[i..].iter().cloned().collect(), | ||
272 | kind: PathKind::Self_, | ||
273 | }; | ||
274 | log::debug!("resolving {:?} in other crate", path); | ||
275 | let defp_map = db.crate_def_map(module.krate); | ||
276 | let (def, s) = defp_map.resolve_path(db, module.module_id, &path); | ||
277 | return ResolvePathResult::with( | ||
278 | def, | ||
279 | ReachedFixedPoint::Yes, | ||
280 | s.map(|s| s + i), | ||
281 | ); | ||
282 | } | ||
283 | |||
284 | match self[module.module_id].scope.items.get(&segment.name) { | ||
285 | Some(res) if !res.def.is_none() => res.def, | ||
286 | _ => { | ||
287 | log::debug!("path segment {:?} not found", segment.name); | ||
288 | return ResolvePathResult::empty(ReachedFixedPoint::No); | ||
289 | } | ||
290 | } | ||
291 | } | ||
292 | ModuleDef::Enum(e) => { | ||
293 | // enum variant | ||
294 | tested_by!(can_import_enum_variant); | ||
295 | match e.variant(db, &segment.name) { | ||
296 | Some(variant) => PerNs::both(variant.into(), variant.into()), | ||
297 | None => { | ||
298 | return ResolvePathResult::with( | ||
299 | PerNs::types((*e).into()), | ||
300 | ReachedFixedPoint::Yes, | ||
301 | Some(i), | ||
302 | ); | ||
303 | } | ||
304 | } | ||
305 | } | ||
306 | s => { | ||
307 | // could be an inherent method call in UFCS form | ||
308 | // (`Struct::method`), or some other kind of associated item | ||
309 | log::debug!( | ||
310 | "path segment {:?} resolved to non-module {:?}, but is not last", | ||
311 | segment.name, | ||
312 | curr, | ||
313 | ); | ||
314 | |||
315 | return ResolvePathResult::with( | ||
316 | PerNs::types((*s).into()), | ||
317 | ReachedFixedPoint::Yes, | ||
318 | Some(i), | ||
319 | ); | ||
320 | } | ||
321 | }; | ||
322 | } | ||
323 | ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None) | ||
324 | } | ||
325 | |||
326 | fn resolve_name_in_crate_root_or_extern_prelude(&self, name: &Name) -> PerNs<ModuleDef> { | ||
327 | let from_crate_root = | ||
328 | self[self.root].scope.items.get(name).map_or(PerNs::none(), |it| it.def); | ||
329 | let from_extern_prelude = self.resolve_name_in_extern_prelude(name); | ||
330 | |||
331 | from_crate_root.or(from_extern_prelude) | ||
332 | } | ||
333 | |||
334 | pub(crate) fn resolve_name_in_module( | ||
335 | &self, | ||
336 | db: &impl PersistentHirDatabase, | ||
337 | module: ModuleId, | ||
338 | name: &Name, | ||
339 | ) -> PerNs<ModuleDef> { | ||
340 | // Resolve in: | ||
341 | // - current module / scope | ||
342 | // - extern prelude | ||
343 | // - std prelude | ||
344 | let from_scope = self[module].scope.items.get(name).map_or(PerNs::none(), |it| it.def); | ||
345 | let from_extern_prelude = | ||
346 | self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it)); | ||
347 | let from_prelude = self.resolve_in_prelude(db, name); | ||
348 | |||
349 | from_scope.or(from_extern_prelude).or(from_prelude) | ||
350 | } | ||
351 | |||
352 | fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs<ModuleDef> { | ||
353 | self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it)) | ||
354 | } | ||
355 | |||
356 | fn resolve_in_prelude(&self, db: &impl PersistentHirDatabase, name: &Name) -> PerNs<ModuleDef> { | ||
357 | if let Some(prelude) = self.prelude { | ||
358 | let resolution = if prelude.krate == self.krate { | ||
359 | self[prelude.module_id].scope.items.get(name).cloned() | ||
360 | } else { | ||
361 | db.crate_def_map(prelude.krate)[prelude.module_id].scope.items.get(name).cloned() | ||
362 | }; | ||
363 | resolution.map(|r| r.def).unwrap_or_else(PerNs::none) | ||
364 | } else { | ||
365 | PerNs::none() | ||
366 | } | ||
367 | } | ||
368 | } | ||