Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit a028463

Browse files
committed
Fix self and super path resolution in block modules
1 parent 8769cd2 commit a028463

File tree

3 files changed

+160
-47
lines changed

3 files changed

+160
-47
lines changed

crates/hir-def/src/body/tests/block.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,47 @@ struct Struct {}
133133
);
134134
}
135135

136+
#[test]
137+
fn super_imports_2() {
138+
check_at(
139+
r#"
140+
fn outer() {
141+
mod m {
142+
struct ResolveMe {}
143+
fn middle() {
144+
mod m2 {
145+
fn inner() {
146+
use super::ResolveMe;
147+
$0
148+
}
149+
}
150+
}
151+
}
152+
}
153+
"#,
154+
expect![[r#"
155+
block scope
156+
ResolveMe: t
157+
158+
block scope
159+
m2: t
160+
161+
block scope::m2
162+
inner: v
163+
164+
block scope
165+
m: t
166+
167+
block scope::m
168+
ResolveMe: t
169+
middle: v
170+
171+
crate
172+
outer: v
173+
"#]],
174+
);
175+
}
176+
136177
#[test]
137178
fn nested_module_scoping() {
138179
check_block_scopes_at(
@@ -155,6 +196,42 @@ fn f() {
155196
);
156197
}
157198

199+
#[test]
200+
fn self_imports() {
201+
check_at(
202+
r#"
203+
fn f() {
204+
mod m {
205+
struct ResolveMe {}
206+
fn g() {
207+
fn h() {
208+
use self::ResolveMe;
209+
$0
210+
}
211+
}
212+
}
213+
}
214+
"#,
215+
expect![[r#"
216+
block scope
217+
ResolveMe: t
218+
219+
block scope
220+
h: v
221+
222+
block scope
223+
m: t
224+
225+
block scope::m
226+
ResolveMe: t
227+
g: v
228+
229+
crate
230+
f: v
231+
"#]],
232+
);
233+
}
234+
158235
#[test]
159236
fn legacy_macro_items() {
160237
// Checks that legacy-scoped `macro_rules!` from parent namespaces are resolved and expanded

crates/hir-def/src/nameres.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,10 @@ impl BlockRelativeModuleId {
196196
fn into_module(self, krate: CrateId) -> ModuleId {
197197
ModuleId { krate, block: self.block, local_id: self.local_id }
198198
}
199+
200+
fn is_block_module(self) -> bool {
201+
self.block.is_some() && self.local_id == DefMap::ROOT
202+
}
199203
}
200204

201205
impl std::ops::Index<LocalModuleId> for DefMap {
@@ -278,7 +282,9 @@ pub struct ModuleData {
278282
pub origin: ModuleOrigin,
279283
/// Declared visibility of this module.
280284
pub visibility: Visibility,
281-
/// Always [`None`] for block modules
285+
/// Parent module in the same `DefMap`.
286+
///
287+
/// [`None`] for block modules because they are always its `DefMap`'s root.
282288
pub parent: Option<LocalModuleId>,
283289
pub children: FxHashMap<Name, LocalModuleId>,
284290
pub scope: ItemScope,

crates/hir-def/src/nameres/path_resolution.rs

Lines changed: 76 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,12 @@
1212
1313
use base_db::Edition;
1414
use hir_expand::name::Name;
15+
use triomphe::Arc;
1516

1617
use crate::{
1718
db::DefDatabase,
1819
item_scope::BUILTIN_SCOPE,
19-
nameres::{sub_namespace_match, BuiltinShadowMode, DefMap, MacroSubNs},
20+
nameres::{sub_namespace_match, BlockInfo, BuiltinShadowMode, DefMap, MacroSubNs},
2021
path::{ModPath, PathKind},
2122
per_ns::PerNs,
2223
visibility::{RawVisibility, Visibility},
@@ -159,13 +160,15 @@ impl DefMap {
159160
(None, new) => new,
160161
};
161162

162-
match &current_map.block {
163-
Some(block) => {
163+
match current_map.block {
164+
Some(block) if original_module == Self::ROOT => {
165+
// Block modules "inherit" names from its parent module.
164166
original_module = block.parent.local_id;
165167
arc = block.parent.def_map(db, current_map.krate);
166-
current_map = &*arc;
168+
current_map = &arc;
167169
}
168-
None => return result,
170+
// Proper (non-block) modules, including those in block `DefMap`s, don't.
171+
_ => return result,
169172
}
170173
}
171174
}
@@ -241,51 +244,54 @@ impl DefMap {
241244
)
242245
}
243246
PathKind::Super(lvl) => {
244-
let mut module = original_module;
245-
for i in 0..lvl {
246-
match self.modules[module].parent {
247-
Some(it) => module = it,
248-
None => match &self.block {
249-
Some(block) => {
250-
// Look up remaining path in parent `DefMap`
251-
let new_path = ModPath::from_segments(
252-
PathKind::Super(lvl - i),
253-
path.segments().to_vec(),
254-
);
255-
tracing::debug!(
256-
"`super` path: {} -> {} in parent map",
257-
path.display(db.upcast()),
258-
new_path.display(db.upcast())
259-
);
260-
return block
261-
.parent
262-
.def_map(db, self.krate)
263-
.resolve_path_fp_with_macro(
264-
db,
265-
mode,
266-
block.parent.local_id,
267-
&new_path,
268-
shadow,
269-
expected_macro_subns,
270-
);
271-
}
272-
None => {
273-
tracing::debug!("super path in root module");
274-
return ResolvePathResult::empty(ReachedFixedPoint::Yes);
275-
}
276-
},
277-
}
247+
let mut local_id = original_module;
248+
let mut ext;
249+
let mut def_map = self;
250+
251+
// Adjust `local_id` to `self`, i.e. the nearest non-block module.
252+
if def_map.module_id(local_id).is_block_module() {
253+
(ext, local_id) = adjust_to_nearest_non_block_module(db, def_map, local_id);
254+
def_map = &ext;
278255
}
279256

280-
// Resolve `self` to the containing crate-rooted module if we're a block
281-
self.with_ancestor_maps(db, module, &mut |def_map, module| {
282-
if def_map.block.is_some() {
283-
None // keep ascending
257+
// Go up the module tree but skip block modules as `super` always refers to the
258+
// nearest non-block module.
259+
for _ in 0..lvl {
260+
// Loop invariant: at the beginning of each loop, `local_id` must refer to a
261+
// non-block module.
262+
if let Some(parent) = def_map.modules[local_id].parent {
263+
local_id = parent;
264+
if def_map.module_id(local_id).is_block_module() {
265+
(ext, local_id) =
266+
adjust_to_nearest_non_block_module(db, def_map, local_id);
267+
def_map = &ext;
268+
}
284269
} else {
285-
Some(PerNs::types(def_map.module_id(module).into(), Visibility::Public))
270+
stdx::always!(def_map.block.is_none());
271+
tracing::debug!("super path in root module");
272+
return ResolvePathResult::empty(ReachedFixedPoint::Yes);
286273
}
287-
})
288-
.expect("block DefMap not rooted in crate DefMap")
274+
}
275+
276+
let module = def_map.module_id(local_id);
277+
stdx::never!(module.is_block_module());
278+
279+
if self.block != def_map.block {
280+
// If we have a different `DefMap` from `self` (the orignal `DefMap` we started
281+
// with), resolve the remaining path segments in that `DefMap`.
282+
let path =
283+
ModPath::from_segments(PathKind::Super(0), path.segments().iter().cloned());
284+
return def_map.resolve_path_fp_with_macro(
285+
db,
286+
mode,
287+
local_id,
288+
&path,
289+
shadow,
290+
expected_macro_subns,
291+
);
292+
}
293+
294+
PerNs::types(module.into(), Visibility::Public)
289295
}
290296
PathKind::Abs => {
291297
// 2018-style absolute path -- only extern prelude
@@ -508,3 +514,27 @@ impl DefMap {
508514
}
509515
}
510516
}
517+
518+
/// Given a block module, returns its nearest non-block module and the `DefMap` it blongs to.
519+
fn adjust_to_nearest_non_block_module(
520+
db: &dyn DefDatabase,
521+
def_map: &DefMap,
522+
mut local_id: LocalModuleId,
523+
) -> (Arc<DefMap>, LocalModuleId) {
524+
// INVARIANT: `local_id` in `def_map` must be a block module.
525+
stdx::always!(def_map.module_id(local_id).is_block_module());
526+
527+
let mut ext;
528+
// This needs to be a local variable due to our mighty lifetime.
529+
let mut def_map = def_map;
530+
loop {
531+
let BlockInfo { parent, .. } = def_map.block.expect("block module without parent module");
532+
533+
ext = parent.def_map(db, def_map.krate);
534+
def_map = &ext;
535+
local_id = parent.local_id;
536+
if !parent.is_block_module() {
537+
return (ext, local_id);
538+
}
539+
}
540+
}

0 commit comments

Comments
 (0)