Skip to content

Commit 0e48151

Browse files
icecrasher321claude
andcommitted
fix(ci): close the pending-drop resolver under variable assignment
A table parked in an ordinary binding (`const t = userStats`) escaped the audit, since only alias() results were re-bound. Binding collection now resolves any declarator whose right side names a table — the table itself, a namespace member, an earlier binding, or an alias() call — and repeats to a fixpoint so declaration order does not matter. Verified by probe: argless reads through a direct assignment, a namespace-member assignment, and a chained binding are all reported now, where the previous version reported none; the repo stays clean, including a file where a local array shadows a pending table's name. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent f1c2e79 commit 0e48151

1 file changed

Lines changed: 21 additions & 12 deletions

File tree

scripts/check-pending-drop-tables.ts

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -318,22 +318,31 @@ function collectTableBindings(
318318
}
319319
visitImports(program)
320320

321-
const visitAliases = (node: SyntaxNode) => {
322-
if (node.type === 'VariableDeclarator' && isSyntaxNode(node.init)) {
323-
const init = unwrap(node.init)
324-
if (init?.type === 'CallExpression' && identifierName(init.callee) === 'alias') {
325-
const canonical = resolveTable(
326-
Array.isArray(init.arguments) ? init.arguments[0] : undefined,
327-
pendingTables,
328-
bindings
329-
)
321+
/**
322+
* Any `const x = <table expression>` re-binds the table, whether the right
323+
* side is the table itself, a namespace member, an earlier binding, or an
324+
* `alias()` call. Repeats to a fixpoint so a chain of bindings resolves
325+
* regardless of declaration order; each pass adds at least one binding, and
326+
* bindings are bounded by the declarator count, so it terminates.
327+
*/
328+
let changed = true
329+
while (changed) {
330+
changed = false
331+
const visitAssignments = (node: SyntaxNode) => {
332+
if (node.type === 'VariableDeclarator' && isSyntaxNode(node.init)) {
330333
const bound = propertyName(node.id)
331-
if (canonical && bound) bindings.locals.set(bound, canonical)
334+
if (bound && !bindings.locals.has(bound) && !pendingTables.has(bound)) {
335+
const canonical = resolveTable(node.init, pendingTables, bindings)
336+
if (canonical) {
337+
bindings.locals.set(bound, canonical)
338+
changed = true
339+
}
340+
}
332341
}
342+
for (const child of getChildNodes(node)) visitAssignments(child)
333343
}
334-
for (const child of getChildNodes(node)) visitAliases(child)
344+
visitAssignments(program)
335345
}
336-
visitAliases(program)
337346

338347
return bindings
339348
}

0 commit comments

Comments
 (0)