Skip to content

feat(extract): VB6 class modules as Class + Methods (stacked on grammar PR) - #2087

Draft
Tokarzewski wants to merge 3 commits into
DeusData:mainfrom
Tokarzewski:feat/721-vb6-class-modules
Draft

feat(extract): VB6 class modules as Class + Methods (stacked on grammar PR)#2087
Tokarzewski wants to merge 3 commits into
DeusData:mainfrom
Tokarzewski:feat/721-vb6-class-modules

Conversation

@Tokarzewski

Copy link
Copy Markdown
Contributor

What does this PR do?

Draft, stacked on #2086. Only the top two commits are this change; until that PR merges the diff here also shows its grammar commit. Rebased onto main, this is an 8-file change (+379/−3). Opened as a draft so the design can be reviewed alongside the grammar — happy to fold it into the grammar PR, keep it separate, or drop it, whichever you prefer.

Refs #721.

Why: a VB6 .cls/.frm/.ctl/.dsr/.pag file is exactly one COM class, but the grammar has no class node. With the grammar PR alone every procedure in a class module is a free Function, and a VB6 project has no Class nodes, no DEFINES_METHOD edges and no Implements/form inheritance in the graph.

What it does:

  • Synthesises one Class per class-module file, named from Attribute VB_Name = "…" (the IDE-written identity; falls back to the file stem), with base classes from the designer block type (Begin VB.Form Form1VB.Form; GUID-typed blocks skipped) and every Implements.
  • Walks the file inside that class scope, so procedures become Methods (class.Name, parent_class set → DEFINES_METHOD), and nested Type/Enum declarations nest under the class. .bas standard modules are untouched (still Functions).
  • Call attribution: the scope-frame walker already saves enclosing_class_qn on every push and restores it on pop, so the seeding is one line in cbm_extract_unified() — the baseline becomes the file-class QN for VB6 class modules and stays NULL for everything else. compute_func_qn then produces exactly the Method QN the def side mints; both sides derive the class QN from a single helper (cbm_vb6_file_class_qn), so they cannot drift. No new state field.

Tests: regression — Widget.cls yields 1 Class (base IShape), 2 Methods, 0 Functions, every Method's parent_class is the class QN, and the call to Helper is attributed to class.Go; a .bas module yields no Class/Method. Contract — a .cls CALL_CASES row plus contract_vb6_class_module_calls_source_from_method, which indexes the fixture through the production pipeline and requires the only caller of Helper to be the Method Go (never the Module). Amends one assertion in the grammar PR's vb6_implements_is_an_import (Go is now a Method).

Open design points for you:

  1. Property Get/Let/Set triplets share one QN (class.Name) and collapse to one Method node — consistent on the def and call side, and the same collapse the grammar PR has for .bas. A .get/.let/.set suffix would need matching changes on both sides.
  2. Declare … Lib and Event declarations inside a .cls become Methods of that class (they are module members).
  3. Module-level Dim/Const in a .cls remain module-level Variables rather than class Fields — a possible follow-up.
  4. Two commits (feature + tests) — squash if you prefer one.

Checklist

  • Every commit is signed off (git commit -s)
  • Tests pass locally — stacked on the grammar branch, same Windows zig/clang toolchain without ASan/UBSan: language extraction grammar_regression grammar_labels lang_contract → 628 passed, 0 failed; grammar_label_goldens unchanged; [CALLS-BREADTH] 56 langs: 0 FAILURES; language-count contract unchanged (this PR adds no grammar). CI's sanitised runs are authoritative.
  • Lint: clang-format-20 --dry-run --Werror clean on helpers.c/.h, extract_defs.c, extract_unified.c/.h; cppcheck not run locally — relying on CI
  • New behaviour is covered by tests (above); all fixtures synthetic

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown

Thanks for opening this — it has been seen, and it is queued.

This note is automated, but it is not a brush-off: it exists so you know where your PR stands instead of having to guess from silence.

Current review status: working through a backlog. 0.9.1-rc.1 is out, so the release freeze that held reviews is over — but it left a large queue of open pull requests behind it, and we are reading through them oldest-first. The background is in discussion #1144.

What that means for this PR, concretely:

  • It will not be closed for inactivity. No stale bot touches pull requests here.
  • It may still sit a while before a human reads it. That is on us, not on you.
  • Older PRs are read first, so a recent one is not being skipped — it is behind a queue.

Things that will genuinely speed it up whenever review does happen:

  • Keep it rebased on main — the tree is moving quickly right now, and a conflicting branch cannot be reviewed as the diff you intended.
  • Get CI green, or say which failures you believe are pre-existing.
  • Keep the change to one claim. Bundled features and refactors get split before they get merged, which costs you a round trip.
  • Every commit needs a sign-off (git commit -s) — CI enforces DCO.

If this fixes a bug, a reproduction we can run is worth more than a description of the symptom.

Thanks for contributing, and sorry in advance for the wait.

