mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-10-30 07:23:47 +08:00
fix(pipeline): guard USAGE/WRITES/READS against cross-language binds
USAGE, WRITES and READS edges resolve through the same short-name registry as CALLS but never consulted the #725 cross-language guard. On a Go tree with eBPF C probes every Go identifier spelled like a C one produced a reference edge into the C file: 31.5% of all WRITES on the originally measured repo crossed the Go->C boundary, led by dozens of Go test locals named event writing a C probe's automatic variable. Add cbm_suppress_cross_language_ref() - the reference-edge analog of cbm_suppress_cross_language_suffix_match - and consult it on BOTH resolvers of each edge type: the sequential pass (pass_usages.c resolve_usage_edges registry-fallback branch, resolve_rw_edges) and their parallel twins (pass_parallel.c resolve_file_usages, resolve_file_rw). The sequential-only version of this change left 344 Go->C WRITES alive on a ~1150-file repo because large repos resolve through pass_parallel.c - the field census caught it, and the parallel-twin test now pins it. Unlike the CALLS guard the predicate takes no strategy parameter: a reference edge carries no import-closure evidence, so every registry strategy is a bare-name guess across a boundary. LSP-backed semantic references resolve before the fallback and are unaffected. JS/TS stay one family, and C/C++ count as one family too (.h maps to CBM_LANG_CPP, so a .c file referencing its own header is not a boundary). Field-validated on the ~1150-file Go+C repo: Go->C/C++ WRITES 835->0, USAGE 1545->0; C->Go 15/90->0; 6432 reference edges dropped in total, every one cross-language (Go->.json 3034, Go->.hpp/.h 1612, Go->.c 746, Go->.sh/.yaml/.yml 443, ...) and none same-language. CALLS and IMPORTS totals are byte-identical to main. Fixes #1928 Signed-off-by: Ilya Brykau <ilya.brykau@orca.security>
This commit is contained in:
@@ -2654,6 +2654,12 @@ static void resolve_file_usages(resolve_ctx_t *rc, resolve_worker_state_t *ws,
|
||||
continue;
|
||||
}
|
||||
tgt = cbm_gbuf_find_by_qn(rc->main_gbuf, res.qualified_name);
|
||||
/* #1928: the registry fallback is a bare-name guess — never let it
|
||||
* bind a reference across a language boundary. Mirrors the
|
||||
* sequential twin (pass_usages.c). */
|
||||
if (tgt && cbm_suppress_cross_language_ref(lang, tgt->file_path)) {
|
||||
continue;
|
||||
}
|
||||
if (usage->semantic_reference_blocked && (usage->semantic_reference_local_shadow ||
|
||||
cbm_pipeline_node_is_callable_target(tgt))) {
|
||||
continue;
|
||||
@@ -2713,7 +2719,7 @@ static void resolve_file_throws(resolve_ctx_t *rc, resolve_worker_state_t *ws,
|
||||
/* Resolve reads/writes for one file. */
|
||||
static void resolve_file_rw(resolve_ctx_t *rc, resolve_worker_state_t *ws, CBMFileResult *result,
|
||||
const char *rel, const char *module_qn, const char **imp_keys,
|
||||
const char **imp_vals, int imp_count) {
|
||||
const char **imp_vals, int imp_count, CBMLanguage lang) {
|
||||
for (int r = 0; r < result->rw.count; r++) {
|
||||
CBMReadWrite *rw = &result->rw.items[r];
|
||||
if (!rw->var_name) {
|
||||
@@ -2733,6 +2739,12 @@ static void resolve_file_rw(resolve_ctx_t *rc, resolve_worker_state_t *ws, CBMFi
|
||||
if (!tgt || src->id == tgt->id) {
|
||||
continue;
|
||||
}
|
||||
/* #1928: every resolution here is a bare-name registry guess — never
|
||||
* let it bind a read/write across a language boundary. Mirrors the
|
||||
* sequential twin (pass_usages.c). */
|
||||
if (cbm_suppress_cross_language_ref(lang, tgt->file_path)) {
|
||||
continue;
|
||||
}
|
||||
const char *etype = rw->is_write ? "WRITES" : "READS";
|
||||
cbm_gbuf_insert_edge(ws->local_edge_buf, src->id, tgt->id, etype, "{}");
|
||||
}
|
||||
@@ -3179,7 +3191,7 @@ static void resolve_worker(int worker_id, void *ctx_ptr) {
|
||||
|
||||
/* ── READS / WRITES ────────────────────────────────────── */
|
||||
_ph_t0 = extract_now_ns();
|
||||
resolve_file_rw(rc, ws, result, rel, module_qn, imp_keys, imp_vals, imp_count);
|
||||
resolve_file_rw(rc, ws, result, rel, module_qn, imp_keys, imp_vals, imp_count, lang);
|
||||
atomic_fetch_add_explicit(&rc->time_ns_rw, extract_now_ns() - _ph_t0, memory_order_relaxed);
|
||||
|
||||
/* ── INHERITS + DECORATES + IMPLEMENTS ──────────────────── */
|
||||
|
||||
@@ -210,6 +210,12 @@ static int resolve_usage_edges(cbm_pipeline_ctx_t *ctx, const CBMFileResult *res
|
||||
continue;
|
||||
}
|
||||
tgt = cbm_gbuf_find_by_qn(ctx->gbuf, res.qualified_name);
|
||||
/* #1928: the registry fallback is a bare-name guess — never let it
|
||||
* bind a reference across a language boundary (the LSP-backed
|
||||
* semantic branch above is not affected). */
|
||||
if (tgt && cbm_suppress_cross_language_ref(lang, tgt->file_path)) {
|
||||
continue;
|
||||
}
|
||||
if (usage->semantic_reference_blocked && (usage->semantic_reference_local_shadow ||
|
||||
cbm_pipeline_node_is_callable_target(tgt))) {
|
||||
continue;
|
||||
@@ -270,7 +276,7 @@ static int resolve_throw_edges(cbm_pipeline_ctx_t *ctx, const CBMFileResult *res
|
||||
/* Resolve READS/WRITES edges for one file's extracted read/write accesses. */
|
||||
static int resolve_rw_edges(cbm_pipeline_ctx_t *ctx, const CBMFileResult *result, const char *rel,
|
||||
const char *module_qn, const char **imp_keys, const char **imp_vals,
|
||||
int imp_count) {
|
||||
int imp_count, CBMLanguage lang) {
|
||||
int resolved = 0;
|
||||
for (int r = 0; r < result->rw.count; r++) {
|
||||
CBMReadWrite *rw = &result->rw.items[r];
|
||||
@@ -293,6 +299,11 @@ static int resolve_rw_edges(cbm_pipeline_ctx_t *ctx, const CBMFileResult *result
|
||||
if (!tgt || src->id == tgt->id) {
|
||||
continue;
|
||||
}
|
||||
/* #1928: every resolution here is a bare-name registry guess — never
|
||||
* let it bind a read/write across a language boundary. */
|
||||
if (cbm_suppress_cross_language_ref(lang, tgt->file_path)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const char *edge_type = rw->is_write ? "WRITES" : "READS";
|
||||
cbm_gbuf_insert_edge(ctx->gbuf, src->id, tgt->id, edge_type, "{}");
|
||||
@@ -359,7 +370,8 @@ int cbm_pipeline_pass_usages(cbm_pipeline_ctx_t *ctx, const cbm_file_info_t *fil
|
||||
imp_count, files[i].language);
|
||||
throw_resolved +=
|
||||
resolve_throw_edges(ctx, result, rel, module_qn, imp_keys, imp_vals, imp_count);
|
||||
rw_resolved += resolve_rw_edges(ctx, result, rel, module_qn, imp_keys, imp_vals, imp_count);
|
||||
rw_resolved += resolve_rw_edges(ctx, result, rel, module_qn, imp_keys, imp_vals, imp_count,
|
||||
files[i].language);
|
||||
|
||||
free(module_qn);
|
||||
free_import_map(imp_keys, imp_vals, imp_count);
|
||||
|
||||
@@ -288,6 +288,14 @@ bool cbm_suppress_weak_member_match(bool enabled, bool is_method, const char *st
|
||||
bool cbm_suppress_cross_language_suffix_match(CBMLanguage caller_lang, const char *target_file_path,
|
||||
const char *strategy);
|
||||
|
||||
/* #1928: USAGE/WRITES/READS analog of the CALLS guard above. Reference edges
|
||||
* resolved by the short-name registry carry no import-closure evidence, so a
|
||||
* cross-language binding is a bare-name collision for EVERY strategy — drop
|
||||
* it whenever the caller's language and the target file's language disagree
|
||||
* (JS/TS family members and the C/C++ header family excepted). Pure;
|
||||
* unit-tested in test_registry.c. */
|
||||
bool cbm_suppress_cross_language_ref(CBMLanguage caller_lang, const char *target_file_path);
|
||||
|
||||
/* Get the label of a qualified name, or NULL if not found. */
|
||||
const char *cbm_registry_label_of(const cbm_registry_t *r, const char *qn);
|
||||
|
||||
|
||||
@@ -469,6 +469,13 @@ static bool js_ts_family(CBMLanguage lang) {
|
||||
lang == CBM_LANG_ARKTS;
|
||||
}
|
||||
|
||||
/* C and C++ are one family for cross-language checks: .h maps to CBM_LANG_CPP
|
||||
* in the extension table, so a .c file referencing a symbol declared in its
|
||||
* own header would otherwise read as a language boundary. */
|
||||
static bool c_cpp_family(CBMLanguage lang) {
|
||||
return lang == CBM_LANG_C || lang == CBM_LANG_CPP;
|
||||
}
|
||||
|
||||
static const char *path_basename(const char *path) {
|
||||
if (!path || !path[0]) {
|
||||
return path;
|
||||
@@ -508,6 +515,37 @@ bool cbm_suppress_cross_language_suffix_match(CBMLanguage caller_lang, const cha
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cbm_suppress_cross_language_ref(CBMLanguage caller_lang, const char *target_file_path) {
|
||||
/* #1928: USAGE / WRITES / READS analog of the CALLS guard above. A
|
||||
* variable or field reference resolved by the short-name registry must
|
||||
* not cross a language boundary: unlike CALLS, a reference edge carries
|
||||
* no import-closure evidence at all — a Go test's local `event` and an
|
||||
* eBPF C probe's automatic `event` share nothing but the spelling, so
|
||||
* EVERY registry strategy is a bare-name guess here and none is exempt.
|
||||
* LSP-backed semantic references resolve before the registry fallback
|
||||
* and never reach this predicate, which is where a genuine cross-language
|
||||
* binding (a future cgo resolver) would live. The JS/TS family keeps its
|
||||
* exemption (.js/.ts/.d.ts pairs legitimately share symbols), and C/C++
|
||||
* count as one family (.h maps to CBM_LANG_CPP). */
|
||||
if (caller_lang == CBM_LANG_COUNT || !target_file_path || !target_file_path[0]) {
|
||||
return false;
|
||||
}
|
||||
CBMLanguage target_lang = cbm_language_for_filename(path_basename(target_file_path));
|
||||
if (target_lang == CBM_LANG_COUNT) {
|
||||
return false;
|
||||
}
|
||||
if (caller_lang == target_lang) {
|
||||
return false;
|
||||
}
|
||||
if (js_ts_family(caller_lang) && js_ts_family(target_lang)) {
|
||||
return false;
|
||||
}
|
||||
if (c_cpp_family(caller_lang) && c_cpp_family(target_lang)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ── Lifecycle ──────────────────────────────────────────────────── */
|
||||
|
||||
cbm_registry_t *cbm_registry_new(void) {
|
||||
|
||||
+149
-3
@@ -824,8 +824,8 @@ TEST(pipeline_calls_resolution) {
|
||||
|
||||
/* True iff a CALLS edge exists from a node named src_name to a node named
|
||||
* tgt_name. Used to assert cross-file call resolution survives a reindex. */
|
||||
static bool cross_file_call_exists(cbm_store_t *s, const char *project, const char *src_name,
|
||||
const char *tgt_name) {
|
||||
static bool cross_file_edge_exists(cbm_store_t *s, const char *project, const char *src_name,
|
||||
const char *tgt_name, const char *edge_type) {
|
||||
cbm_node_t *srcs = NULL;
|
||||
cbm_node_t *tgts = NULL;
|
||||
int sc = 0;
|
||||
@@ -836,7 +836,7 @@ static bool cross_file_call_exists(cbm_store_t *s, const char *project, const ch
|
||||
for (int i = 0; i < sc && !found; i++) {
|
||||
cbm_edge_t *edges = NULL;
|
||||
int ec = 0;
|
||||
cbm_store_find_edges_by_source_type(s, srcs[i].id, "CALLS", &edges, &ec);
|
||||
cbm_store_find_edges_by_source_type(s, srcs[i].id, edge_type, &edges, &ec);
|
||||
for (int j = 0; j < ec && !found; j++) {
|
||||
for (int k = 0; k < tc; k++) {
|
||||
if (edges[j].target_id == tgts[k].id) {
|
||||
@@ -858,6 +858,11 @@ static bool cross_file_call_exists(cbm_store_t *s, const char *project, const ch
|
||||
return found;
|
||||
}
|
||||
|
||||
static bool cross_file_call_exists(cbm_store_t *s, const char *project, const char *src_name,
|
||||
const char *tgt_name) {
|
||||
return cross_file_edge_exists(s, project, src_name, tgt_name, "CALLS");
|
||||
}
|
||||
|
||||
/* True iff the exact named CALLS edge exists and its serialized strategy
|
||||
* contains `strategy_fragment`. Parallel synthetic-carrier regressions use
|
||||
* this on a separate ordinary-call control: it proves the cross-file LSP ran
|
||||
@@ -4688,6 +4693,145 @@ TEST(pipeline_python_receiver_suppresses_weak_method_edge) {
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* Fixture for the #1928 cross-language reference-guard probes (sequential and
|
||||
* parallel twins). pad_files > 0 adds filler files to push the index over the
|
||||
* parallel-pipeline threshold, since USAGE/WRITES/READS have one resolver per
|
||||
* path and both must consult the guard. */
|
||||
static void write_go_c_ref_guard_fixture(const char *tmp, int pad_files) {
|
||||
write_temp_file(tmp, "go.mod", "module example.com/fxguard\n\ngo 1.22\n");
|
||||
/* The C probe: a local named `event` and a file-scope function `handle`,
|
||||
* the two shapes Go identifiers collide with. */
|
||||
write_temp_file(tmp, "probe/probe.c",
|
||||
"static int total_events = 0;\n"
|
||||
"\n"
|
||||
"static int handle(void) {\n"
|
||||
" int event = 0;\n"
|
||||
" total_events += event;\n"
|
||||
" return event;\n"
|
||||
"}\n");
|
||||
/* Go: a local write named like the C local, and a value use named like
|
||||
* the C function. Neither can touch anything in a C translation unit. */
|
||||
write_temp_file(tmp, "app/app.go",
|
||||
"package app\n"
|
||||
"\n"
|
||||
"func TrackEvent() int {\n"
|
||||
"\tevent := 1\n"
|
||||
"\treturn event\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"func UsesHandle() int {\n"
|
||||
"\th := handle\n"
|
||||
"\t_ = h\n"
|
||||
"\treturn 4\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"func WriteTotal() {\n"
|
||||
"\ttotal_events := 5\n"
|
||||
"\t_ = total_events\n"
|
||||
"}\n");
|
||||
/* Same-language control: a Go package-level var written from another
|
||||
* file must keep its WRITES edge. */
|
||||
write_temp_file(tmp, "state/vars.go",
|
||||
"package state\n"
|
||||
"\n"
|
||||
"var Counter int\n");
|
||||
write_temp_file(tmp, "state/state.go",
|
||||
"package state\n"
|
||||
"\n"
|
||||
"func Bump() {\n"
|
||||
"\tCounter = 2\n"
|
||||
"}\n");
|
||||
for (int i = 0; i < pad_files; i++) {
|
||||
char name[64];
|
||||
char body[128];
|
||||
snprintf(name, sizeof(name), "pad/filler%d.go", i);
|
||||
snprintf(body, sizeof(body), "package pad\n\nfunc filler%d() int { return %d }\n", i, i);
|
||||
write_temp_file(tmp, name, body);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(pipeline_go_rw_usage_never_cross_into_c) {
|
||||
/* #1928: USAGE and WRITES/READS resolve through the same short-name
|
||||
* registry as CALLS but never consulted the #725 cross-language guard.
|
||||
* On a Go tree with eBPF C probes every Go identifier spelled like a C
|
||||
* one produced an edge into the C file — 31.5% of all WRITES on the
|
||||
* measured repo crossed the Go/C boundary. Go code cannot write a C
|
||||
* automatic variable, so every edge in that class is false. This is the
|
||||
* SEQUENTIAL-path twin; the parallel twin follows. */
|
||||
char tmp[256];
|
||||
snprintf(tmp, sizeof(tmp), "/tmp/cbm_go_rw_XXXXXX");
|
||||
if (!cbm_mkdtemp(tmp)) {
|
||||
FAIL("tmpdir");
|
||||
}
|
||||
write_go_c_ref_guard_fixture(tmp, 0);
|
||||
|
||||
char db_path[512];
|
||||
snprintf(db_path, sizeof(db_path), "%s/go_rw.db", tmp);
|
||||
cbm_pipeline_t *p = cbm_pipeline_new(tmp, db_path, CBM_MODE_FULL);
|
||||
ASSERT_NOT_NULL(p);
|
||||
ASSERT_EQ(cbm_pipeline_run(p), 0);
|
||||
const char *project = cbm_pipeline_project_name(p);
|
||||
|
||||
cbm_store_t *s = cbm_store_open_path(db_path);
|
||||
ASSERT_NOT_NULL(s);
|
||||
|
||||
/* Reproduce-first: RED before the fix — the Go local write binds the C
|
||||
* probe's global, and the Go value use binds the C `handle`. */
|
||||
ASSERT_FALSE(cross_file_edge_exists(s, project, "TrackEvent", "event", "WRITES"));
|
||||
ASSERT_FALSE(cross_file_edge_exists(s, project, "TrackEvent", "event", "READS"));
|
||||
ASSERT_FALSE(cross_file_edge_exists(s, project, "UsesHandle", "handle", "WRITES"));
|
||||
ASSERT_FALSE(cross_file_edge_exists(s, project, "UsesHandle", "handle", "READS"));
|
||||
ASSERT_FALSE(cross_file_edge_exists(s, project, "UsesHandle", "handle", "USAGE"));
|
||||
ASSERT_FALSE(cross_file_edge_exists(s, project, "WriteTotal", "total_events", "WRITES"));
|
||||
ASSERT_FALSE(cross_file_edge_exists(s, project, "WriteTotal", "total_events", "USAGE"));
|
||||
/* Same-language reference edges survive the guard. */
|
||||
ASSERT_TRUE(cross_file_edge_exists(s, project, "Bump", "Counter", "WRITES"));
|
||||
|
||||
cbm_store_close(s);
|
||||
cbm_pipeline_free(p);
|
||||
th_rmtree(tmp);
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST(pipeline_go_rw_usage_never_cross_into_c_parallel) {
|
||||
/* Parallel twin of the test above: USAGE and WRITES/READS each have a
|
||||
* second, independent resolver in pass_parallel.c (resolve_file_usages /
|
||||
* resolve_file_rw) that must consult the same guard — the field census
|
||||
* that motivated this caught the sequential-only fix leaving 344 Go→C
|
||||
* WRITES alive on a ~1150-file repo. >= 50 files forces the parallel
|
||||
* pipeline. */
|
||||
char tmp[256];
|
||||
snprintf(tmp, sizeof(tmp), "/tmp/cbm_go_rwp_XXXXXX");
|
||||
if (!cbm_mkdtemp(tmp)) {
|
||||
FAIL("tmpdir");
|
||||
}
|
||||
write_go_c_ref_guard_fixture(tmp, 52);
|
||||
|
||||
char db_path[512];
|
||||
snprintf(db_path, sizeof(db_path), "%s/go_rwp.db", tmp);
|
||||
cbm_pipeline_t *p = cbm_pipeline_new(tmp, db_path, CBM_MODE_FULL);
|
||||
ASSERT_NOT_NULL(p);
|
||||
ASSERT_EQ(cbm_pipeline_run(p), 0);
|
||||
const char *project = cbm_pipeline_project_name(p);
|
||||
|
||||
cbm_store_t *s = cbm_store_open_path(db_path);
|
||||
ASSERT_NOT_NULL(s);
|
||||
|
||||
ASSERT_FALSE(cross_file_edge_exists(s, project, "TrackEvent", "event", "WRITES"));
|
||||
ASSERT_FALSE(cross_file_edge_exists(s, project, "TrackEvent", "event", "READS"));
|
||||
ASSERT_FALSE(cross_file_edge_exists(s, project, "UsesHandle", "handle", "WRITES"));
|
||||
ASSERT_FALSE(cross_file_edge_exists(s, project, "UsesHandle", "handle", "READS"));
|
||||
ASSERT_FALSE(cross_file_edge_exists(s, project, "UsesHandle", "handle", "USAGE"));
|
||||
ASSERT_FALSE(cross_file_edge_exists(s, project, "WriteTotal", "total_events", "WRITES"));
|
||||
ASSERT_FALSE(cross_file_edge_exists(s, project, "WriteTotal", "total_events", "USAGE"));
|
||||
ASSERT_TRUE(cross_file_edge_exists(s, project, "Bump", "Counter", "WRITES"));
|
||||
|
||||
cbm_store_close(s);
|
||||
cbm_pipeline_free(p);
|
||||
th_rmtree(tmp);
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* Count nodes with the given exact name in the project (e.g. a Route path). */
|
||||
static int count_nodes_named(cbm_store_t *s, const char *project, const char *name) {
|
||||
cbm_node_t *ns = NULL;
|
||||
@@ -12805,6 +12949,8 @@ SUITE(pipeline) {
|
||||
#endif
|
||||
RUN_TEST(pipeline_tsjs_receiver_suppresses_weak_method_edge);
|
||||
RUN_TEST(pipeline_python_receiver_suppresses_weak_method_edge);
|
||||
RUN_TEST(pipeline_go_rw_usage_never_cross_into_c);
|
||||
RUN_TEST(pipeline_go_rw_usage_never_cross_into_c_parallel);
|
||||
RUN_TEST(pipeline_tsjs_receiver_parallel_keeps_service_edges);
|
||||
RUN_TEST(pipeline_python_receiver_parallel_suppresses_weak_method_edges);
|
||||
RUN_TEST(pipeline_parallel_python_cross_only_dunder_gets_synthetic_carrier);
|
||||
|
||||
@@ -811,6 +811,34 @@ TEST(cross_language_suffix_match_drops_py_vs_js) {
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST(cross_language_ref_drops_go_vs_c) {
|
||||
/* #1928: the USAGE/WRITES/READS analog of #725. Reference edges carry no
|
||||
* import-closure evidence, so EVERY registry strategy is a bare-name
|
||||
* guess and no strategy-level carve-out applies — the predicate takes no
|
||||
* strategy at all. */
|
||||
ASSERT_TRUE(cbm_suppress_cross_language_ref(CBM_LANG_GO, "bpf/probe.c"));
|
||||
ASSERT_TRUE(cbm_suppress_cross_language_ref(CBM_LANG_GO, "driver/mock.hpp"));
|
||||
ASSERT_TRUE(cbm_suppress_cross_language_ref(CBM_LANG_C, "pkg/events/event.go"));
|
||||
ASSERT_TRUE(cbm_suppress_cross_language_ref(CBM_LANG_PYTHON, "web/src/pages/Editor.js"));
|
||||
/* Same language → keep. */
|
||||
ASSERT_FALSE(cbm_suppress_cross_language_ref(CBM_LANG_GO, "pkg/state/state.go"));
|
||||
/* C and C++ are one family: .h maps to CBM_LANG_CPP, and a .c file
|
||||
* referencing its own header's declarations is not a boundary. */
|
||||
ASSERT_FALSE(cbm_suppress_cross_language_ref(CBM_LANG_C, "bpf/probe.h"));
|
||||
ASSERT_FALSE(cbm_suppress_cross_language_ref(CBM_LANG_CPP, "driver/compat.c"));
|
||||
/* …but Go into a header is still a boundary. */
|
||||
ASSERT_TRUE(cbm_suppress_cross_language_ref(CBM_LANG_GO, "bpf/probe.h"));
|
||||
/* JS/TS/TSX/ArkTS are one family. */
|
||||
ASSERT_FALSE(cbm_suppress_cross_language_ref(CBM_LANG_JAVASCRIPT, "lib/util.ts"));
|
||||
ASSERT_FALSE(cbm_suppress_cross_language_ref(CBM_LANG_TYPESCRIPT, "ui/Panel.tsx"));
|
||||
/* Unknown caller/target language or no path → nothing to judge → keep. */
|
||||
ASSERT_FALSE(cbm_suppress_cross_language_ref(CBM_LANG_COUNT, "store.py"));
|
||||
ASSERT_FALSE(cbm_suppress_cross_language_ref(CBM_LANG_GO, NULL));
|
||||
ASSERT_FALSE(cbm_suppress_cross_language_ref(CBM_LANG_GO, ""));
|
||||
ASSERT_FALSE(cbm_suppress_cross_language_ref(CBM_LANG_GO, "Makefile.inc.unknownext"));
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST(dynamic_suppress_drops_weak_method_matches) {
|
||||
/* #592/#606/#1276: a member call whose receiver the LSP could not type, that
|
||||
* landed via a WEAK short-name strategy, is generic-resolver noise → drop.
|
||||
@@ -945,6 +973,7 @@ SUITE(registry) {
|
||||
RUN_TEST(perl_suppress_drops_weak_builtin_and_method_matches);
|
||||
RUN_TEST(perl_suppress_keeps_high_confidence_and_genuine_calls);
|
||||
RUN_TEST(cross_language_suffix_match_drops_py_vs_js);
|
||||
RUN_TEST(cross_language_ref_drops_go_vs_c);
|
||||
RUN_TEST(dynamic_suppress_drops_weak_method_matches);
|
||||
RUN_TEST(dynamic_suppress_keeps_high_confidence_and_non_methods);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user