Resolve paths to snapshot test libraries absolutely

That is, resolve them globally, not from the test's location.

This *should* result in more robust resolution; for example, previously the code failed to detect the presence of snapshot testing if the snapshot library was renamed in the dependency or was an indirect dependency.
This commit is contained in:
Chayim Refael Friedman 2025-09-09 10:38:20 +03:00
parent 9edc9cbe5d
commit f91b99b471
2 changed files with 85 additions and 23 deletions

View file

@ -10569,6 +10569,77 @@ macro_rules! str {
);
}
#[test]
fn test_runnables_with_snapshot_tests_indirect_dep() {
check_actions(
r#"
//- /lib.rs crate:foo deps:utils
use utils::expect_test::expect;
#[test]
fn test$0() {
let actual = "new25";
expect!["new25"].assert_eq(&actual);
}
//- /expect-test/lib.rs crate:expect_test
struct Expect;
impl Expect {
fn assert_eq(&self, actual: &str) {}
}
#[macro_export]
macro_rules! expect {
($e:expr) => Expect; // dummy
}
//- /utils/lib.rs crate:utils deps:expect_test
pub use expect_test;
"#,
expect![[r#"
[
Reference(
FilePositionWrapper {
file_id: FileId(
0,
),
offset: 44,
},
),
Runnable(
Runnable {
use_name_in_title: false,
nav: NavigationTarget {
file_id: FileId(
0,
),
full_range: 33..121,
focus_range: 44..48,
name: "test",
kind: Function,
},
kind: Test {
test_id: Path(
"test",
),
attr: TestAttr {
ignore: false,
},
},
cfg: None,
update_test: UpdateTest {
expect_test: true,
insta: false,
snapbox: false,
},
},
),
]
"#]],
);
}
#[test]
fn drop_glue() {
check(

View file

@ -4,8 +4,8 @@ use arrayvec::ArrayVec;
use ast::HasName;
use cfg::{CfgAtom, CfgExpr};
use hir::{
AsAssocItem, AttrsWithOwner, HasAttrs, HasCrate, HasSource, ModPath, Name, PathKind, Semantics,
Symbol, db::HirDatabase, sym,
AsAssocItem, AttrsWithOwner, HasAttrs, HasCrate, HasSource, Semantics, Symbol, db::HirDatabase,
sym,
};
use ide_assists::utils::{has_test_related_attribute, test_related_attribute_syn};
use ide_db::{
@ -352,8 +352,7 @@ pub(crate) fn runnable_fn(
.call_site();
let file_range = fn_source.syntax().original_file_range_with_macro_call_input(sema.db);
let update_test =
UpdateTest::find_snapshot_macro(sema, &fn_source.file_syntax(sema.db), file_range);
let update_test = UpdateTest::find_snapshot_macro(sema, file_range);
let cfg = def.attrs(sema.db).cfg();
Some(Runnable { use_name_in_title: false, nav, kind, cfg, update_test })
@ -388,7 +387,7 @@ pub(crate) fn runnable_mod(
file_id: module_source.file_id.original_file(sema.db),
range: module_syntax.text_range(),
};
let update_test = UpdateTest::find_snapshot_macro(sema, &module_syntax, file_range);
let update_test = UpdateTest::find_snapshot_macro(sema, file_range);
Some(Runnable {
use_name_in_title: false,
@ -428,8 +427,7 @@ pub(crate) fn runnable_impl(
let impl_source = sema.source(*def)?;
let impl_syntax = impl_source.syntax();
let file_range = impl_syntax.original_file_range_with_macro_call_input(sema.db);
let update_test =
UpdateTest::find_snapshot_macro(sema, &impl_syntax.file_syntax(sema.db), file_range);
let update_test = UpdateTest::find_snapshot_macro(sema, file_range);
Some(Runnable {
use_name_in_title: false,
@ -475,7 +473,7 @@ fn runnable_mod_outline_definition(
file_id: mod_source.file_id.original_file(sema.db),
range: mod_syntax.text_range(),
};
let update_test = UpdateTest::find_snapshot_macro(sema, &mod_syntax, file_range);
let update_test = UpdateTest::find_snapshot_macro(sema, file_range);
Some(Runnable {
use_name_in_title: false,
@ -641,7 +639,7 @@ pub struct UpdateTest {
pub snapbox: bool,
}
static SNAPSHOT_TEST_MACROS: OnceLock<FxHashMap<&str, Vec<ModPath>>> = OnceLock::new();
static SNAPSHOT_TEST_MACROS: OnceLock<FxHashMap<&str, Vec<[Symbol; 2]>>> = OnceLock::new();
impl UpdateTest {
const EXPECT_CRATE: &str = "expect_test";
@ -665,22 +663,17 @@ impl UpdateTest {
const SNAPBOX_CRATE: &str = "snapbox";
const SNAPBOX_MACROS: &[&str] = &["assert_data_eq", "file", "str"];
fn find_snapshot_macro(
sema: &Semantics<'_, RootDatabase>,
scope: &SyntaxNode,
file_range: hir::FileRange,
) -> Self {
fn find_snapshot_macro(sema: &Semantics<'_, RootDatabase>, file_range: hir::FileRange) -> Self {
fn init<'a>(
krate_name: &'a str,
paths: &[&str],
map: &mut FxHashMap<&'a str, Vec<ModPath>>,
map: &mut FxHashMap<&'a str, Vec<[Symbol; 2]>>,
) {
let mut res = Vec::with_capacity(paths.len());
let krate = Name::new_symbol_root(Symbol::intern(krate_name));
let krate = Symbol::intern(krate_name);
for path in paths {
let segments = [krate.clone(), Name::new_symbol_root(Symbol::intern(path))];
let mod_path = ModPath::from_segments(PathKind::Abs, segments);
res.push(mod_path);
let segments = [krate.clone(), Symbol::intern(path)];
res.push(segments);
}
map.insert(krate_name, res);
}
@ -694,11 +687,9 @@ impl UpdateTest {
});
let search_scope = SearchScope::file_range(file_range);
let find_macro = |paths: &[ModPath]| {
let find_macro = |paths: &[[Symbol; 2]]| {
for path in paths {
let Some(items) = sema.resolve_mod_path(scope, path) else {
continue;
};
let items = hir::resolve_absolute_path(sema.db, path.iter().cloned());
for item in items {
if let hir::ItemInNs::Macros(makro) = item
&& Definition::Macro(makro)