Skip to content

fix(extract): descend into Go struct field_declaration_list - #1940

Open
ilyabrykau-orca wants to merge 4 commits into
DeusData:mainfrom
ilyabrykau-orca:fix/go-struct-fields
Open

fix(extract): descend into Go struct field_declaration_list#1940
ilyabrykau-orca wants to merge 4 commits into
DeusData:mainfrom
ilyabrykau-orca:fix/go-struct-fields

Conversation

@ilyabrykau-orca

Copy link
Copy Markdown

Fixes #1935. Stacked on #1937 (the USAGE/WRITES cross-language guard) — the issue's own measurement shows why this must not land alone: ~4600 recovered Field nodes named err/ctx/path/_ would otherwise mint ~5700 WRITES / ~22000 USAGE name-collision edges through the unguarded reference resolver. The two tip commits + the test are this PR's own change; the base commit is #1937's.

What

Go struct fields were never extracted — 0 Field nodes for ~5055 field declarations on the measured repo, while the 850 Field nodes in the graph were all C/C++. find_class_body() returns type_spec's type child:

  • interface: interface_type holds its method specs directly → always worked;
  • struct: struct_type's only named child is a field_declaration_list, one level above the field_declaration nodes the member loop looks for → every field silently skipped. go_field_types[] = {"field_declaration"} was already registered in lang_specs.c; the extractor just never reached them.

How

go_normalize_struct_body() descends struct_typefield_declaration_list before extract_class_fields() iterates, the same normalization step the Java enum_body_declarations path already does. A second commit skips the Go blank identifier _ (struct padding in generated code — not a referenceable field; 55 nodes that collected 241 collision edges).

Measured effect (from the issue)

0 → 4588 Go Field nodes, each carrying its declared type in return_type. Embedded fields (~208) stay unrepresented by construction (no name child) — separate follow-up; lsp_embed_dispatch is unaffected. With #1937 under it, CALLS onto Go fields stay guarded (2466 → 417 by #1907's gate in the issue's stacked measurement) and the WRITES/USAGE collision classes are dropped by the reference guard. Also unblocks func-typed-field call dispatch (r.fn(4)Router.fn), which needs the field node to exist.

Tests

  • Reproduce-first extract_go_struct_fields_have_nodes: RED with the descend fix reverted — count_defs_with_label(r, "Field") == 0, expected 3 — GREEN on the branch. Asserts the three named fields with declared types (string, int, *Config), the blank identifier's absence, and that interface members keep extracting exactly as before.
  • Full scripts/test.sh venue leg (ASan+UBSan, all suites + contract steps): green on the stack. git clang-format --diff: clean.

#1932 tracks the family; ordering constraint (#1928 first) is from the issue's own two-index measurement.

USAGE, WRITES and READS edges resolve through the same short-name
registry as CALLS but never consulted the DeusData#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 DeusData#1928

Signed-off-by: Ilya Brykau <ilya.brykau@orca.security>
Signed-off-by: Ilya Brykau <ilya.brykau@orca.security>
Signed-off-by: Ilya Brykau <ilya.brykau@orca.security>
Reproduce-first probe for the two fixes on this branch: RED without the
descend fix (count_defs_with_label(r, "Field") == 0, expected 3), GREEN
with it. Asserts the three named fields with their declared types in
return_type, the absence of the blank identifier, and that interface
members keep extracting exactly as before.

Signed-off-by: Ilya Brykau <ilya.brykau@orca.security>
@github-actions

Copy link
Copy Markdown

Thanks for opening this — it has been seen, and it is queued.

This note is automated, but it is not a brush-off: it exists so you know where your PR stands instead of having to guess from silence.

Current review status: working through a backlog. 0.9.1-rc.1 is out, so the release freeze that held reviews is over — but it left a large queue of open pull requests behind it, and we are reading through them oldest-first. The background is in discussion #1144.

What that means for this PR, concretely:

  • It will not be closed for inactivity. No stale bot touches pull requests here.
  • It may still sit a while before a human reads it. That is on us, not on you.
  • Older PRs are read first, so a recent one is not being skipped — it is behind a queue.

Things that will genuinely speed it up whenever review does happen:

  • Keep it rebased on main — the tree is moving quickly right now, and a conflicting branch cannot be reviewed as the diff you intended.
  • Get CI green, or say which failures you believe are pre-existing.
  • Keep the change to one claim. Bundled features and refactors get split before they get merged, which costs you a round trip.
  • Every commit needs a sign-off (git commit -s) — CI enforces DCO.

If this fixes a bug, a reproduction we can run is worth more than a description of the symptom.

Thanks for contributing, and sorry in advance for the wait.

@ilyabrykau-orca

Copy link
Copy Markdown
Author

Rebased onto the amended #1937 (its guard now covers the parallel resolvers). No semantic changes to this PR's commits; extraction+pipeline suites green, full venue leg green on the stack.

Heads-up from field validation: with #1937 under it, this fix still surfaces ~26k same-language bare-name reference edges onto the new Field nodes (a local err := … binding whichever field is named err). That is its own bounded defect — filed as #1942 with counts, fix PR incoming stacked on this branch. Suggested merge order: #1937 → this → #1942's PR (or the last two together).

ilyabrykau-orca added a commit to ilyabrykau-orca/codebase-memory-mcp that referenced this pull request Aug 30, 2026
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 (DeusData#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 DeusData#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 DeusData#1940 stack: USAGE onto Go fields 21308 -> 0,
WRITES 5191 -> 0; the only remaining field-targeted edges are 2466
CALLS, which are DeusData#1906/DeusData#1907's selector-guard territory. Reproduce-
first pipeline probes (sequential + parallel twins) were RED on the
stack without this commit.

Fixes DeusData#1942

Signed-off-by: Ilya Brykau <ilya.brykau@orca.security>
ilyabrykau-orca added a commit to ilyabrykau-orca/codebase-memory-mcp that referenced this pull request Aug 30, 2026
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 (DeusData#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 DeusData#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 DeusData#1940 stack: USAGE onto Go fields 21308 -> 0,
WRITES 5191 -> 0; the only remaining field-targeted edges are 2466
CALLS, which are DeusData#1906/DeusData#1907's selector-guard territory. Reproduce-
first pipeline probes (sequential + parallel twins) were RED on the
stack without this commit.

Fixes DeusData#1942

Signed-off-by: Ilya Brykau <ilya.brykau@orca.security>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(extract): Go struct fields are never extracted — 0 Field nodes for ~5000 field declarations (structs miss one level of descent)

1 participant