aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/nameres/crate_def_map.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir/src/nameres/crate_def_map.rs')
-rw-r--r--crates/ra_hir/src/nameres/crate_def_map.rs368
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
44mod raw;
45mod collector;
46#[cfg(test)]
47mod tests;
48
49use rustc_hash::FxHashMap;
50use test_utils::tested_by;
51use ra_arena::{Arena, RawId, impl_arena_id};
52use ra_db::FileId;
53
54use std::sync::Arc;
55
56use 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
63pub(crate) use self::raw::RawItems;
64
65#[derive(Default, Debug, PartialEq, Eq)]
66pub(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)]
79pub(crate) struct ModuleId(RawId);
80impl_arena_id!(ModuleId);
81
82/// Contans all top-level defs from a macro-expanded crate
83#[derive(Debug, PartialEq, Eq)]
84pub 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)]
99pub(crate) struct CrateDefMapProblems {
100 problems: Vec<(SourceItemId, Problem)>,
101}
102
103impl 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
113impl std::ops::Index<ModuleId> for CrateDefMap {
114 type Output = ModuleData;
115 fn index(&self, id: ModuleId) -> &ModuleData {
116 &self.modules[id]
117 }
118}
119
120impl 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}