test(rust): add regression coverage for trait-object dispatch (#2604)

New minimal fixture (single trait + impl + &dyn Trait call site, no other
same-named callers) proves the dyn-dispatch CALLS edge discriminates: fails
against the pre-fix source (0 edges) and passes against the two preceding
commits' fix (exactly 1 edge, verified via the CLI analyze pipeline against
a standalone repo).

The existing rust-abstract-dispatch fixture was NOT extended for this,
deliberately: it already has other callers referencing the same method
names (process()'s repo.find()/save()/count()), and an existing resolution
fallback picks those up via simple-name matching regardless of receiver
type — masking this specific defect in the in-process test-pipeline path.
A dedicated, single-caller fixture keeps the regression test load-bearing.
This commit is contained in:
Gergo Magyar 2026-07-21 14:48:54 +00:00
parent 2d545dcd2a
commit aa42cc593b
2 changed files with 38 additions and 0 deletions

View file

@ -0,0 +1,15 @@
pub trait Behaviour {
fn trait_target(&self) -> u32;
}
pub struct Impl1;
impl Behaviour for Impl1 {
fn trait_target(&self) -> u32 {
7
}
}
pub fn calls_via_dyn(b: &dyn Behaviour) -> u32 {
b.trait_target()
}

View file

@ -1951,6 +1951,29 @@ describe('Rust abstract dispatch (Repository trait)', () => {
});
});
// ---------------------------------------------------------------------------
// #2604: trait-object (&dyn Trait) receiver dispatch
// ---------------------------------------------------------------------------
describe('Rust dyn trait-object dispatch (#2604)', () => {
let result: PipelineResult;
beforeAll(async () => {
result = await runPipelineFromRepo(path.join(FIXTURES, 'rust-dyn-trait-object'), () => {});
}, 60000);
it('detects Impl1 struct and Behaviour trait', () => {
expect(getNodesByLabel(result, 'Struct')).toContain('Impl1');
expect(getNodesByLabel(result, 'Trait')).toContain('Behaviour');
});
it('emits exactly one CALLS edge from calls_via_dyn(b: &dyn Behaviour) to trait_target', () => {
const calls = getRelationships(result, 'CALLS');
const dynCalls = calls.filter((c) => c.source === 'calls_via_dyn' && c.target === 'trait_target');
expect(dynCalls.length).toBe(1);
});
});
// ---------------------------------------------------------------------------
// SM-11: Rust Child extends Parent — qualified-syntax MRO
//