aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/nameres.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir/src/nameres.rs')
-rw-r--r--crates/ra_hir/src/nameres.rs127
1 files changed, 116 insertions, 11 deletions
diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs
index ffb20d564..e35b4b129 100644
--- a/crates/ra_hir/src/nameres.rs
+++ b/crates/ra_hir/src/nameres.rs
@@ -18,9 +18,11 @@ pub(crate) mod lower;
18 18
19use std::{time, sync::Arc}; 19use std::{time, sync::Arc};
20 20
21use rustc_hash::{FxHashMap, FxHashSet};
22
21use ra_arena::map::ArenaMap; 23use ra_arena::map::ArenaMap;
24use ra_db::Edition;
22use test_utils::tested_by; 25use test_utils::tested_by;
23use rustc_hash::{FxHashMap, FxHashSet};
24 26
25use crate::{ 27use crate::{
26 Module, ModuleDef, 28 Module, ModuleDef,
@@ -32,8 +34,13 @@ use crate::{
32 34
33/// `ItemMap` is the result of module name resolution. It contains, for each 35/// `ItemMap` is the result of module name resolution. It contains, for each
34/// module, the set of visible items. 36/// module, the set of visible items.
35#[derive(Default, Debug, PartialEq, Eq)] 37#[derive(Debug, PartialEq, Eq)]
36pub struct ItemMap { 38pub struct ItemMap {
39 edition: Edition,
40 /// 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
42 /// a dependency (`std` or `core`).
43 pub(crate) prelude: Option<Module>,
37 pub(crate) extern_prelude: FxHashMap<Name, ModuleDef>, 44 pub(crate) extern_prelude: FxHashMap<Name, ModuleDef>,
38 per_module: ArenaMap<ModuleId, ModuleScope>, 45 per_module: ArenaMap<ModuleId, ModuleScope>,
39} 46}
@@ -176,7 +183,12 @@ where
176 module_tree, 183 module_tree,
177 processed_imports: FxHashSet::default(), 184 processed_imports: FxHashSet::default(),
178 glob_imports: FxHashMap::default(), 185 glob_imports: FxHashMap::default(),
179 result: ItemMap::default(), 186 result: ItemMap {
187 edition: krate.edition(db),
188 prelude: None,
189 extern_prelude: FxHashMap::default(),
190 per_module: ArenaMap::default(),
191 },
180 } 192 }
181 } 193 }
182 194
@@ -211,6 +223,13 @@ where
211 if let Some(module) = dep.krate.root_module(self.db) { 223 if let Some(module) = dep.krate.root_module(self.db) {
212 self.result.extern_prelude.insert(dep.name.clone(), module.into()); 224 self.result.extern_prelude.insert(dep.name.clone(), module.into());
213 } 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 }
214 } 233 }
215 } 234 }
216 235
@@ -266,10 +285,20 @@ where
266 import_id: ImportId, 285 import_id: ImportId,
267 import: &ImportData, 286 import: &ImportData,
268 ) -> ReachedFixedPoint { 287 ) -> ReachedFixedPoint {
269 log::debug!("resolving import: {:?}", import); 288 log::debug!("resolving import: {:?} ({:?})", import, self.result.edition);
270 let original_module = Module { krate: self.krate, module_id }; 289 let original_module = Module { krate: self.krate, module_id };
271 let (def, reached_fixedpoint) = 290
272 self.result.resolve_path_fp(self.db, original_module, &import.path); 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 self.result.resolve_path_fp(self.db, ResolveMode::Import, original_module, &import.path)
301 };
273 302
274 if reached_fixedpoint != ReachedFixedPoint::Yes { 303 if reached_fixedpoint != ReachedFixedPoint::Yes {
275 return reached_fixedpoint; 304 return reached_fixedpoint;
@@ -279,7 +308,10 @@ where
279 log::debug!("glob import: {:?}", import); 308 log::debug!("glob import: {:?}", import);
280 match def.take_types() { 309 match def.take_types() {
281 Some(ModuleDef::Module(m)) => { 310 Some(ModuleDef::Module(m)) => {
282 if m.krate != self.krate { 311 if import.is_prelude {
312 tested_by!(std_prelude);
313 self.result.prelude = Some(m);
314 } else if m.krate != self.krate {
283 tested_by!(glob_across_crates); 315 tested_by!(glob_across_crates);
284 // glob import from other crate => we can just import everything once 316 // glob import from other crate => we can just import everything once
285 let item_map = self.db.item_map(m.krate); 317 let item_map = self.db.item_map(m.krate);
@@ -404,6 +436,12 @@ where
404} 436}
405 437
406#[derive(Debug, Clone, Copy, PartialEq, Eq)] 438#[derive(Debug, Clone, Copy, PartialEq, Eq)]
439enum ResolveMode {
440 Import,
441 Other,
442}
443
444#[derive(Debug, Clone, Copy, PartialEq, Eq)]
407enum ReachedFixedPoint { 445enum ReachedFixedPoint {
408 Yes, 446 Yes,
409 No, 447 No,
@@ -431,15 +469,61 @@ impl ItemMap {
431 original_module: Module, 469 original_module: Module,
432 path: &Path, 470 path: &Path,
433 ) -> PerNs<ModuleDef> { 471 ) -> PerNs<ModuleDef> {
434 self.resolve_path_fp(db, original_module, path).0 472 self.resolve_path_fp(db, ResolveMode::Other, original_module, path).0
473 }
474
475 fn resolve_in_prelude(
476 &self,
477 db: &impl PersistentHirDatabase,
478 original_module: Module,
479 name: &Name,
480 ) -> PerNs<ModuleDef> {
481 if let Some(prelude) = self.prelude {
482 let resolution = if prelude.krate == original_module.krate {
483 self[prelude.module_id].items.get(name).cloned()
484 } else {
485 db.item_map(prelude.krate)[prelude.module_id].items.get(name).cloned()
486 };
487 resolution.map(|r| r.def).unwrap_or_else(PerNs::none)
488 } else {
489 PerNs::none()
490 }
435 } 491 }
436 492
437 pub(crate) fn resolve_name_in_module(&self, module: Module, name: &Name) -> PerNs<ModuleDef> { 493 pub(crate) fn resolve_name_in_module(
494 &self,
495 db: &impl PersistentHirDatabase,
496 module: Module,
497 name: &Name,
498 ) -> PerNs<ModuleDef> {
499 // Resolve in:
500 // - current module / scope
501 // - extern prelude
502 // - std prelude
438 let from_scope = self[module.module_id].items.get(name).map_or(PerNs::none(), |it| it.def); 503 let from_scope = self[module.module_id].items.get(name).map_or(PerNs::none(), |it| it.def);
439 let from_extern_prelude = 504 let from_extern_prelude =
440 self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it)); 505 self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it));
506 let from_prelude = self.resolve_in_prelude(db, module, name);
507
508 from_scope.or(from_extern_prelude).or(from_prelude)
509 }
510
511 fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs<ModuleDef> {
512 self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it))
513 }
441 514
442 from_scope.or(from_extern_prelude) 515 fn resolve_name_in_crate_root_or_extern_prelude(
516 &self,
517 db: &impl PersistentHirDatabase,
518 module: Module,
519 name: &Name,
520 ) -> PerNs<ModuleDef> {
521 let crate_root = module.crate_root(db);
522 let from_crate_root =
523 self[crate_root.module_id].items.get(name).map_or(PerNs::none(), |it| it.def);
524 let from_extern_prelude = self.resolve_name_in_extern_prelude(name);
525
526 from_crate_root.or(from_extern_prelude)
443 } 527 }
444 528
445 // Returns Yes if we are sure that additions to `ItemMap` wouldn't change 529 // Returns Yes if we are sure that additions to `ItemMap` wouldn't change
@@ -447,6 +531,7 @@ impl ItemMap {
447 fn resolve_path_fp( 531 fn resolve_path_fp(
448 &self, 532 &self,
449 db: &impl PersistentHirDatabase, 533 db: &impl PersistentHirDatabase,
534 mode: ResolveMode,
450 original_module: Module, 535 original_module: Module,
451 path: &Path, 536 path: &Path,
452 ) -> (PerNs<ModuleDef>, ReachedFixedPoint) { 537 ) -> (PerNs<ModuleDef>, ReachedFixedPoint) {
@@ -454,12 +539,32 @@ impl ItemMap {
454 let mut curr_per_ns: PerNs<ModuleDef> = match path.kind { 539 let mut curr_per_ns: PerNs<ModuleDef> = match path.kind {
455 PathKind::Crate => PerNs::types(original_module.crate_root(db).into()), 540 PathKind::Crate => PerNs::types(original_module.crate_root(db).into()),
456 PathKind::Self_ => PerNs::types(original_module.into()), 541 PathKind::Self_ => PerNs::types(original_module.into()),
542 // plain import or absolute path in 2015: crate-relative with
543 // fallback to extern prelude (with the simplification in
544 // rust-lang/rust#57745)
545 // TODO there must be a nicer way to write this condition
546 PathKind::Plain | PathKind::Abs
547 if self.edition == Edition::Edition2015
548 && (path.kind == PathKind::Abs || mode == ResolveMode::Import) =>
549 {
550 let segment = match segments.next() {
551 Some((_, segment)) => segment,
552 None => return (PerNs::none(), ReachedFixedPoint::Yes),
553 };
554 log::debug!("resolving {:?} in crate root (+ extern prelude)", segment);
555 self.resolve_name_in_crate_root_or_extern_prelude(
556 db,
557 original_module,
558 &segment.name,
559 )
560 }
457 PathKind::Plain => { 561 PathKind::Plain => {
458 let segment = match segments.next() { 562 let segment = match segments.next() {
459 Some((_, segment)) => segment, 563 Some((_, segment)) => segment,
460 None => return (PerNs::none(), ReachedFixedPoint::Yes), 564 None => return (PerNs::none(), ReachedFixedPoint::Yes),
461 }; 565 };
462 self.resolve_name_in_module(original_module, &segment.name) 566 log::debug!("resolving {:?} in module", segment);
567 self.resolve_name_in_module(db, original_module, &segment.name)
463 } 568 }
464 PathKind::Super => { 569 PathKind::Super => {
465 if let Some(p) = original_module.parent(db) { 570 if let Some(p) = original_module.parent(db) {