Skip to content

Commit 39e0cc6

Browse files
yuitweswigham
authored andcommitted
Fix 16628: "undefined" exception when name of binding element in binding pattern is empty (microsoft#17132)
* Handle the case where binding pattern name element is empty * Update tests and baselines * Feedback from PR * Handle empty binding patterns more generally in emitter * Dont simply handling fo empty binding patterns and stay spec compliant * PR feedback
1 parent f124e19 commit 39e0cc6

12 files changed

+67
-7
lines changed

src/compiler/declarationEmitter.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1367,6 +1367,10 @@ namespace ts {
13671367
}
13681368

13691369
function writeVariableStatement(node: VariableStatement) {
1370+
// If binding pattern doesn't have name, then there is nothing to be emitted for declaration file i.e. const [,] = [1,2].
1371+
if (every(node.declarationList && node.declarationList.declarations, decl => decl.name && isEmptyBindingPattern(decl.name))) {
1372+
return;
1373+
}
13701374
emitJsDocComments(node);
13711375
emitModuleElementDeclarationFlags(node);
13721376
if (isLet(node.declarationList)) {

src/compiler/transformers/destructuring.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,11 +331,14 @@ namespace ts {
331331
location
332332
);
333333
}
334-
else if (numElements !== 1 && (flattenContext.level < FlattenLevel.ObjectRest || numElements === 0)) {
334+
else if (numElements !== 1 && (flattenContext.level < FlattenLevel.ObjectRest || numElements === 0)
335+
|| every(elements, isOmittedExpression)) {
335336
// For anything other than a single-element destructuring we need to generate a temporary
336337
// to ensure value is evaluated exactly once. Additionally, if we have zero elements
337338
// we need to emit *something* to ensure that in case a 'var' keyword was already emitted,
338339
// so in that case, we'll intentionally create that temporary.
340+
// Or all the elements of the binding pattern are omitted expression such as "var [,] = [1,2]",
341+
// then we will create temporary variable.
339342
const reuseIdentifierExpressions = !isDeclarationBindingElement(parent) || numElements !== 0;
340343
value = ensureIdentifier(flattenContext, value, reuseIdentifierExpressions, location);
341344
}

src/compiler/transformers/es2015.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2105,13 +2105,14 @@ namespace ts {
21052105
setCommentRange(declarationList, node);
21062106

21072107
if (node.transformFlags & TransformFlags.ContainsBindingPattern
2108-
&& (isBindingPattern(node.declarations[0].name)
2109-
|| isBindingPattern(lastOrUndefined(node.declarations).name))) {
2108+
&& (isBindingPattern(node.declarations[0].name) || isBindingPattern(lastOrUndefined(node.declarations).name))) {
21102109
// If the first or last declaration is a binding pattern, we need to modify
21112110
// the source map range for the declaration list.
21122111
const firstDeclaration = firstOrUndefined(declarations);
2113-
const lastDeclaration = lastOrUndefined(declarations);
2114-
setSourceMapRange(declarationList, createRange(firstDeclaration.pos, lastDeclaration.end));
2112+
if (firstDeclaration) {
2113+
const lastDeclaration = lastOrUndefined(declarations);
2114+
setSourceMapRange(declarationList, createRange(firstDeclaration.pos, lastDeclaration.end));
2115+
}
21152116
}
21162117

21172118
return declarationList;

src/compiler/utilities.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3856,6 +3856,20 @@ namespace ts {
38563856
return hasModifier(node, ModifierFlags.ParameterPropertyModifier) && node.parent.kind === SyntaxKind.Constructor && isClassLike(node.parent.parent);
38573857
}
38583858

3859+
export function isEmptyBindingPattern(node: BindingName): node is BindingPattern {
3860+
if (isBindingPattern(node)) {
3861+
return every(node.elements, isEmptyBindingElement);
3862+
}
3863+
return false;
3864+
}
3865+
3866+
export function isEmptyBindingElement(node: BindingElement): boolean {
3867+
if (isOmittedExpression(node)) {
3868+
return true;
3869+
}
3870+
return isEmptyBindingPattern(node.name);
3871+
}
3872+
38593873
function walkUpBindingElementsAndPatterns(node: Node): Node {
38603874
while (node && (node.kind === SyntaxKind.BindingElement || isBindingPattern(node))) {
38613875
node = node.parent;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
//// [bindingPatternOmittedExpressionNesting.ts]
2+
export let [,,[,[],,[],]] = undefined as any;
3+
4+
//// [bindingPatternOmittedExpressionNesting.js]
5+
"use strict";
6+
exports.__esModule = true;
7+
exports._a = (_b = undefined, _c = _b[2], _d = _c[1], _e = _c[3]);
8+
var _b, _c, _d, _e;
9+
10+
11+
//// [bindingPatternOmittedExpressionNesting.d.ts]
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
=== tests/cases/compiler/bindingPatternOmittedExpressionNesting.ts ===
2+
export let [,,[,[],,[],]] = undefined as any;
3+
>undefined : Symbol(undefined)
4+
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
=== tests/cases/compiler/bindingPatternOmittedExpressionNesting.ts ===
2+
export let [,,[,[],,[],]] = undefined as any;
3+
> : undefined
4+
> : undefined
5+
> : undefined
6+
> : undefined
7+
>undefined as any : any
8+
>undefined : undefined
9+

tests/baselines/reference/emptyAssignmentPatterns01_ES5.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@
22
var a: any;
33

44
({} = a);
5-
([] = a);
5+
([] = a);
6+
7+
var [,] = [1,2];
68

79
//// [emptyAssignmentPatterns01_ES5.js]
810
var a;
911
(a);
1012
(a);
13+
var _a = [1, 2];
1114

1215

1316
//// [emptyAssignmentPatterns01_ES5.d.ts]

tests/baselines/reference/emptyAssignmentPatterns01_ES5.symbols

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ var a: any;
88
([] = a);
99
>a : Symbol(a, Decl(emptyAssignmentPatterns01_ES5.ts, 0, 3))
1010

11+
var [,] = [1,2];

tests/baselines/reference/emptyAssignmentPatterns01_ES5.types

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,9 @@ var a: any;
1414
>[] : undefined[]
1515
>a : any
1616

17+
var [,] = [1,2];
18+
> : undefined
19+
>[1,2] : [number, number]
20+
>1 : 1
21+
>2 : 2
22+

0 commit comments

Comments
 (0)