fix(vba): bind form code-behind on VB_Name when the filename disagrees - #272
Merged
Conversation
Event-handler synthesis and the `Me.<Control>` sweep both gated on the file basename starting with `Form_` / `Report_`. When the filename and the module's `Attribute VB_Name` disagreed, the file still parsed and still emitted its procedures, but the form came out with no event wiring and no control references — and nothing errored or warned. The basename stays the fast path; only on a miss does the decision to bind fall back to the resolved `VB_Name` (`ctx.classNamePrefix`, which is null for `.bas` and holds the VB_Name for `.cls`). The sibling layout path and the layout node's name are still derived from the FILE path — that is where the `.form.txt` / `.report.txt` actually lives on disk. The prefix test itself is unchanged, so the guard that keeps a plain service class such as `InformeRiesgoPDFServicio.cls` from synthesizing control stubs still holds: it matches on neither its filename nor its VB_Name. Edges and references produced through the fallback carry `metadata.bindingSource: 'vb-name'` so the mismatch is diagnosable rather than invisible; the fast path's metadata is byte-for-byte unchanged. Closes #249
ardelperal
force-pushed
the
fix/issue-249-vbname-binding
branch
from
September 1, 2026 18:41
e0a7689 to
a352c0c
Compare
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 #249
What was wrong
Event-handler synthesis and the
Me.<Control>sweep both gated on the file basename starting withForm_/Report_. When the filename and the module'sAttribute VB_Namedisagreed, the file still parsed and still emitted its procedures — it just came out with no event wiring and no control references. Nothing errored, nothing warned.The fix
The basename check stays the fast path. Only on a miss does the decision to bind fall back to the resolved
VB_Name(ctx.classNamePrefix, which isnullfor.basand holds the resolvedVB_Namefor.cls, so the fallback can only ever fire for a class module).The sibling path — and the layout node's name — are still derived from the file path either way. That is where the
.form.txt/.report.txtactually sits on disk, whatever the module calls itself.Edges and references produced through the fallback carry
metadata.bindingSource: 'vb-name', so a mismatch is diagnosable instead of invisible. The field is set only on the mismatch, so the fast path's metadata is byte-for-byte what it was.The prefix test itself did not widen — the same
Form_/Report_convention, applied to one more string. Both new binding sites share one helper (codeBehindExtFromVbName).The guard that must not move
A plain service class must still produce zero control stubs — that guard is what stopped ~550 spurious nodes in real projects, and widening it was the failure mode this change had to avoid.
The
InformeRiesgoPDFServicio.clsfixture is deliberately hostile: its methodsDocumento_PrintandCabecera_Formatend in real Access event names, so the prefix guard is the only thing standing between it and a pile of invented controls. It matches on neither its filename nor itsVB_Name, so it still binds to nothing. A second fixture covers the near miss — aVB_Namestarting with the lettersFormbut with no separating underscore.Tests
__tests__/extraction-vba-vbname-binding.test.ts— real files, real extractors, no mocking.Form_Expediente.clsandsample2.clsare byte-identical fixtures; the only difference between them is the name on disk, so every difference the tests find between the two extractions is the bug. The suite asserts the premise (same bytes), the same event-handler edges and control references from both, the sibling path still coming from the file,bindingSourcepresent on the mismatch and absent on the fast path, the report half of the fallback picking.report.txt, the fast path still converging on the id the real form extractor produces, and the two guard fixtures producing nothing.Verified load-bearing: with the fallback disabled, 9 of the 22 tests fail.
npx tsc --noEmitcleanpnpm run buildcleanpnpm exec vitest run vba extraction-sql-query sql-query-discovery→ 48 files passed (baseline onmainis 47; the one added file is this PR's). 756 tests passed, 1 skipped.Corpus measurement
The expectation going in was that counts would not move: Dysflow exports keep filename and
VB_Namein sync, so this buys robustness rather than recall.They moved slightly, and the reason is a real instance of the bug in the corpus. A sweep of all 211
.clsfiles across both projects found exactly one file where the filename andVB_Namedisagree on the code-behind prefix:frmSplash.cls, carryingAttribute VB_Name = "Form_frmSplash", with itsfrmSplash.form.txtsitting right beside it. Before this change that form was wired to nothing.npm run probe:vba -- .../00_EXPEDIENTES/src .../00_GESTION_RIESGOS/srcform-instance-controlform-layoutevent-handleredgesreferences(unresolved)property-get/property-setEvery one of those is
frmSplash.cls, and each is accounted for:Form_OpenandForm_Timerare its two form-level lifecycle handlers → +2 event-handler edges, each pushing the layout stub (the form-level branch has emitted one stub per handler since feat(vba): connect form and report lifecycle events to their layout node #247;INSERT OR REPLACEcollapses them in the DB) → +2 form-layout.Me.lblProgresoBarraandMe.lblEstado→ +2 references. Both controls exist infrmSplash.form.txt, so they resolve to real nodes.Me.TimerInterval = 0andMe.Caption = "..."→ +2 property-set;Me.Nametwice → +2 property-get. These are the built-in form members, which the resolver declines as before.form-instance-controlis flat at 2,485 and the procedure/stub counts are untouched: nothing widened, and no service class gained a stub. The delta is one previously-invisible form becoming visible.Not included
No version bump, no publish, no tag.