mirror of
https://github.com/colbymchenry/codegraph.git
synced 2026-10-27 04:49:43 +08:00
fix(rust): index unit structs — bodiless is a definition, not a forward decl
`extractStruct` skips a struct with no body as a forward declaration. That is right for C/C++ (`struct Foo;`), but Rust has no forward declarations — `struct Unit;` is a complete unit struct. The type never entered the graph, and neither did anything attached to it: every `impl SomeTrait for Unit` lost its edge because the source endpoint did not exist. Brace and tuple structs were unaffected — both carry a body field (`field_declaration_list` / `ordered_field_declaration_list`), so only the unit form fell through. This is the same situation two earlier patches already handled, just a case each of them left uncovered: #831 hardcoded a carve-out for C# positional records — "complete definitions with no body block". #1093 introduced `skipBodilessClass` so a bodiless CLASS is kept unless a language opts into skipping it. Struct kept the unconditional skip. This adds `allowBodilessStruct`, the per-language opt-in for the struct side, and sets it for Rust only. The polarity is inverted relative to `skipBodilessClass` because the defaults differ — that is documented on both flags. The kernel's Rust walker had the same guard, placed before create_node, and its header listed the behavior as a mirrored bug-for-bug quirk. Node creation now happens first and the body is walked only when present, which is how extract_interface in the same file was already shaped. Kernel↔wasm parity is preserved (the parity suite compares the two, and passes). Four lines of logic across four source files; the rest is comments, docs and the two regression tests. The kernel header's quirk list, the parity suite's docstring and the R7b port checklist all documented the old behavior as intended, so each is updated — the checklist declares itself the living quirk reference for the walker, so a stale entry there would misinform the next reader. Effect on a real Rust/TS repository (2669 nodes): 13 structs and 9 implements edges recovered. Tests: two regression cases in extraction.test.ts. Verified failing without the fix on the wasm path (CODEGRAPH_KERNEL=0) and passing with it, on both the kernel and wasm paths. Full suite green — 170 files, 2922 tests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
d6d17288be
commit
a94c9dc94e
@@ -135,7 +135,7 @@ undefined; **no isConst means `const_item`/`static_item` extract as kind
|
||||
|---|---|---|
|
||||
| `function_item` (top level) | functionTypes, tree-sitter.ts:994 → extractFunction:1517 | not inside class-like at file scope → extractFunction; **first line of extractFunction (1522): if getReceiverType returns a value → extractMethod instead** (this is how impl-block fns become methods — impl_item does NOT push a scope) |
|
||||
| `function_signature_item` | same | in a trait body (trait pushed, class-like) → extractMethod; no `body` field → no body walk |
|
||||
| `struct_item` | structTypes:1059 → extractStruct:1869 | `body` field required: **unit structs `struct Unit;` have no body → NO node minted** (1876, `record_declaration` exemption is C#-only). Tuple structs have body `ordered_field_declaration_list` → extracted. `field_declaration` children make NO nodes (rust has no fieldTypes) — visitNode recurses into them and finds nothing |
|
||||
| `struct_item` | structTypes:1059 → extractStruct:1869 | ~~`body` field required: unit structs `struct Unit;` have no body → NO node minted~~ — **superseded: Rust now sets `allowBodilessStruct`, so `struct Unit;` mints a node with no members.** Rust has no forward declarations, so the bodiless skip (meant for C/C++) never applied here; the `record_declaration` exemption is the C# form of the same carve-out. Tuple structs have body `ordered_field_declaration_list` → extracted. `field_declaration` children make NO nodes (rust has no fieldTypes) — visitNode recurses into them and finds nothing |
|
||||
| `enum_item` | enumTypes:1064 → extractEnum:1914 | body `enum_variant_list`; `enum_variant` children → extractEnumMembers:1958 — **`name` field path: one `enum_member` node from `getChildByField(node,'name')`, then return** (variant payload bodies `B(u32)` / `C { x }` are never walked). Non-variant children (e.g. `attribute_item`) → visitNode (no-op) |
|
||||
| `trait_item` | interfaceTypes:1054 → extractInterface:1834 | kind `'trait'` (interfaceKind); extractInheritance sees the `trait_bounds` child (see below); body `declaration_list` children visited with the trait pushed → fn items become methods with QN `Trait::name` via nodeStack |
|
||||
| `impl_item` | dedicated branch:1273-1276 → extractRustImplItem:5690 | emits the implements back-reference (below); **skipChildren stays false** → the `declaration_list` is then visited normally by the loop at 1295 (that's how impl members are reached; impl pushes NOTHING on the nodeStack) |
|
||||
@@ -480,7 +480,7 @@ inner `array_expression`, but `const CB: fn() = handler;` captures nothing
|
||||
## Gates (per plan §5, no exceptions)
|
||||
|
||||
- **Torture fixture `torture.rs`** (+ CRLF variant, derived in-memory), pinning
|
||||
at minimum: unit struct (NO node) / tuple struct / field struct; enum with
|
||||
at minimum: unit struct (node, no members) / tuple struct / field struct; enum with
|
||||
unit+tuple+struct variants; trait with supertraits incl. a SCOPED one
|
||||
(`fmt::Debug` — dropped) + `function_signature_item` + default method +
|
||||
associated type/const (no node; const value call attributes to trait);
|
||||
|
||||
Reference in New Issue
Block a user