Update control flow for correct destructuring evaluation order

This commit is contained in:
Ron Buckton 2020-10-28 16:30:48 -07:00
parent 0b303ff08a
commit 8a15291541
7 changed files with 457 additions and 0 deletions

View file

@ -218,6 +218,9 @@ namespace ts {
// or if compiler options contain alwaysStrict.
let inStrictMode: boolean;
// If we are binding an assignment pattern, we will bind certain expressions differently.
let inAssignmentPattern = false;
let symbolCount = 0;
let Symbol: new (flags: SymbolFlags, name: __String) => Symbol;
@ -275,6 +278,7 @@ namespace ts {
currentExceptionTarget = undefined;
activeLabelList = undefined;
hasExplicitReturn = false;
inAssignmentPattern = false;
emitFlags = NodeFlags.None;
}
@ -733,9 +737,14 @@ namespace ts {
}
function bindChildren(node: Node): void {
const saveInAssignmentPattern = inAssignmentPattern;
// Most nodes aren't valid in an assignment pattern, so we clear the value here
// and set it before we descend into nodes that could actually be part of an assignment pattern.
inAssignmentPattern = false;
if (checkUnreachable(node)) {
bindEachChild(node);
bindJSDoc(node);
inAssignmentPattern = saveInAssignmentPattern;
return;
}
if (node.kind >= SyntaxKind.FirstStatement && node.kind <= SyntaxKind.LastStatement && !options.allowUnreachableCode) {
@ -791,6 +800,13 @@ namespace ts {
bindPostfixUnaryExpressionFlow(<PostfixUnaryExpression>node);
break;
case SyntaxKind.BinaryExpression:
if (isDestructuringAssignment(node)) {
// Carry over whether we are in an assignment pattern to
// binary expressions that could actually be an initializer
inAssignmentPattern = saveInAssignmentPattern;
bindDestructuringAssignmentFlow(node);
return;
}
bindBinaryExpressionFlow(<BinaryExpression>node);
break;
case SyntaxKind.DeleteExpression:
@ -827,11 +843,23 @@ namespace ts {
case SyntaxKind.ModuleBlock:
bindEachFunctionsFirst((node as Block).statements);
break;
case SyntaxKind.BindingElement:
bindBindingElementFlow(<BindingElement>node);
break;
case SyntaxKind.ObjectLiteralExpression:
case SyntaxKind.ArrayLiteralExpression:
case SyntaxKind.PropertyAssignment:
case SyntaxKind.SpreadElement:
// Carry over whether we are in an assignment pattern of Object and Array literals
// as well as their children that are valid assignment targets.
inAssignmentPattern = saveInAssignmentPattern;
// falls through
default:
bindEachChild(node);
break;
}
bindJSDoc(node);
inAssignmentPattern = saveInAssignmentPattern;
}
function isNarrowingExpression(expr: Expression): boolean {
@ -1449,6 +1477,24 @@ namespace ts {
}
}
function bindDestructuringAssignmentFlow(node: DestructuringAssignment) {
if (inAssignmentPattern) {
inAssignmentPattern = false;
bind(node.operatorToken);
bind(node.right);
inAssignmentPattern = true;
bind(node.left);
}
else {
inAssignmentPattern = true;
bind(node.left);
inAssignmentPattern = false;
bind(node.operatorToken);
bind(node.right);
}
bindAssignmentTargetFlow(node.left);
}
const enum BindBinaryExpressionFlowState {
BindThenBindChildren,
MaybeBindLeft,
@ -1617,6 +1663,25 @@ 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?`
bindEach(node.decorators);
bindEach(node.modifiers);
bind(node.dotDotDotToken);
bind(node.propertyName);
bind(node.initializer);
bind(node.name);
}
else {
bindEachChild(node);
}
}
function bindJSDocTypeAlias(node: JSDocTypedefTag | JSDocCallbackTag | JSDocEnumTag) {
setParent(node.tagName, node);
if (node.kind !== SyntaxKind.JSDocEnumTag && node.fullName) {

View file

@ -0,0 +1,70 @@
=== tests/cases/conformance/controlFlow/controlFlowAssignmentPatternOrder.ts ===
// https://github.com/microsoft/TypeScript/pull/41094#issuecomment-716044363
{
let a: 0 | 1 = 0;
>a : Symbol(a, Decl(controlFlowAssignmentPatternOrder.ts, 2, 7))
let b: 0 | 1 | 9;
>b : Symbol(b, Decl(controlFlowAssignmentPatternOrder.ts, 3, 7))
[{ [(a = 1)]: b } = [9, a] as const] = [];
>[(a = 1)] : Symbol([(a = 1)], Decl(controlFlowAssignmentPatternOrder.ts, 4, 6))
>a : Symbol(a, Decl(controlFlowAssignmentPatternOrder.ts, 2, 7))
>b : Symbol(b, Decl(controlFlowAssignmentPatternOrder.ts, 3, 7))
>a : Symbol(a, Decl(controlFlowAssignmentPatternOrder.ts, 2, 7))
const bb: 0 = b;
>bb : Symbol(bb, Decl(controlFlowAssignmentPatternOrder.ts, 5, 9))
>b : Symbol(b, Decl(controlFlowAssignmentPatternOrder.ts, 3, 7))
}
{
let a: 0 | 1 = 1;
>a : Symbol(a, Decl(controlFlowAssignmentPatternOrder.ts, 8, 7))
let b: 0 | 1 | 9;
>b : Symbol(b, Decl(controlFlowAssignmentPatternOrder.ts, 9, 7))
[{ [a]: b } = [9, a = 0] as const] = [];
>[a] : Symbol([a], Decl(controlFlowAssignmentPatternOrder.ts, 10, 6))
>a : Symbol(a, Decl(controlFlowAssignmentPatternOrder.ts, 8, 7))
>b : Symbol(b, Decl(controlFlowAssignmentPatternOrder.ts, 9, 7))
>a : Symbol(a, Decl(controlFlowAssignmentPatternOrder.ts, 8, 7))
const bb: 9 = b;
>bb : Symbol(bb, Decl(controlFlowAssignmentPatternOrder.ts, 11, 9))
>b : Symbol(b, Decl(controlFlowAssignmentPatternOrder.ts, 9, 7))
}
{
let a: 0 | 1 = 0;
>a : Symbol(a, Decl(controlFlowAssignmentPatternOrder.ts, 14, 7))
let b: 0 | 1 | 8 | 9;
>b : Symbol(b, Decl(controlFlowAssignmentPatternOrder.ts, 15, 7))
[{ [(a = 1)]: b } = [9, a] as const] = [[9, 8] as const];
>[(a = 1)] : Symbol([(a = 1)], Decl(controlFlowAssignmentPatternOrder.ts, 16, 6))
>a : Symbol(a, Decl(controlFlowAssignmentPatternOrder.ts, 14, 7))
>b : Symbol(b, Decl(controlFlowAssignmentPatternOrder.ts, 15, 7))
>a : Symbol(a, Decl(controlFlowAssignmentPatternOrder.ts, 14, 7))
const bb: 0 | 8 = b;
>bb : Symbol(bb, Decl(controlFlowAssignmentPatternOrder.ts, 17, 9))
>b : Symbol(b, Decl(controlFlowAssignmentPatternOrder.ts, 15, 7))
}
{
let a: 0 | 1 = 1;
>a : Symbol(a, Decl(controlFlowAssignmentPatternOrder.ts, 20, 7))
let b: 0 | 1 | 8 | 9;
>b : Symbol(b, Decl(controlFlowAssignmentPatternOrder.ts, 21, 7))
[{ [a]: b } = [a = 0, 9] as const] = [[8, 9] as const];
>[a] : Symbol([a], Decl(controlFlowAssignmentPatternOrder.ts, 22, 6))
>a : Symbol(a, Decl(controlFlowAssignmentPatternOrder.ts, 20, 7))
>b : Symbol(b, Decl(controlFlowAssignmentPatternOrder.ts, 21, 7))
>a : Symbol(a, Decl(controlFlowAssignmentPatternOrder.ts, 20, 7))
const bb: 0 | 8 = b;
>bb : Symbol(bb, Decl(controlFlowAssignmentPatternOrder.ts, 23, 9))
>b : Symbol(b, Decl(controlFlowAssignmentPatternOrder.ts, 21, 7))
}

View file

@ -0,0 +1,124 @@
=== tests/cases/conformance/controlFlow/controlFlowAssignmentPatternOrder.ts ===
// https://github.com/microsoft/TypeScript/pull/41094#issuecomment-716044363
{
let a: 0 | 1 = 0;
>a : 0 | 1
>0 : 0
let b: 0 | 1 | 9;
>b : 0 | 1 | 9
[{ [(a = 1)]: b } = [9, a] as const] = [];
>[{ [(a = 1)]: b } = [9, a] as const] = [] : []
>[{ [(a = 1)]: b } = [9, a] as const] : [readonly [9, 0]]
>{ [(a = 1)]: b } = [9, a] as const : readonly [9, 0]
>{ [(a = 1)]: b } : { 1: 0 | 1 | 9; }
>[(a = 1)] : 0 | 1 | 9
>(a = 1) : 1
>a = 1 : 1
>a : 0 | 1
>1 : 1
>b : 0 | 1 | 9
>[9, a] as const : readonly [9, 0]
>[9, a] : readonly [9, 0]
>9 : 9
>a : 0
>[] : []
const bb: 0 = b;
>bb : 0
>b : 0
}
{
let a: 0 | 1 = 1;
>a : 0 | 1
>1 : 1
let b: 0 | 1 | 9;
>b : 0 | 1 | 9
[{ [a]: b } = [9, a = 0] as const] = [];
>[{ [a]: b } = [9, a = 0] as const] = [] : []
>[{ [a]: b } = [9, a = 0] as const] : [readonly [9, 0]]
>{ [a]: b } = [9, a = 0] as const : readonly [9, 0]
>{ [a]: b } : { 0: 0 | 1 | 9; }
>[a] : 0 | 1 | 9
>a : 0
>b : 0 | 1 | 9
>[9, a = 0] as const : readonly [9, 0]
>[9, a = 0] : readonly [9, 0]
>9 : 9
>a = 0 : 0
>a : 0 | 1
>0 : 0
>[] : []
const bb: 9 = b;
>bb : 9
>b : 9
}
{
let a: 0 | 1 = 0;
>a : 0 | 1
>0 : 0
let b: 0 | 1 | 8 | 9;
>b : 0 | 1 | 9 | 8
[{ [(a = 1)]: b } = [9, a] as const] = [[9, 8] as const];
>[{ [(a = 1)]: b } = [9, a] as const] = [[9, 8] as const] : [readonly [9, 8]]
>[{ [(a = 1)]: b } = [9, a] as const] : [readonly [9, 0]]
>{ [(a = 1)]: b } = [9, a] as const : readonly [9, 0]
>{ [(a = 1)]: b } : { 1: 0 | 1 | 9 | 8; }
>[(a = 1)] : 0 | 1 | 9 | 8
>(a = 1) : 1
>a = 1 : 1
>a : 0 | 1
>1 : 1
>b : 0 | 1 | 9 | 8
>[9, a] as const : readonly [9, 0]
>[9, a] : readonly [9, 0]
>9 : 9
>a : 0
>[[9, 8] as const] : [readonly [9, 8]]
>[9, 8] as const : readonly [9, 8]
>[9, 8] : readonly [9, 8]
>9 : 9
>8 : 8
const bb: 0 | 8 = b;
>bb : 0 | 8
>b : 0 | 8
}
{
let a: 0 | 1 = 1;
>a : 0 | 1
>1 : 1
let b: 0 | 1 | 8 | 9;
>b : 0 | 1 | 9 | 8
[{ [a]: b } = [a = 0, 9] as const] = [[8, 9] as const];
>[{ [a]: b } = [a = 0, 9] as const] = [[8, 9] as const] : [readonly [8, 9]]
>[{ [a]: b } = [a = 0, 9] as const] : [readonly [0, 9]]
>{ [a]: b } = [a = 0, 9] as const : readonly [0, 9]
>{ [a]: b } : { 0: 0 | 1 | 9 | 8; }
>[a] : 0 | 1 | 9 | 8
>a : 0
>b : 0 | 1 | 9 | 8
>[a = 0, 9] as const : readonly [0, 9]
>[a = 0, 9] : readonly [0, 9]
>a = 0 : 0
>a : 0 | 1
>0 : 0
>9 : 9
>[[8, 9] as const] : [readonly [8, 9]]
>[8, 9] as const : readonly [8, 9]
>[8, 9] : readonly [8, 9]
>8 : 8
>9 : 9
const bb: 0 | 8 = b;
>bb : 0 | 8
>b : 0 | 8
}

View file

@ -0,0 +1,54 @@
=== tests/cases/conformance/controlFlow/controlFlowBindingPatternOrder.ts ===
// https://github.com/microsoft/TypeScript/pull/41094#issuecomment-716044363
{
let a: 0 | 1 = 0;
>a : Symbol(a, Decl(controlFlowBindingPatternOrder.ts, 2, 7))
const [{ [(a = 1)]: b } = [9, a] as const] = [];
>a : Symbol(a, Decl(controlFlowBindingPatternOrder.ts, 2, 7))
>b : Symbol(b, Decl(controlFlowBindingPatternOrder.ts, 3, 12))
>a : Symbol(a, Decl(controlFlowBindingPatternOrder.ts, 2, 7))
const bb: 0 = b;
>bb : Symbol(bb, Decl(controlFlowBindingPatternOrder.ts, 4, 9))
>b : Symbol(b, Decl(controlFlowBindingPatternOrder.ts, 3, 12))
}
{
let a: 0 | 1 = 1;
>a : Symbol(a, Decl(controlFlowBindingPatternOrder.ts, 7, 7))
const [{ [a]: b } = [9, a = 0] as const] = [];
>a : Symbol(a, Decl(controlFlowBindingPatternOrder.ts, 7, 7))
>b : Symbol(b, Decl(controlFlowBindingPatternOrder.ts, 8, 12))
>a : Symbol(a, Decl(controlFlowBindingPatternOrder.ts, 7, 7))
const bb: 9 = b;
>bb : Symbol(bb, Decl(controlFlowBindingPatternOrder.ts, 9, 9))
>b : Symbol(b, Decl(controlFlowBindingPatternOrder.ts, 8, 12))
}
{
let a: 0 | 1 = 0;
>a : Symbol(a, Decl(controlFlowBindingPatternOrder.ts, 12, 7))
const [{ [(a = 1)]: b } = [9, a] as const] = [[9, 8] 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 bb: 0 | 8 = b;
>bb : Symbol(bb, Decl(controlFlowBindingPatternOrder.ts, 14, 9))
>b : Symbol(b, Decl(controlFlowBindingPatternOrder.ts, 13, 12))
}
{
let a: 0 | 1 = 1;
>a : Symbol(a, Decl(controlFlowBindingPatternOrder.ts, 17, 7))
const [{ [a]: b } = [a = 0, 9] as const] = [[8, 9] 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))
const bb: 0 | 8 = b;
>bb : Symbol(bb, Decl(controlFlowBindingPatternOrder.ts, 19, 9))
>b : Symbol(b, Decl(controlFlowBindingPatternOrder.ts, 18, 12))
}

View file

@ -0,0 +1,92 @@
=== tests/cases/conformance/controlFlow/controlFlowBindingPatternOrder.ts ===
// https://github.com/microsoft/TypeScript/pull/41094#issuecomment-716044363
{
let a: 0 | 1 = 0;
>a : 0 | 1
>0 : 0
const [{ [(a = 1)]: b } = [9, a] as const] = [];
>(a = 1) : 1
>a = 1 : 1
>a : 0 | 1
>1 : 1
>b : 0
>[9, a] as const : readonly [9, 0]
>[9, a] : readonly [9, 0]
>9 : 9
>a : 0
>[] : []
const bb: 0 = b;
>bb : 0
>b : 0
}
{
let a: 0 | 1 = 1;
>a : 0 | 1
>1 : 1
const [{ [a]: b } = [9, a = 0] as const] = [];
>a : 0
>b : 9
>[9, a = 0] as const : readonly [9, 0]
>[9, a = 0] : readonly [9, 0]
>9 : 9
>a = 0 : 0
>a : 0 | 1
>0 : 0
>[] : []
const bb: 9 = b;
>bb : 9
>b : 9
}
{
let a: 0 | 1 = 0;
>a : 0 | 1
>0 : 0
const [{ [(a = 1)]: b } = [9, a] as const] = [[9, 8] as const];
>(a = 1) : 1
>a = 1 : 1
>a : 0 | 1
>1 : 1
>b : 0 | 8
>[9, a] as const : readonly [9, 0]
>[9, a] : readonly [9, 0]
>9 : 9
>a : 0
>[[9, 8] as const] : [readonly [9, 8]]
>[9, 8] as const : readonly [9, 8]
>[9, 8] : readonly [9, 8]
>9 : 9
>8 : 8
const bb: 0 | 8 = b;
>bb : 0 | 8
>b : 0 | 8
}
{
let a: 0 | 1 = 1;
>a : 0 | 1
>1 : 1
const [{ [a]: b } = [a = 0, 9] as const] = [[8, 9] as const];
>a : 0
>b : 0 | 8
>[a = 0, 9] as const : readonly [0, 9]
>[a = 0, 9] : readonly [0, 9]
>a = 0 : 0
>a : 0 | 1
>0 : 0
>9 : 9
>[[8, 9] as const] : [readonly [8, 9]]
>[8, 9] as const : readonly [8, 9]
>[8, 9] : readonly [8, 9]
>8 : 8
>9 : 9
const bb: 0 | 8 = b;
>bb : 0 | 8
>b : 0 | 8
}

View file

@ -0,0 +1,28 @@
// @target: esnext
// @noEmit: true
// https://github.com/microsoft/TypeScript/pull/41094#issuecomment-716044363
{
let a: 0 | 1 = 0;
let b: 0 | 1 | 9;
[{ [(a = 1)]: b } = [9, a] as const] = [];
const bb: 0 = b;
}
{
let a: 0 | 1 = 1;
let b: 0 | 1 | 9;
[{ [a]: b } = [9, a = 0] as const] = [];
const bb: 9 = b;
}
{
let a: 0 | 1 = 0;
let b: 0 | 1 | 8 | 9;
[{ [(a = 1)]: b } = [9, a] as const] = [[9, 8] as const];
const bb: 0 | 8 = b;
}
{
let a: 0 | 1 = 1;
let b: 0 | 1 | 8 | 9;
[{ [a]: b } = [a = 0, 9] as const] = [[8, 9] as const];
const bb: 0 | 8 = b;
}

View file

@ -0,0 +1,24 @@
// @target: esnext
// @noEmit: true
// 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;
}
{
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;
}