#![cfg(feature = "lang-rust")]
mod language_fixture;
use language_fixture::Fixture;
use weavatrix_rust::{Analyzer, EdgeKind, NodeKind};
const PIPELINE: &str = r"
pub struct Buffer;
pub fn push(value: u32) -> u32 {
value
}
pub fn helper() -> u32 {
2
}
impl Buffer {
pub fn step(&self) -> u32 {
1
}
pub fn run(&self, values: &mut Vec<u32>, path: &str) -> u32 {
values.push(9);
let _ = std::fs::read_to_string(path);
let _ = Elsewhere::open(path);
self.step() + helper()
}
}
pub struct Elsewhere;
";
fn analyze() -> weavatrix_rust::Snapshot {
let fixture = Fixture::new();
fixture.write("src/lib.rs", "mod pipeline;\n");
fixture.write("src/pipeline.rs", PIPELINE);
Analyzer::default().analyze(&fixture.root).unwrap()
}
fn node(snapshot: &weavatrix_rust::Snapshot, kind: &NodeKind, label: &str) -> String {
snapshot
.nodes
.iter()
.find(|node| node.kind == *kind && node.label == label)
.unwrap_or_else(|| panic!("{label} must be a {kind:?} node"))
.id
.to_string()
}
fn calls(snapshot: &weavatrix_rust::Snapshot, from: &str, to: &str) -> bool {
snapshot.edges.iter().any(|edge| {
edge.kind == EdgeKind::Calls && edge.source.as_str() == from && edge.target.as_str() == to
})
}
#[test]
fn a_method_call_never_binds_to_a_free_function_of_the_same_name() {
let snapshot = analyze();
let run = node(&snapshot, &NodeKind::Method, "run");
let push = node(&snapshot, &NodeKind::Function, "push");
assert!(
!calls(&snapshot, &run, &push),
"values.push(9) is Vec::push, not this file's free `fn push`"
);
}
#[test]
fn a_self_method_call_binds_to_the_method_on_the_implementing_type() {
let snapshot = analyze();
let run = node(&snapshot, &NodeKind::Method, "run");
let step = node(&snapshot, &NodeKind::Method, "step");
assert!(
calls(&snapshot, &run, &step),
"inside an impl block `self` is the type being implemented, so this \
one receiver is proven by position"
);
let detail = snapshot
.edges
.iter()
.find(|edge| {
edge.kind == EdgeKind::Calls
&& edge.source.as_str() == run
&& edge.target.as_str() == step
})
.and_then(|edge| edge.provenance.detail.clone());
assert_eq!(
detail.as_deref(),
Some("resolved as a method on the implementing type")
);
}
#[test]
fn a_bare_call_in_the_files_own_scope_still_binds() {
let snapshot = analyze();
let run = node(&snapshot, &NodeKind::Method, "run");
let helper = node(&snapshot, &NodeKind::Function, "helper");
assert!(
calls(&snapshot, &run, &helper),
"a bare `helper()` names a symbol this file can see"
);
}
#[test]
fn a_type_path_call_records_the_type_and_not_a_guessed_target() {
let snapshot = analyze();
let run = node(&snapshot, &NodeKind::Method, "run");
let elsewhere = node(&snapshot, &NodeKind::Struct, "Elsewhere");
assert!(
snapshot.edges.iter().any(|edge| {
edge.kind == EdgeKind::References
&& edge.source.as_str() == run
&& edge.target.as_str() == elsewhere
}),
"the owning segment of the path is still evidence"
);
assert!(
snapshot.edges.iter().all(|edge| {
edge.kind != EdgeKind::Calls
|| edge.source.as_str() != run
|| !edge.target.as_str().contains("read_to_string")
}),
"std::fs::read_to_string is not a symbol of this repository"
);
}