Fix visited module tracking not clearing itself on backtracking

This commit is contained in:
Lukas Wirth 2024-07-21 12:17:50 +02:00
parent 2a32976e90
commit 4b2a123280
2 changed files with 85 additions and 45 deletions

View file

@ -13,7 +13,7 @@ use rustc_hash::FxHashSet;
use crate::{ use crate::{
db::DefDatabase, db::DefDatabase,
item_scope::ItemInNs, item_scope::ItemInNs,
nameres::DefMap, nameres::{DefMap, ModuleData},
path::{ModPath, PathKind}, path::{ModPath, PathKind},
visibility::{Visibility, VisibilityExplicitness}, visibility::{Visibility, VisibilityExplicitness},
ImportPathConfig, ModuleDefId, ModuleId, ImportPathConfig, ModuleDefId, ModuleId,
@ -161,7 +161,7 @@ fn find_path_inner(ctx: &FindPathCtx<'_>, item: ItemInNs, max_len: usize) -> Opt
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
fn find_path_for_module( fn find_path_for_module(
ctx: &FindPathCtx<'_>, ctx: &FindPathCtx<'_>,
visited_modules: &mut FxHashSet<ModuleId>, visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>,
module_id: ModuleId, module_id: ModuleId,
maybe_extern: bool, maybe_extern: bool,
max_len: usize, max_len: usize,
@ -338,7 +338,7 @@ fn is_kw_kind_relative_to_from(
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
fn calculate_best_path( fn calculate_best_path(
ctx: &FindPathCtx<'_>, ctx: &FindPathCtx<'_>,
visited_modules: &mut FxHashSet<ModuleId>, visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>,
item: ItemInNs, item: ItemInNs,
max_len: usize, max_len: usize,
best_choice: &mut Option<Choice>, best_choice: &mut Option<Choice>,
@ -445,28 +445,32 @@ fn calculate_best_path(
fn calculate_best_path_local( fn calculate_best_path_local(
ctx: &FindPathCtx<'_>, ctx: &FindPathCtx<'_>,
visited_modules: &mut FxHashSet<ModuleId>, visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>,
item: ItemInNs, item: ItemInNs,
max_len: usize, max_len: usize,
best_choice: &mut Option<Choice>, best_choice: &mut Option<Choice>,
) { ) {
// FIXME: cache the `find_local_import_locations` output? // FIXME: cache the `find_local_import_locations` output?
find_local_import_locations(ctx.db, item, ctx.from, ctx.from_def_map, |name, module_id| { find_local_import_locations(
if !visited_modules.insert(module_id) { ctx.db,
return; item,
} ctx.from,
// we are looking for paths of length up to best_path_len, any longer will make it be ctx.from_def_map,
// less optimal. The -1 is due to us pushing name onto it afterwards. visited_modules,
if let Some(choice) = find_path_for_module( |visited_modules, name, module_id| {
ctx, // we are looking for paths of length up to best_path_len, any longer will make it be
visited_modules, // less optimal. The -1 is due to us pushing name onto it afterwards.
module_id, if let Some(choice) = find_path_for_module(
false, ctx,
best_choice.as_ref().map_or(max_len, |it| it.path.len()) - 1, visited_modules,
) { module_id,
Choice::try_select(best_choice, choice, ctx.cfg.prefer_prelude, name.clone()); false,
} best_choice.as_ref().map_or(max_len, |it| it.path.len()) - 1,
}); ) {
Choice::try_select(best_choice, choice, ctx.cfg.prefer_prelude, name.clone());
}
},
);
} }
struct Choice { struct Choice {
@ -551,7 +555,8 @@ fn find_local_import_locations(
item: ItemInNs, item: ItemInNs,
from: ModuleId, from: ModuleId,
def_map: &DefMap, def_map: &DefMap,
mut cb: impl FnMut(&Name, ModuleId), visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>,
mut cb: impl FnMut(&mut FxHashSet<(ItemInNs, ModuleId)>, &Name, ModuleId),
) { ) {
let _p = tracing::info_span!("find_local_import_locations").entered(); let _p = tracing::info_span!("find_local_import_locations").entered();
@ -564,32 +569,29 @@ fn find_local_import_locations(
let mut worklist = def_map[from.local_id] let mut worklist = def_map[from.local_id]
.children .children
.values() .values()
.map(|child| def_map.module_id(*child)) .map(|&child| def_map.module_id(child))
// FIXME: do we need to traverse out of block expressions here?
.chain(iter::successors(from.containing_module(db), |m| m.containing_module(db))) .chain(iter::successors(from.containing_module(db), |m| m.containing_module(db)))
.zip(iter::repeat(false))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let mut seen: FxHashSet<_> = FxHashSet::default();
let def_map = def_map.crate_root().def_map(db); let def_map = def_map.crate_root().def_map(db);
let mut block_def_map;
let mut cursor = 0;
while let Some(module) = worklist.pop() { while let Some(&mut (module, ref mut processed)) = worklist.get_mut(cursor) {
if !seen.insert(module) { cursor += 1;
continue; // already processed this module if !visited_modules.insert((item, module)) {
// already processed this module
continue;
} }
let ext_def_map; *processed = true;
let data = if module.krate == from.krate { let data = if module.block.is_some() {
if module.block.is_some() { // Re-query the block's DefMap
// Re-query the block's DefMap block_def_map = module.def_map(db);
ext_def_map = module.def_map(db); &block_def_map[module.local_id]
&ext_def_map[module.local_id]
} else {
// Reuse the root DefMap
&def_map[module.local_id]
}
} else { } else {
// The crate might reexport a module defined in another crate. // Reuse the root DefMap
ext_def_map = module.def_map(db); &def_map[module.local_id]
&ext_def_map[module.local_id]
}; };
if let Some((name, vis, declared)) = data.scope.name_of(item) { if let Some((name, vis, declared)) = data.scope.name_of(item) {
@ -612,18 +614,30 @@ fn find_local_import_locations(
// the item and we're a submodule of it, so can we. // the item and we're a submodule of it, so can we.
// Also this keeps the cached data smaller. // Also this keeps the cached data smaller.
if declared || is_pub_or_explicit { if declared || is_pub_or_explicit {
cb(name, module); cb(visited_modules, name, module);
} }
} }
} }
// Descend into all modules visible from `from`. // Descend into all modules visible from `from`.
for (module, vis) in data.scope.modules_in_scope() { for (module, vis) in data.scope.modules_in_scope() {
if module.krate != from.krate {
// We don't need to look at modules from other crates as our item has to be in the
// current crate
continue;
}
if visited_modules.contains(&(item, module)) {
continue;
}
if vis.is_visible_from(db, from) { if vis.is_visible_from(db, from) {
worklist.push(module); worklist.push((module, false));
} }
} }
} }
worklist.into_iter().filter(|&(_, processed)| processed).for_each(|(module, _)| {
visited_modules.remove(&(item, module));
});
} }
#[cfg(test)] #[cfg(test)]
@ -1591,8 +1605,6 @@ fn main() {
#[test] #[test]
fn from_inside_module() { fn from_inside_module() {
// This worked correctly, but the test suite logic was broken.
cov_mark::check!(submodule_in_testdb);
check_found_path( check_found_path(
r#" r#"
mod baz { mod baz {
@ -1617,6 +1629,35 @@ mod bar {
) )
} }
#[test]
fn from_inside_module2() {
check_found_path(
r#"
mod qux {
pub mod baz {
pub struct Foo {}
}
mod bar {
fn bar() {
$0;
}
}
}
"#,
"crate::qux::baz::Foo",
expect![[r#"
Plain (imports ): super::baz::Foo
Plain (imports ): super::baz::Foo
ByCrate(imports ): crate::qux::baz::Foo
ByCrate(imports ): crate::qux::baz::Foo
BySelf (imports ): super::baz::Foo
BySelf (imports ): super::baz::Foo
"#]],
)
}
#[test] #[test]
fn from_inside_module_with_inner_items() { fn from_inside_module_with_inner_items() {
check_found_path( check_found_path(

View file

@ -148,7 +148,6 @@ impl TestDB {
}; };
if size != Some(new_size) { if size != Some(new_size) {
cov_mark::hit!(submodule_in_testdb);
size = Some(new_size); size = Some(new_size);
res = module; res = module;
} }