fix(extract): Go cross-file type refs emit sourceless stubs (#1500)
Go's ensure_named_node emitted a SOURCED stub for a cross-file type reference, unlike the generic/ObjC/Java extractors which emit sourceless ones. A sourced stub bakes the referencing file's path into the node id at disambiguation time, which blocks the corpus-level rewire from collapsing it onto the real definition — so a type defined in one Go file and referenced from two others produced three nodes (the real def plus two phantom per-file duplicates). Go now emits a sourceless stub like the other languages, so the rewire collapses them to a single node. Ported from PR #1501 by @TPAteeq. Closes the #1402 gap for Go. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
committed by
safishamsi
co-authored by
Claude Opus 4.8
parent
b17e88cc49
commit
36b76ce8e0
@@ -2,6 +2,10 @@
|
||||
|
||||
Full release notes with details on each version: [GitHub Releases](https://github.com/safishamsi/graphify/releases)
|
||||
|
||||
## Unreleased
|
||||
|
||||
- Fix: the Go AST extractor no longer creates phantom duplicate nodes for cross-file type references — the Go copy of `ensure_named_node` still used the older sourced-stub fallback; it now emits a sourceless stub like the other extractors, extending the #1402 fix to Go (#1500).
|
||||
|
||||
## 0.8.50 (2026-06-27)
|
||||
|
||||
- Feat: `graphify label --missing-only` relabels only communities that are unnamed or still hold a `Community N` placeholder, preserving existing non-placeholder labels from `.graphify_labels.json` (#1481, thanks @jiangyq9; supersedes #1421 by @matiasduartee, who proposed the same flag). Lets a large graph be relabeled incrementally without re-naming (and paying for) communities that already have good names.
|
||||
|
||||
+15
-1
@@ -6472,7 +6472,21 @@ def extract_go(path: Path) -> dict:
|
||||
return nid
|
||||
nid = _make_id(name)
|
||||
if nid not in seen_ids:
|
||||
add_node(nid, name, line)
|
||||
# The name isn't declared in this file, so this is a cross-file reference
|
||||
# (e.g. a type defined in another file of the package). Emit a SOURCELESS
|
||||
# stub — like the inheritance-base path in the other extractors — so the
|
||||
# corpus-level rewire can collapse it onto the real definition. A sourced
|
||||
# stub here makes _disambiguate_colliding_node_ids bake the referencing
|
||||
# file's path (with extension) into the id and blocks the rewire, which is
|
||||
# the phantom-duplicate-node bug (#1402).
|
||||
seen_ids.add(nid)
|
||||
nodes.append({
|
||||
"id": nid,
|
||||
"label": name,
|
||||
"file_type": "code",
|
||||
"source_file": "",
|
||||
"source_location": "",
|
||||
})
|
||||
return nid
|
||||
|
||||
def emit_go_method_refs(func_node, func_nid: str, line: int) -> None:
|
||||
|
||||
@@ -123,6 +123,35 @@ def test_cross_file_type_annotation_refs_resolve_to_single_node(tmp_path):
|
||||
assert "_py" not in thing_nodes[0]["id"], thing_nodes[0]["id"]
|
||||
|
||||
|
||||
def test_go_cross_file_type_refs_resolve_to_single_node(tmp_path):
|
||||
"""#1402 (Go): the sourceless-stub fix landed in six extractors but the Go copy
|
||||
of ``ensure_named_node`` was missed, so a Go type defined once but referenced via
|
||||
parameter/return types in N sibling files produced 1+N phantom duplicate nodes
|
||||
with the referencing file's path (extension and all) baked into the id
|
||||
(e.g. ``pkg_a_go_thing``). Same-package references must resolve to the single
|
||||
canonical type node instead."""
|
||||
pkg = tmp_path / "pkg"
|
||||
pkg.mkdir()
|
||||
(pkg / "thing.go").write_text(
|
||||
"package pkg\n\ntype Thing struct{}\n\nfunc (t Thing) Run() int { return 1 }\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
(pkg / "a.go").write_text(
|
||||
"package pkg\n\nfunc UseA(obj Thing) Thing { return obj }\n", encoding="utf-8"
|
||||
)
|
||||
(pkg / "b.go").write_text(
|
||||
"package pkg\n\nfunc UseB(obj Thing) Thing { return obj }\n", encoding="utf-8"
|
||||
)
|
||||
|
||||
result = extract([pkg / "thing.go", pkg / "a.go", pkg / "b.go"], cache_root=tmp_path)
|
||||
|
||||
thing_nodes = [n for n in result["nodes"] if n["label"] == "Thing"]
|
||||
assert len(thing_nodes) == 1, [n["id"] for n in thing_nodes]
|
||||
# The phantom signature is the referencing file's path (with .go extension)
|
||||
# baked into the id — must not appear.
|
||||
assert "_go" not in thing_nodes[0]["id"], thing_nodes[0]["id"]
|
||||
|
||||
|
||||
def test_extract_updates_raw_call_callers_after_duplicate_id_disambiguation(tmp_path):
|
||||
first = tmp_path / "apps/api/Program.cs"
|
||||
second = tmp_path / "tools/api/Program.cs"
|
||||
|
||||
Reference in New Issue
Block a user