Skip to content

Commit 709805a

Browse files
Properly resolve prelude paths inside modules inside blocks
I.e. the following situation: ``` fn foo() { mod bar { fn qux() { // Prelude path here (e.g. macro use prelude or extern prelude). } } } ``` Those were previously unresolved, because, in order to support `self` and `super` properly, since rust-lang#15148 we do not ascend block paths when there is a module in between, but only crate def maps register preludes, not block def maps, and we can't change this because block def map prelude can always be overridden by another block. E.g. ``` fn foo() { struct WithTheSameNameAsPreludeItem; { WithTheSameNameAsPreludeItem } } ``` Here `WithTheSameNameAsPreludeItem` refer to the item from the top block, but if we would register prelude items in each block the child block would overwrite it incorrectly.
1 parent 14607ba commit 709805a

File tree

2 files changed

+180
-52
lines changed

2 files changed

+180
-52
lines changed

src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs

+155-52
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
//!
1111
//! `ReachedFixedPoint` signals about this.
1212
13+
use either::Either;
1314
use hir_expand::{name::Name, Lookup};
1415
use span::Edition;
1516
use triomphe::Arc;
@@ -150,17 +151,8 @@ impl DefMap {
150151

151152
let mut arc;
152153
let mut current_map = self;
153-
loop {
154-
let new = current_map.resolve_path_fp_with_macro_single(
155-
db,
156-
mode,
157-
original_module,
158-
path,
159-
shadow,
160-
expected_macro_subns,
161-
);
162154

163-
// Merge `new` into `result`.
155+
let mut merge = |new: ResolvePathResult| {
164156
result.resolved_def = result.resolved_def.or(new.resolved_def);
165157
if result.reached_fixedpoint == ReachedFixedPoint::No {
166158
result.reached_fixedpoint = new.reached_fixedpoint;
@@ -171,7 +163,9 @@ impl DefMap {
171163
(Some(old), Some(new)) => Some(old.max(new)),
172164
(None, new) => new,
173165
};
166+
};
174167

168+
loop {
175169
match current_map.block {
176170
Some(block) if original_module == Self::ROOT => {
177171
// Block modules "inherit" names from its parent module.
@@ -180,8 +174,38 @@ impl DefMap {
180174
current_map = &arc;
181175
}
182176
// Proper (non-block) modules, including those in block `DefMap`s, don't.
183-
_ => return result,
177+
_ => {
178+
if original_module != Self::ROOT && current_map.block.is_some() {
179+
// A module inside a block. Do not resolve items declared in upper blocks, but we do need to get
180+
// the prelude items (which are not inserted into blocks because they can be overridden there).
181+
original_module = Self::ROOT;
182+
arc = db.crate_def_map(self.krate);
183+
current_map = &arc;
184+
185+
let new = current_map.resolve_path_fp_in_all_preludes(
186+
db,
187+
mode,
188+
original_module,
189+
path,
190+
shadow,
191+
);
192+
merge(new);
193+
}
194+
195+
return result;
196+
}
184197
}
198+
199+
let new = current_map.resolve_path_fp_with_macro_single(
200+
db,
201+
mode,
202+
original_module,
203+
path,
204+
shadow,
205+
expected_macro_subns,
206+
);
207+
208+
merge(new);
185209
}
186210
}
187211

@@ -195,7 +219,7 @@ impl DefMap {
195219
expected_macro_subns: Option<MacroSubNs>,
196220
) -> ResolvePathResult {
197221
let mut segments = path.segments().iter().enumerate();
198-
let mut curr_per_ns = match path.kind {
222+
let curr_per_ns = match path.kind {
199223
PathKind::DollarCrate(krate) => {
200224
if krate == self.krate {
201225
cov_mark::hit!(macro_dollar_crate_self);
@@ -296,25 +320,96 @@ impl DefMap {
296320

297321
PerNs::types(module.into(), Visibility::Public, None)
298322
}
299-
PathKind::Abs => {
300-
// 2018-style absolute path -- only extern prelude
301-
let segment = match segments.next() {
302-
Some((_, segment)) => segment,
323+
PathKind::Abs => match self.resolve_path_abs(&mut segments, path) {
324+
Either::Left(it) => it,
325+
Either::Right(reached_fixed_point) => {
326+
return ResolvePathResult::empty(reached_fixed_point)
327+
}
328+
},
329+
};
330+
331+
self.resolve_remaining_segments(segments, curr_per_ns, path, db, shadow, original_module)
332+
}
333+
334+
/// Resolves a path only in the preludes, without accounting for item scopes.
335+
pub(super) fn resolve_path_fp_in_all_preludes(
336+
&self,
337+
db: &dyn DefDatabase,
338+
mode: ResolveMode,
339+
original_module: LocalModuleId,
340+
path: &ModPath,
341+
shadow: BuiltinShadowMode,
342+
) -> ResolvePathResult {
343+
let mut segments = path.segments().iter().enumerate();
344+
let curr_per_ns = match path.kind {
345+
// plain import or absolute path in 2015: crate-relative with
346+
// fallback to extern prelude (with the simplification in
347+
// rust-lang/rust#57745)
348+
// FIXME there must be a nicer way to write this condition
349+
PathKind::Plain | PathKind::Abs
350+
if self.data.edition == Edition::Edition2015
351+
&& (path.kind == PathKind::Abs || mode == ResolveMode::Import) =>
352+
{
353+
let (_, segment) = match segments.next() {
354+
Some((idx, segment)) => (idx, segment),
303355
None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
304356
};
305-
if let Some(&(def, extern_crate)) = self.data.extern_prelude.get(segment) {
306-
tracing::debug!("absolute path {:?} resolved to crate {:?}", path, def);
307-
PerNs::types(
308-
def.into(),
309-
Visibility::Public,
310-
extern_crate.map(ImportOrExternCrate::ExternCrate),
311-
)
312-
} else {
313-
return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude
357+
tracing::debug!("resolving {:?} in crate root (+ extern prelude)", segment);
358+
self.resolve_name_in_extern_prelude(segment)
359+
}
360+
PathKind::Plain => {
361+
let (_, segment) = match segments.next() {
362+
Some((idx, segment)) => (idx, segment),
363+
None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
364+
};
365+
tracing::debug!("resolving {:?} in module", segment);
366+
self.resolve_name_in_all_preludes(db, segment)
367+
}
368+
PathKind::Abs => match self.resolve_path_abs(&mut segments, path) {
369+
Either::Left(it) => it,
370+
Either::Right(reached_fixed_point) => {
371+
return ResolvePathResult::empty(reached_fixed_point)
314372
}
373+
},
374+
PathKind::DollarCrate(_) | PathKind::Crate | PathKind::Super(_) => {
375+
return ResolvePathResult::empty(ReachedFixedPoint::Yes)
315376
}
316377
};
317378

379+
self.resolve_remaining_segments(segments, curr_per_ns, path, db, shadow, original_module)
380+
}
381+
382+
/// 2018-style absolute path -- only extern prelude
383+
fn resolve_path_abs<'a>(
384+
&self,
385+
segments: &mut impl Iterator<Item = (usize, &'a Name)>,
386+
path: &ModPath,
387+
) -> Either<PerNs, ReachedFixedPoint> {
388+
let segment = match segments.next() {
389+
Some((_, segment)) => segment,
390+
None => return Either::Right(ReachedFixedPoint::Yes),
391+
};
392+
if let Some(&(def, extern_crate)) = self.data.extern_prelude.get(segment) {
393+
tracing::debug!("absolute path {:?} resolved to crate {:?}", path, def);
394+
Either::Left(PerNs::types(
395+
def.into(),
396+
Visibility::Public,
397+
extern_crate.map(ImportOrExternCrate::ExternCrate),
398+
))
399+
} else {
400+
Either::Right(ReachedFixedPoint::No) // extern crate declarations can add to the extern prelude
401+
}
402+
}
403+
404+
fn resolve_remaining_segments<'a>(
405+
&self,
406+
segments: impl Iterator<Item = (usize, &'a Name)>,
407+
mut curr_per_ns: PerNs,
408+
path: &ModPath,
409+
db: &dyn DefDatabase,
410+
shadow: BuiltinShadowMode,
411+
original_module: LocalModuleId,
412+
) -> ResolvePathResult {
318413
for (i, segment) in segments {
319414
let (curr, vis, imp) = match curr_per_ns.take_types_full() {
320415
Some(r) => r,
@@ -475,24 +570,9 @@ impl DefMap {
475570
// they might been shadowed by local names.
476571
return PerNs::none();
477572
}
478-
self.data.extern_prelude.get(name).map_or(PerNs::none(), |&(it, extern_crate)| {
479-
PerNs::types(
480-
it.into(),
481-
Visibility::Public,
482-
extern_crate.map(ImportOrExternCrate::ExternCrate),
483-
)
484-
})
485-
};
486-
let macro_use_prelude = || {
487-
self.macro_use_prelude.get(name).map_or(PerNs::none(), |&(it, _extern_crate)| {
488-
PerNs::macros(
489-
it,
490-
Visibility::Public,
491-
// FIXME?
492-
None, // extern_crate.map(ImportOrExternCrate::ExternCrate),
493-
)
494-
})
573+
self.resolve_name_in_extern_prelude(name)
495574
};
575+
let macro_use_prelude = || self.resolve_in_macro_use_prelude(name);
496576
let prelude = || {
497577
if self.block.is_some() && module == DefMap::ROOT {
498578
return PerNs::none();
@@ -507,6 +587,38 @@ impl DefMap {
507587
.or_else(prelude)
508588
}
509589

590+
fn resolve_name_in_all_preludes(&self, db: &dyn DefDatabase, name: &Name) -> PerNs {
591+
// Resolve in:
592+
// - extern prelude / macro_use prelude
593+
// - std prelude
594+
let extern_prelude = self.resolve_name_in_extern_prelude(name);
595+
let macro_use_prelude = || self.resolve_in_macro_use_prelude(name);
596+
let prelude = || self.resolve_in_prelude(db, name);
597+
598+
extern_prelude.or_else(macro_use_prelude).or_else(prelude)
599+
}
600+
601+
fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs {
602+
self.data.extern_prelude.get(name).map_or(PerNs::none(), |&(it, extern_crate)| {
603+
PerNs::types(
604+
it.into(),
605+
Visibility::Public,
606+
extern_crate.map(ImportOrExternCrate::ExternCrate),
607+
)
608+
})
609+
}
610+
611+
fn resolve_in_macro_use_prelude(&self, name: &Name) -> PerNs {
612+
self.macro_use_prelude.get(name).map_or(PerNs::none(), |&(it, _extern_crate)| {
613+
PerNs::macros(
614+
it,
615+
Visibility::Public,
616+
// FIXME?
617+
None, // extern_crate.map(ImportOrExternCrate::ExternCrate),
618+
)
619+
})
620+
}
621+
510622
fn resolve_name_in_crate_root_or_extern_prelude(
511623
&self,
512624
db: &dyn DefDatabase,
@@ -525,16 +637,7 @@ impl DefMap {
525637
// Don't resolve extern prelude in pseudo-module of a block.
526638
return PerNs::none();
527639
}
528-
self.data.extern_prelude.get(name).copied().map_or(
529-
PerNs::none(),
530-
|(it, extern_crate)| {
531-
PerNs::types(
532-
it.into(),
533-
Visibility::Public,
534-
extern_crate.map(ImportOrExternCrate::ExternCrate),
535-
)
536-
},
537-
)
640+
self.resolve_name_in_extern_prelude(name)
538641
};
539642

540643
from_crate_root.or_else(from_extern_prelude)

src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs

+25
Original file line numberDiff line numberDiff line change
@@ -82,4 +82,29 @@ self::m!(); self::m2!();
8282
"#,
8383
);
8484
}
85+
86+
#[test]
87+
fn no_unresolved_panic_inside_mod_inside_fn() {
88+
check_diagnostics(
89+
r#"
90+
//- /core.rs library crate:core
91+
#[macro_export]
92+
macro_rules! panic {
93+
() => {};
94+
}
95+
96+
//- /lib.rs crate:foo deps:core
97+
#[macro_use]
98+
extern crate core;
99+
100+
fn foo() {
101+
mod init {
102+
pub fn init() {
103+
panic!();
104+
}
105+
}
106+
}
107+
"#,
108+
);
109+
}
85110
}

0 commit comments

Comments
 (0)