Skip to content

Commit 8becdf2

Browse files
authored
Fix emit for nested object rest in assignment pattern (#52922)
1 parent 718e63b commit 8becdf2

File tree

8 files changed

+112
-26
lines changed

8 files changed

+112
-26
lines changed

src/compiler/factory/nodeFactory.ts

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ import {
5858
ConstructorDeclaration,
5959
ConstructorTypeNode,
6060
ConstructSignatureDeclaration,
61+
containsObjectRestOrSpread,
6162
ContinueStatement,
6263
createBaseNodeFactory,
6364
createNodeConverters,
@@ -114,7 +115,6 @@ import {
114115
getAllUnscopedEmitHelpers,
115116
getBuildInfo,
116117
getCommentRange,
117-
getElementsOfBindingOrAssignmentPattern,
118118
getEmitFlags,
119119
getIdentifierTypeArguments,
120120
getJSDocTypeAliasName,
@@ -124,7 +124,6 @@ import {
124124
getSourceMapRange,
125125
getSyntheticLeadingComments,
126126
getSyntheticTrailingComments,
127-
getTargetOfBindingOrAssignmentElement,
128127
getTextOfIdentifierOrLiteral,
129128
hasInvalidEscape,
130129
HasModifiers,
@@ -150,7 +149,6 @@ import {
150149
isArray,
151150
isArrayLiteralExpression,
152151
isArrowFunction,
153-
isAssignmentPattern,
154152
isBinaryExpression,
155153
isCallChain,
156154
isClassDeclaration,
@@ -3326,24 +3324,7 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode
33263324
}
33273325

33283326
function propagateAssignmentPatternFlags(node: AssignmentPattern): TransformFlags {
3329-
if (node.transformFlags & TransformFlags.ContainsObjectRestOrSpread) return TransformFlags.ContainsObjectRestOrSpread;
3330-
if (node.transformFlags & TransformFlags.ContainsES2018) {
3331-
// check for nested spread assignments, otherwise '{ x: { a, ...b } = foo } = c'
3332-
// will not be correctly interpreted by the ES2018 transformer
3333-
for (const element of getElementsOfBindingOrAssignmentPattern(node)) {
3334-
const target = getTargetOfBindingOrAssignmentElement(element);
3335-
if (target && isAssignmentPattern(target)) {
3336-
if (target.transformFlags & TransformFlags.ContainsObjectRestOrSpread) {
3337-
return TransformFlags.ContainsObjectRestOrSpread;
3338-
}
3339-
if (target.transformFlags & TransformFlags.ContainsES2018) {
3340-
const flags = propagateAssignmentPatternFlags(target);
3341-
if (flags) return flags;
3342-
}
3343-
}
3344-
}
3345-
}
3346-
return TransformFlags.None;
3327+
return containsObjectRestOrSpread(node) ? TransformFlags.ContainsObjectRestOrSpread : TransformFlags.None;
33473328
}
33483329

33493330
// @api

src/compiler/factory/utilities.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
AssertionLevel,
88
AssignmentExpression,
99
AssignmentOperatorOrHigher,
10+
AssignmentPattern,
1011
BinaryExpression,
1112
BinaryOperator,
1213
BinaryOperatorToken,
@@ -76,6 +77,7 @@ import {
7677
InternalEmitFlags,
7778
isAssignmentExpression,
7879
isAssignmentOperator,
80+
isAssignmentPattern,
7981
isBlock,
8082
isCommaListExpression,
8183
isComputedPropertyName,
@@ -174,6 +176,7 @@ import {
174176
TextRange,
175177
ThisTypeNode,
176178
Token,
179+
TransformFlags,
177180
TypeNode,
178181
} from "../_namespaces/ts";
179182

@@ -1742,3 +1745,31 @@ export function flattenCommaList(node: Expression) {
17421745
flattenCommaListWorker(node, expressions);
17431746
return expressions;
17441747
}
1748+
1749+
/**
1750+
* Walk an AssignmentPattern to determine if it contains object rest (`...`) syntax. We cannot rely on
1751+
* propagation of `TransformFlags.ContainsObjectRestOrSpread` since it isn't propagated by default in
1752+
* ObjectLiteralExpression and ArrayLiteralExpression since we do not know whether they belong to an
1753+
* AssignmentPattern at the time the nodes are parsed.
1754+
*
1755+
* @internal
1756+
*/
1757+
export function containsObjectRestOrSpread(node: AssignmentPattern): boolean {
1758+
if (node.transformFlags & TransformFlags.ContainsObjectRestOrSpread) return true;
1759+
if (node.transformFlags & TransformFlags.ContainsES2018) {
1760+
// check for nested spread assignments, otherwise '{ x: { a, ...b } = foo } = c'
1761+
// will not be correctly interpreted by the ES2018 transformer
1762+
for (const element of getElementsOfBindingOrAssignmentPattern(node)) {
1763+
const target = getTargetOfBindingOrAssignmentElement(element);
1764+
if (target && isAssignmentPattern(target)) {
1765+
if (target.transformFlags & TransformFlags.ContainsObjectRestOrSpread) {
1766+
return true;
1767+
}
1768+
if (target.transformFlags & TransformFlags.ContainsES2018) {
1769+
if (containsObjectRestOrSpread(target)) return true;
1770+
}
1771+
}
1772+
}
1773+
}
1774+
return false;
1775+
}

src/compiler/transformers/es2018.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
concatenate,
2020
ConciseBody,
2121
ConstructorDeclaration,
22+
containsObjectRestOrSpread,
2223
createForOfBindingStatement,
2324
createSuperAccessVariableStatement,
2425
Debug,
@@ -577,7 +578,7 @@ export function transformES2018(context: TransformationContext): (x: SourceFile
577578
* expression of an `ExpressionStatement`).
578579
*/
579580
function visitBinaryExpression(node: BinaryExpression, expressionResultIsUnused: boolean): Expression {
580-
if (isDestructuringAssignment(node) && node.left.transformFlags & TransformFlags.ContainsObjectRestOrSpread) {
581+
if (isDestructuringAssignment(node) && containsObjectRestOrSpread(node.left)) {
581582
return flattenDestructuringAssignment(
582583
node,
583584
visitor,
@@ -703,7 +704,8 @@ export function transformES2018(context: TransformationContext): (x: SourceFile
703704
*/
704705
function visitForOfStatement(node: ForOfStatement, outermostLabeledStatement: LabeledStatement | undefined): VisitResult<Statement> {
705706
const ancestorFacts = enterSubtree(HierarchyFacts.IterationStatementExcludes, HierarchyFacts.IterationStatementIncludes);
706-
if (node.initializer.transformFlags & TransformFlags.ContainsObjectRestOrSpread) {
707+
if (node.initializer.transformFlags & TransformFlags.ContainsObjectRestOrSpread ||
708+
isAssignmentPattern(node.initializer) && containsObjectRestOrSpread(node.initializer)) {
707709
node = transformForOfStatementWithObjectRest(node);
708710
}
709711
const result = node.awaitModifier ?
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//// [nestedObjectRest.ts]
2+
// https://github.com/microsoft/TypeScript/issues/43400
3+
var x, y;
4+
5+
[{ ...x }] = [{ abc: 1 }];
6+
for ([{ ...y }] of [[{ abc: 1 }]]) ;
7+
8+
//// [nestedObjectRest.js]
9+
var __rest = (this && this.__rest) || function (s, e) {
10+
var t = {};
11+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
12+
t[p] = s[p];
13+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
14+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
15+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
16+
t[p[i]] = s[p[i]];
17+
}
18+
return t;
19+
};
20+
var _a, _b;
21+
// https://github.com/microsoft/TypeScript/issues/43400
22+
var x, y;
23+
[_a] = [{ abc: 1 }], x = __rest(_a, []);
24+
for (let _c of [[{ abc: 1 }]]) {
25+
[_b] = _c, y = __rest(_b, []);
26+
;
27+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
=== tests/cases/compiler/nestedObjectRest.ts ===
2+
// https://github.com/microsoft/TypeScript/issues/43400
3+
var x, y;
4+
>x : Symbol(x, Decl(nestedObjectRest.ts, 1, 3))
5+
>y : Symbol(y, Decl(nestedObjectRest.ts, 1, 6))
6+
7+
[{ ...x }] = [{ abc: 1 }];
8+
>x : Symbol(x, Decl(nestedObjectRest.ts, 1, 3))
9+
>abc : Symbol(abc, Decl(nestedObjectRest.ts, 3, 15))
10+
11+
for ([{ ...y }] of [[{ abc: 1 }]]) ;
12+
>y : Symbol(y, Decl(nestedObjectRest.ts, 1, 6))
13+
>abc : Symbol(abc, Decl(nestedObjectRest.ts, 4, 22))
14+
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
=== tests/cases/compiler/nestedObjectRest.ts ===
2+
// https://github.com/microsoft/TypeScript/issues/43400
3+
var x, y;
4+
>x : any
5+
>y : any
6+
7+
[{ ...x }] = [{ abc: 1 }];
8+
>[{ ...x }] = [{ abc: 1 }] : [{ abc: number; }]
9+
>[{ ...x }] : [any]
10+
>{ ...x } : any
11+
>x : any
12+
>[{ abc: 1 }] : [{ abc: number; }]
13+
>{ abc: 1 } : { abc: number; }
14+
>abc : number
15+
>1 : 1
16+
17+
for ([{ ...y }] of [[{ abc: 1 }]]) ;
18+
>[{ ...y }] : [any]
19+
>{ ...y } : any
20+
>y : any
21+
>[[{ abc: 1 }]] : { abc: number; }[][]
22+
>[{ abc: 1 }] : { abc: number; }[]
23+
>{ abc: 1 } : { abc: number; }
24+
>abc : number
25+
>1 : 1
26+

tests/baselines/reference/privateWriteOnlyAccessorRead.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,7 @@ class Test {
7272
(_a = this, { o: ({ set value(_e) { __classPrivateFieldSet(_a, _Test_instances, _e, "a", _Test_value_set); } }).value } = { o: { foo } }); //ok
7373
(_b = this, ({ set value(_e) { __classPrivateFieldSet(_b, _Test_instances, _e, "a", _Test_value_set); } }).value = __rest({ foo }, [])); //ok
7474
({ foo: __classPrivateFieldGet(this, _Test_instances, "a").foo } = { foo }); //error
75-
({
76-
foo: Object.assign({}, __classPrivateFieldGet(this, _Test_instances, "a").foo),
77-
} = { foo }); //error
75+
(__classPrivateFieldGet(this, _Test_instances, "a").foo = __rest({ foo }.foo, [])); //error
7876
let r = { o: __classPrivateFieldGet(this, _Test_instances, "a") }; //error
7977
_c = this, _d = this, [({ set value(_e) { __classPrivateFieldSet(_c, _Test_instances, _e, "a", _Test_valueOne_set); } }).value, ...({ set value(_e) { __classPrivateFieldSet(_d, _Test_instances, _e, "a", _Test_valueRest_set); } }).value] = [1, 2, 3];
8078
let arr = [
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// @target: es2017
2+
3+
// https://github.com/microsoft/TypeScript/issues/43400
4+
var x, y;
5+
6+
[{ ...x }] = [{ abc: 1 }];
7+
for ([{ ...y }] of [[{ abc: 1 }]]) ;

0 commit comments

Comments
 (0)