Issue 4 — [P1] fix: vba-extractor — audit and fix VBA structural-recognition gaps from F.1 spike
Context
The F.1 spike report (docs/spikes/vbnet-as-vba.md, "VBA constructs the vbnet grammar fails on" section) identified 12 specific VBA constructs where the vbnet grammar fails to emit a dedicated node type. The full regex-based pipeline ALSO doesn't extract some of these directly — they fall back to heuristics or are not emitted.
The 12 gaps:
| # |
Construct |
Current regex behavior |
Fix or accept? |
| 1 |
class_declaration / module_declaration wrapper |
Heuristic (isCls ? kind='class' : kind='module' at vba-extractor.ts:281) |
Accept (heuristic is fine for the bench corpus) |
| 2 |
method_declaration / constructor_declaration / abstract_method_declaration |
Extracted by PROC_RE in procedures.ts |
Accept |
| 3 |
field_declaration |
Extracted by DIMS_RULES in dims.ts |
Accept |
| 4 |
property_declaration |
Extracted via PROCEDURES_RULES (treating Property Get/Set/Let as procedures) |
Accept |
| 5 |
event_declaration / custom_event_declaration / raiseevent_statement |
Partially handled by event-synth.ts |
Audit (see below) |
| 6 |
implements_clause |
Handled by IMPLEMENTS_RULES |
Accept |
| 7 |
parameter_list / parameter |
Handled inside procedures.ts signature parsing |
Accept |
| 8 |
const_declaration |
BUG: Public Const X = Y is parsed as member_modifier (Public Const) + assignment_statement (X = Y) — the const nature is LOST |
FIX (highest priority within this issue) |
| 9 |
Wend loop terminator |
Handled by pre-processing rewrite |
Accept |
| 10 |
VERSION 1.0 CLASS + BEGIN...END header |
Handled by vba-extractor.ts:detectVbName |
Accept |
| 11 |
Attribute VB_* lines |
Handled by stripVbaComments in vba-preprocess.ts |
Accept |
| 12 |
Option Compare Database / Option Explicit |
Recognized as option_statement but position is wrong |
Audit (see below) |
Acceptance criteria
Suggested approach
For the Public Const fix, add a rule to enums-consts.ts:
defineRule({
id: 'module-const',
description: 'Match `Public Const NAME = VALUE` or `Private Const NAME = VALUE`; emit a const symbol with kind:"const" so the const nature survives (vs. the dim-const rule which treats it as an assignment).',
pattern: /^\s*(?:Public|Private)\s+Const\s+(\w+)\s*(?:As\s+(\w+))?\s*=\s*(.+?)\s*$/i,
requires: 'module',
emit: (m, ctx, line, lineNum) => {
const name = m[1];
const asType = m[2] ?? 'Variant';
const value = m[3];
ctx.nodes.push({
id: generateNodeId(ctx.filePath, 'const', name, lineNum),
kind: 'const',
name,
qualifiedName: name,
filePath: ctx.filePath,
language: 'vba',
startLine: lineNum,
endLine: lineNum,
metadata: { value, asType, visibility: /Public/i.test(line) ? 'public' : 'private' },
});
return null;
},
});
Place this rule BEFORE the dim-const rule in the table so the more specific pattern wins. Add a test fixture to __tests__/extraction-vba-realfixtures.test.ts.
Files touched
Issue 4 —
[P1] fix: vba-extractor — audit and fix VBA structural-recognition gaps from F.1 spikeContext
The F.1 spike report (
docs/spikes/vbnet-as-vba.md, "VBA constructs the vbnet grammar fails on" section) identified 12 specific VBA constructs where the vbnet grammar fails to emit a dedicated node type. The full regex-based pipeline ALSO doesn't extract some of these directly — they fall back to heuristics or are not emitted.The 12 gaps:
class_declaration/module_declarationwrapperisCls ? kind='class' : kind='module'atvba-extractor.ts:281)method_declaration/constructor_declaration/abstract_method_declarationPROC_REinprocedures.tsfield_declarationDIMS_RULESindims.tsproperty_declarationPROCEDURES_RULES(treatingProperty Get/Set/Letas procedures)event_declaration/custom_event_declaration/raiseevent_statementevent-synth.tsimplements_clauseIMPLEMENTS_RULESparameter_list/parameterprocedures.tssignature parsingconst_declarationPublic Const X = Yis parsed asmember_modifier (Public Const) + assignment_statement (X = Y)— theconstnature is LOSTWendloop terminatorVERSION 1.0 CLASS+BEGIN...ENDheadervba-extractor.ts:detectVbNameAttribute VB_*linesstripVbaCommentsinvba-preprocess.tsOption Compare Database/Option Explicitoption_statementbut position is wrongAcceptance criteria
Public Constbug (docs(readme): align header and add fork notes for codegraph-vba #8): emit aconstsymbol withkind: 'const', or tag the dim withmetadata.isConst: true. Updateenums-consts.ts:RULESto match^\s*(?:Public|Private)\s+Const\s+\w+\s*=with a new emit bodyevent_declaration/raiseevent_statement): verifyevent-synth.tscorrectly emits event-related edges forPublic Event X(...)declarations andRaiseEvent Xcalls. If incomplete, document and fixOptionstatements): verify the regex layer's handling. If position-wrong, either accept (no real consequence) or fixPublic Constfix: a fixture withPublic Const FOO As Long = 42must emit aconstsymbolSuggested approach
For the
Public Constfix, add a rule toenums-consts.ts:Place this rule BEFORE the
dim-construle in the table so the more specific pattern wins. Add a test fixture to__tests__/extraction-vba-realfixtures.test.ts.Files touched
src/extraction/vba/enums-consts.ts(new rule)__tests__/extraction-vba-realfixtures.test.ts(regression test)docs/spikes/vbnet-as-vba.md(note: 11 of 12 gaps accepted as-is; docs(readme): align header and add fork notes for codegraph-vba #8 fixed; fix(bin): allow Node 25 by default and silence banner in MCP mode #5 and docs(all): align references and templates from .codegraph to .codegraph-vba #12 audited)