mirror of
https://github.com/safishamsi/graphify.git
synced 2026-09-23 14:05:43 +00:00
fix(swift): type-qualified static calls resolve as EXTRACTED, not INFERRED (#1533)
A type-qualified Swift call (`Type.staticMethod()`, `Singleton.shared.method()`) names the receiver type explicitly in source, so the resolved edge is an exact reference — now emitted as EXTRACTED (1.0), matching the Python qualified-class-method pass (_resolve_python_member_calls). Instance calls whose receiver type comes from local inference (`obj.method()`) stay INFERRED (0.8). Resolution and the single-definition god-node guard are unchanged. This addresses the actionable part of #1533's "static calls" report: the edge was always produced (graphify models calls as method->method), it was just under-confident. Updated the confidence test to assert the instance/type-qualified split. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
1652dadd60
commit
51fc00a30f
@@ -70,32 +70,42 @@ def test_swift_cross_file_member_calls_resolve(tmp_path: Path):
|
||||
assert (".go()", "calls", ".method()") in edges # Singleton.shared.method()
|
||||
|
||||
|
||||
def test_swift_cross_file_member_calls_are_inferred_and_resolve_to_real_nodes(tmp_path: Path):
|
||||
# The new edges must be INFERRED (type inference, not an explicit import) and
|
||||
# land on real definition nodes so build_from_json keeps them.
|
||||
def test_swift_cross_file_member_calls_have_correct_confidence_and_resolve(tmp_path: Path):
|
||||
# Instance calls typed via local inference (vm.update(), self.svc.fetch()) are
|
||||
# INFERRED; type-qualified static calls (SessionType.staticMethod(),
|
||||
# Singleton.shared.method()) name the receiver type explicitly in source, so
|
||||
# they are EXTRACTED, matching the Python qualified-class-method pass (#1533).
|
||||
# All must land on real definition nodes so build_from_json keeps them.
|
||||
files = _issue_fixture(tmp_path / "src")
|
||||
result = extract(files, cache_root=tmp_path / "cache")
|
||||
|
||||
node_ids = {n["id"] for n in result["nodes"]}
|
||||
src_by_id = {n["id"]: n.get("source_file") for n in result["nodes"]}
|
||||
|
||||
member_targets = {".update()", ".fetch()", ".staticMethod()", ".method()"}
|
||||
seen_targets: set[str] = set()
|
||||
inferred_targets = {".update()", ".fetch()"}
|
||||
extracted_targets = {".staticMethod()", ".method()"}
|
||||
seen_inferred: set[str] = set()
|
||||
seen_extracted: set[str] = set()
|
||||
for e in result["edges"]:
|
||||
tgt_label = _label(result, e["target"])
|
||||
if e.get("relation") == "calls" and tgt_label in member_targets:
|
||||
assert e["confidence"] == "INFERRED"
|
||||
assert e["confidence_score"] == 0.8
|
||||
assert e["target"] in node_ids
|
||||
assert src_by_id.get(e["target"]) # resolved to a real, source-backed def
|
||||
seen_targets.add(tgt_label)
|
||||
assert seen_targets == member_targets
|
||||
if e.get("relation") != "calls":
|
||||
continue
|
||||
if tgt_label in inferred_targets:
|
||||
assert e["confidence"] == "INFERRED" and e["confidence_score"] == 0.8
|
||||
assert e["target"] in node_ids and src_by_id.get(e["target"])
|
||||
seen_inferred.add(tgt_label)
|
||||
elif tgt_label in extracted_targets:
|
||||
assert e["confidence"] == "EXTRACTED" and e["confidence_score"] == 1.0
|
||||
assert e["target"] in node_ids and src_by_id.get(e["target"])
|
||||
seen_extracted.add(tgt_label)
|
||||
assert seen_inferred == inferred_targets
|
||||
assert seen_extracted == extracted_targets
|
||||
|
||||
# Edges survive graph construction (no dangling targets pruned).
|
||||
g = build_from_json(result)
|
||||
surviving = sum(
|
||||
1 for _, _, d in g.edges(data=True)
|
||||
if d.get("confidence") == "INFERRED" and d.get("relation") == "calls"
|
||||
if d.get("relation") == "calls" and d.get("confidence") in ("INFERRED", "EXTRACTED")
|
||||
)
|
||||
assert surviving >= 5
|
||||
|
||||
|
||||
Reference in New Issue
Block a user