fix(extraction): C# record-struct kind fidelity + bodiless positional records (#831 follow-up) (#838)

The shipped grammar parses every record form as record_declaration (no
record_struct_declaration node), so 'record struct' mis-kinded as class.
classifyClassNode now distinguishes the value-type form by its struct
keyword child, and extractStruct accepts bodiless positional records
(the no-body gate is for C/C++ forward declarations) instead of
crashing mid-file on them.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Colby Mchenry
2026-06-12 13:17:25 -05:00
committed by GitHub
co-authored by Claude Opus 4.8
parent ed117ef90b
commit 2c7bbd5387
4 changed files with 62 additions and 11 deletions
+13 -8
View File
@@ -1238,8 +1238,10 @@ export class TreeSitterExtractor {
if (!this.extractor) return;
// Skip forward declarations and type references (no body = not a definition)
// — EXCEPT C# positional records (`record struct M(decimal Amount);`),
// complete definitions with no body block. (#831)
const body = getChildByField(node, this.extractor.bodyField);
if (!body) return;
if (!body && node.type !== 'record_declaration') return;
const name = extractName(node, this.source, this.extractor);
const docstring = getPrecedingDocstring(node, this.source);
@@ -1260,15 +1262,18 @@ export class TreeSitterExtractor {
// `record struct M(decimal Amount)` which the grammar nests here).
this.extractCsharpPrimaryCtorParamRefs(node, structNode.id);
// Push to stack for field extraction
this.nodeStack.push(structNode.id);
for (let i = 0; i < body.namedChildCount; i++) {
const child = body.namedChild(i);
if (child) {
this.visitNode(child);
// Push to stack for field extraction (bodiless positional records have
// no members to visit)
if (body) {
this.nodeStack.push(structNode.id);
for (let i = 0; i < body.namedChildCount; i++) {
const child = body.namedChild(i);
if (child) {
this.visitNode(child);
}
}
this.nodeStack.pop();
}
this.nodeStack.pop();
}
/**