use super::support::{
build_graph_only, build_workspace, callees_of, empty_cfg_test, graph_contains_edge, three_layer,
};
use std::collections::HashSet;
#[test]
fn concrete_ufcs_via_sibling_submodule_import_traces_edge() {
let ws = build_workspace(&[
(
"src/application/mod.rs",
r#"
pub mod response;
use serde::Serialize;
use response::ConcreteOutput;
pub fn dispatcher<T: Serialize>(value: &T) -> ConcreteOutput {
let body = serde_json::to_string(value).unwrap_or_default();
ConcreteOutput::new(body)
}
"#,
),
(
"src/application/response.rs",
r#"
pub struct ConcreteOutput { pub body: String }
impl ConcreteOutput {
pub fn new(body: String) -> Self { Self { body } }
}
"#,
),
(
"src/cli/mod.rs",
r#"
use crate::application::dispatcher;
pub fn cmd_run() -> String {
let out = dispatcher(&42u32);
out.body
}
"#,
),
]);
let graph = build_graph_only(&ws, &three_layer(), &empty_cfg_test(), &HashSet::new());
let dispatcher = "crate::application::dispatcher";
let new_target = "crate::application::response::ConcreteOutput::new";
assert!(
graph_contains_edge(&graph, dispatcher, new_target),
"concrete UFCS `ConcreteOutput::new(body)` inside generic \
`dispatcher<T>` body must emit edge to `{new_target}` when \
`ConcreteOutput` is named-imported from a sibling submodule. \
dispatcher callees: {:?}",
callees_of(&graph, dispatcher),
);
}
#[test]
fn sibling_submodule_wins_over_crate_root_with_same_leaf_name() {
let ws = build_workspace(&[
(
"src/response/mod.rs",
r#"
pub struct OuterRoot;
impl OuterRoot {
pub fn run() {}
}
"#,
),
(
"src/application/response.rs",
r#"
pub struct Local;
impl Local {
pub fn run() {}
}
"#,
),
(
"src/application/mod.rs",
r#"
pub mod response;
use response::Local;
pub fn dispatch() {
Local::run();
}
"#,
),
(
"src/cli/mod.rs",
r#"
use crate::application::dispatch;
pub fn cmd_run() {
dispatch();
}
"#,
),
]);
let graph = build_graph_only(&ws, &three_layer(), &empty_cfg_test(), &HashSet::new());
let dispatch = "crate::application::dispatch";
let local_target = "crate::application::response::Local::run";
let crate_root_target = "crate::response::OuterRoot::run";
assert!(
graph_contains_edge(&graph, dispatch, local_target),
"Rust 2018+: `use response::Local` in `application/mod.rs` must \
resolve to local sibling `crate::application::response::Local`, \
not crate-root `crate::response`. dispatch callees: {:?}",
callees_of(&graph, dispatch),
);
assert!(
!graph_contains_edge(&graph, dispatch, crate_root_target),
"must NOT route to crate-root `{crate_root_target}` — that's \
the wrong target. dispatch callees: {:?}",
callees_of(&graph, dispatch),
);
}
#[test]
fn inline_mod_sibling_import_traces_edge() {
let ws = build_workspace(&[
(
"src/application/mod.rs",
r#"
pub mod outer {
pub mod inner {
pub struct Local;
impl Local {
pub fn run() {}
}
}
use inner::Local;
pub fn dispatch() {
Local::run();
}
}
"#,
),
(
"src/cli/mod.rs",
r#"
use crate::application::outer::dispatch;
pub fn cmd_run() {
dispatch();
}
"#,
),
]);
let graph = build_graph_only(&ws, &three_layer(), &empty_cfg_test(), &HashSet::new());
let dispatch = "crate::application::outer::dispatch";
let target = "crate::application::outer::inner::Local::run";
assert!(
graph_contains_edge(&graph, dispatch, target),
"inline-mod sibling import `use inner::Local` from `mod outer` \
must trace to `{target}`. dispatch callees: {:?}",
callees_of(&graph, dispatch),
);
}
#[test]
fn orphan_file_does_not_act_as_sibling_submodule() {
let ws = build_workspace(&[
(
"src/application/mod.rs",
r#"
use orphan::Local;
pub fn dispatch() {
Local::run();
}
"#,
),
(
"src/application/orphan.rs",
r#"
pub struct Local;
impl Local {
pub fn run() {}
}
"#,
),
(
"src/cli/mod.rs",
r#"
use crate::application::dispatch;
pub fn cmd_run() {
dispatch();
}
"#,
),
]);
let graph = build_graph_only(&ws, &three_layer(), &empty_cfg_test(), &HashSet::new());
let dispatch = "crate::application::dispatch";
let phantom_target = "crate::application::orphan::Local::run";
assert!(
!graph_contains_edge(&graph, dispatch, phantom_target),
"orphan file `application/orphan.rs` (no `mod orphan;` decl in \
application/mod.rs) must NOT be treated as a real sibling \
submodule — `use orphan::Local` is an external/unresolved \
import, not a workspace canonical. dispatch callees: {:?}",
callees_of(&graph, dispatch),
);
}
#[test]
fn private_mod_sibling_import_resolves_even_when_ancestor_chain_is_hidden() {
let ws = build_workspace(&[
(
"src/lib.rs",
r#"
pub mod application;
"#,
),
(
"src/application/mod.rs",
r#"
mod hidden;
"#,
),
(
"src/application/hidden.rs",
r#"
mod response;
use response::Local;
pub fn dispatch() {
Local::run();
}
"#,
),
(
"src/application/hidden/response.rs",
r#"
pub struct Local;
impl Local {
pub fn run() {}
}
"#,
),
]);
let graph = build_graph_only(&ws, &three_layer(), &empty_cfg_test(), &HashSet::new());
let dispatch = "crate::application::hidden::dispatch";
let target = "crate::application::hidden::response::Local::run";
assert!(
graph_contains_edge(&graph, dispatch, target),
"private `mod response;` declared INSIDE a file whose ancestor \
chain is hidden by a non-root private `mod hidden;` is still a \
real child module for code inside `hidden.rs`. \
`use response::Local` must normalise to `{target}` regardless \
of public-visibility filtering. dispatch callees: {:?}",
callees_of(&graph, dispatch),
);
}
#[test]
fn pub_use_reexport_call_resolves_to_real_definition() {
let ws = build_workspace(&[
(
"src/application/middleware/mod.rs",
r#"
pub mod savings_recorder;
pub use savings_recorder::record_operation;
"#,
),
(
"src/application/middleware/savings_recorder.rs",
r#"
pub fn record_operation() {}
"#,
),
(
"src/application/session.rs",
r#"
use crate::application::middleware;
pub struct Session;
impl Session {
pub fn search(&self) {
middleware::record_operation();
}
}
"#,
),
]);
let graph = build_graph_only(&ws, &three_layer(), &empty_cfg_test(), &HashSet::new());
let search = "crate::application::session::Session::search";
let real_target = "crate::application::middleware::savings_recorder::record_operation";
assert!(
graph_contains_edge(&graph, search, real_target),
"call site `middleware::record_operation()` not resolved through \
the `pub use` re-export to the real definition `{real_target}`.\n\
search callees: {:?}",
callees_of(&graph, search),
);
}
#[test]
fn fallback_walker_skips_stale_mod_rs_when_file_rs_is_the_winner() {
let ws = build_workspace(&[
(
"src/foo.rs",
r#"
// Live module file. Does NOT declare `mod beta;`. The
// legitimate behaviour for `use beta::T;` here is "extern
// import" — not a workspace edge.
use beta::T;
pub fn dispatch() { T::run(); }
"#,
),
("src/foo/mod.rs", "pub mod beta;\n"),
(
"src/foo/beta.rs",
r#"
pub struct T;
impl T { pub fn run() {} }
"#,
),
]);
let graph = build_graph_only(&ws, &three_layer(), &empty_cfg_test(), &HashSet::new());
let dispatch = "crate::foo::dispatch";
let stale_phantom = "crate::foo::beta::T::run";
assert!(
!graph_contains_edge(&graph, dispatch, stale_phantom),
"in the fallback (no `src/lib.rs` / `src/main.rs`) walker, a \
stale `src/foo/mod.rs` must NOT register its submodules once \
`src/foo.rs` is the tie-break winner — otherwise \
`use beta::T;` inside `foo.rs` false-resolves to \
`{stale_phantom}`. dispatch callees: {:?}",
callees_of(&graph, dispatch),
);
}
#[test]
fn absolute_use_alias_does_not_re_canonicalise_to_workspace() {
let ws = build_workspace(&[
("src/lib.rs", "pub mod ext;\npub mod app;\n"),
(
"src/ext.rs",
r#"
pub struct A;
impl A { pub fn m() {} }
"#,
),
(
"src/app.rs",
r#"
// Absolute path — refers to the extern crate `ext`, NOT
// the workspace's `crate::ext`.
use ::ext::A as Local;
pub fn dispatch() { Local::m(); }
"#,
),
]);
let graph = build_graph_only(&ws, &three_layer(), &empty_cfg_test(), &HashSet::new());
let dispatch = "crate::app::dispatch";
let workspace_phantom = "crate::ext::A::m";
assert!(
!graph_contains_edge(&graph, dispatch, workspace_phantom),
"`use ::ext::A as Local;` is an extern-rooted import (the \
leading `::` means extern crate `ext`, not the workspace's \
`crate::ext`). `Local::m()` must NOT route to \
`{workspace_phantom}` even when a same-named workspace \
module exists. dispatch callees: {:?}",
callees_of(&graph, dispatch),
);
}
#[test]
fn file_rs_wins_over_dir_mod_rs_when_both_back_the_same_module_path() {
let ws = build_workspace(&[
("src/lib.rs", "pub mod foo;\n"),
(
"src/foo.rs",
r#"
pub mod alpha;
use alpha::Local;
pub fn dispatch() { Local::run(); }
"#,
),
(
"src/foo/alpha.rs",
r#"
pub struct Local;
impl Local { pub fn run() {} }
"#,
),
("src/foo/mod.rs", "pub mod beta;\n"),
("src/foo/beta.rs", "pub fn unused() {}\n"),
]);
let graph = build_graph_only(&ws, &three_layer(), &empty_cfg_test(), &HashSet::new());
let dispatch = "crate::foo::dispatch";
let target = "crate::foo::alpha::Local::run";
assert!(
graph_contains_edge(&graph, dispatch, target),
"when both `src/foo.rs` and `src/foo/mod.rs` exist, the analyser \
must deterministically prefer `foo.rs` (Rust 2018+ convention) \
and walk its declared submodules. `use alpha::Local;` inside \
`foo.rs` must resolve to `{target}`. dispatch callees: {:?}",
callees_of(&graph, dispatch),
);
}