Skip to content

fix(resolver): VBA stdlib allowlist misses statement-form calls (MsgBox statement form) #192

Description

@ardelperal

What

PR #185 (feat(vba): classify stdlib calls as runtime) catches VBA stdlib function calls and marks them status='declined-runtime' instead of 'failed'. It works for the paren-call form (MsgBox("foo") → emits reference_kind='calls') but misses the statement-call form (MsgBox "foo" → emits reference_kind='unqualified-ident').

Bench corpus post-v1.13.0 reindex shows 49 statement-form stdlib calls still leaking as failed:

Name Count
MsgBox 44
DoEvents 4
Shell 1

Why

PR #185's logic in src/resolution/index.ts keys on referenceKind === 'calls':

status: r.language === 'vba' && r.referenceKind === 'calls' && isVbaStdlibFunction(r.referenceName)
  ? 'declined-runtime' as const
  : 'failed' as const,

But VBA also supports statement-form calls (no parens), which the call-sweep extractor classifies as reference_kind='unqualified-ident'. The two syntaxes are semantically identical at runtime; only the surface form differs.

Where

  • src/resolution/index.ts:1184 (and the two duplicate blocks at ~1232, ~1435) — the isVbaStdlibFunction check is gated on 'calls'
  • src/resolution/vba-runtime-objects.tsVBA_STDLIB_FUNCTIONS set + isVbaStdlibFunction() helper
  • src/extraction/vba/call-sweep.ts — where statement-form calls are emitted as unqualified-ident

Empirical evidence

After destructive reindex of the bench corpus with v1.13.0, the stdlib allowlist caught 5,368 paren-form calls but missed all 49 statement-form calls:

SELECT reference_name, COUNT(*) FROM unresolved_refs
WHERE language='vba' AND status='failed' AND reference_kind='unqualified-ident'
  AND LOWER(reference_name) IN ('cstr','clng','nz','isnull','array','instr','len','typename','vartype','msgbox','doevents','shell', /* full VBA_STDLIB_FUNCTIONS set */)
GROUP BY reference_name ORDER BY 2 DESC;

-- Result (top 5):
-- 44  MsgBox
--  4  DoEvents
--  1  Shell

Statement-form occurrences look like MsgBox "..." at line-start, not MsgBox(...) mid-expression. Detection: the keyword is the first identifier of the line (no leading ., no receiver, no (...) immediately after).

Proposed fix

  1. Extend the three isVbaStdlibFunction checks in src/resolution/index.ts to also match referenceKind === 'unqualified-ident' when the source line starts with the stdlib name.
  2. Add a tighter check via the metadata column (synthesizedBy='vba-statement-call-unresolved') — only fire when extraction flagged the row as statement-form call, not for any bare-ident read.
  3. Add test atoms in __tests__/extraction-vba-reference-kind.test.ts covering: MsgBox "hi", DoEvents, Shell "calc.exe", MsgBox at start of line vs x = MsgBox (the latter is NOT statement-form — it's a paren-call).

Expected vs actual

  • Expected: statement-form stdlib calls (e.g. MsgBox "foo") get status='declined-runtime', total VBA failed drops by ~49.
  • Actual: statement-form stdlib calls leak as unqualified-ident/failed because the resolver's stdlib check is gated on referenceKind='calls'.

Learned

  1. PR feat(vba): classify stdlib calls as runtime #185 was scoped to paren-calls because that's what the original issue (enh(resolver): add VBA stdlib function allowlist — CStr/CLng/Nz/IsNull/etc. currently flagged as missing callees #181) asked for. Statement-form is the same problem under a different syntactic shape.
  2. Extractor's metadata.synthesizedBy discriminates the two shapes — that signal is already in the row, just not consumed by the resolver.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:vbaVBA/Access-specific work (parent codegraph product)bugSomething isn't workingstatus:approvedApproved for implementation

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions