Fix destructuring assignment when assignment target is RHS

This commit is contained in:
Ron Buckton 2018-01-16 16:58:56 -08:00
parent cd525fb6de
commit 4aca0c8121
8 changed files with 119 additions and 6 deletions

View file

@ -70,7 +70,13 @@ namespace ts {
if (value) {
value = visitNode(value, visitor, isExpression);
if (needsValue) {
if (isIdentifier(value) && bindingOrAssignmentElementAssignsToName(node, value.escapedText)) {
// If the right-hand value of the assignment is also an assignment target then
// we need to cache the right-hand value.
value = ensureIdentifier(flattenContext, value, /*reuseIdentifierExpressions*/ false, location);
}
else if (needsValue) {
// If the right-hand value of the destructuring assignment needs to be preserved (as
// is the case when the destructuring assignment is part of a larger expression),
// then we need to cache the right-hand value.
@ -123,6 +129,27 @@ namespace ts {
}
}
function bindingOrAssignmentElementAssignsToName(element: BindingOrAssignmentElement, escapedName: __String): boolean {
const target = getTargetOfBindingOrAssignmentElement(element);
if (isBindingOrAssignmentPattern(target)) {
return bindingOrAssignmentPatternAssignsToName(target, escapedName);
}
else if (isIdentifier(target)) {
return target.escapedText === escapedName;
}
return false;
}
function bindingOrAssignmentPatternAssignsToName(pattern: BindingOrAssignmentPattern, escapedName: __String): boolean {
const elements = getElementsOfBindingOrAssignmentPattern(pattern);
for (const element of elements) {
if (bindingOrAssignmentElementAssignsToName(element, escapedName)) {
return true;
}
}
return false;
}
/**
* Flattens a VariableDeclaration or ParameterDeclaration to one or more variable declarations.
*
@ -157,6 +184,17 @@ namespace ts {
createArrayBindingOrAssignmentElement: makeBindingElement,
visitor
};
if (isVariableDeclaration(node)) {
let initializer = getInitializerOfBindingOrAssignmentElement(node);
if (initializer && isIdentifier(initializer) && bindingOrAssignmentElementAssignsToName(node, initializer.escapedText)) {
// If the right-hand value of the assignment is also an assignment target then
// we need to cache the right-hand value.
initializer = ensureIdentifier(flattenContext, initializer, /*reuseIdentifierExpressions*/ false, initializer);
node = updateVariableDeclaration(node, node.name, node.type, initializer);
}
}
flattenBindingOrAssignmentElement(flattenContext, node, rval, node, skipInitializer);
if (pendingExpressions) {
const temp = createTempVariable(/*recordTempVariable*/ undefined);

View file

@ -1467,7 +1467,7 @@ namespace ts {
| SpreadAssignment // AssignmentRestProperty
;
export type BindingOrAssignmentElementTarget = BindingOrAssignmentPattern | Expression;
export type BindingOrAssignmentElementTarget = BindingOrAssignmentPattern | Identifier | PropertyAccessExpression | ElementAccessExpression | OmittedExpression;
export type ObjectBindingOrAssignmentPattern
= ObjectBindingPattern

View file

@ -877,7 +877,7 @@ declare namespace ts {
type DestructuringAssignment = ObjectDestructuringAssignment | ArrayDestructuringAssignment;
type BindingOrAssignmentElement = VariableDeclaration | ParameterDeclaration | BindingElement | PropertyAssignment | ShorthandPropertyAssignment | SpreadAssignment | OmittedExpression | SpreadElement | ArrayLiteralExpression | ObjectLiteralExpression | AssignmentExpression<EqualsToken> | Identifier | PropertyAccessExpression | ElementAccessExpression;
type BindingOrAssignmentElementRestIndicator = DotDotDotToken | SpreadElement | SpreadAssignment;
type BindingOrAssignmentElementTarget = BindingOrAssignmentPattern | Expression;
type BindingOrAssignmentElementTarget = BindingOrAssignmentPattern | Identifier | PropertyAccessExpression | ElementAccessExpression | OmittedExpression;
type ObjectBindingOrAssignmentPattern = ObjectBindingPattern | ObjectLiteralExpression;
type ArrayBindingOrAssignmentPattern = ArrayBindingPattern | ArrayLiteralExpression;
type AssignmentPattern = ObjectLiteralExpression | ArrayLiteralExpression;

View file

@ -0,0 +1,18 @@
//// [destructuringReassignsRightHandSide.ts]
var foo: any = { foo: 1, bar: 2 };
var bar: any;
// reassignment in destructuring pattern
({ foo, bar } = foo);
// reassignment in subsequent var
var { foo, baz } = foo;
//// [destructuringReassignsRightHandSide.js]
var foo = { foo: 1, bar: 2 };
var bar;
// reassignment in destructuring pattern
(_a = foo, foo = _a.foo, bar = _a.bar);
// reassignment in subsequent var
var _b = foo, foo = _b.foo, baz = _b.baz;
var _a;

View file

@ -0,0 +1,21 @@
=== tests/cases/conformance/es6/destructuring/destructuringReassignsRightHandSide.ts ===
var foo: any = { foo: 1, bar: 2 };
>foo : Symbol(foo, Decl(destructuringReassignsRightHandSide.ts, 0, 3), Decl(destructuringReassignsRightHandSide.ts, 7, 5))
>foo : Symbol(foo, Decl(destructuringReassignsRightHandSide.ts, 0, 16))
>bar : Symbol(bar, Decl(destructuringReassignsRightHandSide.ts, 0, 24))
var bar: any;
>bar : Symbol(bar, Decl(destructuringReassignsRightHandSide.ts, 1, 3))
// reassignment in destructuring pattern
({ foo, bar } = foo);
>foo : Symbol(foo, Decl(destructuringReassignsRightHandSide.ts, 4, 2))
>bar : Symbol(bar, Decl(destructuringReassignsRightHandSide.ts, 4, 7))
>foo : Symbol(foo, Decl(destructuringReassignsRightHandSide.ts, 0, 3), Decl(destructuringReassignsRightHandSide.ts, 7, 5))
// reassignment in subsequent var
var { foo, baz } = foo;
>foo : Symbol(foo, Decl(destructuringReassignsRightHandSide.ts, 0, 3), Decl(destructuringReassignsRightHandSide.ts, 7, 5))
>baz : Symbol(baz, Decl(destructuringReassignsRightHandSide.ts, 7, 10))
>foo : Symbol(foo, Decl(destructuringReassignsRightHandSide.ts, 0, 3), Decl(destructuringReassignsRightHandSide.ts, 7, 5))

View file

@ -0,0 +1,27 @@
=== tests/cases/conformance/es6/destructuring/destructuringReassignsRightHandSide.ts ===
var foo: any = { foo: 1, bar: 2 };
>foo : any
>{ foo: 1, bar: 2 } : { foo: number; bar: number; }
>foo : number
>1 : 1
>bar : number
>2 : 2
var bar: any;
>bar : any
// reassignment in destructuring pattern
({ foo, bar } = foo);
>({ foo, bar } = foo) : any
>{ foo, bar } = foo : any
>{ foo, bar } : { foo: any; bar: any; }
>foo : any
>bar : any
>foo : any
// reassignment in subsequent var
var { foo, baz } = foo;
>foo : any
>baz : any
>foo : any

View file

@ -85,10 +85,10 @@ var i = removable;
var { removed } = i, removableRest2 = __rest(i, ["removed"]);
let computed = 'b';
let computed2 = 'a';
var _g = computed, stillNotGreat = o[_g], _h = computed2, soSo = o[_h], o = __rest(o, [typeof _g === "symbol" ? _g : _g + "", typeof _h === "symbol" ? _h : _h + ""]);
(_j = computed, stillNotGreat = o[_j], _k = computed2, soSo = o[_k], o = __rest(o, [typeof _j === "symbol" ? _j : _j + "", typeof _k === "symbol" ? _k : _k + ""]));
var _g = o, _h = computed, stillNotGreat = _g[_h], _j = computed2, soSo = _g[_j], o = __rest(_g, [typeof _h === "symbol" ? _h : _h + "", typeof _j === "symbol" ? _j : _j + ""]);
(_k = o, _l = computed, stillNotGreat = _k[_l], _m = computed2, soSo = _k[_m], o = __rest(_k, [typeof _l === "symbol" ? _l : _l + "", typeof _m === "symbol" ? _m : _m + ""]));
var noContextualType = (_a) => {
var { aNumber = 12 } = _a, notEmptyObject = __rest(_a, ["aNumber"]);
return aNumber + notEmptyObject.anythingGoes;
};
var _d, _f, _j, _k;
var _d, _f, _k, _l, _m;

View file

@ -0,0 +1,9 @@
// @target: es5
var foo: any = { foo: 1, bar: 2 };
var bar: any;
// reassignment in destructuring pattern
({ foo, bar } = foo);
// reassignment in subsequent var
var { foo, baz } = foo;