fix(go): preserve interface method signature references

(cherry picked from commit 00fb309983)
This commit is contained in:
oleksii-tumanov authored and safishamsi committed 2026-09-22 14:58:12 +01:00
1 parent 6fdc26db03
commit bda82f9ded
2 files changed
+60

No files matched your search

+1
View File
@@ -390,6 +390,7 @@ def extract_go(path: Path) -> dict:
m_nid = symbol_nid(_make_id(type_nid, m_name), m_name)
add_node(m_nid, f".{m_name}()", m_line)
add_edge(type_nid, m_nid, "method", m_line)
emit_go_method_refs(elem, m_nid, m_line)
continue
if elem.type != "type_elem":
continue
+59
View File
@@ -8,6 +8,8 @@ an empty node with no members - the method requirements were dropped entirely.
from pathlib import Path
import pytest
from graphify.extract import extract
@@ -106,3 +108,60 @@ def test_interface_method_is_distinct_from_a_concrete_method(tmp_path: Path) ->
}
assert shape_area and rect_area
assert shape_area.isdisjoint(rect_area), "interface and struct methods collapsed"
def _signature_refs(result: dict, owner_label: str) -> tuple[str, list[dict]]:
by_id = {node["id"]: node for node in result["nodes"]}
method_ids = {
edge["target"]
for edge in result["edges"]
if edge.get("relation") == "method"
and by_id[edge["source"]].get("label") == owner_label
}
assert len(method_ids) == 1
method_id = method_ids.pop()
return method_id, [
{**edge, "target_label": by_id[edge["target"]]["label"]}
for edge in result["edges"]
if edge.get("relation") == "references" and edge["source"] == method_id
]
@pytest.mark.parametrize(
("signature", "body"),
[
("Handle(Request) Response", "return Response{}"),
("Handle(req *Request) (res *Response, err error)", "return nil, nil"),
],
)
def test_interface_method_signature_keeps_type_references(tmp_path: Path, signature, body):
path = tmp_path / "contract.go"
path.write_text(
"package contracts\n\n"
"type Request struct{}\n"
"type Response struct{}\n"
"type Service interface {\n"
f" {signature}\n"
"}\n\n"
"type Impl struct{}\n"
f"func (Impl) {signature} {{ {body} }}\n",
encoding="utf-8",
)
result = _extract(tmp_path)
interface_id, interface_refs = _signature_refs(result, "Service")
concrete_id, concrete_refs = _signature_refs(result, "Impl")
expected = {("Request", "parameter_type"), ("Response", "return_type")}
assert {(edge["target_label"], edge["context"]) for edge in concrete_refs} == expected
assert {(edge["target_label"], edge["context"]) for edge in interface_refs} == expected
assert len(interface_refs) == 2
assert interface_id != concrete_id
assert not any(node["label"] == "error" for node in result["nodes"])
for edge in interface_refs:
assert edge["source_file"] == "contract.go"
assert edge["source_location"] == "L6"
assert edge["confidence"] == "EXTRACTED"
# A warm extraction replays the same source-owned signature edges.
warm = _extract(tmp_path)
assert _signature_refs(warm, "Service") == (interface_id, interface_refs)
assert _signature_refs(warm, "Impl") == (concrete_id, concrete_refs)