Add regression tests for the split impl block self call fix

Covers the issue's own shape (impl Foo split across two files, a
self call in one block reaching a method defined in the other) and a
same file negative control proving the pre existing bare name
resolution is unaffected.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit 2d161af639)
This commit is contained in:
ayushcodes10 authored and safishamsi committed 2026-09-16 14:43:12 +01:00
1 parent 0a92b56bd1
commit d77dcb9a67
1 file changed
+86
+86
View File
@@ -0,0 +1,86 @@
"""Rust cross-file `self.method()` resolution (#2234).
The shared cross-file call pass drops every member call (a bare method name
like ``log`` has no import evidence and collides with any top-level function
named ``log`` in the corpus, #543/#1219). Every other member-call-heavy
language has a dedicated recovery pass behind that guard; Rust had none, so
`self.apply_block()` never produced a `calls` edge unless the caller and the
method happened to live in the same file.
`self.method()` inside `impl Foo { .. }` types the receiver as `Foo`
syntactically, no inference needed -- these tests exercise that resolution,
including the case Rust makes routine: an `impl Foo` block split across many
files, each minting its own graph node for `Foo`.
"""
from __future__ import annotations
from pathlib import Path
from graphify.extract import extract
def _calls(tmp_path: Path, files: dict[str, str]):
paths = []
for name, body in files.items():
path = tmp_path / name
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(body, encoding="utf-8")
paths.append(path)
result = extract(paths, cache_root=tmp_path / "graphify-out")
calls = {
(edge["source"], edge["target"]): edge
for edge in result["edges"]
if edge.get("relation") == "calls"
}
return calls, result
def _find(result: dict, label: str, id_contains: str) -> str:
return next(
node["id"]
for node in result["nodes"]
if node.get("label") == label and id_contains in node["id"]
)
def test_self_call_resolves_across_files_to_a_split_impl_block(tmp_path: Path):
"""The issue's own shape: `impl Foo` split across two files, a `self.`
call in one block reaching a method defined in the other."""
calls, result = _calls(tmp_path, {
"state.rs": (
"pub struct BlockchainState { height: u64 }\n"
),
"apply.rs": (
"use crate::state::BlockchainState;\n"
"impl BlockchainState {\n"
" pub fn apply_block(&self) -> u64 { self.height }\n"
"}\n"
),
"rollback.rs": (
"use crate::state::BlockchainState;\n"
"impl BlockchainState {\n"
" pub fn rollback_one_block(&self) -> u64 { self.apply_block() }\n"
"}\n"
),
})
caller = _find(result, ".rollback_one_block()", "rollback")
callee = _find(result, ".apply_block()", "apply")
assert (caller, callee) in calls
assert calls[(caller, callee)]["confidence"] == "EXTRACTED"
def test_self_call_same_file_control_is_unaffected(tmp_path: Path):
"""Negative control: the pre-existing same-file bare-name resolution
(which never needed this pass) must keep working exactly as before."""
calls, result = _calls(tmp_path, {
"lib.rs": (
"struct Widget { n: u32 }\n"
"impl Widget {\n"
" fn get(&self) -> u32 { self.n }\n"
" fn show(&self) -> u32 { self.get() }\n"
"}\n"
),
})
caller = _find(result, ".show()", "widget")
callee = _find(result, ".get()", "widget")
assert (caller, callee) in calls