From 1a6ce69c9b495856821dd4cd4ee7e5a1a670029b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= <mateuszburzynski@gmail.com>
Date: Sat, 11 Jan 2025 11:49:32 +0100
Subject: [PATCH] Fixed an issue with for statement's incrementor missing
 continue's control flow information

---
 src/compiler/binder.ts                        |   5 +-
 ...atementContinueIntoIncrementor1.errors.txt |  45 ++++++
 ...rStatementContinueIntoIncrementor1.symbols |  65 +++++++++
 ...ForStatementContinueIntoIncrementor1.types | 129 ++++++++++++++++++
 ...lowForStatementContinueIntoIncrementor1.ts |  34 +++++
 5 files changed, 277 insertions(+), 1 deletion(-)
 create mode 100644 tests/baselines/reference/controlFlowForStatementContinueIntoIncrementor1.errors.txt
 create mode 100644 tests/baselines/reference/controlFlowForStatementContinueIntoIncrementor1.symbols
 create mode 100644 tests/baselines/reference/controlFlowForStatementContinueIntoIncrementor1.types
 create mode 100644 tests/cases/compiler/controlFlowForStatementContinueIntoIncrementor1.ts

diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts
index b07a0a075080e..c83e11fc2a4a0 100644
--- a/src/compiler/binder.ts
+++ b/src/compiler/binder.ts
@@ -1536,13 +1536,16 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
     function bindForStatement(node: ForStatement): void {
         const preLoopLabel = setContinueTarget(node, createLoopLabel());
         const preBodyLabel = createBranchLabel();
+        const preIncrementorLabel = createBranchLabel();
         const postLoopLabel = createBranchLabel();
         bind(node.initializer);
         addAntecedent(preLoopLabel, currentFlow);
         currentFlow = preLoopLabel;
         bindCondition(node.condition, preBodyLabel, postLoopLabel);
         currentFlow = finishFlowLabel(preBodyLabel);
-        bindIterativeStatement(node.statement, postLoopLabel, preLoopLabel);
+        bindIterativeStatement(node.statement, postLoopLabel, preIncrementorLabel);
+        addAntecedent(preIncrementorLabel, currentFlow);
+        currentFlow = finishFlowLabel(preIncrementorLabel);
         bind(node.incrementor);
         addAntecedent(preLoopLabel, currentFlow);
         currentFlow = finishFlowLabel(postLoopLabel);
diff --git a/tests/baselines/reference/controlFlowForStatementContinueIntoIncrementor1.errors.txt b/tests/baselines/reference/controlFlowForStatementContinueIntoIncrementor1.errors.txt
new file mode 100644
index 0000000000000..01465c856fd43
--- /dev/null
+++ b/tests/baselines/reference/controlFlowForStatementContinueIntoIncrementor1.errors.txt
@@ -0,0 +1,45 @@
+controlFlowForStatementContinueIntoIncrementor1.ts(8,5): error TS2322: Type 'string | number' is not assignable to type 'number'.
+  Type 'string' is not assignable to type 'number'.
+controlFlowForStatementContinueIntoIncrementor1.ts(23,5): error TS2322: Type 'string | number' is not assignable to type 'number'.
+  Type 'string' is not assignable to type 'number'.
+
+
+==== controlFlowForStatementContinueIntoIncrementor1.ts (2 errors) ====
+    // https://github.com/microsoft/TypeScript/issues/60945
+    
+    {
+      let iNext;
+      for (
+        let i = 0;
+        i < 10;
+        i = iNext // error
+        ~
+!!! error TS2322: Type 'string | number' is not assignable to type 'number'.
+!!! error TS2322:   Type 'string' is not assignable to type 'number'.
+      ) {
+        if (i == 5) {
+          iNext = "bad";
+          continue;
+        }
+        iNext = i + 1;
+      }
+    }
+    
+    {
+      let iNext: string | number = "";
+      for (
+        let i = 0;
+        i < 10;
+        i = iNext // error
+        ~
+!!! error TS2322: Type 'string | number' is not assignable to type 'number'.
+!!! error TS2322:   Type 'string' is not assignable to type 'number'.
+      ) {
+        if (i == 5) {
+          iNext = "bad";
+          continue;
+        }
+        iNext = i + 1;
+      }
+    }
+    
\ No newline at end of file
diff --git a/tests/baselines/reference/controlFlowForStatementContinueIntoIncrementor1.symbols b/tests/baselines/reference/controlFlowForStatementContinueIntoIncrementor1.symbols
new file mode 100644
index 0000000000000..2ef70777fa4d7
--- /dev/null
+++ b/tests/baselines/reference/controlFlowForStatementContinueIntoIncrementor1.symbols
@@ -0,0 +1,65 @@
+//// [tests/cases/compiler/controlFlowForStatementContinueIntoIncrementor1.ts] ////
+
+=== controlFlowForStatementContinueIntoIncrementor1.ts ===
+// https://github.com/microsoft/TypeScript/issues/60945
+
+{
+  let iNext;
+>iNext : Symbol(iNext, Decl(controlFlowForStatementContinueIntoIncrementor1.ts, 3, 5))
+
+  for (
+    let i = 0;
+>i : Symbol(i, Decl(controlFlowForStatementContinueIntoIncrementor1.ts, 5, 7))
+
+    i < 10;
+>i : Symbol(i, Decl(controlFlowForStatementContinueIntoIncrementor1.ts, 5, 7))
+
+    i = iNext // error
+>i : Symbol(i, Decl(controlFlowForStatementContinueIntoIncrementor1.ts, 5, 7))
+>iNext : Symbol(iNext, Decl(controlFlowForStatementContinueIntoIncrementor1.ts, 3, 5))
+
+  ) {
+    if (i == 5) {
+>i : Symbol(i, Decl(controlFlowForStatementContinueIntoIncrementor1.ts, 5, 7))
+
+      iNext = "bad";
+>iNext : Symbol(iNext, Decl(controlFlowForStatementContinueIntoIncrementor1.ts, 3, 5))
+
+      continue;
+    }
+    iNext = i + 1;
+>iNext : Symbol(iNext, Decl(controlFlowForStatementContinueIntoIncrementor1.ts, 3, 5))
+>i : Symbol(i, Decl(controlFlowForStatementContinueIntoIncrementor1.ts, 5, 7))
+  }
+}
+
+{
+  let iNext: string | number = "";
+>iNext : Symbol(iNext, Decl(controlFlowForStatementContinueIntoIncrementor1.ts, 18, 5))
+
+  for (
+    let i = 0;
+>i : Symbol(i, Decl(controlFlowForStatementContinueIntoIncrementor1.ts, 20, 7))
+
+    i < 10;
+>i : Symbol(i, Decl(controlFlowForStatementContinueIntoIncrementor1.ts, 20, 7))
+
+    i = iNext // error
+>i : Symbol(i, Decl(controlFlowForStatementContinueIntoIncrementor1.ts, 20, 7))
+>iNext : Symbol(iNext, Decl(controlFlowForStatementContinueIntoIncrementor1.ts, 18, 5))
+
+  ) {
+    if (i == 5) {
+>i : Symbol(i, Decl(controlFlowForStatementContinueIntoIncrementor1.ts, 20, 7))
+
+      iNext = "bad";
+>iNext : Symbol(iNext, Decl(controlFlowForStatementContinueIntoIncrementor1.ts, 18, 5))
+
+      continue;
+    }
+    iNext = i + 1;
+>iNext : Symbol(iNext, Decl(controlFlowForStatementContinueIntoIncrementor1.ts, 18, 5))
+>i : Symbol(i, Decl(controlFlowForStatementContinueIntoIncrementor1.ts, 20, 7))
+  }
+}
+
diff --git a/tests/baselines/reference/controlFlowForStatementContinueIntoIncrementor1.types b/tests/baselines/reference/controlFlowForStatementContinueIntoIncrementor1.types
new file mode 100644
index 0000000000000..cb632f36c5192
--- /dev/null
+++ b/tests/baselines/reference/controlFlowForStatementContinueIntoIncrementor1.types
@@ -0,0 +1,129 @@
+//// [tests/cases/compiler/controlFlowForStatementContinueIntoIncrementor1.ts] ////
+
+=== controlFlowForStatementContinueIntoIncrementor1.ts ===
+// https://github.com/microsoft/TypeScript/issues/60945
+
+{
+  let iNext;
+>iNext : any
+>      : ^^^
+
+  for (
+    let i = 0;
+>i : number
+>  : ^^^^^^
+>0 : 0
+>  : ^
+
+    i < 10;
+>i < 10 : boolean
+>       : ^^^^^^^
+>i : number
+>  : ^^^^^^
+>10 : 10
+>   : ^^
+
+    i = iNext // error
+>i = iNext : string | number
+>          : ^^^^^^^^^^^^^^^
+>i : number
+>  : ^^^^^^
+>iNext : string | number
+>      : ^^^^^^^^^^^^^^^
+
+  ) {
+    if (i == 5) {
+>i == 5 : boolean
+>       : ^^^^^^^
+>i : number
+>  : ^^^^^^
+>5 : 5
+>  : ^
+
+      iNext = "bad";
+>iNext = "bad" : "bad"
+>              : ^^^^^
+>iNext : any
+>      : ^^^
+>"bad" : "bad"
+>      : ^^^^^
+
+      continue;
+    }
+    iNext = i + 1;
+>iNext = i + 1 : number
+>              : ^^^^^^
+>iNext : any
+>      : ^^^
+>i + 1 : number
+>      : ^^^^^^
+>i : number
+>  : ^^^^^^
+>1 : 1
+>  : ^
+  }
+}
+
+{
+  let iNext: string | number = "";
+>iNext : string | number
+>      : ^^^^^^^^^^^^^^^
+>"" : ""
+>   : ^^
+
+  for (
+    let i = 0;
+>i : number
+>  : ^^^^^^
+>0 : 0
+>  : ^
+
+    i < 10;
+>i < 10 : boolean
+>       : ^^^^^^^
+>i : number
+>  : ^^^^^^
+>10 : 10
+>   : ^^
+
+    i = iNext // error
+>i = iNext : string | number
+>          : ^^^^^^^^^^^^^^^
+>i : number
+>  : ^^^^^^
+>iNext : string | number
+>      : ^^^^^^^^^^^^^^^
+
+  ) {
+    if (i == 5) {
+>i == 5 : boolean
+>       : ^^^^^^^
+>i : number
+>  : ^^^^^^
+>5 : 5
+>  : ^
+
+      iNext = "bad";
+>iNext = "bad" : "bad"
+>              : ^^^^^
+>iNext : string | number
+>      : ^^^^^^^^^^^^^^^
+>"bad" : "bad"
+>      : ^^^^^
+
+      continue;
+    }
+    iNext = i + 1;
+>iNext = i + 1 : number
+>              : ^^^^^^
+>iNext : string | number
+>      : ^^^^^^^^^^^^^^^
+>i + 1 : number
+>      : ^^^^^^
+>i : number
+>  : ^^^^^^
+>1 : 1
+>  : ^
+  }
+}
+
diff --git a/tests/cases/compiler/controlFlowForStatementContinueIntoIncrementor1.ts b/tests/cases/compiler/controlFlowForStatementContinueIntoIncrementor1.ts
new file mode 100644
index 0000000000000..2b791d271d086
--- /dev/null
+++ b/tests/cases/compiler/controlFlowForStatementContinueIntoIncrementor1.ts
@@ -0,0 +1,34 @@
+// @strict: true
+// @noEmit: true
+
+// https://github.com/microsoft/TypeScript/issues/60945
+
+{
+  let iNext;
+  for (
+    let i = 0;
+    i < 10;
+    i = iNext // error
+  ) {
+    if (i == 5) {
+      iNext = "bad";
+      continue;
+    }
+    iNext = i + 1;
+  }
+}
+
+{
+  let iNext: string | number = "";
+  for (
+    let i = 0;
+    i < 10;
+    i = iNext // error
+  ) {
+    if (i == 5) {
+      iNext = "bad";
+      continue;
+    }
+    iNext = i + 1;
+  }
+}