Repository navigation
Meta: Go / cgo / Go+C extraction — tracking issue #1932
Description
Tracking issue for the Go / cgo / Go+C extraction work. Method: count a construct in source, count the same thing in the graph, treat any mismatch as a defect. Every item was minimised to a dummy fixture before filing, measured on one real Go + C/C++ repository (~1150 Go files, eBPF C probes, a Windows C++ driver — counts only), and carries a SQL detection recipe that works on any indexed repo. Each fix PR ships a reproduce-first test (RED proven before the change) and a fresh field census on the same tree.
Ledger — bugs
| Issue | Defect | PR | State | Measured effect |
|---|---|---|---|---|
| #1906 | Weak short-name strategies fabricate CALLS for Go selector calls | #1907 | approved on shape; direction accepted 2026-09-03 on maintainers' k8s census; rebased onto current main (memory-core), suite green | suffix_match 3817 → 113, same_module 3267 → 224; weak share of CALLS 36.0% → 12.4%, lsp_* 8481 → 10672 (re-resolved, not deleted) |
| #1909 | Method QNs ignore the receiver — same-name methods collapse onto one node | #1913 | direction accepted 2026-09-03 (maintainers ran their own k8s census, 150-edge hand-classification); go_lsp caller-QN join gap fixed in-PR; rebasing behind #1907; 377-loss instrument answered on-thread | Close methods 15 → 86; stack census: LSP strategies end above baseline (lsp_direct 8896 → 9130), same_module 931 → 117 |
| #1910 | Multiple init() per package collapse onto one QN |
#1915 | ordinal suffix implemented on review feedback; rebase held per maintainer until #1907/#1913 land | init nodes 7 → 29; stability pinned by test |
| #1926 | cgo's import "C" resolves to an arbitrary project symbol named C |
#1931 | open, CI green; maintainer verified the 27-edge symptom is already gone on main via #1938's Strategy-3 gate — description updated to lead with the import-map effect | 27 false IMPORTS (pre-#1938) → 0 on current main; remaining effect: pseudo-package removed from the per-file import map |
| #1927 | field_type_hint binds by receiver variable-name substring, not declared type |
#1936 | open; rebase held per maintainer until #1907/#1913 land; stack census: trims field_type_hint 1205 → 186 on the stack head | 1484 → 356; single-char receivers 376 → 0; Go→C/C++ 34 → 0; zero relocation |
| #1928 | USAGE/WRITES/READS never consult the #725 cross-language guard (both resolvers) |
#1937 | merged (fcd8bfc1), issue closed |
Go→C/C++ WRITES 835 → 0, USAGE 1545 → 0; 6432 cross-language refs dropped, none same-language; CALLS/IMPORTS byte-identical |
| #1934 | A Go import can resolve to a Function/Method (Strategies 1b + 3 are language-blind) |
#1938 | merged (8d7d3892), issue closed |
IMPORTS targets: Method 85 → 0, Function 4 → 0, Folder 2206 kept |
| #1935 | Go struct fields never extracted (find_class_body stops one level above field_declaration_list) |
#1940 | merged (61de19bb), issue closed |
0 → 4533 Field nodes with declared types; blank _ padding skipped |
| #1942 | A bare Go reference binds a struct Field (found field-validating #1940) |
#1944 | merged (20e86c02), issue closed; follow-up #1962 |
USAGE onto Go fields 21308 → 0, WRITES 5191 → 0 |
| #1962 | #1944 guard was a blanket veto (dot-check inert): is_member_access recorded at extraction, guard keys on selector shape | #2006 | merged (192f2960), issue closed |
census: Field-ref edges 0 → USAGE 8503 / WRITES 1368, 2220/4497 fields reached |
Combined stack (#1937 + #1940 + #1944) vs main: USAGE 29198 → 21035, WRITES 2681 → 774, +4533 field nodes — the graph gets nodes and loses junk edges.
Ledger — design-shaped (awaiting maintainer ack, no PR)
| Issue | Gap | Damage |
|---|---|---|
| #1911 | Build-tag twin files collide on one QN — draft PR #1946 (τ #-suffix, #495 pattern), ready on design ack |
2177 τ-suffixed nodes / 372 files; swallowed-twin files 319 → 281; CALLS +62 — no edge-mass loss |
| #1929 | cgo invisible — draft PR #1947: //export → 22 export_linkage contracts (exact), C.* CALLS 1 → 0, preamble flagged in coverage; C-grammar preamble parse stays open |
39 call-shaped C.f( → 0 CALLS; 12 preamble functions → 0 nodes on main |
| #1930 | Go chan/goroutines unmodelled — draft PR #1949: grammar-precise send/receive/select → gochan topology |
Channel nodes 2 → 205, EMITS 1 → 162, LISTENS_ON 2 → 256 on the measured repo; range/go/element-types deferred |
Ledger — CI
| Issue | Problem | PR | State |
|---|---|---|---|
| #1952 | test-windows-guards setup flake (nodes: None): counts read from list_projects, whose stats publish asynchronously — the index response carries them synchronously |
#1958 | merged, issue closed |
| #1959 | CI never indexes Go end to end — the only binary-level harness fixtures are TS-only, which is where this whole defect family hid | #1961 (stacked on #1958) | merged, issue closed; polyglot fixture (structs, build-tag twins, cgo, channels) + Go-definition count in the path-equality gate; measured 28/46/12 defs, go=7, equal across ASCII/Latin-1/Cyrillic |
Merge order
- Stack A: fix(pipeline): suppress weak short-name matches for Go selector calls #1907 → fix(extract): receiver-qualify Go method QNs #1913 → fix(extract): give each Go init() its own QN #1915 → fix(pipeline): bind field_type_hint by owning QN segment, not substring #1936 (each rides the previous; fix(pipeline): bind field_type_hint by owning QN segment, not substring #1936's probe relies on fix(pipeline): suppress weak short-name matches for Go selector calls #1907's guard + fix(extract): receiver-qualify Go method QNs #1913's receiver QNs). Direction accepted 2026-09-03; fix(pipeline): suppress weak short-name matches for Go selector calls #1907/fix(extract): receiver-qualify Go method QNs #1913 rebased onto post-memory-core main, fix(extract): give each Go init() its own QN #1915/fix(pipeline): bind field_type_hint by owning QN segment, not substring #1936 held per maintainer until those two land.
- Stack B: fix(pipeline): guard USAGE/WRITES/READS against cross-language binds #1937 → fix(extract): descend into Go struct field_declaration_list #1940 → fix(pipeline): a bare Go reference never binds a struct Field #1944 (fields must not land before the reference guards; fix(extract): descend into Go struct field_declaration_list #1940 + fix(pipeline): a bare Go reference never binds a struct Field #1944 ideally merge together).
- Independent: fix(extract): drop cgo's
import "C"pseudo-package #1931, fix(pipeline): never bind a Go import to a symbol #1938. - Related external: fix(go): resolve cross-package field-chain calls via type dispatch #1939 (@xkchok) — cross-package field-chain dispatch via
lsp_type_dispatch; complementary (consumes fix(extract): descend into Go struct field_declaration_list #1940's field nodes, replaces the weak path fix(pipeline): bind field_type_hint by owning QN segment, not substring #1936 tightens). Overlapsextract_defs.cwith fix(extract): descend into Go struct field_declaration_list #1940 — whichever merges second rebases.
Cross-cutting theme
One failure mode at every layer: a heuristic binds by name shape and nothing checks the language or what the name can denote. #1906 (CALLS, guarded) → #1927 (field_type_hint, exempted on a false premise) → #1928 (references, never guarded) → #1934 (imports, label-blind) → #1942 (references, label-blind). Guarding one relocates mass to the next unguarded one — which is why every fix PR above now carries negative/exact-count probes (ei_edge_count_is, zero-project-edge assertions, parallel-twin fixtures) rather than floor-only asserts, closing #1932's original test-infra gap in place.
Verified working (audit surface closed, no issues needed)
Generics (receiver-qualified QNs unwrap generic_type; no [ in any QN) · embedding + interface dispatch (lsp_embed_dispatch 42, lsp_interface_dispatch 79, fan-out via 434 IMPLEMENTS) · //go:linkname (body-less decls get nodes; callers bind lsp_direct) · defer/closures (attributed to the enclosing named function) · implicit interface satisfaction + method values (lsp_callable_alias) · TESTS edges and Variable coverage · internal import resolution (2206/~2242 same-module imports → correct package Folder; residual was #1934). Assembly (.s): untested — no fixtures in the measured repo.
Detection recipes (run on any indexed repo)
-- call-graph shape
SELECT json_extract(properties,'$.strategy') AS strategy, count(*) AS n,
round(avg(json_extract(properties,'$.confidence')), 3) AS conf
FROM edges WHERE type='CALLS' GROUP BY 1 ORDER BY n DESC;
-- node recovery vs source: grep -rc '^func init()' --include='*.go' .
SELECT count(*) FROM nodes WHERE name='init';
-- swallowed twins: a file whose only node is its own __file__
SELECT file_path, count(*) c FROM nodes GROUP BY 1 HAVING c = 1;
-- cross-language reference edges (any Go↔C/C++ row is suspect)
SELECT e.type, count(*) FROM edges e
JOIN nodes s ON s.id=e.source_id JOIN nodes t ON t.id=e.target_id
WHERE s.file_path LIKE '%.go'
AND (t.file_path LIKE '%.c' OR t.file_path LIKE '%.h'
OR t.file_path LIKE '%.cpp' OR t.file_path LIKE '%.hpp')
GROUP BY 1;
-- references onto Go fields (must be zero: field access is selector-only)
SELECT e.type, count(*) FROM edges e JOIN nodes t ON t.id=e.target_id
WHERE t.label='Field' AND t.file_path LIKE '%.go'
AND e.type IN ('READS','WRITES','USAGE') GROUP BY 1;
-- Go import targets (anything not Folder/Module/File is false)
SELECT t.label, count(*) FROM edges e
JOIN nodes s ON s.id=e.source_id JOIN nodes t ON t.id=e.target_id
WHERE e.type='IMPORTS' AND s.file_path LIKE '%.go' GROUP BY 1;Every census lives inline in its issue's body (and in the fix PR's description); issues in this family are self-contained.
Related work in other ecosystems: #1893 (Swift, method-QN class), #1276 (Python, closed), #495 (Rust cfg twins — the pattern #1911 wants), #725 (cross-language CALLS guard), #1114 (dispatch fan-out), #787 (file attribution).
Activity
feat(extract): model Go native channels as gochan Channel topology
test(ci): make the Windows path guard fixture polyglot so CI indexes …
feat(extract): model Go native channels as gochan Channel topology
DeusData commentedon Sep 3, 2026
Thank you for maintaining this as an evidence ledger rather than an umbrella implementation.
I have classified it as a tracking task and removed the unrelated Windows label. The individual issues and PRs remain the authoritative places for design decisions, review, and closure, while this thread can continue to show measured interactions and merge ordering across the Go/cgo work.
The source counts, graph counts, and negative-edge checks make this genuinely useful coordination material. Thank you for keeping each defect self-contained underneath it.