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

Commit 4b06d3c

Browse files
committed
Auto merge of rust-lang#15148 - lowr:fix/super-nameres-in-block, r=Veykril
Fix `self` and `super` path resolution in block modules This PR fixes `self` and `super` path resolution with block modules involved. Previously, we were just going up the module tree count-of-`super` times without considering block modules in the way, and then if we ended up in a block `DefMap`, we adjust "to the containing crate-rooted module". While this seems to work in most real-world cases, we failed to resolve them within peculiar module structures. `self` and `super` should actually be resolved to the nearest non-block module, and the paths don't necessarily resolve to a crate-rooted module. This PR makes sure every `self` and `super` segment in paths are resolved to a non-block module.
2 parents 77ccd64 + 56dd536 commit 4b06d3c

File tree

7 files changed

+182
-73
lines changed

7 files changed

+182
-73
lines changed

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ mod block;
33
use base_db::{fixture::WithFixture, SourceDatabase};
44
use expect_test::Expect;
55

6-
use crate::ModuleDefId;
6+
use crate::{test_db::TestDB, ModuleDefId};
77

88
use super::*;
99

1010
fn lower(ra_fixture: &str) -> Arc<Body> {
11-
let db = crate::test_db::TestDB::with_files(ra_fixture);
11+
let db = TestDB::with_files(ra_fixture);
1212

1313
let krate = db.crate_graph().iter().next().unwrap();
1414
let def_map = db.crate_def_map(krate);
@@ -25,23 +25,23 @@ fn lower(ra_fixture: &str) -> Arc<Body> {
2525
db.body(fn_def.unwrap().into())
2626
}
2727

28-
fn block_def_map_at(ra_fixture: &str) -> String {
29-
let (db, position) = crate::test_db::TestDB::with_position(ra_fixture);
28+
fn def_map_at(ra_fixture: &str) -> String {
29+
let (db, position) = TestDB::with_position(ra_fixture);
3030

3131
let module = db.module_at_position(position);
3232
module.def_map(&db).dump(&db)
3333
}
3434

3535
fn check_block_scopes_at(ra_fixture: &str, expect: Expect) {
36-
let (db, position) = crate::test_db::TestDB::with_position(ra_fixture);
36+
let (db, position) = TestDB::with_position(ra_fixture);
3737

3838
let module = db.module_at_position(position);
3939
let actual = module.def_map(&db).dump_block_scopes(&db);
4040
expect.assert_eq(&actual);
4141
}
4242

4343
fn check_at(ra_fixture: &str, expect: Expect) {
44-
let actual = block_def_map_at(ra_fixture);
44+
let actual = def_map_at(ra_fixture);
4545
expect.assert_eq(&actual);
4646
}
4747

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/lib.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -145,24 +145,28 @@ pub struct ModuleId {
145145
}
146146

147147
impl ModuleId {
148-
pub fn def_map(&self, db: &dyn db::DefDatabase) -> Arc<DefMap> {
148+
pub fn def_map(self, db: &dyn db::DefDatabase) -> Arc<DefMap> {
149149
match self.block {
150150
Some(block) => db.block_def_map(block),
151151
None => db.crate_def_map(self.krate),
152152
}
153153
}
154154

155-
pub fn krate(&self) -> CrateId {
155+
pub fn krate(self) -> CrateId {
156156
self.krate
157157
}
158158

159-
pub fn containing_module(&self, db: &dyn db::DefDatabase) -> Option<ModuleId> {
159+
pub fn containing_module(self, db: &dyn db::DefDatabase) -> Option<ModuleId> {
160160
self.def_map(db).containing_module(self.local_id)
161161
}
162162

163-
pub fn containing_block(&self) -> Option<BlockId> {
163+
pub fn containing_block(self) -> Option<BlockId> {
164164
self.block
165165
}
166+
167+
pub fn is_block_module(self) -> bool {
168+
self.block.is_some() && self.local_id == DefMap::ROOT
169+
}
166170
}
167171

168172
/// An ID of a module, **local** to a `DefMap`.

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/collector.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -808,11 +808,8 @@ impl DefCollector<'_> {
808808
}
809809
}
810810

811-
// Check whether all namespace is resolved
812-
if def.take_types().is_some()
813-
&& def.take_values().is_some()
814-
&& def.take_macros().is_some()
815-
{
811+
// Check whether all namespaces are resolved.
812+
if def.is_full() {
816813
PartialResolvedImport::Resolved(def)
817814
} else {
818815
PartialResolvedImport::Indeterminate(def)
@@ -821,7 +818,7 @@ impl DefCollector<'_> {
821818
}
822819

823820
fn resolve_extern_crate(&self, name: &Name) -> Option<CrateRootModuleId> {
824-
if *name == name!(self) {
821+
if *name == name![self] {
825822
cov_mark::hit!(extern_crate_self_as);
826823
Some(self.def_map.crate_root())
827824
} else {

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

Lines changed: 77 additions & 47 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
}
@@ -189,7 +192,7 @@ impl DefMap {
189192
));
190193

191194
let mut segments = path.segments().iter().enumerate();
192-
let mut curr_per_ns: PerNs = match path.kind {
195+
let mut curr_per_ns = match path.kind {
193196
PathKind::DollarCrate(krate) => {
194197
if krate == self.krate {
195198
cov_mark::hit!(macro_dollar_crate_self);
@@ -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)