From 681501e1231bde9f8b2d0c7ada63f581b5b6de68 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 9 Jan 2026 18:41:04 +0000 Subject: [PATCH 1/3] Initial plan From 07198acaca81235bbf673856aab9986cd6fce422 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 9 Jan 2026 18:55:36 +0000 Subject: [PATCH 2/3] Fix spurious "used before being assigned" errors in for of/in loops Port of microsoft/TypeScript#61376. Adds check for isForInOrOfStatement to prevent false positive TS2454 errors for variables declared in for-in/for-of loops when referenced in closures within the loop body. Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com> --- internal/checker/checker.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/checker/checker.go b/internal/checker/checker.go index 0ef37ebeca..b62c619c33 100644 --- a/internal/checker/checker.go +++ b/internal/checker/checker.go @@ -10806,9 +10806,9 @@ func (c *Checker) checkIdentifier(node *ast.Node, checkMode CheckMode) *Type { // We only look for uninitialized variables in strict null checking mode, and only when we can analyze // the entire control flow graph from the variable's declaration (i.e. when the flow container and // declaration container are the same). - isNeverInitialized := immediateDeclaration != nil && ast.IsVariableDeclaration(immediateDeclaration) && immediateDeclaration.Initializer() == nil && - immediateDeclaration.AsVariableDeclaration().ExclamationToken == nil && c.isMutableLocalVariableDeclaration(immediateDeclaration) && - !c.isSymbolAssignedDefinitely(symbol) + isNeverInitialized := immediateDeclaration != nil && ast.IsVariableDeclaration(immediateDeclaration) && !ast.IsForInOrOfStatement(immediateDeclaration.Parent.Parent) && + immediateDeclaration.Initializer() == nil && immediateDeclaration.AsVariableDeclaration().ExclamationToken == nil && + c.isMutableLocalVariableDeclaration(immediateDeclaration) && !c.isSymbolAssignedDefinitely(symbol) assumeInitialized := isParameter || isAlias || (isOuterVariable && !isNeverInitialized) || From 9208fd42325bb1cb2c04ae58da493b64fe773c39 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 15 Jan 2026 21:46:08 +0000 Subject: [PATCH 3/3] Add local test for unused locals in for-in/for-of loops Added test from upstream PR microsoft/TypeScript#61376 to verify the fix for spurious "used before being assigned" errors (TS2454) in for-in/of loops. Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com> --- .../unusedLocalsInForInOrOf1.errors.txt | 84 ++++++++++ .../compiler/unusedLocalsInForInOrOf1.symbols | 117 ++++++++++++++ .../compiler/unusedLocalsInForInOrOf1.types | 153 ++++++++++++++++++ .../compiler/unusedLocalsInForInOrOf1.ts | 59 +++++++ 4 files changed, 413 insertions(+) create mode 100644 testdata/baselines/reference/compiler/unusedLocalsInForInOrOf1.errors.txt create mode 100644 testdata/baselines/reference/compiler/unusedLocalsInForInOrOf1.symbols create mode 100644 testdata/baselines/reference/compiler/unusedLocalsInForInOrOf1.types create mode 100644 testdata/tests/cases/compiler/unusedLocalsInForInOrOf1.ts diff --git a/testdata/baselines/reference/compiler/unusedLocalsInForInOrOf1.errors.txt b/testdata/baselines/reference/compiler/unusedLocalsInForInOrOf1.errors.txt new file mode 100644 index 0000000000..2b0a014ec9 --- /dev/null +++ b/testdata/baselines/reference/compiler/unusedLocalsInForInOrOf1.errors.txt @@ -0,0 +1,84 @@ +unusedLocalsInForInOrOf1.ts(2,12): error TS6133: 'f' is declared but its value is never read. +unusedLocalsInForInOrOf1.ts(8,7): error TS6133: 'f' is declared but its value is never read. +unusedLocalsInForInOrOf1.ts(14,12): error TS6133: 'g' is declared but its value is never read. +unusedLocalsInForInOrOf1.ts(20,12): error TS6133: 'f2' is declared but its value is never read. +unusedLocalsInForInOrOf1.ts(26,7): error TS6133: 'f2' is declared but its value is never read. +unusedLocalsInForInOrOf1.ts(32,12): error TS6133: 'g2' is declared but its value is never read. +unusedLocalsInForInOrOf1.ts(38,12): error TS6133: 'f3' is declared but its value is never read. +unusedLocalsInForInOrOf1.ts(44,7): error TS6133: 'f3' is declared but its value is never read. +unusedLocalsInForInOrOf1.ts(50,12): error TS6133: 'g3' is declared but its value is never read. + + +==== unusedLocalsInForInOrOf1.ts (9 errors) ==== + for (let x of [1, 2]) { + function f() { + ~ +!!! error TS6133: 'f' is declared but its value is never read. + x; + } + } + + for (let x of [1, 2]) { + let f = () => { + ~ +!!! error TS6133: 'f' is declared but its value is never read. + x; + }; + } + + for (const x of [1, 2]) { + function g() { + ~ +!!! error TS6133: 'g' is declared but its value is never read. + x; + } + } + + for (let x in { a: 1, b: 2 }) { + function f2() { + ~~ +!!! error TS6133: 'f2' is declared but its value is never read. + x; + } + } + + for (let x in { a: 1, b: 2 }) { + let f2 = () => { + ~~ +!!! error TS6133: 'f2' is declared but its value is never read. + x; + }; + } + + for (const x in { a: 1, b: 2 }) { + function g2() { + ~~ +!!! error TS6133: 'g2' is declared but its value is never read. + x; + } + } + + for (let { x } of [{ x: 1 }, { x: 2 }]) { + function f3() { + ~~ +!!! error TS6133: 'f3' is declared but its value is never read. + x; + } + } + + for (let { x } of [{ x: 1 }, { x: 2 }]) { + let f3 = () => { + ~~ +!!! error TS6133: 'f3' is declared but its value is never read. + x; + }; + } + + for (const { x } of [{ x: 1 }, { x: 2 }]) { + function g3() { + ~~ +!!! error TS6133: 'g3' is declared but its value is never read. + x; + } + } + \ No newline at end of file diff --git a/testdata/baselines/reference/compiler/unusedLocalsInForInOrOf1.symbols b/testdata/baselines/reference/compiler/unusedLocalsInForInOrOf1.symbols new file mode 100644 index 0000000000..c4c2381baf --- /dev/null +++ b/testdata/baselines/reference/compiler/unusedLocalsInForInOrOf1.symbols @@ -0,0 +1,117 @@ +//// [tests/cases/compiler/unusedLocalsInForInOrOf1.ts] //// + +=== unusedLocalsInForInOrOf1.ts === +for (let x of [1, 2]) { +>x : Symbol(x, Decl(unusedLocalsInForInOrOf1.ts, 0, 8)) + + function f() { +>f : Symbol(f, Decl(unusedLocalsInForInOrOf1.ts, 0, 23)) + + x; +>x : Symbol(x, Decl(unusedLocalsInForInOrOf1.ts, 0, 8)) + } +} + +for (let x of [1, 2]) { +>x : Symbol(x, Decl(unusedLocalsInForInOrOf1.ts, 6, 8)) + + let f = () => { +>f : Symbol(f, Decl(unusedLocalsInForInOrOf1.ts, 7, 5)) + + x; +>x : Symbol(x, Decl(unusedLocalsInForInOrOf1.ts, 6, 8)) + + }; +} + +for (const x of [1, 2]) { +>x : Symbol(x, Decl(unusedLocalsInForInOrOf1.ts, 12, 10)) + + function g() { +>g : Symbol(g, Decl(unusedLocalsInForInOrOf1.ts, 12, 25)) + + x; +>x : Symbol(x, Decl(unusedLocalsInForInOrOf1.ts, 12, 10)) + } +} + +for (let x in { a: 1, b: 2 }) { +>x : Symbol(x, Decl(unusedLocalsInForInOrOf1.ts, 18, 8)) +>a : Symbol(a, Decl(unusedLocalsInForInOrOf1.ts, 18, 15)) +>b : Symbol(b, Decl(unusedLocalsInForInOrOf1.ts, 18, 21)) + + function f2() { +>f2 : Symbol(f2, Decl(unusedLocalsInForInOrOf1.ts, 18, 31)) + + x; +>x : Symbol(x, Decl(unusedLocalsInForInOrOf1.ts, 18, 8)) + } +} + +for (let x in { a: 1, b: 2 }) { +>x : Symbol(x, Decl(unusedLocalsInForInOrOf1.ts, 24, 8)) +>a : Symbol(a, Decl(unusedLocalsInForInOrOf1.ts, 24, 15)) +>b : Symbol(b, Decl(unusedLocalsInForInOrOf1.ts, 24, 21)) + + let f2 = () => { +>f2 : Symbol(f2, Decl(unusedLocalsInForInOrOf1.ts, 25, 5)) + + x; +>x : Symbol(x, Decl(unusedLocalsInForInOrOf1.ts, 24, 8)) + + }; +} + +for (const x in { a: 1, b: 2 }) { +>x : Symbol(x, Decl(unusedLocalsInForInOrOf1.ts, 30, 10)) +>a : Symbol(a, Decl(unusedLocalsInForInOrOf1.ts, 30, 17)) +>b : Symbol(b, Decl(unusedLocalsInForInOrOf1.ts, 30, 23)) + + function g2() { +>g2 : Symbol(g2, Decl(unusedLocalsInForInOrOf1.ts, 30, 33)) + + x; +>x : Symbol(x, Decl(unusedLocalsInForInOrOf1.ts, 30, 10)) + } +} + +for (let { x } of [{ x: 1 }, { x: 2 }]) { +>x : Symbol(x, Decl(unusedLocalsInForInOrOf1.ts, 36, 10)) +>x : Symbol(x, Decl(unusedLocalsInForInOrOf1.ts, 36, 20)) +>x : Symbol(x, Decl(unusedLocalsInForInOrOf1.ts, 36, 30)) + + function f3() { +>f3 : Symbol(f3, Decl(unusedLocalsInForInOrOf1.ts, 36, 41)) + + x; +>x : Symbol(x, Decl(unusedLocalsInForInOrOf1.ts, 36, 10)) + } +} + +for (let { x } of [{ x: 1 }, { x: 2 }]) { +>x : Symbol(x, Decl(unusedLocalsInForInOrOf1.ts, 42, 10)) +>x : Symbol(x, Decl(unusedLocalsInForInOrOf1.ts, 42, 20)) +>x : Symbol(x, Decl(unusedLocalsInForInOrOf1.ts, 42, 30)) + + let f3 = () => { +>f3 : Symbol(f3, Decl(unusedLocalsInForInOrOf1.ts, 43, 5)) + + x; +>x : Symbol(x, Decl(unusedLocalsInForInOrOf1.ts, 42, 10)) + + }; +} + +for (const { x } of [{ x: 1 }, { x: 2 }]) { +>x : Symbol(x, Decl(unusedLocalsInForInOrOf1.ts, 48, 12)) +>x : Symbol(x, Decl(unusedLocalsInForInOrOf1.ts, 48, 22)) +>x : Symbol(x, Decl(unusedLocalsInForInOrOf1.ts, 48, 32)) + + function g3() { +>g3 : Symbol(g3, Decl(unusedLocalsInForInOrOf1.ts, 48, 43)) + + x; +>x : Symbol(x, Decl(unusedLocalsInForInOrOf1.ts, 48, 12)) + } +} + diff --git a/testdata/baselines/reference/compiler/unusedLocalsInForInOrOf1.types b/testdata/baselines/reference/compiler/unusedLocalsInForInOrOf1.types new file mode 100644 index 0000000000..b891594c4b --- /dev/null +++ b/testdata/baselines/reference/compiler/unusedLocalsInForInOrOf1.types @@ -0,0 +1,153 @@ +//// [tests/cases/compiler/unusedLocalsInForInOrOf1.ts] //// + +=== unusedLocalsInForInOrOf1.ts === +for (let x of [1, 2]) { +>x : number +>[1, 2] : number[] +>1 : 1 +>2 : 2 + + function f() { +>f : () => void + + x; +>x : number + } +} + +for (let x of [1, 2]) { +>x : number +>[1, 2] : number[] +>1 : 1 +>2 : 2 + + let f = () => { +>f : () => void +>() => { x; } : () => void + + x; +>x : number + + }; +} + +for (const x of [1, 2]) { +>x : number +>[1, 2] : number[] +>1 : 1 +>2 : 2 + + function g() { +>g : () => void + + x; +>x : number + } +} + +for (let x in { a: 1, b: 2 }) { +>x : string +>{ a: 1, b: 2 } : { a: number; b: number; } +>a : number +>1 : 1 +>b : number +>2 : 2 + + function f2() { +>f2 : () => void + + x; +>x : string + } +} + +for (let x in { a: 1, b: 2 }) { +>x : string +>{ a: 1, b: 2 } : { a: number; b: number; } +>a : number +>1 : 1 +>b : number +>2 : 2 + + let f2 = () => { +>f2 : () => void +>() => { x; } : () => void + + x; +>x : string + + }; +} + +for (const x in { a: 1, b: 2 }) { +>x : string +>{ a: 1, b: 2 } : { a: number; b: number; } +>a : number +>1 : 1 +>b : number +>2 : 2 + + function g2() { +>g2 : () => void + + x; +>x : string + } +} + +for (let { x } of [{ x: 1 }, { x: 2 }]) { +>x : number +>[{ x: 1 }, { x: 2 }] : { x: number; }[] +>{ x: 1 } : { x: number; } +>x : number +>1 : 1 +>{ x: 2 } : { x: number; } +>x : number +>2 : 2 + + function f3() { +>f3 : () => void + + x; +>x : number + } +} + +for (let { x } of [{ x: 1 }, { x: 2 }]) { +>x : number +>[{ x: 1 }, { x: 2 }] : { x: number; }[] +>{ x: 1 } : { x: number; } +>x : number +>1 : 1 +>{ x: 2 } : { x: number; } +>x : number +>2 : 2 + + let f3 = () => { +>f3 : () => void +>() => { x; } : () => void + + x; +>x : number + + }; +} + +for (const { x } of [{ x: 1 }, { x: 2 }]) { +>x : number +>[{ x: 1 }, { x: 2 }] : { x: number; }[] +>{ x: 1 } : { x: number; } +>x : number +>1 : 1 +>{ x: 2 } : { x: number; } +>x : number +>2 : 2 + + function g3() { +>g3 : () => void + + x; +>x : number + } +} + diff --git a/testdata/tests/cases/compiler/unusedLocalsInForInOrOf1.ts b/testdata/tests/cases/compiler/unusedLocalsInForInOrOf1.ts new file mode 100644 index 0000000000..edbdad5c59 --- /dev/null +++ b/testdata/tests/cases/compiler/unusedLocalsInForInOrOf1.ts @@ -0,0 +1,59 @@ +// @strict: true +// @target: esnext +// @noEmit: true +// @noUnusedLocals: true +// @noUnusedParameters: true + +for (let x of [1, 2]) { + function f() { + x; + } +} + +for (let x of [1, 2]) { + let f = () => { + x; + }; +} + +for (const x of [1, 2]) { + function g() { + x; + } +} + +for (let x in { a: 1, b: 2 }) { + function f2() { + x; + } +} + +for (let x in { a: 1, b: 2 }) { + let f2 = () => { + x; + }; +} + +for (const x in { a: 1, b: 2 }) { + function g2() { + x; + } +} + +for (let { x } of [{ x: 1 }, { x: 2 }]) { + function f3() { + x; + } +} + +for (let { x } of [{ x: 1 }, { x: 2 }]) { + let f3 = () => { + x; + }; +} + +for (const { x } of [{ x: 1 }, { x: 2 }]) { + function g3() { + x; + } +}