Skip to content

Commit 96a8158

Browse files
Merge pull request #4657 from Microsoft/emptyVariableDeclarationBindingPattern
Fix emitted code for variable statement with no bound variables
2 parents 3d8c336 + fb889be commit 96a8158

File tree

63 files changed

+993
-86
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+993
-86
lines changed

src/compiler/emitter.ts

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3210,22 +3210,32 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
32103210
}
32113211
}
32123212

3213-
function ensureIdentifier(expr: Expression): Expression {
3214-
if (expr.kind !== SyntaxKind.Identifier) {
3215-
let identifier = createTempVariable(TempFlags.Auto);
3216-
if (!canDefineTempVariablesInPlace) {
3217-
recordTempDeclaration(identifier);
3218-
}
3219-
emitAssignment(identifier, expr);
3220-
expr = identifier;
3213+
/**
3214+
* Ensures that there exists a declared identifier whose value holds the given expression.
3215+
* This function is useful to ensure that the expression's value can be read from in subsequent expressions.
3216+
* Unless 'reuseIdentifierExpressions' is false, 'expr' will be returned if it is just an identifier.
3217+
*
3218+
* @param expr the expression whose value needs to be bound.
3219+
* @param reuseIdentifierExpressions true if identifier expressions can simply be returned;
3220+
* false if it is necessary to always emit an identifier.
3221+
*/
3222+
function ensureIdentifier(expr: Expression, reuseIdentifierExpressions: boolean): Expression {
3223+
if (expr.kind === SyntaxKind.Identifier && reuseIdentifierExpressions) {
3224+
return expr;
3225+
}
3226+
3227+
let identifier = createTempVariable(TempFlags.Auto);
3228+
if (!canDefineTempVariablesInPlace) {
3229+
recordTempDeclaration(identifier);
32213230
}
3222-
return expr;
3231+
emitAssignment(identifier, expr);
3232+
return identifier;
32233233
}
32243234

32253235
function createDefaultValueCheck(value: Expression, defaultValue: Expression): Expression {
32263236
// The value expression will be evaluated twice, so for anything but a simple identifier
32273237
// we need to generate a temporary variable
3228-
value = ensureIdentifier(value);
3238+
value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true);
32293239
// Return the expression 'value === void 0 ? defaultValue : value'
32303240
let equals = <BinaryExpression>createSynthesizedNode(SyntaxKind.BinaryExpression);
32313241
equals.left = value;
@@ -3276,7 +3286,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
32763286
if (properties.length !== 1) {
32773287
// For anything but a single element destructuring we need to generate a temporary
32783288
// to ensure value is evaluated exactly once.
3279-
value = ensureIdentifier(value);
3289+
value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true);
32803290
}
32813291
for (let p of properties) {
32823292
if (p.kind === SyntaxKind.PropertyAssignment || p.kind === SyntaxKind.ShorthandPropertyAssignment) {
@@ -3291,7 +3301,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
32913301
if (elements.length !== 1) {
32923302
// For anything but a single element destructuring we need to generate a temporary
32933303
// to ensure value is evaluated exactly once.
3294-
value = ensureIdentifier(value);
3304+
value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true);
32953305
}
32963306
for (let i = 0; i < elements.length; i++) {
32973307
let e = elements[i];
@@ -3336,7 +3346,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
33363346
if (root.parent.kind !== SyntaxKind.ParenthesizedExpression) {
33373347
write("(");
33383348
}
3339-
value = ensureIdentifier(value);
3349+
value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true);
33403350
emitDestructuringAssignment(target, value);
33413351
write(", ");
33423352
emit(value);
@@ -3346,7 +3356,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
33463356
}
33473357
}
33483358

