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

Commit 80dd021

Browse files
committed
Fix visited module tracking not clearing itself on backtracking
1 parent d934433 commit 80dd021

File tree

2 files changed

+86
-46
lines changed

2 files changed

+86
-46
lines changed

src/tools/rust-analyzer/crates/hir-def/src/find_path.rs

Lines changed: 86 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use rustc_hash::FxHashSet;
1313
use crate::{
1414
db::DefDatabase,
1515
item_scope::ItemInNs,
16-
nameres::DefMap,
16+
nameres::{DefMap, ModuleData},
1717
path::{ModPath, PathKind},
1818
visibility::{Visibility, VisibilityExplicitness},
1919
ImportPathConfig, ModuleDefId, ModuleId,
@@ -161,7 +161,7 @@ fn find_path_inner(ctx: &FindPathCtx<'_>, item: ItemInNs, max_len: usize) -> Opt
161161
#[tracing::instrument(skip_all)]
162162
fn find_path_for_module(
163163
ctx: &FindPathCtx<'_>,
164-
visited_modules: &mut FxHashSet<ModuleId>,
164+
visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>,
165165
module_id: ModuleId,
166166
maybe_extern: bool,
167167
max_len: usize,
@@ -338,7 +338,7 @@ fn is_kw_kind_relative_to_from(
338338
#[tracing::instrument(skip_all)]
339339
fn calculate_best_path(
340340
ctx: &FindPathCtx<'_>,
341-
visited_modules: &mut FxHashSet<ModuleId>,
341+
visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>,
342342
item: ItemInNs,
343343
max_len: usize,
344344
best_choice: &mut Option<Choice>,
@@ -445,28 +445,32 @@ fn calculate_best_path(
445445

446446
fn calculate_best_path_local(
447447
ctx: &FindPathCtx<'_>,
448-
visited_modules: &mut FxHashSet<ModuleId>,
448+
visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>,
449449
item: ItemInNs,
450450
max_len: usize,
451451
best_choice: &mut Option<Choice>,
452452
) {
453453
// FIXME: cache the `find_local_import_locations` output?
454-
find_local_import_locations(ctx.db, item, ctx.from, ctx.from_def_map, |name, module_id| {
455-
if !visited_modules.insert(module_id) {
456-
return;
457-
}
458-
// we are looking for paths of length up to best_path_len, any longer will make it be
459-
// less optimal. The -1 is due to us pushing name onto it afterwards.
460-
if let Some(choice) = find_path_for_module(
461-
ctx,
462-
visited_modules,
463-
module_id,
464-
false,
465-
best_choice.as_ref().map_or(max_len, |it| it.path.len()) - 1,
466-
) {
467-
Choice::try_select(best_choice, choice, ctx.cfg.prefer_prelude, name.clone());
468-
}
469-
});
454+
find_local_import_locations(
455+
ctx.db,
456+
item,
457+
ctx.from,
458+
ctx.from_def_map,
459+
visited_modules,
460+
|visited_modules, name, module_id| {
461+
// we are looking for paths of length up to best_path_len, any longer will make it be
462+
// less optimal. The -1 is due to us pushing name onto it afterwards.
463+
if let Some(choice) = find_path_for_module(
464+
ctx,
465+
visited_modules,
466+
module_id,
467+
false,
468+
best_choice.as_ref().map_or(max_len, |it| it.path.len()) - 1,
469+
) {
470+
Choice::try_select(best_choice, choice, ctx.cfg.prefer_prelude, name.clone());
471+
}
472+
},
473+
);
470474
}
471475

472476
struct Choice {
@@ -551,7 +555,8 @@ fn find_local_import_locations(
551555
item: ItemInNs,
552556
from: ModuleId,
553557
def_map: &DefMap,
554-
mut cb: impl FnMut(&Name, ModuleId),
558+
visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>,
559+
mut cb: impl FnMut(&mut FxHashSet<(ItemInNs, ModuleId)>, &Name, ModuleId),
555560
) {
556561
let _p = tracing::info_span!("find_local_import_locations").entered();
557562

@@ -564,32 +569,29 @@ fn find_local_import_locations(
564569
let mut worklist = def_map[from.local_id]
565570
.children
566571
.values()
567-
.map(|child| def_map.module_id(*child))
568-
// FIXME: do we need to traverse out of block expressions here?
572+
.map(|&child| def_map.module_id(child))
569573
.chain(iter::successors(from.containing_module(db), |m| m.containing_module(db)))
574+
.zip(iter::repeat(false))
570575
.collect::<Vec<_>>();
571-
let mut seen: FxHashSet<_> = FxHashSet::default();
572576

573577
let def_map = def_map.crate_root().def_map(db);
574-
575-
while let Some(module) = worklist.pop() {
576-
if !seen.insert(module) {
577-
continue; // already processed this module
578+
let mut block_def_map;
579+
let mut cursor = 0;
580+
581+
while let Some(&mut (module, ref mut processed)) = worklist.get_mut(cursor) {
582+
cursor += 1;
583+
if !visited_modules.insert((item, module)) {
584+
// already processed this module
585+
continue;
578586
}
579-
let ext_def_map;
580-
let data = if module.krate == from.krate {
581-
if module.block.is_some() {
582-
// Re-query the block's DefMap
583-
ext_def_map = module.def_map(db);
584-
&ext_def_map[module.local_id]
585-
} else {
586-
// Reuse the root DefMap
587-
&def_map[module.local_id]
588-
}
587+
*processed = true;
588+
let data = if module.block.is_some() {
589+
// Re-query the block's DefMap
590+
block_def_map = module.def_map(db);
591+
&block_def_map[module.local_id]
589592
} else {
590-
// The crate might reexport a module defined in another crate.
591-
ext_def_map = module.def_map(db);
592-
&ext_def_map[module.local_id]
593+
// Reuse the root DefMap
594+
&def_map[module.local_id]
593595
};
594596

595597
if let Some((name, vis, declared)) = data.scope.name_of(item) {
@@ -612,18 +614,30 @@ fn find_local_import_locations(
612614
// the item and we're a submodule of it, so can we.
613615
// Also this keeps the cached data smaller.
614616
if declared || is_pub_or_explicit {
615-
cb(name, module);
617+
cb(visited_modules, name, module);
616618
}
617619
}
618620
}
619621

620622
// Descend into all modules visible from `from`.
621623
for (module, vis) in data.scope.modules_in_scope() {
624+
if module.krate != from.krate {
625+
// We don't need to look at modules from other crates as our item has to be in the
626+
// current crate
627+
continue;
628+
}
629+
if visited_modules.contains(&(item, module)) {
630+
continue;
631+
}
632+
622633
if vis.is_visible_from(db, from) {
623-
worklist.push(module);
634+
worklist.push((module, false));
624635
}
625636
}
626637
}
638+
worklist.into_iter().filter(|&(_, processed)| processed).for_each(|(module, _)| {
639+
visited_modules.remove(&(item, module));
640+
});
627641
}
628642

629643
#[cfg(test)]
@@ -1591,8 +1605,6 @@ fn main() {
15911605

15921606
#[test]
15931607
fn from_inside_module() {
1594-
// This worked correctly, but the test suite logic was broken.
1595-
cov_mark::check!(submodule_in_testdb);
15961608
check_found_path(
15971609
r#"
15981610
mod baz {
@@ -1617,6 +1629,35 @@ mod bar {
16171629
)
16181630
}
16191631

1632+
#[test]
1633+
fn from_inside_module2() {
1634+
check_found_path(
1635+
r#"
1636+
mod qux {
1637+
pub mod baz {
1638+
pub struct Foo {}
1639+
}
1640+
1641+
mod bar {
1642+
fn bar() {
1643+
$0;
1644+
}
1645+
}
1646+
}
1647+
1648+
"#,
1649+
"crate::qux::baz::Foo",
1650+
expect![[r#"
1651+
Plain (imports ✔): super::baz::Foo
1652+
Plain (imports ✖): super::baz::Foo
1653+
ByCrate(imports ✔): crate::qux::baz::Foo
1654+
ByCrate(imports ✖): crate::qux::baz::Foo
1655+
BySelf (imports ✔): super::baz::Foo
1656+
BySelf (imports ✖): super::baz::Foo
1657+
"#]],
1658+
)
1659+
}
1660+
16201661
#[test]
16211662
fn from_inside_module_with_inner_items() {
16221663
check_found_path(

src/tools/rust-analyzer/crates/hir-def/src/test_db.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,6 @@ impl TestDB {
148148
};
149149

150150
if size != Some(new_size) {
151-
cov_mark::hit!(submodule_in_testdb);
152151
size = Some(new_size);
153152
res = module;
154153
}

0 commit comments

Comments
 (0)