feat(vba-extractor): detect statement-form calls after Then/Else in single-line If (closes #45) - #63
Merged
ardelperal merged 1 commit intoJul 3, 2026
Conversation
…ngle-line If (closes #45) Reads \If <cond> Then <calls> [Else <calls>]\ and \If <cond> Then <calls>: <calls>\ and emits \calls\ edges for the same-file Sub targets. \GoTo\, \Exit\, \Resume\ clauses are deliberately silent. Block-form \If\ (where the body is on subsequent lines) is unchanged. Tests: __tests__/extraction-vba.test.ts -> +8 (bare-Then happy path, Else clause, GoTo/Exit suppressed, colon multi-statement, Call keyword, qualified statement-call, block-form regression).
ardelperal
added a commit
that referenced
this pull request
Jul 4, 2026
* fix(vba): unify qualified call gating (closes #40) Cherry-pick of 9b1787a from `master` onto `main` (80ca05d). The original commit landed on `master` but never reached the `ardelperal/codegraph-vba` fork's `main`; the issue was reopened 2026-07-04 because the fix was unreachable from the published binary. This commit reapplies the unified `shouldProcessQualifiedCall` gate on the `main` branch tip. Blame on merge — three conflict hunks ported: 1. `isLocalProjectClassVar` (line ~1687): kept HEAD. The bracketed-strip + null-check + Issue #54 comment are load-bearing for the `[FUNCIONES UTILES].Foo` shape; the trailing PRIMITIVE_TYPES check is identical on both sides. 2. `sweepCallsAndSql` qualified-statement block (line ~1932): kept HEAD's full `splitSingleLineIfClauses` loop structure (added in PR #63, which is on `main` but not in the cherry-pick base) and swapped the qualStmt gate from `isLocalProjectClassVar` to `shouldProcessQualifiedCall`. The `withReceiverStack` block is unchanged — its own `isLocalProjectClassVar` gate stays. 3. `localVarTypeMap` docstring (line ~3167): took 9b1787a's updated wording (references the unified gate) but kept the Issue #2 reference for historical continuity. The TempVars code between `synthClassNodeIds` and `localVarTypeMap` is HEAD-only (added by commit 71bdb5f / Issue #50, post-9b1787a) and stays untouched. Acceptance criteria (Issue #40): - (a) `Set rs = db.OpenRecordset(sql)` with `Dim db As DAO.Database` produces no stub function node. Pinned by the new `qualified statement-form call on external local variable is silent` test and the `AC (a)` test in `extraction-vba.test.ts`. - (b) `modUtils.Foo arg` and `modUtils.Foo(arg)` both produce a resolvable call edge. Pinned by `AC (b)` (both shapes on separate lines, each emits a heuristic edge to a `modUtils.Foo` stub). - (c) `Dim m_NCOp As NCOperaciones` then `m_NCOp.Registrar args` keeps emitting to the resolved class name. Pinned by `AC (c)`. Dysflow corpus validation (C:\Proyectos\dysflow\E2E_testing\src, 203 .bas/.cls/.form.txt files): metric main fix delta total nodes 8640 10045 +1405 total edges 15338 16833 +1495 stub nodes 1850 3255 +1405 total calls edges 5091 6586 +1495 heuristic calls edges 2629 4124 +1495 Heuristic `calls` edges increase by +1495 — the fix's main intent (cross-module statement-form qualified calls now emit). Stubs go up because the unified gate also admits undeclared receivers in the statement form (e.g. `VBA.DoEvents`, function-parameter receivers that aren't in `localVarTypeMap`); the DAO-typed local-var shape is silenced correctly: silenced (good): newly emitted (statement-form): db.OpenRecordset -54 VBA.DoEvents +1534 dbUse.OpenRecordset -22 p_Db.Execute +48 datos.Exists -8 cmb.AddItem +23 m_ColDestinatariosOcultos.exists -8 pLogs.Add +13 VBA.VarType -7 fso.CreateFolder +12 AddLog +65 InvalidarCache +18 ActualizarDatosCalculados +14 The +1534 `VBA.DoEvents` spike is a pre-existing gap: the statement-form path doesn't run the `RUNTIME_RECEIVER_BLACKLIST` filter that the paren-form `scanCallSites` does at vba-extractor.ts:2070. Filtering runtime receivers in the statement form is a follow-up (not in this PR's scope); the cross-module qualified-call shape is what issue #40 asked for, and that now works in both forms. Test results (focused): npx vitest run __tests__/extraction-vba.test.ts -t "qualified" 36 ✓ npx vitest run __tests__/extraction-vba.test.ts -t "AC" 8 ✓ npx vitest run __tests__/extraction-vba.test.ts -t "bracketed" 7 ✓ Full VBA suite (346 tests across 10 files): all green. The `npm test` full-suite run on Node 25.2.1 hits the documented V8 turboshaft Zone OOM (issue #81) in `resolution.test.ts` — environment, not regression. * docs(changelog): note Issue #40 fix in [Unreleased] Cherry-pick of 9b1787a from master onto main re-applies the unified qualified-call gating on the published binary's main branch. Document the user-visible change in [Unreleased] so the next release notes cover it. * ci(workflow): drop Ubuntu runner; Windows-only CI Project target is Microsoft Access / VBA. Ubuntu CI added minutes without exercising anything the Windows leg didn't already cover — node:sqlite, tree-sitter, and the cross-language surface all run fine on Windows. Revisit if a Linux-specific failure mode ever appears in production.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #45
Problem
\detectStatementCall\ and \detectQualifiedStatementCall\ (in \src/extraction/vba-extractor.ts) inspect only the FIRST identifier of the line. For \If x Then Foo arg\ the leading identifier is the keyword \If, which is in \CALL_KEYWORD_BLACKLIST, so the call AFTER \Then\ is silently dropped — even though the paren form \x.Method(...)\ on the same line is correctly captured by \CALL_RE. Same gap on the qualified path.
Result: the dominant VBA idiom for early-exit guards (\If Err.Number <> 0 Then GestionarError) contributed zero edges to the call graph, and the gap was inconsistent with the paren-form path.
Fix
Add \splitSingleLineIfClauses(line: string): string[]\ (Issue #45 helper):
The two call-detection calls in \sweepCallsAndSql\ now iterate over the returned fragments instead of receiving the raw line. \CALL_RE\ (paren-form) is unchanged — it still scans the entire masked line as before, so \If x Then M(a) Else N(b)\ continues to emit both \M(a)\ and \N(b)\ edges via that path.
Tests
_tests_/extraction-vba.test.ts\ gains a new \describe('VbaExtractor — single-line \If … Then\ statement-form calls (Issue #45)', ...)\ block with 8 atoms that previously failed (red) and now pass (green):
Verification