mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-11 03:38:07 +00:00
fix(cfg): harvest all names of a Dart multi-variable declaration (#2195)
`var a = 1, b = 2;` is one initialized_variable_definition whose first binding is the name/value field pair and whose subsequent bindings are trailing initialized_identifier children. Both prescan (declareInitializedVar) and the walkValue case read only the name/value fields, so every name after the first was never declared or def'd — `b` resolved to a synthetic module binding and its REACHING_DEF/taint flow was lost. Iterate the trailing initialized_identifier nodes in both phases. (Dart-3 record/list pattern declarations `var (a,b)=pair` remain a separate follow-up.) dart suite 35 passed, tsc + grammar gate green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
31936a0929
commit
324bde0368
2 changed files with 28 additions and 1 deletions
|
|
@ -212,10 +212,18 @@ export class DartHarvester {
|
|||
}
|
||||
}
|
||||
|
||||
/** Declare the `name:identifier` of an `initialized_variable_definition`. */
|
||||
/** Declare every name of an `initialized_variable_definition` (`var a = 1, b = 2`). */
|
||||
private declareInitializedVar(node: SyntaxNode, kind: BindingEntry['kind']): void {
|
||||
const name = node.childForFieldName('name');
|
||||
if (name) this.declare(name, kind);
|
||||
// Trailing comma-separated bindings: each `initialized_identifier` (`b = 2`)
|
||||
// names another local that the `name` field alone misses.
|
||||
for (let i = 0; i < node.namedChildCount; i++) {
|
||||
const c = node.namedChild(i);
|
||||
if (c?.type !== 'initialized_identifier') continue;
|
||||
const id = c.namedChildren.find((g) => g.type === 'identifier');
|
||||
if (id) this.declare(id, kind);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -367,6 +375,16 @@ export class DartHarvester {
|
|||
if (value) this.walkValue(value, acc);
|
||||
const name = node.childForFieldName('name');
|
||||
if (name) this.def(name, acc);
|
||||
// Trailing comma-separated bindings (`var a = 1, b = 2;`): each
|
||||
// `initialized_identifier` is an `identifier` + its own value expr.
|
||||
for (let i = 0; i < node.namedChildCount; i++) {
|
||||
const c = node.namedChild(i);
|
||||
if (c?.type !== 'initialized_identifier') continue;
|
||||
const id = c.namedChildren.find((g) => g.type === 'identifier');
|
||||
const val = c.namedChildren.find((g) => g.type !== 'identifier');
|
||||
if (val) this.walkValue(val, acc);
|
||||
if (id) this.def(id, acc);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case 'assignment_expression': {
|
||||
|
|
|
|||
|
|
@ -387,6 +387,15 @@ describe('Dart CfgVisitor — def/use harvest', () => {
|
|||
// `obj` is used (it is the assignment target's root); no scalar `x` binding.
|
||||
expect(usesBinding(cfg, bindingIdx(cfg, 'obj'))).toBe(true);
|
||||
});
|
||||
|
||||
it('multi-variable declaration `var a = 1, b = 2;` defines BOTH names (#2195 P2)', () => {
|
||||
const cfg = dart.cfgOf(`void f() { var a = 1, b = 2; use(a); use(b); }`);
|
||||
// The bug dropped every name after the first — `b` became a synthetic
|
||||
// global. Both must be real locals defined by the declaration.
|
||||
expect(definesBinding(cfg, bindingIdx(cfg, 'a'))).toBe(true);
|
||||
expect(definesBinding(cfg, bindingIdx(cfg, 'b'))).toBe(true);
|
||||
expect(usesBinding(cfg, bindingIdx(cfg, 'b'))).toBe(true); // use(b)
|
||||
});
|
||||
});
|
||||
|
||||
describe('Dart CfgVisitor — functionStartColumn', () => {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue