mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 21:43:37 +00:00
Fix visited module tracking not clearing itself on backtracking
This commit is contained in:
parent
2a32976e90
commit
4b2a123280
2 changed files with 85 additions and 45 deletions
|
@ -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(
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue