From 54a1fb9f099b7be67b9f13715ddfccd2029d21ac Mon Sep 17 00:00:00 2001 From: colbymchenry Date: Tue, 21 Jul 2026 21:15:41 +0200 Subject: [PATCH 1/4] test(vba): pin internal classifier surface --- __tests__/extraction-vba-dead-exports.test.ts | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 __tests__/extraction-vba-dead-exports.test.ts diff --git a/__tests__/extraction-vba-dead-exports.test.ts b/__tests__/extraction-vba-dead-exports.test.ts new file mode 100644 index 0000000..19a073c --- /dev/null +++ b/__tests__/extraction-vba-dead-exports.test.ts @@ -0,0 +1,38 @@ +import { readFileSync } from 'node:fs'; +import { describe, expect, it } from 'vitest'; + +const readSource = (relativePath: string): string => + readFileSync(new URL(`../src/${relativePath}`, import.meta.url), 'utf8'); + +describe('VBA extraction public surface', () => { + it('keeps the split-once classifiers free of legacy source wrappers', () => { + const wrappers = [ + ['extraction/vba/procedures.ts', 'sweepProcedures'], + ['extraction/vba/dims.ts', 'sweepDimsAndWithEvents'], + ['extraction/vba/declarations.ts', 'sweepEventsTypesAndDeclares'], + ['extraction/vba/implements.ts', 'sweepImplements'], + ['extraction/vba/enums-consts.ts', 'sweepEnumsAndConsts'], + ['extraction/vba/call-sweep.ts', 'sweepCallsAndSql'], + ] as const; + + for (const [path, wrapper] of wrappers) { + const source = readSource(path); + expect(source, path).not.toContain(`function ${wrapper}`); + expect(source, path).not.toContain("src.split('\\n')"); + } + }); + + it('removes or narrows internal-only exports while preserving RUNTIME_OBJECTS', () => { + expect(readSource('extraction/vba-source.ts')).not.toContain('function stripUtf8Bom'); + expect(readSource('extraction/vba/text-utils.ts')).not.toContain( + 'export function splitOutsideVbaStrings', + ); + expect(readSource('extraction/vba-test-manifest-extractor.ts')).not.toContain( + 'export function isVbaTestManifestShape', + ); + + const runtimeObjects = readSource('resolution/vba-runtime-objects.ts'); + expect(runtimeObjects).not.toContain('export const VBA_STDLIB_FUNCTIONS'); + expect(runtimeObjects).toContain('export const RUNTIME_OBJECTS'); + }); +}); From 721d01a5eb0a79781aacb1b57d0785b236876786 Mon Sep 17 00:00:00 2001 From: colbymchenry Date: Tue, 21 Jul 2026 21:17:35 +0200 Subject: [PATCH 2/4] chore(vba): delete dead sweep wrappers --- src/extraction/vba-source.ts | 13 ------------- src/extraction/vba-test-manifest-extractor.ts | 2 +- src/extraction/vba/call-sweep.ts | 16 +--------------- src/extraction/vba/declarations.ts | 15 +-------------- src/extraction/vba/dims.ts | 15 +-------------- src/extraction/vba/enums-consts.ts | 15 +-------------- src/extraction/vba/implements.ts | 15 +-------------- src/extraction/vba/procedures.ts | 18 +----------------- src/extraction/vba/text-utils.ts | 2 +- src/resolution/vba-runtime-objects.ts | 2 +- 10 files changed, 9 insertions(+), 104 deletions(-) diff --git a/src/extraction/vba-source.ts b/src/extraction/vba-source.ts index 8b3b6c9..2dba000 100644 --- a/src/extraction/vba-source.ts +++ b/src/extraction/vba-source.ts @@ -122,16 +122,3 @@ export function readVbaSource( return { text, bomStripped }; } } - -/** - * String-level BOM strip — survives when the upstream `fsp.readFile(path, 'utf-8')` - * already decoded the bytes (it's the only way the `\uFEFF` char survives in - * a string). Used as a defensive last resort when a read site has NOT gone - * through `readVbaSource` and the source text starts with the BOM marker. - * - * Most call sites should prefer `readVbaSource` for fresh reads; this is - * a string post-process for callers that already have a `string` in hand. - */ -export function stripUtf8Bom(text: string): string { - return text.charCodeAt(0) === 0xfeff ? text.slice(1) : text; -} diff --git a/src/extraction/vba-test-manifest-extractor.ts b/src/extraction/vba-test-manifest-extractor.ts index 715b480..1617890 100644 --- a/src/extraction/vba-test-manifest-extractor.ts +++ b/src/extraction/vba-test-manifest-extractor.ts @@ -37,7 +37,7 @@ interface ManifestTestEntry { * array with at least one item carrying a string `procedure`? Pure; the file's * basename is gated separately by `isVbaTestManifestFile` in `grammars.ts`. */ -export function isVbaTestManifestShape(parsed: unknown): boolean { +function isVbaTestManifestShape(parsed: unknown): boolean { if (!parsed || typeof parsed !== 'object') return false; const tests = (parsed as { tests?: unknown }).tests; if (!Array.isArray(tests)) return false; diff --git a/src/extraction/vba/call-sweep.ts b/src/extraction/vba/call-sweep.ts index a33ff29..f2b70d2 100644 --- a/src/extraction/vba/call-sweep.ts +++ b/src/extraction/vba/call-sweep.ts @@ -7,7 +7,7 @@ */ import { PROC_RE, PROCEDURE_END_RE, PRIMITIVE_TYPES, isVbaKeyword } from './constants'; import { maskStringContent } from './text-utils'; -import { VbaExtractorContext, ProcInfo, VbaClassifier } from './context'; +import { ProcInfo, VbaClassifier } from './context'; import { defineRule, matchRuleForScan, VbaExtractionRule } from './rules'; import { scanRaiseEvents, @@ -450,17 +450,3 @@ export function createCallsAndSqlClassifier( return cls; } - -/** - * Backward-compat wrapper (see procedures.ts). Returns void — the calls - * sweep never contributed to `hasAnySymbols` directly (every other - * concern's `count` is the signal the orchestrator reads). - */ -export function sweepCallsAndSql(ctx: VbaExtractorContext, src: string): void { - const lines = src.split('\n'); - const cls = createCallsAndSqlClassifier(lines); - for (let i = 0; i < lines.length; i++) { - cls.classifyLine(lines[i] ?? '', i, ctx); - } - cls.finalize?.(ctx); -} diff --git a/src/extraction/vba/declarations.ts b/src/extraction/vba/declarations.ts index 02b5a2f..d9b5ae8 100644 --- a/src/extraction/vba/declarations.ts +++ b/src/extraction/vba/declarations.ts @@ -8,7 +8,7 @@ import { Node } from '../../types'; import { generateNodeId } from '../tree-sitter-helpers'; import { foldVisibility } from './text-utils'; -import { VbaExtractorContext, VbaClassifier } from './context'; +import { VbaClassifier } from './context'; import { defineRule, matchRule, VbaExtractionRule } from './rules'; /** `[visibility] Event (...)` custom event declaration. */ @@ -257,16 +257,3 @@ export function createEventsTypesDeclaresClassifier(): VbaClassifier { }; return cls; } - -/** - * Backward-compat wrapper (see procedures.ts). Returns the classifier's - * `count` so the orchestrator can decide `hasAnySymbols`. - */ -export function sweepEventsTypesAndDeclares(ctx: VbaExtractorContext, src: string): number { - const cls = createEventsTypesDeclaresClassifier(); - const lines = src.split('\n'); - for (let i = 0; i < lines.length; i++) { - cls.classifyLine(lines[i] ?? '', i, ctx); - } - return cls.count; -} diff --git a/src/extraction/vba/dims.ts b/src/extraction/vba/dims.ts index 02d45b2..9dc753d 100644 --- a/src/extraction/vba/dims.ts +++ b/src/extraction/vba/dims.ts @@ -7,7 +7,7 @@ import { Edge } from '../../types'; import { generateNodeId } from '../tree-sitter-helpers'; import { PRIMITIVE_TYPES, PROC_RE, PROCEDURE_END_RE } from './constants'; -import { VbaExtractorContext, VbaClassifier } from './context'; +import { VbaClassifier } from './context'; import { defineRule, matchRule, VbaExtractionRule } from './rules'; /** @@ -346,16 +346,3 @@ export function createDimsClassifier(): VbaClassifier { }, }; } - -/** - * Backward-compat wrapper (see procedures.ts). Returns the classifier's - * `count` so the orchestrator can decide `hasAnySymbols`. - */ -export function sweepDimsAndWithEvents(ctx: VbaExtractorContext, src: string): number { - const cls = createDimsClassifier(); - const lines = src.split('\n'); - for (let i = 0; i < lines.length; i++) { - cls.classifyLine(lines[i] ?? '', i, ctx); - } - return cls.count; -} diff --git a/src/extraction/vba/enums-consts.ts b/src/extraction/vba/enums-consts.ts index eace8a6..a5a60d2 100644 --- a/src/extraction/vba/enums-consts.ts +++ b/src/extraction/vba/enums-consts.ts @@ -7,7 +7,7 @@ import { generateNodeId } from '../tree-sitter-helpers'; import { PROC_RE, PROCEDURE_END_RE } from './constants'; import { foldVisibility, parseConstDeclarations } from './text-utils'; -import { VbaExtractorContext, VbaClassifier } from './context'; +import { VbaClassifier } from './context'; import { defineRule, matchRule, VbaExtractionRule } from './rules'; /** `[visibility] Enum ` — opens an enum block. */ @@ -264,16 +264,3 @@ export function createEnumsConstsClassifier(): VbaClassifier { }; return cls; } - -/** - * Backward-compat wrapper (see procedures.ts). Returns the classifier's - * `count` so the orchestrator can decide `hasAnySymbols`. - */ -export function sweepEnumsAndConsts(ctx: VbaExtractorContext, src: string): number { - const cls = createEnumsConstsClassifier(); - const lines = src.split('\n'); - for (let i = 0; i < lines.length; i++) { - cls.classifyLine(lines[i] ?? '', i, ctx); - } - return cls.count; -} diff --git a/src/extraction/vba/implements.ts b/src/extraction/vba/implements.ts index 3ff84d4..071c89a 100644 --- a/src/extraction/vba/implements.ts +++ b/src/extraction/vba/implements.ts @@ -5,7 +5,7 @@ */ import { Edge } from '../../types'; import { generateNodeId } from '../tree-sitter-helpers'; -import { VbaExtractorContext, VbaClassifier } from './context'; +import { VbaClassifier } from './context'; import { defineRule, matchRule, VbaExtractionRule } from './rules'; /** Implements regex. */ @@ -88,16 +88,3 @@ export function createImplementsClassifier(): VbaClassifier { }, }; } - -/** - * Backward-compat wrapper (see procedures.ts). Returns the classifier's - * `count` so the orchestrator can decide `hasAnySymbols`. - */ -export function sweepImplements(ctx: VbaExtractorContext, src: string): number { - const cls = createImplementsClassifier(); - const lines = src.split('\n'); - for (let i = 0; i < lines.length; i++) { - cls.classifyLine(lines[i] ?? '', i, ctx); - } - return cls.count; -} diff --git a/src/extraction/vba/procedures.ts b/src/extraction/vba/procedures.ts index ee5be6a..6a56cb6 100644 --- a/src/extraction/vba/procedures.ts +++ b/src/extraction/vba/procedures.ts @@ -9,7 +9,7 @@ import { Node, Edge } from '../../types'; import { generateNodeId } from '../tree-sitter-helpers'; import { PROC_RE, PRIMITIVE_TYPES } from './constants'; import { parseEventHandlerName } from './text-utils'; -import { VbaExtractorContext, ProcInfo, VbaClassifier } from './context'; +import { ProcInfo, VbaClassifier } from './context'; import { defineRule, matchRule, VbaExtractionRule } from './rules'; /** @@ -309,19 +309,3 @@ export function createProceduresClassifier(): VbaClassifier { }, }; } - -/** - * Backward-compat wrapper: pre-#83 callers (e.g. legacy test fixtures) - * used `sweepProcedures(ctx, src)` and got back the ProcInfo[]. - * Now it returns `ctx.procedures` (the same flat list the factory - * appends to). The implementation still calls the classifier once per - * pre-split line, so the count is identical to the new walker path. - */ -export function sweepProcedures(ctx: VbaExtractorContext, src: string): ProcInfo[] { - const cls = createProceduresClassifier(); - const lines = src.split('\n'); - for (let i = 0; i < lines.length; i++) { - cls.classifyLine(lines[i] ?? '', i, ctx); - } - return ctx.procedures; -} diff --git a/src/extraction/vba/text-utils.ts b/src/extraction/vba/text-utils.ts index 35c90d0..9fb7688 100644 --- a/src/extraction/vba/text-utils.ts +++ b/src/extraction/vba/text-utils.ts @@ -84,7 +84,7 @@ export function parseConstDeclarations( return declarations; } -export function splitOutsideVbaStrings(value: string, separator: string): string[] { +function splitOutsideVbaStrings(value: string, separator: string): string[] { const parts: string[] = []; let current = ''; let inString = false; diff --git a/src/resolution/vba-runtime-objects.ts b/src/resolution/vba-runtime-objects.ts index 5732b77..81fd3aa 100644 --- a/src/resolution/vba-runtime-objects.ts +++ b/src/resolution/vba-runtime-objects.ts @@ -63,7 +63,7 @@ export function isRuntimeObject(receiver: string | null | undefined): boolean { } /** Canonical VBA and Access built-in functions that never resolve to project code. */ -export const VBA_STDLIB_FUNCTIONS: ReadonlySet = new Set([ +const VBA_STDLIB_FUNCTIONS: ReadonlySet = new Set([ 'cstr', 'cint', 'clng', 'cdbl', 'csng', 'cbyte', 'cbool', 'cdate', 'cverr', 'isnull', 'isempty', 'isnumeric', 'isdate', 'isarray', 'isobject', 'ismissing', 'typename', 'vartype', 'len', 'lenb', 'instr', 'instrb', 'instrrev', 'lcase', From 21158b9343329616d45579507e242f62c34f59fd Mon Sep 17 00:00:00 2001 From: colbymchenry Date: Tue, 21 Jul 2026 21:19:16 +0200 Subject: [PATCH 3/4] docs(changelog): note classifier surface cleanup --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc7d990..161dae7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Changed + +- VBA extraction now exposes only the classifier factories used by the shared source walker, removing obsolete compatibility entry points and other unused public helpers. (#217) + ### Fixes - VBA references mentioned only inside messages, logs, and other string literals no longer create false form, query, or temporary-variable relationships. (#209) From 7db59093fc2cb9f22d98bb195f09fc47adea743d Mon Sep 17 00:00:00 2001 From: colbymchenry Date: Tue, 21 Jul 2026 21:26:48 +0200 Subject: [PATCH 4/4] test(vba): use classifier factory in issue 207 coverage --- __tests__/extraction-vba-issue-207.test.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/__tests__/extraction-vba-issue-207.test.ts b/__tests__/extraction-vba-issue-207.test.ts index 464bcf5..6fe7cd4 100644 --- a/__tests__/extraction-vba-issue-207.test.ts +++ b/__tests__/extraction-vba-issue-207.test.ts @@ -24,7 +24,7 @@ import { describe, it, expect } from 'vitest'; import { VbaExtractor } from '../src/extraction/vba-extractor'; import { VbaExtractorContext } from '../src/extraction/vba/context'; -import { sweepDimsAndWithEvents } from '../src/extraction/vba/dims'; +import { createDimsClassifier } from '../src/extraction/vba/dims'; function extract(filePath: string, source: string) { return new VbaExtractor(filePath, source).extract(); @@ -56,7 +56,11 @@ function dimMapFor(source: string): Map< { outer: string; qualified: boolean } > { const ctx = new VbaExtractorContext('src/modules/m.bas'); - sweepDimsAndWithEvents(ctx, source); + const classifier = createDimsClassifier(); + const lines = source.split('\n'); + for (let i = 0; i < lines.length; i++) { + classifier.classifyLine(lines[i] ?? '', i, ctx); + } return ctx.localVarTypeMap; } @@ -232,4 +236,4 @@ describe('Issue #207 — DIM_DECL_PREFIX_RE lookahead must exclude Declare/Event expect(referencedTypeNames(r)).toHaveLength(0); }); }); -}); \ No newline at end of file +});