diff --git a/crates/hir-def/src/body/tests.rs b/crates/hir-def/src/body/tests.rs
index edee2c7ff96..d5582011645 100644
--- a/crates/hir-def/src/body/tests.rs
+++ b/crates/hir-def/src/body/tests.rs
@@ -3,12 +3,12 @@ mod block;
use base_db::{fixture::WithFixture, SourceDatabase};
use expect_test::Expect;
-use crate::ModuleDefId;
+use crate::{test_db::TestDB, ModuleDefId};
use super::*;
fn lower(ra_fixture: &str) -> Arc
{
- let db = crate::test_db::TestDB::with_files(ra_fixture);
+ let db = TestDB::with_files(ra_fixture);
let krate = db.crate_graph().iter().next().unwrap();
let def_map = db.crate_def_map(krate);
@@ -25,15 +25,15 @@ fn lower(ra_fixture: &str) -> Arc {
db.body(fn_def.unwrap().into())
}
-fn block_def_map_at(ra_fixture: &str) -> String {
- let (db, position) = crate::test_db::TestDB::with_position(ra_fixture);
+fn def_map_at(ra_fixture: &str) -> String {
+ let (db, position) = TestDB::with_position(ra_fixture);
let module = db.module_at_position(position);
module.def_map(&db).dump(&db)
}
fn check_block_scopes_at(ra_fixture: &str, expect: Expect) {
- let (db, position) = crate::test_db::TestDB::with_position(ra_fixture);
+ let (db, position) = TestDB::with_position(ra_fixture);
let module = db.module_at_position(position);
let actual = module.def_map(&db).dump_block_scopes(&db);
@@ -41,7 +41,7 @@ fn check_block_scopes_at(ra_fixture: &str, expect: Expect) {
}
fn check_at(ra_fixture: &str, expect: Expect) {
- let actual = block_def_map_at(ra_fixture);
+ let actual = def_map_at(ra_fixture);
expect.assert_eq(&actual);
}
diff --git a/crates/hir-def/src/body/tests/block.rs b/crates/hir-def/src/body/tests/block.rs
index 6e77744f215..4e015a7fbbb 100644
--- a/crates/hir-def/src/body/tests/block.rs
+++ b/crates/hir-def/src/body/tests/block.rs
@@ -133,6 +133,47 @@ struct Struct {}
);
}
+#[test]
+fn super_imports_2() {
+ check_at(
+ r#"
+fn outer() {
+ mod m {
+ struct ResolveMe {}
+ fn middle() {
+ mod m2 {
+ fn inner() {
+ use super::ResolveMe;
+ $0
+ }
+ }
+ }
+ }
+}
+"#,
+ expect![[r#"
+ block scope
+ ResolveMe: t
+
+ block scope
+ m2: t
+
+ block scope::m2
+ inner: v
+
+ block scope
+ m: t
+
+ block scope::m
+ ResolveMe: t
+ middle: v
+
+ crate
+ outer: v
+ "#]],
+ );
+}
+
#[test]
fn nested_module_scoping() {
check_block_scopes_at(
@@ -155,6 +196,42 @@ fn f() {
);
}
+#[test]
+fn self_imports() {
+ check_at(
+ r#"
+fn f() {
+ mod m {
+ struct ResolveMe {}
+ fn g() {
+ fn h() {
+ use self::ResolveMe;
+ $0
+ }
+ }
+ }
+}
+"#,
+ expect![[r#"
+ block scope
+ ResolveMe: t
+
+ block scope
+ h: v
+
+ block scope
+ m: t
+
+ block scope::m
+ ResolveMe: t
+ g: v
+
+ crate
+ f: v
+ "#]],
+ );
+}
+
#[test]
fn legacy_macro_items() {
// Checks that legacy-scoped `macro_rules!` from parent namespaces are resolved and expanded
diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs
index 65e8c83b5d7..e5149ab577c 100644
--- a/crates/hir-def/src/lib.rs
+++ b/crates/hir-def/src/lib.rs
@@ -145,24 +145,28 @@ pub struct ModuleId {
}
impl ModuleId {
- pub fn def_map(&self, db: &dyn db::DefDatabase) -> Arc {
+ pub fn def_map(self, db: &dyn db::DefDatabase) -> Arc {
match self.block {
Some(block) => db.block_def_map(block),
None => db.crate_def_map(self.krate),
}
}
- pub fn krate(&self) -> CrateId {
+ pub fn krate(self) -> CrateId {
self.krate
}
- pub fn containing_module(&self, db: &dyn db::DefDatabase) -> Option {
+ pub fn containing_module(self, db: &dyn db::DefDatabase) -> Option {
self.def_map(db).containing_module(self.local_id)
}
- pub fn containing_block(&self) -> Option {
+ pub fn containing_block(self) -> Option {
self.block
}
+
+ pub fn is_block_module(self) -> bool {
+ self.block.is_some() && self.local_id == DefMap::ROOT
+ }
}
/// An ID of a module, **local** to a `DefMap`.
diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs
index e7a4355d258..86818ce26dd 100644
--- a/crates/hir-def/src/nameres.rs
+++ b/crates/hir-def/src/nameres.rs
@@ -196,6 +196,10 @@ impl BlockRelativeModuleId {
fn into_module(self, krate: CrateId) -> ModuleId {
ModuleId { krate, block: self.block, local_id: self.local_id }
}
+
+ fn is_block_module(self) -> bool {
+ self.block.is_some() && self.local_id == DefMap::ROOT
+ }
}
impl std::ops::Index for DefMap {
@@ -278,7 +282,9 @@ pub struct ModuleData {
pub origin: ModuleOrigin,
/// Declared visibility of this module.
pub visibility: Visibility,
- /// Always [`None`] for block modules
+ /// Parent module in the same `DefMap`.
+ ///
+ /// [`None`] for block modules because they are always its `DefMap`'s root.
pub parent: Option,
pub children: FxHashMap,
pub scope: ItemScope,
diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs
index d5cfe5bc706..fb332c06d0a 100644
--- a/crates/hir-def/src/nameres/collector.rs
+++ b/crates/hir-def/src/nameres/collector.rs
@@ -808,11 +808,8 @@ impl DefCollector<'_> {
}
}
- // Check whether all namespace is resolved
- if def.take_types().is_some()
- && def.take_values().is_some()
- && def.take_macros().is_some()
- {
+ // Check whether all namespaces are resolved.
+ if def.is_full() {
PartialResolvedImport::Resolved(def)
} else {
PartialResolvedImport::Indeterminate(def)
@@ -821,7 +818,7 @@ impl DefCollector<'_> {
}
fn resolve_extern_crate(&self, name: &Name) -> Option {
- if *name == name!(self) {
+ if *name == name![self] {
cov_mark::hit!(extern_crate_self_as);
Some(self.def_map.crate_root())
} else {
diff --git a/crates/hir-def/src/nameres/path_resolution.rs b/crates/hir-def/src/nameres/path_resolution.rs
index 5f6163175a7..de22ea10146 100644
--- a/crates/hir-def/src/nameres/path_resolution.rs
+++ b/crates/hir-def/src/nameres/path_resolution.rs
@@ -12,11 +12,12 @@
use base_db::Edition;
use hir_expand::name::Name;
+use triomphe::Arc;
use crate::{
db::DefDatabase,
item_scope::BUILTIN_SCOPE,
- nameres::{sub_namespace_match, BuiltinShadowMode, DefMap, MacroSubNs},
+ nameres::{sub_namespace_match, BlockInfo, BuiltinShadowMode, DefMap, MacroSubNs},
path::{ModPath, PathKind},
per_ns::PerNs,
visibility::{RawVisibility, Visibility},
@@ -159,13 +160,15 @@ impl DefMap {
(None, new) => new,
};
- match ¤t_map.block {
- Some(block) => {
+ match current_map.block {
+ Some(block) if original_module == Self::ROOT => {
+ // Block modules "inherit" names from its parent module.
original_module = block.parent.local_id;
arc = block.parent.def_map(db, current_map.krate);
- current_map = &*arc;
+ current_map = &arc;
}
- None => return result,
+ // Proper (non-block) modules, including those in block `DefMap`s, don't.
+ _ => return result,
}
}
}
@@ -189,7 +192,7 @@ impl DefMap {
));
let mut segments = path.segments().iter().enumerate();
- let mut curr_per_ns: PerNs = match path.kind {
+ let mut curr_per_ns = match path.kind {
PathKind::DollarCrate(krate) => {
if krate == self.krate {
cov_mark::hit!(macro_dollar_crate_self);
@@ -241,51 +244,54 @@ impl DefMap {
)
}
PathKind::Super(lvl) => {
- let mut module = original_module;
- for i in 0..lvl {
- match self.modules[module].parent {
- Some(it) => module = it,
- None => match &self.block {
- Some(block) => {
- // Look up remaining path in parent `DefMap`
- let new_path = ModPath::from_segments(
- PathKind::Super(lvl - i),
- path.segments().to_vec(),
- );
- tracing::debug!(
- "`super` path: {} -> {} in parent map",
- path.display(db.upcast()),
- new_path.display(db.upcast())
- );
- return block
- .parent
- .def_map(db, self.krate)
- .resolve_path_fp_with_macro(
- db,
- mode,
- block.parent.local_id,
- &new_path,
- shadow,
- expected_macro_subns,
- );
- }
- None => {
- tracing::debug!("super path in root module");
- return ResolvePathResult::empty(ReachedFixedPoint::Yes);
- }
- },
+ let mut local_id = original_module;
+ let mut ext;
+ let mut def_map = self;
+
+ // Adjust `local_id` to `self`, i.e. the nearest non-block module.
+ if def_map.module_id(local_id).is_block_module() {
+ (ext, local_id) = adjust_to_nearest_non_block_module(db, def_map, local_id);
+ def_map = &ext;
+ }
+
+ // Go up the module tree but skip block modules as `super` always refers to the
+ // nearest non-block module.
+ for _ in 0..lvl {
+ // Loop invariant: at the beginning of each loop, `local_id` must refer to a
+ // non-block module.
+ if let Some(parent) = def_map.modules[local_id].parent {
+ local_id = parent;
+ if def_map.module_id(local_id).is_block_module() {
+ (ext, local_id) =
+ adjust_to_nearest_non_block_module(db, def_map, local_id);
+ def_map = &ext;
+ }
+ } else {
+ stdx::always!(def_map.block.is_none());
+ tracing::debug!("super path in root module");
+ return ResolvePathResult::empty(ReachedFixedPoint::Yes);
}
}
- // Resolve `self` to the containing crate-rooted module if we're a block
- self.with_ancestor_maps(db, module, &mut |def_map, module| {
- if def_map.block.is_some() {
- None // keep ascending
- } else {
- Some(PerNs::types(def_map.module_id(module).into(), Visibility::Public))
- }
- })
- .expect("block DefMap not rooted in crate DefMap")
+ let module = def_map.module_id(local_id);
+ stdx::never!(module.is_block_module());
+
+ if self.block != def_map.block {
+ // If we have a different `DefMap` from `self` (the orignal `DefMap` we started
+ // with), resolve the remaining path segments in that `DefMap`.
+ let path =
+ ModPath::from_segments(PathKind::Super(0), path.segments().iter().cloned());
+ return def_map.resolve_path_fp_with_macro(
+ db,
+ mode,
+ local_id,
+ &path,
+ shadow,
+ expected_macro_subns,
+ );
+ }
+
+ PerNs::types(module.into(), Visibility::Public)
}
PathKind::Abs => {
// 2018-style absolute path -- only extern prelude
@@ -508,3 +514,27 @@ impl DefMap {
}
}
}
+
+/// Given a block module, returns its nearest non-block module and the `DefMap` it blongs to.
+fn adjust_to_nearest_non_block_module(
+ db: &dyn DefDatabase,
+ def_map: &DefMap,
+ mut local_id: LocalModuleId,
+) -> (Arc, LocalModuleId) {
+ // INVARIANT: `local_id` in `def_map` must be a block module.
+ stdx::always!(def_map.module_id(local_id).is_block_module());
+
+ let mut ext;
+ // This needs to be a local variable due to our mighty lifetime.
+ let mut def_map = def_map;
+ loop {
+ let BlockInfo { parent, .. } = def_map.block.expect("block module without parent module");
+
+ ext = parent.def_map(db, def_map.krate);
+ def_map = &ext;
+ local_id = parent.local_id;
+ if !parent.is_block_module() {
+ return (ext, local_id);
+ }
+ }
+}
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 352fa481509..74eb9f65883 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -47,7 +47,7 @@ use hir_def::{
lang_item::LangItemTarget,
layout::{self, ReprOptions, TargetDataLayout},
macro_id_to_def_id,
- nameres::{self, diagnostics::DefDiagnostic, ModuleOrigin},
+ nameres::{self, diagnostics::DefDiagnostic},
per_ns::PerNs,
resolver::{HasResolver, Resolver},
src::HasSource as _,
@@ -505,15 +505,10 @@ impl Module {
/// Finds nearest non-block ancestor `Module` (`self` included).
pub fn nearest_non_block_module(self, db: &dyn HirDatabase) -> Module {
let mut id = self.id;
- loop {
- let def_map = id.def_map(db.upcast());
- let origin = def_map[id.local_id].origin;
- if matches!(origin, ModuleOrigin::BlockExpr { .. }) {
- id = id.containing_module(db.upcast()).expect("block without parent module")
- } else {
- return Module { id };
- }
+ while id.is_block_module() {
+ id = id.containing_module(db.upcast()).expect("block without parent module");
}
+ Module { id }
}
pub fn path_to_root(self, db: &dyn HirDatabase) -> Vec {