feat(vba-extractor): model DoCmd.OpenReport and DoCmd.OpenQuery like OpenForm (closes #48) - #69
Merged
Merged
Conversation
…OpenForm (closes #48) Hueco 6 (DoCmd.OpenForm "X" -> opens-form edge) was always labelled follow-up work for OpenReport and OpenQuery. This lands both. The OpenForm pipeline is preserved byte-identically (existing hueco-6 + Const-fallback + W4-guard tests stay green without modification) while OpenReport mirrors it through a small dispatch table and OpenQuery opts for an UnresolvedReference so the resolver binds to the REAL query node emitted by SqlQueryExtractor. Changes: * `NodeKind` extended with `'report-layout'`; `EdgeKind` extended with `'opens-report'` (`src/types.ts`). Distinct from `form-layout` / `opens-form` so downstream tooling can filter form vs report without inspecting qualifiedName. * `OPEN_FORM_ARG_RE` retained byte-identical. New `OPEN_REPORT_ARG_RE` mirrors it for `DoCmd.OpenReport "X"`; new `OPEN_QUERY_ARG_RE` covers `DoCmd.OpenQuery "X"` (Issue #48 spec acceptance). * `DOCMD_OPEN_DISPATCH` table introduced (`{ method, re, edgeKind, stubKind, syntheticPrefix, syntheticExtension, moduleNamePrefix, cacheKey, metadataTargetKey, synthesizedBy }`). OpenForm and OpenReport share the entire scanner + emitter pipeline; only the per-method metadata differs. OpenQuery intentionally is NOT in this dispatch because it emits an UnresolvedReference, not a stub + edge. * `scanOpenFormCalls` generalized into `scanDoCmdOpenCalls` — iterates the dispatch table; OpenForm behavior byte-identical (same regex, same arg resolution, same stub id formula). * `emitOpensFormEdge` generalized into `emitOpensStubEdge` — parametric on the dispatch entry. Cache key is now `${cacheKey}:${loweredName}` so OpenForm and OpenReport de-dup buckets stay disjoint within a file. Stub synthetic path uses `${syntheticPrefix}/${Name}${syntheticExtension}` — `.form.txt` for OpenForm, `.report.txt` for OpenReport (mirrors the real file extension per Issue #48 spec). * New `scanDoCmdOpenQuery` — pushes ONE UnresolvedReference per match (`referenceName` = resolved query name, `referenceKind` = 'references', `metadata.synthesizedBy` = 'vba-opens-query'). Falls back to bare identifier when a local Const isn't found. The reference resolves to the REAL `query` node SqlQueryExtractor emits for `queries/<Name>.sql` — no synthetic stub, no synthetic `query` node, no synthetic function node (W4 graph-pollution invariant preserved). * Wiring updated in the line-scanner: `scanDoCmdOpenCalls` replaces the old `scanOpenFormCalls` call; `scanDoCmdOpenQuery` runs alongside it on the original unmasked line (consistent with OpenForm — the literal `"X"` form has the form/query name in a string literal that masking would destroy). * 9 regression tests in `__tests__/extraction-vba.test.ts` (Issue #48 describe block): - OpenReport literal + Const-resolved + Const-fallback (mirrors OpenForm's Const-fallback test at line ~2268) - OpenReport deterministic stub id across re-index (asserted against `generateNodeId('synthetic:opensReportStub/<Name>.report.txt', ...)`) - OpenReport W4 no-synthetic-fn guard - OpenReport de-dup: two calls produce exactly ONE stub - OpenQuery literal + Const-resolved + Const-fallback - OpenQuery W4 no-synthetic-fn guard Existing OpenForm tests (hueco-6 in `__tests__/extraction-vba-control-modeling.test.ts`; the "DoCmd.OpenForm does not emit a synthetic function node" and the "resolves local string constants in DoCmd.OpenForm..." tests in extraction-vba.test.ts) stay green without modification — the dispatch refactor preserves OpenForm emission exactly. Disjointness note: OpenForm and OpenReport live in separate ID spaces (different synthetic prefixes, different stub kinds, different qualifiedName prefixes `Form_<Name>` vs `Report_<Name>`). The cache key prefix `OpenForm:` vs `OpenReport:` further guarantees the two stubs in the same file don't collide. Validation: * `pnpm exec vitest run __tests__/extraction-vba.test.ts -t "Issue #48"` -> 9/9 pass in 681 ms * Full VBA suite (6 files): **236/236 pass** in 5.82 s — zero regressions in OpenForm or any other extractor path * `pnpm run build` -> exit 0, no TS errors
4 tasks
ardelperal
added a commit
that referenced
this pull request
Jul 4, 2026
… 7 issues, update README banner + CLAUDE.md schema Publishes the 7 VBA-extractor issues closed this session (#44, #48, #49, #50, #51, #52, #53) plus the test-infra flake fix from PR #72. CHANGELOG: - Populate the previously-empty `## [Unreleased]` section with 5 New Features (bang operator, OpenReport/OpenQuery, RecordSource/ RowSource, TempVars, encoding robustness) and 2 Fixes (conditional-compilation evaluator, procedure-local Const scoping). Each entry is plain-language, no internal paths, with PR numbers for traceability. Per the project's CLAUDE.md convention, the actual version section (`## [1.5.0] - <date>`) is auto-promoted by `scripts/prepare-release.mjs` when the release workflow runs — we do NOT pre-create the section. - The `(#NN)` PR numbers after each bullet auto-link in the published release notes. package.json: - `"version": "1.4.0"` → `"1.5.0"`. This triggers the GitHub Actions "Sync package-lock.json" step (`npm install --package-lock-only --ignore-scripts`) which rewrites the lock on the version-bump commit. README.md: - Banner line 5: `## 🎉 1.2 Released — Access forms are first-class` → `## 🎉 1.5 Released — VBA conditional-compilation is now correct + TempVars/RecordSource/RowSource indexed`. The "Already installed? Run codegraph-vba upgrade to update in place" line below is unchanged. CLAUDE.md: - Append `report-layout` to the NodeKind list and `'opens-report'` to the EdgeKind list — both added in #48 (PR #69) but missing from the docs. Also append the related chain (form-layout, form-instance-control, event-handler, opens-form, raises-event, subscribes-event, type-member) so CLAUDE.md matches the source of truth at `src/types.ts` (the prior abbreviated list was already drifting from the schema). No other CLAUDE.md changes. Not in this commit (out of scope per user brief): - Test-infra PRs #70 / #72 are correctly absent from CHANGELOG (per user's "not in changelog body but mention in known-issues if applicable" — no known-issues section exists in this repo's CHANGELOG; vitest.testTimeout bump is test infrastructure, not a user-visible feature). - Scriv fragment — this is the codegraph-vba repo, not the dysflow-managed one; Scriv doesn't apply. Next step: trigger the Release workflow via gh workflow run (or via the Actions → Release → Run workflow UI on the `main` branch). The workflow will: 1. Sync `package-lock.json` to match `package.json` (auto-commit). 2. Run `scripts/prepare-release.mjs` to promote [Unreleased] → `## [1.5.0] - 2026-07-04` + append the link reference. 3. Build per-platform bundles, generate SHA256SUMS, create the GitHub Release, publish the npm packages (requires NPM_TOKEN repo secret).
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 #48.
What
Hueco 6 (
DoCmd.OpenForm "X"->opens-formedge, hueco B4) was always labelled follow-up work for OpenReport and OpenQuery. This PR lands both. The OpenForm pipeline is preserved byte-identically (existing tests stay green without modification); OpenReport mirrors it through a small dispatch table; OpenQuery opts for anUnresolvedReferenceso the resolver binds to the REALquerynode emitted bySqlQueryExtractor.Why
In a real Dysflow-managed Access project, every
DoCmd.OpenReport "InformeMensual"andDoCmd.OpenQuery "ConsultaDepuracion"line produced zero graph edges. Cross-report and cross-query UI traffic was invisible to the resolver, even though.report.txtandqueries/*.sqlare first-class files in the index. This PR closes that gap forOpenReport(synthetic report-layout stub + heuristic edge) andOpenQuery(UnresolvedReference resolving to the existingquerynode).Diff
src/extraction/vba-extractor.ts__tests__/extraction-vba.test.tssrc/types.tsInside the 400-line source-budget; the PR is reviewable as ONE unit (one logical refactor + two new emissions sharing one pipeline).
Design
OpenForm + OpenReport: shared pipeline via dispatch table
New
DOCMD_OPEN_DISPATCHconstant (aReadonlyArray<...>of typed entries) carries everythingscanDoCmdOpenCalls+emitOpensStubEdgeneed:OpenForm: edgeKindopens-form, stubKindform-layout, synthetic prefixsynthetic:opensFormStub, extension.form.txt, qualifiedName prefixForm_, metadata keytargetFormName, tagvba-opens-form.OpenReport: edgeKindopens-report, stubKindreport-layout, synthetic prefixsynthetic:opensReportStub, extension.report.txt, qualifiedName prefixReport_, metadata keytargetReportName, tagvba-opens-report.OpenQueryis intentionally NOT in this dispatch — see below.The scan/emit pipeline iterates the dispatch; OpenForm's emit is byte-identical to pre-#48 (same regex, same arg resolution, same stub id formula, same metadata key — pin tests stay green). The cache key is
${cacheKey}:${loweredName}so OpenForm and OpenReport de-dup buckets stay disjoint within a file. The synthetic path's file extension mirrors the real file extension per Issue #48 spec.OpenQuery: UnresolvedReference pattern (NOT a stub + edge)
Following the
vba-me-controlandvba-forms-bangprecedent.scanDoCmdOpenQueryis a separate scanner (not in the dispatch table) that pushes ONEUnresolvedReferenceper match:referenceName= resolved query namereferenceKind='references'metadata.synthesizedBy='vba-opens-query'functionorquerynode —SqlQueryExtractoralready emits the realquerynode forqueries/<Name>.sql, and the resolver binds the reference to it via name match.Type system
NodeKindextended with'report-layout'.EdgeKindextended with'opens-report'. Distinct fromform-layout/opens-formso downstream tooling can filter form vs report without inspecting qualifiedName.Test coverage
9 new atoms in
__tests__/extraction-vba.test.ts(Issue #48 describe block):OpenReport (5 atoms):
"InformeMensual"→ emitsopens-reportedge +report-layoutstubREPORT_MENSUAL+ unknownREPORT_UNKNOWN(mirrors OpenForm Const-fallback)generateNodeId('synthetic:opensReportStub/<Name>.report.txt', 'report-layout', '<Name>', 0))OpenQuery (4 atoms):
6. Literal
"Consulta1"→ ONE UnresolvedReference, no synthetic nodes7. Const-resolved
CONSULTA_DEPURACION→ UnresolvedReference with resolved name8. Const-unknown
CONSULTA_UNKNOWN→ falls back to bare identifier9. W4 no-synthetic-function-node guard for OpenQuery too
Regression guards (existing tests, all green without modification):
extraction-vba-control-modeling.test.tshueco-6 testextraction-vba.test.ts"DoCmd.OpenForm does not emit a synthetic function node" (W4)extraction-vba.test.ts"resolves local string constants in DoCmd.OpenForm and falls back..." (Const-fallback)Validation
pnpm exec vitest run __tests__/extraction-vba.test.ts -t "Issue #48"→ 9/9 pass in 681 mspnpm run build→ exit 0, no TS errorsOut of scope (intentional)
OpenTableand otherDoCmd.Open*siblings. Same dispatch pattern is ready to absorb them later; not adding them now keeps the diff focused.Constdeclarations, consistent with the existing OpenForm scope.Not done
n/a — issue complete in this PR.