Skip to content

fix(vba): bind form code-behind on VB_Name when the filename disagrees - #272

Merged
ardelperal merged 1 commit into
mainfrom
fix/issue-249-vbname-binding
Sep 2, 2026
Merged

fix(vba): bind form code-behind on VB_Name when the filename disagrees#272
ardelperal merged 1 commit into
mainfrom
fix/issue-249-vbname-binding

Conversation

@ardelperal

Copy link
Copy Markdown
Owner

Closes #249

What was wrong

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 — 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 is null for .bas and holds the resolved VB_Name for .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.txt actually 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.cls fixture is deliberately hostile: its methods Documento_Print and Cabecera_Format end 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 its VB_Name, so it still binds to nothing. A second fixture covers the near miss — a VB_Name starting with the letters Form but with no separating underscore.

Tests

__tests__/extraction-vba-vbname-binding.test.ts — real files, real extractors, no mocking. Form_Expediente.cls and sample2.cls are 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, bindingSource present 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 --noEmit clean
  • pnpm run build clean
  • pnpm exec vitest run vba extraction-sql-query sql-query-discovery48 files passed (baseline on main is 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_Name in 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 .cls files across both projects found exactly one file where the filename and VB_Name disagree on the code-behind prefix: frmSplash.cls, carrying Attribute VB_Name = "Form_frmSplash", with its frmSplash.form.txt sitting right beside it. Before this change that form was wired to nothing.

npm run probe:vba -- .../00_EXPEDIENTES/src .../00_GESTION_RIESGOS/src

metric main this PR delta
declared procedures 3,840 3,840 0
stub function nodes 1,597 1,597 0
form-instance-control 2,485 2,485 0
form-layout 321 323 +2
event-handler edges 808 810 +2
references (unresolved) 2,057 2,059 +2
property-get / property-set 215 / 76 217 / 78 +2 / +2

Every one of those is frmSplash.cls, and each is accounted for:

  • Form_Open and Form_Timer are 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 REPLACE collapses them in the DB) → +2 form-layout.
  • Me.lblProgresoBarra and Me.lblEstado+2 references. Both controls exist in frmSplash.form.txt, so they resolve to real nodes.
  • Me.TimerInterval = 0 and Me.Caption = "..."+2 property-set; Me.Name twice → +2 property-get. These are the built-in form members, which the resolver declines as before.

form-instance-control is 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.

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
ardelperal force-pushed the fix/issue-249-vbname-binding branch from e0a7689 to a352c0c Compare September 1, 2026 18:41
@ardelperal
ardelperal merged commit 383e2a5 into main Sep 2, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(vba): form code-behind binding keys on the filename, not VB_Name

1 participant