diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index e6e3364840d..fe77e9b4df4 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -516,8 +516,9 @@ namespace ts { const saveReturnTarget = currentReturnTarget; const saveActiveLabels = activeLabels; const saveHasExplicitReturn = hasExplicitReturn; - const isIIFE = containerFlags & ContainerFlags.IsFunctionExpression && !hasModifier(node, ModifierFlags.Async) && !!getImmediatelyInvokedFunctionExpression(node); - // A non-async IIFE is considered part of the containing control flow. Return statements behave + const isIIFE = containerFlags & ContainerFlags.IsFunctionExpression && !hasModifier(node, ModifierFlags.Async) && + !(node).asteriskToken && !!getImmediatelyInvokedFunctionExpression(node); + // A non-async, non-generator IIFE is considered part of the containing control flow. Return statements behave // similarly to break statements that exit to a label just past the statement body. if (!isIIFE) { currentFlow = { flags: FlowFlags.Start }; diff --git a/tests/baselines/reference/controlFlowIIFE.errors.txt b/tests/baselines/reference/controlFlowIIFE.errors.txt new file mode 100644 index 00000000000..50053a90f2c --- /dev/null +++ b/tests/baselines/reference/controlFlowIIFE.errors.txt @@ -0,0 +1,82 @@ +tests/cases/conformance/controlFlow/controlFlowIIFE.ts(64,5): error TS2454: Variable 'v' is used before being assigned. +tests/cases/conformance/controlFlow/controlFlowIIFE.ts(72,5): error TS2454: Variable 'v' is used before being assigned. + + +==== tests/cases/conformance/controlFlow/controlFlowIIFE.ts (2 errors) ==== + declare function getStringOrNumber(): string | number; + + function f1() { + let x = getStringOrNumber(); + if (typeof x === "string") { + let n = function() { + return x.length; + }(); + } + } + + function f2() { + let x = getStringOrNumber(); + if (typeof x === "string") { + let n = (function() { + return x.length; + })(); + } + } + + function f3() { + let x = getStringOrNumber(); + let y: number; + if (typeof x === "string") { + let n = (z => x.length + y + z)(y = 1); + } + } + + // Repros from #8381 + + let maybeNumber: number | undefined; + (function () { + maybeNumber = 1; + })(); + maybeNumber++; + if (maybeNumber !== undefined) { + maybeNumber++; + } + + let test: string | undefined; + if (!test) { + throw new Error('Test is not defined'); + } + (() => { + test.slice(1); // No error + })(); + + // Repro from #23565 + + function f4() { + let v: number; + (function() { + v = 1; + })(); + v; + } + + function f5() { + let v: number; + (function*() { + yield 1; + v = 1; + })(); + v; // still undefined + ~ +!!! error TS2454: Variable 'v' is used before being assigned. + } + + function f6() { + let v: number; + (async function() { + v = await 1; + })(); + v; // still undefined + ~ +!!! error TS2454: Variable 'v' is used before being assigned. + } \ No newline at end of file diff --git a/tests/baselines/reference/controlFlowIIFE.js b/tests/baselines/reference/controlFlowIIFE.js index 20bde01cba6..5c7fcb4bd52 100644 --- a/tests/baselines/reference/controlFlowIIFE.js +++ b/tests/baselines/reference/controlFlowIIFE.js @@ -44,34 +44,61 @@ if (!test) { } (() => { test.slice(1); // No error -})(); +})(); + +// Repro from #23565 + +function f4() { + let v: number; + (function() { + v = 1; + })(); + v; +} + +function f5() { + let v: number; + (function*() { + yield 1; + v = 1; + })(); + v; // still undefined +} + +function f6() { + let v: number; + (async function() { + v = await 1; + })(); + v; // still undefined +} //// [controlFlowIIFE.js] function f1() { - var x = getStringOrNumber(); + let x = getStringOrNumber(); if (typeof x === "string") { - var n = function () { + let n = function () { return x.length; }(); } } function f2() { - var x = getStringOrNumber(); + let x = getStringOrNumber(); if (typeof x === "string") { - var n = (function () { + let n = (function () { return x.length; })(); } } function f3() { - var x = getStringOrNumber(); - var y; + let x = getStringOrNumber(); + let y; if (typeof x === "string") { - var n = (function (z) { return x.length + y + z; })(y = 1); + let n = (z => x.length + y + z)(y = 1); } } // Repros from #8381 -var maybeNumber; +let maybeNumber; (function () { maybeNumber = 1; })(); @@ -79,10 +106,33 @@ maybeNumber++; if (maybeNumber !== undefined) { maybeNumber++; } -var test; +let test; if (!test) { throw new Error('Test is not defined'); } -(function () { +(() => { test.slice(1); // No error })(); +// Repro from #23565 +function f4() { + let v; + (function () { + v = 1; + })(); + v; +} +function f5() { + let v; + (function* () { + yield 1; + v = 1; + })(); + v; // still undefined +} +function f6() { + let v; + (async function () { + v = await 1; + })(); + v; // still undefined +} diff --git a/tests/baselines/reference/controlFlowIIFE.symbols b/tests/baselines/reference/controlFlowIIFE.symbols index 4df906e0684..038d398c649 100644 --- a/tests/baselines/reference/controlFlowIIFE.symbols +++ b/tests/baselines/reference/controlFlowIIFE.symbols @@ -16,9 +16,9 @@ function f1() { >n : Symbol(n, Decl(controlFlowIIFE.ts, 5, 11)) return x.length; ->x.length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>x.length : Symbol(String.length, Decl(lib.es5.d.ts, --, --)) >x : Symbol(x, Decl(controlFlowIIFE.ts, 3, 7)) ->length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>length : Symbol(String.length, Decl(lib.es5.d.ts, --, --)) }(); } @@ -38,9 +38,9 @@ function f2() { >n : Symbol(n, Decl(controlFlowIIFE.ts, 14, 11)) return x.length; ->x.length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>x.length : Symbol(String.length, Decl(lib.es5.d.ts, --, --)) >x : Symbol(x, Decl(controlFlowIIFE.ts, 12, 7)) ->length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>length : Symbol(String.length, Decl(lib.es5.d.ts, --, --)) })(); } @@ -62,9 +62,9 @@ function f3() { let n = (z => x.length + y + z)(y = 1); >n : Symbol(n, Decl(controlFlowIIFE.ts, 24, 11)) >z : Symbol(z, Decl(controlFlowIIFE.ts, 24, 17)) ->x.length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>x.length : Symbol(String.length, Decl(lib.es5.d.ts, --, --)) >x : Symbol(x, Decl(controlFlowIIFE.ts, 21, 7)) ->length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>length : Symbol(String.length, Decl(lib.es5.d.ts, --, --)) >y : Symbol(y, Decl(controlFlowIIFE.ts, 22, 7)) >z : Symbol(z, Decl(controlFlowIIFE.ts, 24, 17)) >y : Symbol(y, Decl(controlFlowIIFE.ts, 22, 7)) @@ -99,12 +99,60 @@ if (!test) { >test : Symbol(test, Decl(controlFlowIIFE.ts, 39, 3)) throw new Error('Test is not defined'); ->Error : Symbol(Error, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) } (() => { test.slice(1); // No error ->test.slice : Symbol(String.slice, Decl(lib.d.ts, --, --)) +>test.slice : Symbol(String.slice, Decl(lib.es5.d.ts, --, --)) >test : Symbol(test, Decl(controlFlowIIFE.ts, 39, 3)) ->slice : Symbol(String.slice, Decl(lib.d.ts, --, --)) +>slice : Symbol(String.slice, Decl(lib.es5.d.ts, --, --)) })(); + +// Repro from #23565 + +function f4() { +>f4 : Symbol(f4, Decl(controlFlowIIFE.ts, 45, 5)) + + let v: number; +>v : Symbol(v, Decl(controlFlowIIFE.ts, 50, 7)) + + (function() { + v = 1; +>v : Symbol(v, Decl(controlFlowIIFE.ts, 50, 7)) + + })(); + v; +>v : Symbol(v, Decl(controlFlowIIFE.ts, 50, 7)) +} + +function f5() { +>f5 : Symbol(f5, Decl(controlFlowIIFE.ts, 55, 1)) + + let v: number; +>v : Symbol(v, Decl(controlFlowIIFE.ts, 58, 7)) + + (function*() { + yield 1; + v = 1; +>v : Symbol(v, Decl(controlFlowIIFE.ts, 58, 7)) + + })(); + v; // still undefined +>v : Symbol(v, Decl(controlFlowIIFE.ts, 58, 7)) +} + +function f6() { +>f6 : Symbol(f6, Decl(controlFlowIIFE.ts, 64, 1)) + + let v: number; +>v : Symbol(v, Decl(controlFlowIIFE.ts, 67, 7)) + + (async function() { + v = await 1; +>v : Symbol(v, Decl(controlFlowIIFE.ts, 67, 7)) + + })(); + v; // still undefined +>v : Symbol(v, Decl(controlFlowIIFE.ts, 67, 7)) +} diff --git a/tests/baselines/reference/controlFlowIIFE.types b/tests/baselines/reference/controlFlowIIFE.types index 1132649ae09..7aaaad159fc 100644 --- a/tests/baselines/reference/controlFlowIIFE.types +++ b/tests/baselines/reference/controlFlowIIFE.types @@ -150,3 +150,73 @@ if (!test) { >1 : 1 })(); + +// Repro from #23565 + +function f4() { +>f4 : () => void + + let v: number; +>v : number + + (function() { +>(function() { v = 1; })() : void +>(function() { v = 1; }) : () => void +>function() { v = 1; } : () => void + + v = 1; +>v = 1 : 1 +>v : number +>1 : 1 + + })(); + v; +>v : number +} + +function f5() { +>f5 : () => void + + let v: number; +>v : number + + (function*() { +>(function*() { yield 1; v = 1; })() : IterableIterator +>(function*() { yield 1; v = 1; }) : () => IterableIterator +>function*() { yield 1; v = 1; } : () => IterableIterator + + yield 1; +>yield 1 : any +>1 : 1 + + v = 1; +>v = 1 : 1 +>v : number +>1 : 1 + + })(); + v; // still undefined +>v : number +} + +function f6() { +>f6 : () => void + + let v: number; +>v : number + + (async function() { +>(async function() { v = await 1; })() : Promise +>(async function() { v = await 1; }) : () => Promise +>async function() { v = await 1; } : () => Promise + + v = await 1; +>v = await 1 : 1 +>v : number +>await 1 : 1 +>1 : 1 + + })(); + v; // still undefined +>v : number +} diff --git a/tests/cases/conformance/controlFlow/controlFlowIIFE.ts b/tests/cases/conformance/controlFlow/controlFlowIIFE.ts index c72f038c1ec..8cd9047cb05 100644 --- a/tests/cases/conformance/controlFlow/controlFlowIIFE.ts +++ b/tests/cases/conformance/controlFlow/controlFlowIIFE.ts @@ -1,4 +1,5 @@ // @strictNullChecks: true +// @target: ES2017 declare function getStringOrNumber(): string | number; @@ -45,4 +46,31 @@ if (!test) { } (() => { test.slice(1); // No error -})(); \ No newline at end of file +})(); + +// Repro from #23565 + +function f4() { + let v: number; + (function() { + v = 1; + })(); + v; +} + +function f5() { + let v: number; + (function*() { + yield 1; + v = 1; + })(); + v; // still undefined +} + +function f6() { + let v: number; + (async function() { + v = await 1; + })(); + v; // still undefined +} \ No newline at end of file