diff --git a/tsc/internal/binder/binder.go b/tsc/internal/binder/binder.go index 395ddba3486cf..38769558c01d4 100644 --- a/tsc/internal/binder/binder.go +++ b/tsc/internal/binder/binder.go @@ -1907,9 +1907,19 @@ func (b *Binder) bindForStatement(node *ast.Node) { func (b *Binder) bindForInOrForOfStatement(node *ast.Node) { stmt := node.AsForInOrOfStatement() + b.bind(stmt.Expression) + if b.currentFlow == b.unreachableFlow { + // Like the for-loop initializer, the for-in/for-of expression is bound before the loop's + // flow graph is constructed. If it makes flow unreachable (e.g. a throwing IIFE), addAntecedent + // will filter out the unreachable entry to preLoopLabel, leaving only the back-edge from the + // loop body. This creates a cycle with no exit that crashes isReachableFlowNodeWorker. + // Bail out early and just bind the remaining children with unreachable flow. + b.bind(stmt.Initializer) + b.bind(stmt.Statement) + return + } preLoopLabel := b.setContinueTarget(node, b.createLoopLabel()) postLoopLabel := b.createBranchLabel() - b.bind(stmt.Expression) b.addAntecedent(preLoopLabel, b.currentFlow) b.currentFlow = preLoopLabel if node.Kind == ast.KindForOfStatement { diff --git a/tsc/testdata/baselines/reference/compiler/unreachableFlowAfterThrowingForInOfHead1(allowunreachablecode=false).errors.txt b/tsc/testdata/baselines/reference/compiler/unreachableFlowAfterThrowingForInOfHead1(allowunreachablecode=false).errors.txt new file mode 100644 index 0000000000000..9dd2478d7ce93 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/unreachableFlowAfterThrowingForInOfHead1(allowunreachablecode=false).errors.txt @@ -0,0 +1,17 @@ +unreachableFlowAfterThrowingForInOfHead1.ts(2,21): error TS2488: Type 'never' must have a '[Symbol.iterator]()' method that returns an iterator. +unreachableFlowAfterThrowingForInOfHead1.ts(3,9): error TS7027: Unreachable code detected. + + +==== unreachableFlowAfterThrowingForInOfHead1.ts (2 errors) ==== + try { + for (const x of (function () { throw "1"; })()) { + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2488: Type 'never' must have a '[Symbol.iterator]()' method that returns an iterator. +!!! related TS2773 unreachableFlowAfterThrowingForInOfHead1.ts:2:21: Did you forget to use 'await'? + console.log("1"); + ~~~~~~~~~~~~~~~~~ +!!! error TS7027: Unreachable code detected. + } + } + catch (e) { } + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/unreachableFlowAfterThrowingForInOfHead1(allowunreachablecode=false).symbols b/tsc/testdata/baselines/reference/compiler/unreachableFlowAfterThrowingForInOfHead1(allowunreachablecode=false).symbols new file mode 100644 index 0000000000000..297d3ca381d28 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/unreachableFlowAfterThrowingForInOfHead1(allowunreachablecode=false).symbols @@ -0,0 +1,16 @@ +//// [tests/cases/compiler/unreachableFlowAfterThrowingForInOfHead1.ts] //// + +=== unreachableFlowAfterThrowingForInOfHead1.ts === +try { + for (const x of (function () { throw "1"; })()) { +>x : Symbol(x, Decl(unreachableFlowAfterThrowingForInOfHead1.ts, 1, 14)) + + console.log("1"); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) + } +} +catch (e) { } +>e : Symbol(e, Decl(unreachableFlowAfterThrowingForInOfHead1.ts, 5, 7)) + diff --git a/tsc/testdata/baselines/reference/compiler/unreachableFlowAfterThrowingForInOfHead1(allowunreachablecode=false).types b/tsc/testdata/baselines/reference/compiler/unreachableFlowAfterThrowingForInOfHead1(allowunreachablecode=false).types new file mode 100644 index 0000000000000..9b24d94029a20 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/unreachableFlowAfterThrowingForInOfHead1(allowunreachablecode=false).types @@ -0,0 +1,22 @@ +//// [tests/cases/compiler/unreachableFlowAfterThrowingForInOfHead1.ts] //// + +=== unreachableFlowAfterThrowingForInOfHead1.ts === +try { + for (const x of (function () { throw "1"; })()) { +>x : any +>(function () { throw "1"; })() : never +>(function () { throw "1"; }) : () => never +>function () { throw "1"; } : () => never +>"1" : "1" + + console.log("1"); +>console.log("1") : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>"1" : "1" + } +} +catch (e) { } +>e : unknown + diff --git a/tsc/testdata/baselines/reference/compiler/unreachableFlowAfterThrowingForInOfHead1(allowunreachablecode=true).errors.txt b/tsc/testdata/baselines/reference/compiler/unreachableFlowAfterThrowingForInOfHead1(allowunreachablecode=true).errors.txt new file mode 100644 index 0000000000000..26dad38264a62 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/unreachableFlowAfterThrowingForInOfHead1(allowunreachablecode=true).errors.txt @@ -0,0 +1,14 @@ +unreachableFlowAfterThrowingForInOfHead1.ts(2,21): error TS2488: Type 'never' must have a '[Symbol.iterator]()' method that returns an iterator. + + +==== unreachableFlowAfterThrowingForInOfHead1.ts (1 errors) ==== + try { + for (const x of (function () { throw "1"; })()) { + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2488: Type 'never' must have a '[Symbol.iterator]()' method that returns an iterator. +!!! related TS2773 unreachableFlowAfterThrowingForInOfHead1.ts:2:21: Did you forget to use 'await'? + console.log("1"); + } + } + catch (e) { } + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/unreachableFlowAfterThrowingForInOfHead1(allowunreachablecode=true).symbols b/tsc/testdata/baselines/reference/compiler/unreachableFlowAfterThrowingForInOfHead1(allowunreachablecode=true).symbols new file mode 100644 index 0000000000000..297d3ca381d28 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/unreachableFlowAfterThrowingForInOfHead1(allowunreachablecode=true).symbols @@ -0,0 +1,16 @@ +//// [tests/cases/compiler/unreachableFlowAfterThrowingForInOfHead1.ts] //// + +=== unreachableFlowAfterThrowingForInOfHead1.ts === +try { + for (const x of (function () { throw "1"; })()) { +>x : Symbol(x, Decl(unreachableFlowAfterThrowingForInOfHead1.ts, 1, 14)) + + console.log("1"); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) + } +} +catch (e) { } +>e : Symbol(e, Decl(unreachableFlowAfterThrowingForInOfHead1.ts, 5, 7)) + diff --git a/tsc/testdata/baselines/reference/compiler/unreachableFlowAfterThrowingForInOfHead1(allowunreachablecode=true).types b/tsc/testdata/baselines/reference/compiler/unreachableFlowAfterThrowingForInOfHead1(allowunreachablecode=true).types new file mode 100644 index 0000000000000..9b24d94029a20 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/unreachableFlowAfterThrowingForInOfHead1(allowunreachablecode=true).types @@ -0,0 +1,22 @@ +//// [tests/cases/compiler/unreachableFlowAfterThrowingForInOfHead1.ts] //// + +=== unreachableFlowAfterThrowingForInOfHead1.ts === +try { + for (const x of (function () { throw "1"; })()) { +>x : any +>(function () { throw "1"; })() : never +>(function () { throw "1"; }) : () => never +>function () { throw "1"; } : () => never +>"1" : "1" + + console.log("1"); +>console.log("1") : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>"1" : "1" + } +} +catch (e) { } +>e : unknown + diff --git a/tsc/testdata/baselines/reference/compiler/unreachableFlowAfterThrowingForInOfHead2(allowunreachablecode=false).errors.txt b/tsc/testdata/baselines/reference/compiler/unreachableFlowAfterThrowingForInOfHead2(allowunreachablecode=false).errors.txt new file mode 100644 index 0000000000000..ca0ccb936b369 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/unreachableFlowAfterThrowingForInOfHead2(allowunreachablecode=false).errors.txt @@ -0,0 +1,16 @@ +unreachableFlowAfterThrowingForInOfHead2.ts(2,21): error TS2407: The right-hand side of a 'for...in' statement must be of type 'any', an object type or a type parameter, but here has type 'never'. +unreachableFlowAfterThrowingForInOfHead2.ts(3,9): error TS7027: Unreachable code detected. + + +==== unreachableFlowAfterThrowingForInOfHead2.ts (2 errors) ==== + try { + for (const x in (function () { throw "1"; })()) { + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2407: The right-hand side of a 'for...in' statement must be of type 'any', an object type or a type parameter, but here has type 'never'. + console.log("1"); + ~~~~~~~~~~~~~~~~~ +!!! error TS7027: Unreachable code detected. + } + } + catch (e) { } + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/unreachableFlowAfterThrowingForInOfHead2(allowunreachablecode=false).symbols b/tsc/testdata/baselines/reference/compiler/unreachableFlowAfterThrowingForInOfHead2(allowunreachablecode=false).symbols new file mode 100644 index 0000000000000..de6228c61eec2 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/unreachableFlowAfterThrowingForInOfHead2(allowunreachablecode=false).symbols @@ -0,0 +1,16 @@ +//// [tests/cases/compiler/unreachableFlowAfterThrowingForInOfHead2.ts] //// + +=== unreachableFlowAfterThrowingForInOfHead2.ts === +try { + for (const x in (function () { throw "1"; })()) { +>x : Symbol(x, Decl(unreachableFlowAfterThrowingForInOfHead2.ts, 1, 14)) + + console.log("1"); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) + } +} +catch (e) { } +>e : Symbol(e, Decl(unreachableFlowAfterThrowingForInOfHead2.ts, 5, 7)) + diff --git a/tsc/testdata/baselines/reference/compiler/unreachableFlowAfterThrowingForInOfHead2(allowunreachablecode=false).types b/tsc/testdata/baselines/reference/compiler/unreachableFlowAfterThrowingForInOfHead2(allowunreachablecode=false).types new file mode 100644 index 0000000000000..ced9a9726783e --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/unreachableFlowAfterThrowingForInOfHead2(allowunreachablecode=false).types @@ -0,0 +1,22 @@ +//// [tests/cases/compiler/unreachableFlowAfterThrowingForInOfHead2.ts] //// + +=== unreachableFlowAfterThrowingForInOfHead2.ts === +try { + for (const x in (function () { throw "1"; })()) { +>x : string +>(function () { throw "1"; })() : never +>(function () { throw "1"; }) : () => never +>function () { throw "1"; } : () => never +>"1" : "1" + + console.log("1"); +>console.log("1") : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>"1" : "1" + } +} +catch (e) { } +>e : unknown + diff --git a/tsc/testdata/baselines/reference/compiler/unreachableFlowAfterThrowingForInOfHead2(allowunreachablecode=true).errors.txt b/tsc/testdata/baselines/reference/compiler/unreachableFlowAfterThrowingForInOfHead2(allowunreachablecode=true).errors.txt new file mode 100644 index 0000000000000..af07ea4fd0178 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/unreachableFlowAfterThrowingForInOfHead2(allowunreachablecode=true).errors.txt @@ -0,0 +1,13 @@ +unreachableFlowAfterThrowingForInOfHead2.ts(2,21): error TS2407: The right-hand side of a 'for...in' statement must be of type 'any', an object type or a type parameter, but here has type 'never'. + + +==== unreachableFlowAfterThrowingForInOfHead2.ts (1 errors) ==== + try { + for (const x in (function () { throw "1"; })()) { + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2407: The right-hand side of a 'for...in' statement must be of type 'any', an object type or a type parameter, but here has type 'never'. + console.log("1"); + } + } + catch (e) { } + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/unreachableFlowAfterThrowingForInOfHead2(allowunreachablecode=true).symbols b/tsc/testdata/baselines/reference/compiler/unreachableFlowAfterThrowingForInOfHead2(allowunreachablecode=true).symbols new file mode 100644 index 0000000000000..de6228c61eec2 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/unreachableFlowAfterThrowingForInOfHead2(allowunreachablecode=true).symbols @@ -0,0 +1,16 @@ +//// [tests/cases/compiler/unreachableFlowAfterThrowingForInOfHead2.ts] //// + +=== unreachableFlowAfterThrowingForInOfHead2.ts === +try { + for (const x in (function () { throw "1"; })()) { +>x : Symbol(x, Decl(unreachableFlowAfterThrowingForInOfHead2.ts, 1, 14)) + + console.log("1"); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) + } +} +catch (e) { } +>e : Symbol(e, Decl(unreachableFlowAfterThrowingForInOfHead2.ts, 5, 7)) + diff --git a/tsc/testdata/baselines/reference/compiler/unreachableFlowAfterThrowingForInOfHead2(allowunreachablecode=true).types b/tsc/testdata/baselines/reference/compiler/unreachableFlowAfterThrowingForInOfHead2(allowunreachablecode=true).types new file mode 100644 index 0000000000000..ced9a9726783e --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/unreachableFlowAfterThrowingForInOfHead2(allowunreachablecode=true).types @@ -0,0 +1,22 @@ +//// [tests/cases/compiler/unreachableFlowAfterThrowingForInOfHead2.ts] //// + +=== unreachableFlowAfterThrowingForInOfHead2.ts === +try { + for (const x in (function () { throw "1"; })()) { +>x : string +>(function () { throw "1"; })() : never +>(function () { throw "1"; }) : () => never +>function () { throw "1"; } : () => never +>"1" : "1" + + console.log("1"); +>console.log("1") : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>"1" : "1" + } +} +catch (e) { } +>e : unknown + diff --git a/tsc/testdata/tests/cases/compiler/unreachableFlowAfterThrowingForInOfHead1.ts b/tsc/testdata/tests/cases/compiler/unreachableFlowAfterThrowingForInOfHead1.ts new file mode 100644 index 0000000000000..d34f95d20cef9 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/unreachableFlowAfterThrowingForInOfHead1.ts @@ -0,0 +1,11 @@ +// @strict: true +// @noEmit: true +// @allowUnreachableCode: true, false +// @target: es2015 + +try { + for (const x of (function () { throw "1"; })()) { + console.log("1"); + } +} +catch (e) { } diff --git a/tsc/testdata/tests/cases/compiler/unreachableFlowAfterThrowingForInOfHead2.ts b/tsc/testdata/tests/cases/compiler/unreachableFlowAfterThrowingForInOfHead2.ts new file mode 100644 index 0000000000000..b7057c5505da8 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/unreachableFlowAfterThrowingForInOfHead2.ts @@ -0,0 +1,11 @@ +// @strict: true +// @noEmit: true +// @allowUnreachableCode: true, false +// @target: es2015 + +try { + for (const x in (function () { throw "1"; })()) { + console.log("1"); + } +} +catch (e) { }