Skip to content

fix(pipeline): USAGE and WRITES edges have no cross-language guard — 31% of WRITES on a Go+C repo cross the Go/C boundary #1928

Description

@ilyabrykau-orca

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    windowsWindows-specific issues

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions