mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-07 08:43:35 +08:00
fix(pipeline): a bare Go reference never binds a struct Field
The READS/WRITES resolvers and the USAGE registry fallback hand bare reference text to the short-name registry, which contains Field nodes - so once Go struct fields exist (#1935), every Go local err := ... binds whichever struct field is named err, project-wide: 21308 USAGE and 5191 WRITES onto Go fields on the measured repo, top target a test struct's field T collecting 3013 edges. In Go that binding is impossible by construction: a field is only reachable through a selector expression (x.f), and selector references resolve on the LSP path - every Field-targeted reference edge in the census carried dot-less text. Add cbm_go_suppress_bare_field_ref() next to the #1928 predicate and consult it at the same four sites (both READS/WRITES resolvers, both USAGE registry fallbacks): drop the bind when the file is Go, the target label is Field, and the reference text has no '.'. Go-gated because C#/Java/C++/Python method bodies legitimately reference their own members bare (cp_reads_writes_cs_static_field pins that shape). Field-validated on the #1940 stack: USAGE onto Go fields 21308 -> 0, WRITES 5191 -> 0; the only remaining field-targeted edges are 2466 CALLS, which are #1906/#1907's selector-guard territory. Reproduce- first pipeline probes (sequential + parallel twins) were RED on the stack without this commit. Fixes #1942 Signed-off-by: Ilya Brykau <ilya.brykau@orca.security>
This commit is contained in:
@@ -2660,6 +2660,11 @@ static void resolve_file_usages(resolve_ctx_t *rc, resolve_worker_state_t *ws,
|
||||
if (tgt && cbm_suppress_cross_language_ref(lang, tgt->file_path)) {
|
||||
continue;
|
||||
}
|
||||
/* #1942: a bare Go reference can never denote a struct field. */
|
||||
if (tgt &&
|
||||
cbm_go_suppress_bare_field_ref(lang == CBM_LANG_GO, usage->ref_name, tgt->label)) {
|
||||
continue;
|
||||
}
|
||||
if (usage->semantic_reference_blocked && (usage->semantic_reference_local_shadow ||
|
||||
cbm_pipeline_node_is_callable_target(tgt))) {
|
||||
continue;
|
||||
@@ -2745,6 +2750,10 @@ static void resolve_file_rw(resolve_ctx_t *rc, resolve_worker_state_t *ws, CBMFi
|
||||
if (cbm_suppress_cross_language_ref(lang, tgt->file_path)) {
|
||||
continue;
|
||||
}
|
||||
/* #1942: a bare Go reference can never denote a struct field. */
|
||||
if (cbm_go_suppress_bare_field_ref(lang == CBM_LANG_GO, rw->var_name, tgt->label)) {
|
||||
continue;
|
||||
}
|
||||
const char *etype = rw->is_write ? "WRITES" : "READS";
|
||||
cbm_gbuf_insert_edge(ws->local_edge_buf, src->id, tgt->id, etype, "{}");
|
||||
}
|
||||
|
||||
@@ -216,6 +216,11 @@ static int resolve_usage_edges(cbm_pipeline_ctx_t *ctx, const CBMFileResult *res
|
||||
if (tgt && cbm_suppress_cross_language_ref(lang, tgt->file_path)) {
|
||||
continue;
|
||||
}
|
||||
/* #1942: a bare Go reference can never denote a struct field. */
|
||||
if (tgt &&
|
||||
cbm_go_suppress_bare_field_ref(lang == CBM_LANG_GO, usage->ref_name, tgt->label)) {
|
||||
continue;
|
||||
}
|
||||
if (usage->semantic_reference_blocked && (usage->semantic_reference_local_shadow ||
|
||||
cbm_pipeline_node_is_callable_target(tgt))) {
|
||||
continue;
|
||||
@@ -304,6 +309,10 @@ static int resolve_rw_edges(cbm_pipeline_ctx_t *ctx, const CBMFileResult *result
|
||||
if (cbm_suppress_cross_language_ref(lang, tgt->file_path)) {
|
||||
continue;
|
||||
}
|
||||
/* #1942: a bare Go reference can never denote a struct field. */
|
||||
if (cbm_go_suppress_bare_field_ref(lang == CBM_LANG_GO, rw->var_name, tgt->label)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const char *edge_type = rw->is_write ? "WRITES" : "READS";
|
||||
cbm_gbuf_insert_edge(ctx->gbuf, src->id, tgt->id, edge_type, "{}");
|
||||
|
||||
@@ -296,6 +296,14 @@ bool cbm_suppress_cross_language_suffix_match(CBMLanguage caller_lang, const cha
|
||||
* unit-tested in test_registry.c. */
|
||||
bool cbm_suppress_cross_language_ref(CBMLanguage caller_lang, const char *target_file_path);
|
||||
|
||||
/* #1942: a bare (dot-less) Go reference can never denote a struct field —
|
||||
* field access is always a selector expression, and selector references
|
||||
* resolve on the LSP path. Drops a READS/WRITES/USAGE bind whose target is a
|
||||
* Field when the reference text carries no '.'. Go only: other OO languages
|
||||
* legitimately reference their own members bare inside method bodies. Pure;
|
||||
* unit-tested in test_registry.c. */
|
||||
bool cbm_go_suppress_bare_field_ref(bool is_go, const char *ref_name, const char *target_label);
|
||||
|
||||
/* 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);
|
||||
|
||||
|
||||
@@ -546,6 +546,24 @@ bool cbm_suppress_cross_language_ref(CBMLanguage caller_lang, const char *target
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cbm_go_suppress_bare_field_ref(bool is_go, const char *ref_name, const char *target_label) {
|
||||
/* #1942: a bare (dot-less) Go reference can never denote a struct field —
|
||||
* field access is always a selector expression (x.f), and selector
|
||||
* references resolve through the LSP join, never through the bare-name
|
||||
* registry fallback. Every Field-targeted reference edge in the field
|
||||
* census carried dot-less text, so dropping the bind loses nothing real.
|
||||
* Go-gated: a C#/Java/C++/Python method body legitimately references its
|
||||
* own members bare (cp_reads_writes_cs_static_field pins that shape as
|
||||
* required), so a global veto would break those languages. */
|
||||
if (!is_go || !ref_name || !ref_name[0] || !target_label) {
|
||||
return false;
|
||||
}
|
||||
if (strcmp(target_label, "Field") != 0) {
|
||||
return false;
|
||||
}
|
||||
return strchr(ref_name, '.') == NULL;
|
||||
}
|
||||
|
||||
/* ── Lifecycle ──────────────────────────────────────────────────── */
|
||||
|
||||
cbm_registry_t *cbm_registry_new(void) {
|
||||
|
||||
Reference in New Issue
Block a user