diff --git a/gitnexus/src/core/ingestion/cfg/visitors/dart-harvest.ts b/gitnexus/src/core/ingestion/cfg/visitors/dart-harvest.ts index 0abe244b0..631a7197f 100644 --- a/gitnexus/src/core/ingestion/cfg/visitors/dart-harvest.ts +++ b/gitnexus/src/core/ingestion/cfg/visitors/dart-harvest.ts @@ -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': { diff --git a/gitnexus/test/unit/cfg/dart-visitor.test.ts b/gitnexus/test/unit/cfg/dart-visitor.test.ts index 413b0650a..abadd2019 100644 --- a/gitnexus/test/unit/cfg/dart-visitor.test.ts +++ b/gitnexus/test/unit/cfg/dart-visitor.test.ts @@ -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', () => {