Fix emit for nested object rest in assignment pattern (#52922)

This commit is contained in:
Ron Buckton 2023-02-22 20:41:09 -05:00 committed by GitHub
parent 718e63b9c9
commit 8becdf2b40
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 112 additions and 26 deletions

View File

@ -58,6 +58,7 @@ import {
ConstructorDeclaration,
ConstructorTypeNode,
ConstructSignatureDeclaration,
containsObjectRestOrSpread,
ContinueStatement,
createBaseNodeFactory,
createNodeConverters,
@ -114,7 +115,6 @@ import {
getAllUnscopedEmitHelpers,
getBuildInfo,
getCommentRange,
getElementsOfBindingOrAssignmentPattern,
getEmitFlags,
getIdentifierTypeArguments,
getJSDocTypeAliasName,
@ -124,7 +124,6 @@ import {
getSourceMapRange,
getSyntheticLeadingComments,
getSyntheticTrailingComments,
getTargetOfBindingOrAssignmentElement,
getTextOfIdentifierOrLiteral,
hasInvalidEscape,
HasModifiers,
@ -150,7 +149,6 @@ import {
isArray,
isArrayLiteralExpression,
isArrowFunction,
isAssignmentPattern,
isBinaryExpression,
isCallChain,
isClassDeclaration,
@ -3326,24 +3324,7 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode
}
function propagateAssignmentPatternFlags(node: AssignmentPattern): TransformFlags {
if (node.transformFlags & TransformFlags.ContainsObjectRestOrSpread) return TransformFlags.ContainsObjectRestOrSpread;
if (node.transformFlags & TransformFlags.ContainsES2018) {
// check for nested spread assignments, otherwise '{ x: { a, ...b } = foo } = c'
// will not be correctly interpreted by the ES2018 transformer
for (const element of getElementsOfBindingOrAssignmentPattern(node)) {
const target = getTargetOfBindingOrAssignmentElement(element);
if (target && isAssignmentPattern(target)) {
if (target.transformFlags & TransformFlags.ContainsObjectRestOrSpread) {
return TransformFlags.ContainsObjectRestOrSpread;
}
if (target.transformFlags & TransformFlags.ContainsES2018) {
const flags = propagateAssignmentPatternFlags(target);
if (flags) return flags;
}
}
}
}
return TransformFlags.None;
return containsObjectRestOrSpread(node) ? TransformFlags.ContainsObjectRestOrSpread : TransformFlags.None;
}
// @api

View File

@ -7,6 +7,7 @@ import {
AssertionLevel,
AssignmentExpression,
AssignmentOperatorOrHigher,
AssignmentPattern,
BinaryExpression,
BinaryOperator,
BinaryOperatorToken,
@ -76,6 +77,7 @@ import {
InternalEmitFlags,
isAssignmentExpression,
isAssignmentOperator,
isAssignmentPattern,
isBlock,
isCommaListExpression,
isComputedPropertyName,
@ -174,6 +176,7 @@ import {
TextRange,
ThisTypeNode,
Token,
TransformFlags,
TypeNode,
} from "../_namespaces/ts";
@ -1742,3 +1745,31 @@ export function flattenCommaList(node: Expression) {
flattenCommaListWorker(node, expressions);
return expressions;
}
/**
* Walk an AssignmentPattern to determine if it contains object rest (`...`) syntax. We cannot rely on
* propagation of `TransformFlags.ContainsObjectRestOrSpread` since it isn't propagated by default in
* ObjectLiteralExpression and ArrayLiteralExpression since we do not know whether they belong to an
* AssignmentPattern at the time the nodes are parsed.
*
* @internal
*/
export function containsObjectRestOrSpread(node: AssignmentPattern): boolean {
if (node.transformFlags & TransformFlags.ContainsObjectRestOrSpread) return true;
if (node.transformFlags & TransformFlags.ContainsES2018) {
// check for nested spread assignments, otherwise '{ x: { a, ...b } = foo } = c'
// will not be correctly interpreted by the ES2018 transformer
for (const element of getElementsOfBindingOrAssignmentPattern(node)) {
const target = getTargetOfBindingOrAssignmentElement(element);
if (target && isAssignmentPattern(target)) {
if (target.transformFlags & TransformFlags.ContainsObjectRestOrSpread) {
return true;
}
if (target.transformFlags & TransformFlags.ContainsES2018) {
if (containsObjectRestOrSpread(target)) return true;
}
}
}
}
return false;
}

View File

@ -19,6 +19,7 @@ import {
concatenate,
ConciseBody,
ConstructorDeclaration,
containsObjectRestOrSpread,
createForOfBindingStatement,
createSuperAccessVariableStatement,
Debug,
@ -577,7 +578,7 @@ export function transformES2018(context: TransformationContext): (x: SourceFile
* expression of an `ExpressionStatement`).
*/
function visitBinaryExpression(node: BinaryExpression, expressionResultIsUnused: boolean): Expression {
if (isDestructuringAssignment(node) && node.left.transformFlags & TransformFlags.ContainsObjectRestOrSpread) {
if (isDestructuringAssignment(node) && containsObjectRestOrSpread(node.left)) {
return flattenDestructuringAssignment(
node,
visitor,
@ -703,7 +704,8 @@ export function transformES2018(context: TransformationContext): (x: SourceFile
*/
function visitForOfStatement(node: ForOfStatement, outermostLabeledStatement: LabeledStatement | undefined): VisitResult<Statement> {
const ancestorFacts = enterSubtree(HierarchyFacts.IterationStatementExcludes, HierarchyFacts.IterationStatementIncludes);
if (node.initializer.transformFlags & TransformFlags.ContainsObjectRestOrSpread) {
if (node.initializer.transformFlags & TransformFlags.ContainsObjectRestOrSpread ||
isAssignmentPattern(node.initializer) && containsObjectRestOrSpread(node.initializer)) {
node = transformForOfStatementWithObjectRest(node);
}
const result = node.awaitModifier ?

View File

@ -0,0 +1,27 @@
//// [nestedObjectRest.ts]
// https://github.com/microsoft/TypeScript/issues/43400
var x, y;
[{ ...x }] = [{ abc: 1 }];
for ([{ ...y }] of [[{ abc: 1 }]]) ;
//// [nestedObjectRest.js]
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
var _a, _b;
// https://github.com/microsoft/TypeScript/issues/43400
var x, y;
[_a] = [{ abc: 1 }], x = __rest(_a, []);
for (let _c of [[{ abc: 1 }]]) {
[_b] = _c, y = __rest(_b, []);
;
}

View File

@ -0,0 +1,14 @@
=== tests/cases/compiler/nestedObjectRest.ts ===
// https://github.com/microsoft/TypeScript/issues/43400
var x, y;
>x : Symbol(x, Decl(nestedObjectRest.ts, 1, 3))
>y : Symbol(y, Decl(nestedObjectRest.ts, 1, 6))
[{ ...x }] = [{ abc: 1 }];
>x : Symbol(x, Decl(nestedObjectRest.ts, 1, 3))
>abc : Symbol(abc, Decl(nestedObjectRest.ts, 3, 15))
for ([{ ...y }] of [[{ abc: 1 }]]) ;
>y : Symbol(y, Decl(nestedObjectRest.ts, 1, 6))
>abc : Symbol(abc, Decl(nestedObjectRest.ts, 4, 22))

View File

@ -0,0 +1,26 @@
=== tests/cases/compiler/nestedObjectRest.ts ===
// https://github.com/microsoft/TypeScript/issues/43400
var x, y;
>x : any
>y : any
[{ ...x }] = [{ abc: 1 }];
>[{ ...x }] = [{ abc: 1 }] : [{ abc: number; }]
>[{ ...x }] : [any]
>{ ...x } : any
>x : any
>[{ abc: 1 }] : [{ abc: number; }]
>{ abc: 1 } : { abc: number; }
>abc : number
>1 : 1
for ([{ ...y }] of [[{ abc: 1 }]]) ;
>[{ ...y }] : [any]
>{ ...y } : any
>y : any
>[[{ abc: 1 }]] : { abc: number; }[][]
>[{ abc: 1 }] : { abc: number; }[]
>{ abc: 1 } : { abc: number; }
>abc : number
>1 : 1

View File

@ -72,9 +72,7 @@ class Test {
(_a = this, { o: ({ set value(_e) { __classPrivateFieldSet(_a, _Test_instances, _e, "a", _Test_value_set); } }).value } = { o: { foo } }); //ok
(_b = this, ({ set value(_e) { __classPrivateFieldSet(_b, _Test_instances, _e, "a", _Test_value_set); } }).value = __rest({ foo }, [])); //ok
({ foo: __classPrivateFieldGet(this, _Test_instances, "a").foo } = { foo }); //error
({
foo: Object.assign({}, __classPrivateFieldGet(this, _Test_instances, "a").foo),
} = { foo }); //error
(__classPrivateFieldGet(this, _Test_instances, "a").foo = __rest({ foo }.foo, [])); //error
let r = { o: __classPrivateFieldGet(this, _Test_instances, "a") }; //error
_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];
let arr = [

View File

@ -0,0 +1,7 @@
// @target: es2017
// https://github.com/microsoft/TypeScript/issues/43400
var x, y;
[{ ...x }] = [{ abc: 1 }];
for ([{ ...y }] of [[{ abc: 1 }]]) ;