test(go): cover method-level and all-exported case-only collisions (#2779)

Adds the two cases the deep-dive flagged as untested: a receiver-based method
collision (Get/get on the same type) and the all-salted branch where no member
is the unique exported one (Run/RUN). Also adds the CHANGELOG entry.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
safishamsi
2026-08-16 18:35:46 +01:00
co-authored by Claude Opus 4.8
parent 1e68a7aa76
commit 05e1e4c01d
2 changed files with 39 additions and 0 deletions
+1
View File
@@ -5,6 +5,7 @@ Full release notes with details on each version: [GitHub Releases](https://githu
## 0.9.45 (unreleased)
- Fix: `graphify install <platform>` now advances the `.graphify_version` stamp only for the platform it actually (re)writes, instead of stamping every installed platform as current; a platform whose skill content was left untouched keeps its old stamp so its staleness warning stays truthful (#2694, thanks @ousamabenyounes). This completes #2694 (the CLAUDE_CONFIG_DIR half shipped in 0.9.44).
- Fix: a Go file that declares both an exported and an unexported symbol differing only by case (e.g. `Run` and `run`, which are distinct in Go's case-sensitive visibility rules) no longer collapses them onto one node id and drops one; the exported symbol keeps its stable id and the unexported one is disambiguated, so an intra-file call to the unexported symbol resolves locally instead of phantoming to another package (#2779, thanks @catpotd). Only the Go extractor's id assignment is affected; the shared id normalization is unchanged, so no other language's ids move.
- Fix: loading a `graph.json` that contains a hyperedge with no `id` field (the semantic extractor emits them and they persist verbatim) no longer crashes the incremental re-extract with `KeyError: 'id'`; id-less hyperedges are tolerated and retained (#2775, thanks @ousamabenyounes).
## 0.9.44 (2026-08-15)
+38
View File
@@ -318,3 +318,41 @@ def test_incremental_sibling_addition_keeps_cross_package_edge(tmp_path: Path) -
assert start_targets == {"Run()"}, (
f"cross-package edge re-pointed after partial rebuild: {start_targets}"
)
def test_case_only_sibling_methods_are_both_extracted(tmp_path: Path) -> None:
"""Methods ``Get`` and ``get`` on the same type in one file are two symbols,
not one — the receiver-based id path must salt the collision too (#2779)."""
(tmp_path / "go.mod").write_text("module example.com/repro\n\ngo 1.22\n")
pkg = tmp_path / "pkga"
pkg.mkdir()
(pkg / "a.go").write_text(
"package pkga\n\n"
"type Store struct{}\n\n"
"func (s Store) Get() int { return s.get() }\n\n"
"func (s Store) get() int { return 0 }\n"
)
result = _extract(tmp_path)
exported = _ids(result, label="Get", suffix="a.go")
unexported = _ids(result, label="get", suffix="a.go")
assert len(exported) == 1, f"exported Get missing: {exported}"
assert len(unexported) == 1, f"unexported get missing: {unexported}"
assert exported != unexported, "Get and get collapsed onto one node id"
def test_case_only_sibling_all_exported_are_both_extracted(tmp_path: Path) -> None:
"""When no member is the unique exported one (``Run`` and ``RUN`` both start
uppercase), each is salted so neither is dropped (#2779, all-salted branch)."""
(tmp_path / "go.mod").write_text("module example.com/repro\n\ngo 1.22\n")
pkg = tmp_path / "pkga"
pkg.mkdir()
(pkg / "a.go").write_text(
"package pkga\n\n"
"func Run() int { return 1 }\n\n"
"func RUN() int { return 2 }\n"
)
result = _extract(tmp_path)
run1 = _ids(result, label="Run", suffix="a.go")
run2 = _ids(result, label="RUN", suffix="a.go")
assert len(run1) == 1 and len(run2) == 1, f"Run={run1} RUN={run2}"
assert run1 != run2, "Run and RUN collapsed onto one node id"