diff options
Diffstat (limited to 'crates/ra_hir/src/nameres.rs')
-rw-r--r-- | crates/ra_hir/src/nameres.rs | 129 |
1 files changed, 117 insertions, 12 deletions
diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index 261ebdf97..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 | ||
19 | use std::{time, sync::Arc}; | 19 | use std::{time, sync::Arc}; |
20 | 20 | ||
21 | use rustc_hash::{FxHashMap, FxHashSet}; | ||
22 | |||
21 | use ra_arena::map::ArenaMap; | 23 | use ra_arena::map::ArenaMap; |
24 | use ra_db::Edition; | ||
22 | use test_utils::tested_by; | 25 | use test_utils::tested_by; |
23 | use rustc_hash::{FxHashMap, FxHashSet}; | ||
24 | 26 | ||
25 | use crate::{ | 27 | use 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)] |
36 | pub struct ItemMap { | 38 | pub 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)] |
439 | enum ResolveMode { | ||
440 | Import, | ||
441 | Other, | ||
442 | } | ||
443 | |||
444 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
407 | enum ReachedFixedPoint { | 445 | enum 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) { |
@@ -490,7 +595,7 @@ impl ItemMap { | |||
490 | None => { | 595 | None => { |
491 | // we still have path segments left, but the path so far | 596 | // we still have path segments left, but the path so far |
492 | // didn't resolve in the types namespace => no resolution | 597 | // didn't resolve in the types namespace => no resolution |
493 | // (don't break here because curr_per_ns might contain | 598 | // (don't break here because `curr_per_ns` might contain |
494 | // something in the value namespace, and it would be wrong | 599 | // something in the value namespace, and it would be wrong |
495 | // to return that) | 600 | // to return that) |
496 | return (PerNs::none(), ReachedFixedPoint::No); | 601 | return (PerNs::none(), ReachedFixedPoint::No); |