mirror of
https://github.com/safishamsi/graphify.git
synced 2026-09-25 06:55:43 +00:00
fix: promote cross-file call edges to EXTRACTED when import evidence exists
The cross-file call resolver in `extract()` unconditionally marked every
resolved call edge as INFERRED with confidence_score 0.8 — even when the
caller's file had an explicit `imports` (symbol) or `imports_from`
(module) edge to the callee. The new CJS require handler made this gap
visible: imports were correctly EXTRACTED but the call edges that those
imports backed remained INFERRED, so downstream consumers couldn't tell
high-evidence calls apart from name-match guesses.
This pass runs after the file-id remap (line 4736), so we relativize
node `source_file` paths before computing file_nids — otherwise the
caller's computed file_nid (absolute-path-derived) wouldn't match the
imports_from edge source (already remapped to relative form).
Promotion rule:
- Symbol-level `imports` edge from caller's file -> callee node id
=> EXTRACTED, confidence_score 1.0
- Module-level `imports_from` edge from caller's file -> callee's file
=> EXTRACTED, confidence_score 1.0
- Otherwise => INFERRED, confidence_score 0.8 (existing behavior)
Validated on a 92-file CJS orchestrator: 5 previously-INFERRED edges
from runExecute() now resolve to EXTRACTED, and 88% of cross-file calls
in the corpus (104 of 118) promote, leaving INFERRED only for genuine
heuristic guesses with no import backing.
Adds two tests:
- test_cross_file_call_promoted_to_extracted_with_import_evidence
- test_cross_file_call_remains_inferred_without_import_evidence
This commit is contained in:
@@ -240,3 +240,52 @@ def test_extract_js_arrow_function_still_extracted():
|
||||
assert "greet()" in labels
|
||||
finally:
|
||||
arrow_fixture.unlink()
|
||||
|
||||
|
||||
def test_cross_file_call_promoted_to_extracted_with_import_evidence(tmp_path):
|
||||
"""A cross-file `calls` edge must be EXTRACTED when the caller's file has
|
||||
an `imports` or `imports_from` edge linking it to the callee."""
|
||||
caller = tmp_path / "caller.js"
|
||||
callee = tmp_path / "lib.js"
|
||||
caller.write_text(
|
||||
"const { doWork } = require('./lib');\n"
|
||||
"function run() { doWork(); }\n"
|
||||
)
|
||||
callee.write_text(
|
||||
"function doWork() { return 1; }\n"
|
||||
"module.exports = { doWork };\n"
|
||||
)
|
||||
result = extract([caller, callee], cache_root=tmp_path)
|
||||
nodes = {n["id"]: n for n in result["nodes"]}
|
||||
call_edges = [
|
||||
e for e in result["edges"]
|
||||
if e["relation"] == "calls"
|
||||
and nodes[e["source"]]["label"] == "run()"
|
||||
and nodes[e["target"]]["label"] == "doWork()"
|
||||
]
|
||||
assert len(call_edges) == 1
|
||||
assert call_edges[0]["confidence"] == "EXTRACTED"
|
||||
assert call_edges[0]["confidence_score"] == 1.0
|
||||
|
||||
|
||||
def test_cross_file_call_remains_inferred_without_import_evidence(tmp_path):
|
||||
"""A cross-file `calls` edge must stay INFERRED when there is no import
|
||||
edge — name collision alone is insufficient evidence."""
|
||||
caller = tmp_path / "caller.js"
|
||||
callee = tmp_path / "lib.js"
|
||||
# Caller does NOT require lib — same-name function happens to exist elsewhere
|
||||
caller.write_text("function run() { doUnique(); }\n")
|
||||
callee.write_text(
|
||||
"function doUnique() { return 1; }\n"
|
||||
"module.exports = { doUnique };\n"
|
||||
)
|
||||
result = extract([caller, callee], cache_root=tmp_path)
|
||||
nodes = {n["id"]: n for n in result["nodes"]}
|
||||
call_edges = [
|
||||
e for e in result["edges"]
|
||||
if e["relation"] == "calls"
|
||||
and nodes[e["source"]]["label"] == "run()"
|
||||
and nodes[e["target"]]["label"] == "doUnique()"
|
||||
]
|
||||
assert len(call_edges) == 1
|
||||
assert call_edges[0]["confidence"] == "INFERRED"
|
||||
|
||||
Reference in New Issue
Block a user