Tokarzewski and others added 3 commits September 7, 2026 12:54
Vendors tree-sitter-vba @ 63b2f8d0d65c (v0.13.0, MIT, ABI 15, lexer-only,
no external scanner) as CBM_LANG_VB6 and wires it end to end:

- discover: .bas/.ctl/.dsr/.pag map directly; .cls (Apex/ObjectScript)
  and .frm (FORM) keep their table owners and the DeusData#2074 content sniffers
  now route VB6 modules to CBM_LANG_VB6 instead of CBM_LANG_COUNT.
- lang_specs: Sub/Function/Property Get|Let|Set/Declare/Event as
  callables; Type -> Class with typed Fields (As clause unwrapped);
  Enum -> Enum with members; module-level Dim/Const -> one Variable per
  declarator; call_statement/call_expression/raise_event_statement
  callees; Implements as import; VB6 keyword set; source_file module
  parents.
- release surfaces: MANIFEST (163 grammars, 75x ABI-15, vba row + note
  on the locally generated parser.c), THIRD_PARTY, vendored checksums,
  new-languages.json, and the 162 -> 163 language-count claims on all
  seven contract surfaces.
- tests: extension/sniff mapping, grammar regression + label golden,
  calls contract, extraction block, and the four repro registries
  (capability ledger, call-node manifest, invariant breadth, argument
  matrix B).

Class-module synthesis (.cls/.frm as a Class with Methods) is a
separate follow-up change.

Refs DeusData#721.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Tokarzewski <bartlomiej.tokarzewski@gmail.com>
A VB6 .cls/.frm/.ctl/.dsr/.pag file is one COM class, but tree-sitter-vba
has no class node, so the file's procedures surfaced as free Functions and
the calls inside them were attributed to the Module.

- helpers.c: cbm_vb6_is_class_module_path / cbm_vb6_file_class_name /
  cbm_vb6_file_class_qn resolve the class from `Attribute VB_Name` (file
  stem fallback). One helper feeds both extractors so the QNs cannot drift;
  it returns NULL for .bas standard modules and for every other language.
- extract_defs.c: cbm_extract_definitions synthesises the Class def (bases
  from the designer `Begin VB.Form` type and every `Implements`) and runs
  the def walk with enclosing_class_qn set to it; extract_func_def promotes
  the file's procedures to Methods (QN class.name, parent_class set so
  DEFINES_METHOD links).
- extract_unified.c: the scope-frame walker's baseline enclosing_class_qn
  starts as the file-class QN instead of NULL. push_scope saves and
  pop_expired_scopes restores that baseline exactly like any other frame
  value, so compute_func_qn yields class.name for every procedure and each
  in-body call sources to the Method, never the Module. No extra WalkState
  field is needed.

Stacks on the VB6 grammar commit that introduces CBM_LANG_VB6.

Refs DeusData#721.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Tokarzewski <bartlomiej.tokarzewski@gmail.com>
- grammar_regression: Widget.cls yields exactly one Class (base IShape),
  two Methods whose parent_class is the class QN, and no Function; the call
  inside Go carries enclosing_func_qn class.Go. ModA.bas yields no Class
  and no Method.
- lang_contract: a Widget.cls CALL_CASES row, plus a dedicated contract
  that indexes the file through the production pipeline and checks the
  CALLS edge into Helper is sourced by the Method Go and by nothing else
  (a Module-sourced edge would surface the module's name as the caller).
- extraction: vb6_implements_is_an_import (from the grammar commit) now
  expects Go in Widget.cls to be a Method, not a Function -- the class
  synthesis this series adds makes every .cls procedure a Method.

Refs DeusData#721.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Tokarzewski <bartlomiej.tokarzewski@gmail.com>
@Tokarzewski
Tokarzewski force-pushed the feat/721-vb6-class-modules branch from 45b28f0 to 9a7445e Compare September 7, 2026 10:55
@Tokarzewski

Copy link
Copy Markdown
Contributor Author

test / test-windows (windows-latest, CLANG64, x86_64, 2/2) failed on the last run: tests/test_parallel_harness_contract.shFAIL: Windows timeout race refused without a surviving descendant to refuse over -- the fixture no longer exercises the race (synthetic stubborn_tree subprocess scenario).

This looks pre-existing and unrelated to this PR: the fixture tests the CI harness's own process-timeout/kill logic under synthetic subprocess trees, not extraction/parsing, and the failure text describes the test's own race precondition not being hit on this run rather than an assertion mismatch. I don't have rights to re-run the job on this repo. Happy to investigate further if it recurs or if you'd like a second opinion.

@DeusData DeusData added enhancement New feature or request language-request Request for new language support parsing/quality Graph extraction bugs, false positives, missing edges priority/normal Standard review queue; useful PR with ordinary maintainer urgency. labels Sep 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request language-request Request for new language support parsing/quality Graph extraction bugs, false positives, missing edges priority/normal Standard review queue; useful PR with ordinary maintainer urgency.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants