fix CFA for BindingElement. #49759 (#49836)

* fix CFA for BindingElement. #49759

* fix Parameter

* fix controlFlowBindingPatternOrder

* fix bindParameterFlow

* add tests

* refactor

* refactor

* refactor
This commit is contained in:
Zzzen
2022-08-09 16:37:39 -07:00
committed by GitHub
parent dd98c17b10
commit b94b299a11
13 changed files with 1046 additions and 24 deletions
+35 -12
View File
@@ -850,6 +850,9 @@ namespace ts {
case SyntaxKind.BindingElement:
bindBindingElementFlow(node as BindingElement);
break;
case SyntaxKind.Parameter:
bindParameterFlow(node as ParameterDeclaration);
break;
case SyntaxKind.ObjectLiteralExpression:
case SyntaxKind.ArrayLiteralExpression:
case SyntaxKind.PropertyAssignment:
@@ -1655,20 +1658,40 @@ namespace ts {
}
function bindBindingElementFlow(node: BindingElement) {
if (isBindingPattern(node.name)) {
// When evaluating a binding pattern, the initializer is evaluated before the binding pattern, per:
// - https://tc39.es/ecma262/#sec-destructuring-binding-patterns-runtime-semantics-iteratorbindinginitialization
// - `BindingElement: BindingPattern Initializer?`
// - https://tc39.es/ecma262/#sec-runtime-semantics-keyedbindinginitialization
// - `BindingElement: BindingPattern Initializer?`
bind(node.dotDotDotToken);
bind(node.propertyName);
bind(node.initializer);
bind(node.name);
// When evaluating a binding pattern, the initializer is evaluated before the binding pattern, per:
// - https://tc39.es/ecma262/#sec-destructuring-binding-patterns-runtime-semantics-iteratorbindinginitialization
// - `BindingElement: BindingPattern Initializer?`
// - https://tc39.es/ecma262/#sec-runtime-semantics-keyedbindinginitialization
// - `BindingElement: BindingPattern Initializer?`
bind(node.dotDotDotToken);
bind(node.propertyName);
bindInitializer(node.initializer);
bind(node.name);
}
function bindParameterFlow(node: ParameterDeclaration) {
bindEach(node.modifiers);
bind(node.dotDotDotToken);
bind(node.questionToken);
bind(node.type);
bindInitializer(node.initializer);
bind(node.name);
}
// a BindingElement/Parameter does not have side effects if initializers are not evaluated and used. (see GH#49759)
function bindInitializer(node: Expression | undefined) {
if (!node) {
return;
}
else {
bindEachChild(node);
const entryFlow = currentFlow;
bind(node);
if (entryFlow === unreachableFlow || entryFlow === currentFlow) {
return;
}
const exitFlow = createBranchLabel();
addAntecedent(exitFlow, entryFlow);
addAntecedent(exitFlow, currentFlow);
currentFlow = finishFlowLabel(exitFlow);
}
function bindJSDocTypeAlias(node: JSDocTypedefTag | JSDocCallbackTag | JSDocEnumTag) {
@@ -0,0 +1,136 @@
//// [controlFlowBindingElement.ts]
{
const data = { param: 'value' };
const {
param = (() => { throw new Error('param is not defined') })(),
} = data;
console.log(param); // should not trigger 'Unreachable code detected.'
}
{
const data = { param: 'value' };
let foo: string | undefined = "";
const {
param = (() => { throw new Error('param is not defined') })(),
} = data;
foo; // should be string
}
{
const data = { param: 'value' };
let foo: string | undefined = "";
const {
param = (() => { foo = undefined })(),
} = data;
foo; // should be string | undefined
}
{
const data = { param: 'value' };
let foo: string | undefined = "";
const {
param = (() => { return "" + 1 })(),
} = data;
foo; // should be string
}
{
interface Window {
window: Window;
}
let foo: string | undefined;
let window = {} as Window;
window.window = window;
const { [(() => { foo = ""; return 'window' as const })()]:
{ [(() => { return 'window' as const })()]: bar } } = window;
foo; // should be string
}
{
interface Window {
window: Window;
}
let foo: string | undefined;
let window = {} as Window;
window.window = window;
const { [(() => { return 'window' as const })()]:
{ [(() => { foo = ""; return 'window' as const })()]: bar } } = window;
foo; // should be string
}
{
interface Window {
window: Window;
}
let foo: string | undefined;
let window = {} as Window;
window.window = window;
const { [(() => { return 'window' as const })()]:
{ [(() => { return 'window' as const })()]: bar = (() => { foo = ""; return window; })() } } = window;
foo; // should be string | undefined
}
//// [controlFlowBindingElement.js]
{
var data = { param: 'value' };
var _a = data.param, param = _a === void 0 ? (function () { throw new Error('param is not defined'); })() : _a;
console.log(param); // should not trigger 'Unreachable code detected.'
}
{
var data = { param: 'value' };
var foo = "";
var _b = data.param, param = _b === void 0 ? (function () { throw new Error('param is not defined'); })() : _b;
foo; // should be string
}
{
var data = { param: 'value' };
var foo_1 = "";
var _c = data.param, param = _c === void 0 ? (function () { foo_1 = undefined; })() : _c;
foo_1; // should be string | undefined
}
{
var data = { param: 'value' };
var foo = "";
var _d = data.param, param = _d === void 0 ? (function () { return "" + 1; })() : _d;
foo; // should be string
}
{
var foo_2;
var window_1 = {};
window_1.window = window_1;
var _e = window_1, _f = (function () { foo_2 = ""; return 'window'; })(), _g = (function () { return 'window'; })(), bar = _e[_f][_g];
foo_2; // should be string
}
{
var foo_3;
var window_2 = {};
window_2.window = window_2;
var _h = window_2, _j = (function () { return 'window'; })(), _k = (function () { foo_3 = ""; return 'window'; })(), bar = _h[_j][_k];
foo_3; // should be string
}
{
var foo_4;
var window_3 = {};
window_3.window = window_3;
var _l = window_3, _m = (function () { return 'window'; })(), _o = (function () { return 'window'; })(), _p = _l[_m][_o], bar = _p === void 0 ? (function () { foo_4 = ""; return window_3; })() : _p;
foo_4; // should be string | undefined
}
@@ -0,0 +1,188 @@
=== tests/cases/conformance/controlFlow/controlFlowBindingElement.ts ===
{
const data = { param: 'value' };
>data : Symbol(data, Decl(controlFlowBindingElement.ts, 1, 9))
>param : Symbol(param, Decl(controlFlowBindingElement.ts, 1, 19))
const {
param = (() => { throw new Error('param is not defined') })(),
>param : Symbol(param, Decl(controlFlowBindingElement.ts, 3, 11))
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
} = data;
>data : Symbol(data, Decl(controlFlowBindingElement.ts, 1, 9))
console.log(param); // should not trigger 'Unreachable code detected.'
>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, --, --))
>param : Symbol(param, Decl(controlFlowBindingElement.ts, 3, 11))
}
{
const data = { param: 'value' };
>data : Symbol(data, Decl(controlFlowBindingElement.ts, 12, 9))
>param : Symbol(param, Decl(controlFlowBindingElement.ts, 12, 19))
let foo: string | undefined = "";
>foo : Symbol(foo, Decl(controlFlowBindingElement.ts, 14, 7))
const {
param = (() => { throw new Error('param is not defined') })(),
>param : Symbol(param, Decl(controlFlowBindingElement.ts, 15, 11))
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
} = data;
>data : Symbol(data, Decl(controlFlowBindingElement.ts, 12, 9))
foo; // should be string
>foo : Symbol(foo, Decl(controlFlowBindingElement.ts, 14, 7))
}
{
const data = { param: 'value' };
>data : Symbol(data, Decl(controlFlowBindingElement.ts, 23, 9))
>param : Symbol(param, Decl(controlFlowBindingElement.ts, 23, 19))
let foo: string | undefined = "";
>foo : Symbol(foo, Decl(controlFlowBindingElement.ts, 25, 7))
const {
param = (() => { foo = undefined })(),
>param : Symbol(param, Decl(controlFlowBindingElement.ts, 26, 11))
>foo : Symbol(foo, Decl(controlFlowBindingElement.ts, 25, 7))
>undefined : Symbol(undefined)
} = data;
>data : Symbol(data, Decl(controlFlowBindingElement.ts, 23, 9))
foo; // should be string | undefined
>foo : Symbol(foo, Decl(controlFlowBindingElement.ts, 25, 7))
}
{
const data = { param: 'value' };
>data : Symbol(data, Decl(controlFlowBindingElement.ts, 34, 9))
>param : Symbol(param, Decl(controlFlowBindingElement.ts, 34, 19))
let foo: string | undefined = "";
>foo : Symbol(foo, Decl(controlFlowBindingElement.ts, 36, 7))
const {
param = (() => { return "" + 1 })(),
>param : Symbol(param, Decl(controlFlowBindingElement.ts, 37, 11))
} = data;
>data : Symbol(data, Decl(controlFlowBindingElement.ts, 34, 9))
foo; // should be string
>foo : Symbol(foo, Decl(controlFlowBindingElement.ts, 36, 7))
}
{
interface Window {
>Window : Symbol(Window, Decl(controlFlowBindingElement.ts, 44, 1))
window: Window;
>window : Symbol(Window.window, Decl(controlFlowBindingElement.ts, 45, 22))
>Window : Symbol(Window, Decl(controlFlowBindingElement.ts, 44, 1))
}
let foo: string | undefined;
>foo : Symbol(foo, Decl(controlFlowBindingElement.ts, 49, 7))
let window = {} as Window;
>window : Symbol(window, Decl(controlFlowBindingElement.ts, 50, 7))
>Window : Symbol(Window, Decl(controlFlowBindingElement.ts, 44, 1))
window.window = window;
>window.window : Symbol(Window.window, Decl(controlFlowBindingElement.ts, 45, 22))
>window : Symbol(window, Decl(controlFlowBindingElement.ts, 50, 7))
>window : Symbol(Window.window, Decl(controlFlowBindingElement.ts, 45, 22))
>window : Symbol(window, Decl(controlFlowBindingElement.ts, 50, 7))
const { [(() => { foo = ""; return 'window' as const })()]:
>foo : Symbol(foo, Decl(controlFlowBindingElement.ts, 49, 7))
>const : Symbol(const)
{ [(() => { return 'window' as const })()]: bar } } = window;
>const : Symbol(const)
>bar : Symbol(bar, Decl(controlFlowBindingElement.ts, 54, 9))
>window : Symbol(window, Decl(controlFlowBindingElement.ts, 50, 7))
foo; // should be string
>foo : Symbol(foo, Decl(controlFlowBindingElement.ts, 49, 7))
}
{
interface Window {
>Window : Symbol(Window, Decl(controlFlowBindingElement.ts, 59, 1))
window: Window;
>window : Symbol(Window.window, Decl(controlFlowBindingElement.ts, 60, 22))
>Window : Symbol(Window, Decl(controlFlowBindingElement.ts, 59, 1))
}
let foo: string | undefined;
>foo : Symbol(foo, Decl(controlFlowBindingElement.ts, 64, 7))
let window = {} as Window;
>window : Symbol(window, Decl(controlFlowBindingElement.ts, 65, 7))
>Window : Symbol(Window, Decl(controlFlowBindingElement.ts, 59, 1))
window.window = window;
>window.window : Symbol(Window.window, Decl(controlFlowBindingElement.ts, 60, 22))
>window : Symbol(window, Decl(controlFlowBindingElement.ts, 65, 7))
>window : Symbol(Window.window, Decl(controlFlowBindingElement.ts, 60, 22))
>window : Symbol(window, Decl(controlFlowBindingElement.ts, 65, 7))
const { [(() => { return 'window' as const })()]:
>const : Symbol(const)
{ [(() => { foo = ""; return 'window' as const })()]: bar } } = window;
>foo : Symbol(foo, Decl(controlFlowBindingElement.ts, 64, 7))
>const : Symbol(const)
>bar : Symbol(bar, Decl(controlFlowBindingElement.ts, 69, 9))
>window : Symbol(window, Decl(controlFlowBindingElement.ts, 65, 7))
foo; // should be string
>foo : Symbol(foo, Decl(controlFlowBindingElement.ts, 64, 7))
}
{
interface Window {
>Window : Symbol(Window, Decl(controlFlowBindingElement.ts, 74, 1))
window: Window;
>window : Symbol(Window.window, Decl(controlFlowBindingElement.ts, 75, 22))
>Window : Symbol(Window, Decl(controlFlowBindingElement.ts, 74, 1))
}
let foo: string | undefined;
>foo : Symbol(foo, Decl(controlFlowBindingElement.ts, 79, 7))
let window = {} as Window;
>window : Symbol(window, Decl(controlFlowBindingElement.ts, 80, 7))
>Window : Symbol(Window, Decl(controlFlowBindingElement.ts, 74, 1))
window.window = window;
>window.window : Symbol(Window.window, Decl(controlFlowBindingElement.ts, 75, 22))
>window : Symbol(window, Decl(controlFlowBindingElement.ts, 80, 7))
>window : Symbol(Window.window, Decl(controlFlowBindingElement.ts, 75, 22))
>window : Symbol(window, Decl(controlFlowBindingElement.ts, 80, 7))
const { [(() => { return 'window' as const })()]:
>const : Symbol(const)
{ [(() => { return 'window' as const })()]: bar = (() => { foo = ""; return window; })() } } = window;
>const : Symbol(const)
>bar : Symbol(bar, Decl(controlFlowBindingElement.ts, 84, 9))
>foo : Symbol(foo, Decl(controlFlowBindingElement.ts, 79, 7))
>window : Symbol(window, Decl(controlFlowBindingElement.ts, 80, 7))
>window : Symbol(window, Decl(controlFlowBindingElement.ts, 80, 7))
foo; // should be string | undefined
>foo : Symbol(foo, Decl(controlFlowBindingElement.ts, 79, 7))
}
@@ -0,0 +1,250 @@
=== tests/cases/conformance/controlFlow/controlFlowBindingElement.ts ===
{
const data = { param: 'value' };
>data : { param: string; }
>{ param: 'value' } : { param: string; }
>param : string
>'value' : "value"
const {
param = (() => { throw new Error('param is not defined') })(),
>param : string
>(() => { throw new Error('param is not defined') })() : never
>(() => { throw new Error('param is not defined') }) : () => never
>() => { throw new Error('param is not defined') } : () => never
>new Error('param is not defined') : Error
>Error : ErrorConstructor
>'param is not defined' : "param is not defined"
} = data;
>data : { param: string; }
console.log(param); // should not trigger 'Unreachable code detected.'
>console.log(param) : void
>console.log : (...data: any[]) => void
>console : Console
>log : (...data: any[]) => void
>param : string
}
{
const data = { param: 'value' };
>data : { param: string; }
>{ param: 'value' } : { param: string; }
>param : string
>'value' : "value"
let foo: string | undefined = "";
>foo : string | undefined
>"" : ""
const {
param = (() => { throw new Error('param is not defined') })(),
>param : string
>(() => { throw new Error('param is not defined') })() : never
>(() => { throw new Error('param is not defined') }) : () => never
>() => { throw new Error('param is not defined') } : () => never
>new Error('param is not defined') : Error
>Error : ErrorConstructor
>'param is not defined' : "param is not defined"
} = data;
>data : { param: string; }
foo; // should be string
>foo : string
}
{
const data = { param: 'value' };
>data : { param: string; }
>{ param: 'value' } : { param: string; }
>param : string
>'value' : "value"
let foo: string | undefined = "";
>foo : string | undefined
>"" : ""
const {
param = (() => { foo = undefined })(),
>param : string | void
>(() => { foo = undefined })() : void
>(() => { foo = undefined }) : () => void
>() => { foo = undefined } : () => void
>foo = undefined : undefined
>foo : string | undefined
>undefined : undefined
} = data;
>data : { param: string; }
foo; // should be string | undefined
>foo : string | undefined
}
{
const data = { param: 'value' };
>data : { param: string; }
>{ param: 'value' } : { param: string; }
>param : string
>'value' : "value"
let foo: string | undefined = "";
>foo : string | undefined
>"" : ""
const {
param = (() => { return "" + 1 })(),
>param : string
>(() => { return "" + 1 })() : string
>(() => { return "" + 1 }) : () => string
>() => { return "" + 1 } : () => string
>"" + 1 : string
>"" : ""
>1 : 1
} = data;
>data : { param: string; }
foo; // should be string
>foo : string
}
{
interface Window {
window: Window;
>window : Window
}
let foo: string | undefined;
>foo : string | undefined
let window = {} as Window;
>window : Window
>{} as Window : Window
>{} : {}
window.window = window;
>window.window = window : Window
>window.window : Window
>window : Window
>window : Window
>window : Window
const { [(() => { foo = ""; return 'window' as const })()]:
>(() => { foo = ""; return 'window' as const })() : "window"
>(() => { foo = ""; return 'window' as const }) : () => "window"
>() => { foo = ""; return 'window' as const } : () => "window"
>foo = "" : ""
>foo : string | undefined
>"" : ""
>'window' as const : "window"
>'window' : "window"
{ [(() => { return 'window' as const })()]: bar } } = window;
>(() => { return 'window' as const })() : "window"
>(() => { return 'window' as const }) : () => "window"
>() => { return 'window' as const } : () => "window"
>'window' as const : "window"
>'window' : "window"
>bar : Window
>window : Window
foo; // should be string
>foo : string
}
{
interface Window {
window: Window;
>window : Window
}
let foo: string | undefined;
>foo : string | undefined
let window = {} as Window;
>window : Window
>{} as Window : Window
>{} : {}
window.window = window;
>window.window = window : Window
>window.window : Window
>window : Window
>window : Window
>window : Window
const { [(() => { return 'window' as const })()]:
>(() => { return 'window' as const })() : "window"
>(() => { return 'window' as const }) : () => "window"
>() => { return 'window' as const } : () => "window"
>'window' as const : "window"
>'window' : "window"
{ [(() => { foo = ""; return 'window' as const })()]: bar } } = window;
>(() => { foo = ""; return 'window' as const })() : "window"
>(() => { foo = ""; return 'window' as const }) : () => "window"
>() => { foo = ""; return 'window' as const } : () => "window"
>foo = "" : ""
>foo : string | undefined
>"" : ""
>'window' as const : "window"
>'window' : "window"
>bar : Window
>window : Window
foo; // should be string
>foo : string
}
{
interface Window {
window: Window;
>window : Window
}
let foo: string | undefined;
>foo : string | undefined
let window = {} as Window;
>window : Window
>{} as Window : Window
>{} : {}
window.window = window;
>window.window = window : Window
>window.window : Window
>window : Window
>window : Window
>window : Window
const { [(() => { return 'window' as const })()]:
>(() => { return 'window' as const })() : "window"
>(() => { return 'window' as const }) : () => "window"
>() => { return 'window' as const } : () => "window"
>'window' as const : "window"
>'window' : "window"
{ [(() => { return 'window' as const })()]: bar = (() => { foo = ""; return window; })() } } = window;
>(() => { return 'window' as const })() : "window"
>(() => { return 'window' as const }) : () => "window"
>() => { return 'window' as const } : () => "window"
>'window' as const : "window"
>'window' : "window"
>bar : Window
>(() => { foo = ""; return window; })() : Window
>(() => { foo = ""; return window; }) : () => Window
>() => { foo = ""; return window; } : () => Window
>foo = "" : ""
>foo : string | undefined
>"" : ""
>window : Window
>window : Window
foo; // should be string | undefined
>foo : string | undefined
}
@@ -0,0 +1,39 @@
tests/cases/conformance/controlFlow/controlFlowBindingPatternOrder.ts(10,11): error TS2322: Type '0 | 9' is not assignable to type '9'.
Type '0' is not assignable to type '9'.
tests/cases/conformance/controlFlow/controlFlowBindingPatternOrder.ts(25,11): error TS2322: Type '0 | 9 | 8' is not assignable to type '0 | 8'.
Type '9' is not assignable to type '0 | 8'.
==== tests/cases/conformance/controlFlow/controlFlowBindingPatternOrder.ts (2 errors) ====
// https://github.com/microsoft/TypeScript/pull/41094#issuecomment-716044363
{
let a: 0 | 1 = 0;
const [{ [(a = 1)]: b } = [9, a] as const] = [];
const bb: 0 = b;
}
{
let a: 0 | 1 = 1;
const [{ [a]: b } = [9, a = 0] as const] = [];
const bb: 9 = b;
~~
!!! error TS2322: Type '0 | 9' is not assignable to type '9'.
!!! error TS2322: Type '0' is not assignable to type '9'.
}
{
let a: 0 | 1 | 2 = 1;
const [{ [a]: b } = [9, a = 0, 5] as const] = [];
const bb: 0 | 9 = b;
}
{
let a: 0 | 1 = 0;
const [{ [(a = 1)]: b } = [9, a] as const] = [[9, 8] as const];
const bb: 0 | 8 = b;
}
{
let a: 0 | 1 = 1;
const [{ [a]: b } = [a = 0, 9] as const] = [[8, 9] as const];
const bb: 0 | 8 = b;
~~
!!! error TS2322: Type '0 | 9 | 8' is not assignable to type '0 | 8'.
!!! error TS2322: Type '9' is not assignable to type '0 | 8'.
}
@@ -29,25 +29,24 @@
>b : Symbol(b, Decl(controlFlowBindingPatternOrder.ts, 8, 12))
}
{
let a: 0 | 1 = 0;
let a: 0 | 1 | 2 = 1;
>a : Symbol(a, Decl(controlFlowBindingPatternOrder.ts, 12, 7))
const [{ [(a = 1)]: b } = [9, a] as const] = [[9, 8] as const];
const [{ [a]: b } = [9, a = 0, 5] as const] = [];
>a : Symbol(a, Decl(controlFlowBindingPatternOrder.ts, 12, 7))
>b : Symbol(b, Decl(controlFlowBindingPatternOrder.ts, 13, 12))
>a : Symbol(a, Decl(controlFlowBindingPatternOrder.ts, 12, 7))
>const : Symbol(const)
>const : Symbol(const)
const bb: 0 | 8 = b;
const bb: 0 | 9 = b;
>bb : Symbol(bb, Decl(controlFlowBindingPatternOrder.ts, 14, 9))
>b : Symbol(b, Decl(controlFlowBindingPatternOrder.ts, 13, 12))
}
{
let a: 0 | 1 = 1;
let a: 0 | 1 = 0;
>a : Symbol(a, Decl(controlFlowBindingPatternOrder.ts, 17, 7))
const [{ [a]: b } = [a = 0, 9] as const] = [[8, 9] as const];
const [{ [(a = 1)]: b } = [9, a] as const] = [[9, 8] as const];
>a : Symbol(a, Decl(controlFlowBindingPatternOrder.ts, 17, 7))
>b : Symbol(b, Decl(controlFlowBindingPatternOrder.ts, 18, 12))
>a : Symbol(a, Decl(controlFlowBindingPatternOrder.ts, 17, 7))
@@ -58,3 +57,18 @@
>bb : Symbol(bb, Decl(controlFlowBindingPatternOrder.ts, 19, 9))
>b : Symbol(b, Decl(controlFlowBindingPatternOrder.ts, 18, 12))
}
{
let a: 0 | 1 = 1;
>a : Symbol(a, Decl(controlFlowBindingPatternOrder.ts, 22, 7))
const [{ [a]: b } = [a = 0, 9] as const] = [[8, 9] as const];
>a : Symbol(a, Decl(controlFlowBindingPatternOrder.ts, 22, 7))
>b : Symbol(b, Decl(controlFlowBindingPatternOrder.ts, 23, 12))
>a : Symbol(a, Decl(controlFlowBindingPatternOrder.ts, 22, 7))
>const : Symbol(const)
>const : Symbol(const)
const bb: 0 | 8 = b;
>bb : Symbol(bb, Decl(controlFlowBindingPatternOrder.ts, 24, 9))
>b : Symbol(b, Decl(controlFlowBindingPatternOrder.ts, 23, 12))
}
@@ -27,8 +27,8 @@
>1 : 1
const [{ [a]: b } = [9, a = 0] as const] = [];
>a : 0
>b : 9
>a : 0 | 1
>b : 0 | 9
>[9, a = 0] as const : readonly [9, 0]
>[9, a = 0] : readonly [9, 0]
>9 : 9
@@ -39,7 +39,28 @@
const bb: 9 = b;
>bb : 9
>b : 9
>b : 0 | 9
}
{
let a: 0 | 1 | 2 = 1;
>a : 0 | 1 | 2
>1 : 1
const [{ [a]: b } = [9, a = 0, 5] as const] = [];
>a : 0 | 1
>b : 0 | 9
>[9, a = 0, 5] as const : readonly [9, 0, 5]
>[9, a = 0, 5] : readonly [9, 0, 5]
>9 : 9
>a = 0 : 0
>a : 0 | 1 | 2
>0 : 0
>5 : 5
>[] : []
const bb: 0 | 9 = b;
>bb : 0 | 9
>b : 0 | 9
}
{
let a: 0 | 1 = 0;
@@ -72,8 +93,8 @@
>1 : 1
const [{ [a]: b } = [a = 0, 9] as const] = [[8, 9] as const];
>a : 0
>b : 0 | 8
>a : 0 | 1
>b : 0 | 9 | 8
>[a = 0, 9] as const : readonly [0, 9]
>[a = 0, 9] : readonly [0, 9]
>a = 0 : 0
@@ -88,5 +109,5 @@
const bb: 0 | 8 = b;
>bb : 0 | 8
>b : 0 | 8
>b : 0 | 9 | 8
}
@@ -0,0 +1,60 @@
//// [controlFlowParameter.ts]
function f1(
required: unknown = (() => {
throw new Error("bad");
})()
) {
console.log("ok"); // should not trigger 'Unreachable code detected.'
}
function f2(
a: number | string | undefined,
required: unknown = (() => {
a = 1;
})()
) {
a; // should be number | string | undefined
}
function f3(
a: number | string | undefined = 1,
required: unknown = (() => {
a = "";
})()
) {
a; // should be number | string
}
function f4(
a: number | string | undefined = 1,
{ [(a = "")]: b } = {} as any
) {
a; // should be string
}
//// [controlFlowParameter.js]
function f1(required) {
if (required === void 0) { required = (function () {
throw new Error("bad");
})(); }
console.log("ok"); // should not trigger 'Unreachable code detected.'
}
function f2(a, required) {
if (required === void 0) { required = (function () {
a = 1;
})(); }
a; // should be number | string | undefined
}
function f3(a, required) {
if (a === void 0) { a = 1; }
if (required === void 0) { required = (function () {
a = "";
})(); }
a; // should be number | string
}
function f4(a, _a) {
if (a === void 0) { a = 1; }
var _b = _a === void 0 ? {} : _a, _c = (a = ""), b = _b[_c];
a; // should be string
}
@@ -0,0 +1,69 @@
=== tests/cases/conformance/controlFlow/controlFlowParameter.ts ===
function f1(
>f1 : Symbol(f1, Decl(controlFlowParameter.ts, 0, 0))
required: unknown = (() => {
>required : Symbol(required, Decl(controlFlowParameter.ts, 0, 12))
throw new Error("bad");
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
})()
) {
console.log("ok"); // should not trigger 'Unreachable code detected.'
>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, --, --))
}
function f2(
>f2 : Symbol(f2, Decl(controlFlowParameter.ts, 6, 1))
a: number | string | undefined,
>a : Symbol(a, Decl(controlFlowParameter.ts, 8, 12))
required: unknown = (() => {
>required : Symbol(required, Decl(controlFlowParameter.ts, 9, 33))
a = 1;
>a : Symbol(a, Decl(controlFlowParameter.ts, 8, 12))
})()
) {
a; // should be number | string | undefined
>a : Symbol(a, Decl(controlFlowParameter.ts, 8, 12))
}
function f3(
>f3 : Symbol(f3, Decl(controlFlowParameter.ts, 15, 1))
a: number | string | undefined = 1,
>a : Symbol(a, Decl(controlFlowParameter.ts, 17, 12))
required: unknown = (() => {
>required : Symbol(required, Decl(controlFlowParameter.ts, 18, 37))
a = "";
>a : Symbol(a, Decl(controlFlowParameter.ts, 17, 12))
})()
) {
a; // should be number | string
>a : Symbol(a, Decl(controlFlowParameter.ts, 17, 12))
}
function f4(
>f4 : Symbol(f4, Decl(controlFlowParameter.ts, 24, 1))
a: number | string | undefined = 1,
>a : Symbol(a, Decl(controlFlowParameter.ts, 26, 12))
{ [(a = "")]: b } = {} as any
>a : Symbol(a, Decl(controlFlowParameter.ts, 26, 12))
>b : Symbol(b, Decl(controlFlowParameter.ts, 28, 3))
) {
a; // should be string
>a : Symbol(a, Decl(controlFlowParameter.ts, 26, 12))
}
@@ -0,0 +1,93 @@
=== tests/cases/conformance/controlFlow/controlFlowParameter.ts ===
function f1(
>f1 : (required?: unknown) => void
required: unknown = (() => {
>required : unknown
>(() => { throw new Error("bad"); })() : never
>(() => { throw new Error("bad"); }) : () => never
>() => { throw new Error("bad"); } : () => never
throw new Error("bad");
>new Error("bad") : Error
>Error : ErrorConstructor
>"bad" : "bad"
})()
) {
console.log("ok"); // should not trigger 'Unreachable code detected.'
>console.log("ok") : void
>console.log : (...data: any[]) => void
>console : Console
>log : (...data: any[]) => void
>"ok" : "ok"
}
function f2(
>f2 : (a: number | string | undefined, required?: unknown) => void
a: number | string | undefined,
>a : string | number | undefined
required: unknown = (() => {
>required : unknown
>(() => { a = 1; })() : void
>(() => { a = 1; }) : () => void
>() => { a = 1; } : () => void
a = 1;
>a = 1 : 1
>a : string | number | undefined
>1 : 1
})()
) {
a; // should be number | string | undefined
>a : string | number | undefined
}
function f3(
>f3 : (a?: number | string | undefined, required?: unknown) => void
a: number | string | undefined = 1,
>a : string | number | undefined
>1 : 1
required: unknown = (() => {
>required : unknown
>(() => { a = ""; })() : void
>(() => { a = ""; }) : () => void
>() => { a = ""; } : () => void
a = "";
>a = "" : ""
>a : string | number | undefined
>"" : ""
})()
) {
a; // should be number | string
>a : string | number
}
function f4(
>f4 : (a?: number | string | undefined, { [(a = "")]: b }?: any) => void
a: number | string | undefined = 1,
>a : string | number | undefined
>1 : 1
{ [(a = "")]: b } = {} as any
>(a = "") : ""
>a = "" : ""
>a : string | number | undefined
>"" : ""
>b : any
>{} as any : any
>{} : {}
) {
a; // should be string
>a : string
}
@@ -0,0 +1,90 @@
// @strictNullChecks: true
// @allowUnreachableCode: false
{
const data = { param: 'value' };
const {
param = (() => { throw new Error('param is not defined') })(),
} = data;
console.log(param); // should not trigger 'Unreachable code detected.'
}
{
const data = { param: 'value' };
let foo: string | undefined = "";
const {
param = (() => { throw new Error('param is not defined') })(),
} = data;
foo; // should be string
}
{
const data = { param: 'value' };
let foo: string | undefined = "";
const {
param = (() => { foo = undefined })(),
} = data;
foo; // should be string | undefined
}
{
const data = { param: 'value' };
let foo: string | undefined = "";
const {
param = (() => { return "" + 1 })(),
} = data;
foo; // should be string
}
{
interface Window {
window: Window;
}
let foo: string | undefined;
let window = {} as Window;
window.window = window;
const { [(() => { foo = ""; return 'window' as const })()]:
{ [(() => { return 'window' as const })()]: bar } } = window;
foo; // should be string
}
{
interface Window {
window: Window;
}
let foo: string | undefined;
let window = {} as Window;
window.window = window;
const { [(() => { return 'window' as const })()]:
{ [(() => { foo = ""; return 'window' as const })()]: bar } } = window;
foo; // should be string
}
{
interface Window {
window: Window;
}
let foo: string | undefined;
let window = {} as Window;
window.window = window;
const { [(() => { return 'window' as const })()]:
{ [(() => { return 'window' as const })()]: bar = (() => { foo = ""; return window; })() } } = window;
foo; // should be string | undefined
}
@@ -12,6 +12,11 @@
const [{ [a]: b } = [9, a = 0] as const] = [];
const bb: 9 = b;
}
{
let a: 0 | 1 | 2 = 1;
const [{ [a]: b } = [9, a = 0, 5] as const] = [];
const bb: 0 | 9 = b;
}
{
let a: 0 | 1 = 0;
const [{ [(a = 1)]: b } = [9, a] as const] = [[9, 8] as const];
@@ -0,0 +1,34 @@
// @strictNullChecks: true
// @allowUnreachableCode: false
function f1(
required: unknown = (() => {
throw new Error("bad");
})()
) {
console.log("ok"); // should not trigger 'Unreachable code detected.'
}
function f2(
a: number | string | undefined,
required: unknown = (() => {
a = 1;
})()
) {
a; // should be number | string | undefined
}
function f3(
a: number | string | undefined = 1,
required: unknown = (() => {
a = "";
})()
) {
a; // should be number | string
}
function f4(
a: number | string | undefined = 1,
{ [(a = "")]: b } = {} as any
) {
a; // should be string
}