3349-
function emitBindingElement(target: BindingElement, value: Expression) {
3359+
function emitBindingElement(target: BindingElement | VariableDeclaration, value: Expression) {
33503360
if (target.initializer) {
33513361
// Combine value and initializer
33523362
value = value ? createDefaultValueCheck(value, target.initializer) : target.initializer;
@@ -3356,14 +3366,19 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
33563366
value = createVoidZero();
33573367
}
33583368
if (isBindingPattern(target.name)) {
3359-
let pattern = <BindingPattern>target.name;
3360-
let elements = pattern.elements;
3361-
if (elements.length !== 1) {
3362-
// For anything but a single element destructuring we need to generate a temporary
3363-
// to ensure value is evaluated exactly once.
3364-
value = ensureIdentifier(value);
3365-
}
3366-
for (let i = 0; i < elements.length; i++) {
3369+
const pattern = <BindingPattern>target.name;
3370+
const elements = pattern.elements;
3371+
const numElements = elements.length;
3372+
3373+
if (numElements !== 1) {
3374+
// For anything other than a single-element destructuring we need to generate a temporary
3375+
// to ensure value is evaluated exactly once. Additionally, if we have zero elements
3376+
// we need to emit *something* to ensure that in case a 'var' keyword was already emitted,
3377+
// so in that case, we'll intentionally create that temporary.
3378+
value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ numElements !== 0);
3379+
}
3380+
3381+
for (let i = 0; i < numElements; i++) {
33673382
let element = elements[i];
33683383
if (pattern.kind === SyntaxKind.ObjectBindingPattern) {
33693384
// Rewrite element to a declaration with an initializer that fetches property
@@ -3375,7 +3390,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
33753390
// Rewrite element to a declaration that accesses array element at index i
33763391
emitBindingElement(element, createElementAccessExpression(value, createNumericLiteral(i)));
33773392
}
3378-
else if (i === elements.length - 1) {
3393+
else if (i === numElements - 1) {
33793394
emitBindingElement(element, createSliceCall(value, i));
33803395
}
33813396
}
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
//// [emptyArrayBindingPatternParameter01.ts]
22

3-
43
function f([]) {
54
var x, y, z;
65
}
@@ -9,3 +8,7 @@ function f([]) {
98
function f(_a) {
109
var x, y, z;
1110
}
11+
12+
13+
//// [emptyArrayBindingPatternParameter01.d.ts]
14+
declare function f([]: any[]): void;
Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
=== tests/cases/conformance/es6/destructuring/emptyArrayBindingPatternParameter01.ts ===
22

3-
43
function f([]) {
54
>f : Symbol(f, Decl(emptyArrayBindingPatternParameter01.ts, 0, 0))
65

76
var x, y, z;
8-
>x : Symbol(x, Decl(emptyArrayBindingPatternParameter01.ts, 3, 7))
9-
>y : Symbol(y, Decl(emptyArrayBindingPatternParameter01.ts, 3, 10))
10-
>z : Symbol(z, Decl(emptyArrayBindingPatternParameter01.ts, 3, 13))
7+
>x : Symbol(x, Decl(emptyArrayBindingPatternParameter01.ts, 2, 7))
8+
>y : Symbol(y, Decl(emptyArrayBindingPatternParameter01.ts, 2, 10))
9+
>z : Symbol(z, Decl(emptyArrayBindingPatternParameter01.ts, 2, 13))
1110
}

tests/baselines/reference/emptyArrayBindingPatternParameter01.types

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
=== tests/cases/conformance/es6/destructuring/emptyArrayBindingPatternParameter01.ts ===
22

3-
43
function f([]) {
54
>f : ([]: any[]) => void
65

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
//// [emptyArrayBindingPatternParameter02.ts]
22

3-
43
function f(a, []) {
54
var x, y, z;
65
}
@@ -9,3 +8,7 @@ function f(a, []) {
98
function f(a, _a) {
109
var x, y, z;
1110
}
11+
12+
13+
//// [emptyArrayBindingPatternParameter02.d.ts]
14+
declare function f(a: any, []: any[]): void;
Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
=== tests/cases/conformance/es6/destructuring/emptyArrayBindingPatternParameter02.ts ===
22

3-
43
function f(a, []) {
54
>f : Symbol(f, Decl(emptyArrayBindingPatternParameter02.ts, 0, 0))
6-
>a : Symbol(a, Decl(emptyArrayBindingPatternParameter02.ts, 2, 11))
5+
>a : Symbol(a, Decl(emptyArrayBindingPatternParameter02.ts, 1, 11))
76

87
var x, y, z;
9-
>x : Symbol(x, Decl(emptyArrayBindingPatternParameter02.ts, 3, 7))
10-
>y : Symbol(y, Decl(emptyArrayBindingPatternParameter02.ts, 3, 10))
11-
>z : Symbol(z, Decl(emptyArrayBindingPatternParameter02.ts, 3, 13))
8+
>x : Symbol(x, Decl(emptyArrayBindingPatternParameter02.ts, 2, 7))
9+
>y : Symbol(y, Decl(emptyArrayBindingPatternParameter02.ts, 2, 10))
10+
>z : Symbol(z, Decl(emptyArrayBindingPatternParameter02.ts, 2, 13))
1211
}

tests/baselines/reference/emptyArrayBindingPatternParameter02.types

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
=== tests/cases/conformance/es6/destructuring/emptyArrayBindingPatternParameter02.ts ===
22

3-
43
function f(a, []) {
54
>f : (a: any, []: any[]) => void
65
>a : any
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
//// [emptyArrayBindingPatternParameter03.ts]
22

3-
43
function f(a, []) {
54
var x, y, z;
65
}
@@ -9,3 +8,7 @@ function f(a, []) {
98
function f(a, _a) {
109
var x, y, z;
1110
}
11+
12+
13+
//// [emptyArrayBindingPatternParameter03.d.ts]
14+
declare function f(a: any, []: any[]): void;
Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
=== tests/cases/conformance/es6/destructuring/emptyArrayBindingPatternParameter03.ts ===
22

3-
43
function f(a, []) {
54
>f : Symbol(f, Decl(emptyArrayBindingPatternParameter03.ts, 0, 0))
6-
>a : Symbol(a, Decl(emptyArrayBindingPatternParameter03.ts, 2, 11))
5+
>a : Symbol(a, Decl(emptyArrayBindingPatternParameter03.ts, 1, 11))
76

87
var x, y, z;
9-
>x : Symbol(x, Decl(emptyArrayBindingPatternParameter03.ts, 3, 7))
10-
>y : Symbol(y, Decl(emptyArrayBindingPatternParameter03.ts, 3, 10))
11-
>z : Symbol(z, Decl(emptyArrayBindingPatternParameter03.ts, 3, 13))
8+
>x : Symbol(x, Decl(emptyArrayBindingPatternParameter03.ts, 2, 7))
9+
>y : Symbol(y, Decl(emptyArrayBindingPatternParameter03.ts, 2, 10))
10+
>z : Symbol(z, Decl(emptyArrayBindingPatternParameter03.ts, 2, 13))
1211
}

tests/baselines/reference/emptyArrayBindingPatternParameter03.types

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
=== tests/cases/conformance/es6/destructuring/emptyArrayBindingPatternParameter03.ts ===
22

3-
43
function f(a, []) {
54
>f : (a: any, []: any[]) => void
65
>a : any

0 commit comments

Comments
 (0)