fix(pipeline): USAGE and WRITES edges have no cross-language guard
Status: fix open — PR #1937 (cbm_suppress_cross_language_ref() at all four resolver sites — sequential and parallel twins; the sequential-only first cut left 344 Go→C WRITES alive). Measured: Go→C/C++ WRITES 835 → 0, USAGE 1545 → 0, C→Go 15/90 → 0; 6432 reference edges dropped, all cross-language, none same-language; CALLS/IMPORTS byte-identical. Unblocks #1935 (with #1942 as the same-language other half).
What happens
#725 gave CALLS a cross-language guard (cbm_suppress_cross_language_suffix_match). USAGE, WRITES and READS resolve through the same short-name registry — pass_usages.c (resolve_usage_edges fallback, resolve_rw_edges) and their parallel twins in pass_parallel.c (resolve_file_usages, resolve_file_rw) — and never consult it. On a Go+C tree (any eBPF/kernel component), every identifier existing in both languages produces an edge across the boundary.
Damage measured (~1150-file Go+C repo, 15.8k nodes)
| edge type |
total |
Go → C/C++ |
C/C++ → Go |
cross share |
WRITES |
2681 |
835 |
15 |
31.7% |
USAGE |
29198 |
1545 |
90 |
5.6% |
CALLS |
19920 |
76 |
31 |
0.5% (guard active) |
Representative: dozens of Go test functions each declaring a local event produced WRITES edges to a local variable inside an eBPF C probe. The full dropped-edge breakdown is wider than Go↔C: Go→.json 3034 (config keys), Go→.hpp/.h 1612, Go→.sh/.yaml 443.
Minimal reproduction
// probe_test.go
package app
import "testing"
func TestEvent(t *testing.T) {
event := 1
_ = event
}
/* probe.c */
static int handle(void) {
int event = 0;
return event;
}
Index both → a WRITES edge from the Go test into the C event. Expected: none — Go cannot touch a C translation unit's identifiers.
Detection recipe
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 e.type IN ('WRITES','USAGE','CALLS') AND 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; -- repeat with source/target swapped
Fix shape (as landed)
One pure predicate next to the #725 guard. Two deliberate differences from CALLS: no strategy parameter (a reference edge carries no import-closure evidence, so every registry strategy is a bare-name guess across a boundary — the unpenalized-unique_name carve-out of #1572 does not apply), and C/C++ are one family (.h maps to CBM_LANG_CPP; a .c file referencing its own header is not a boundary). LSP-backed semantic references resolve before the fallback and never reach it — that path is where a genuine cgo binding (#1929) would live. JS/TS family exemption as in #725.
Related: #725, #1935/#1940 (blocked on this), #1942 (same-language other half), #787; tracked in #1932.
fix(pipeline): USAGE and WRITES edges have no cross-language guard
Status: fix open — PR #1937 (
cbm_suppress_cross_language_ref()at all four resolver sites — sequential and parallel twins; the sequential-only first cut left 344 Go→C WRITES alive). Measured: Go→C/C++ WRITES 835 → 0, USAGE 1545 → 0, C→Go 15/90 → 0; 6432 reference edges dropped, all cross-language, none same-language; CALLS/IMPORTS byte-identical. Unblocks #1935 (with #1942 as the same-language other half).What happens
#725 gave CALLS a cross-language guard (
cbm_suppress_cross_language_suffix_match).USAGE,WRITESandREADSresolve through the same short-name registry —pass_usages.c(resolve_usage_edgesfallback,resolve_rw_edges) and their parallel twins inpass_parallel.c(resolve_file_usages,resolve_file_rw) — and never consult it. On a Go+C tree (any eBPF/kernel component), every identifier existing in both languages produces an edge across the boundary.Damage measured (~1150-file Go+C repo, 15.8k nodes)
WRITESUSAGECALLSRepresentative: dozens of Go test functions each declaring a local
eventproduced WRITES edges to a local variable inside an eBPF C probe. The full dropped-edge breakdown is wider than Go↔C: Go→.json3034 (config keys), Go→.hpp/.h1612, Go→.sh/.yaml443.Minimal reproduction
Index both → a WRITES edge from the Go test into the C
event. Expected: none — Go cannot touch a C translation unit's identifiers.Detection recipe
Fix shape (as landed)
One pure predicate next to the #725 guard. Two deliberate differences from CALLS: no strategy parameter (a reference edge carries no import-closure evidence, so every registry strategy is a bare-name guess across a boundary — the unpenalized-
unique_namecarve-out of #1572 does not apply), and C/C++ are one family (.hmaps toCBM_LANG_CPP; a.cfile referencing its own header is not a boundary). LSP-backed semantic references resolve before the fallback and never reach it — that path is where a genuine cgo binding (#1929) would live. JS/TS family exemption as in #725.Related: #725, #1935/#1940 (blocked on this), #1942 (same-language other half), #787; tracked in #1932.