Transform param patterns/initializers after object rest (#47095)

This commit is contained in:
Ron Buckton 2022-02-01 11:46:29 -08:00 committed by GitHub
parent 1ebdcc6fb8
commit 63d9d4c8bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 179 additions and 19 deletions

View File

@ -59,6 +59,7 @@ namespace ts {
let exportedVariableStatement = false;
let enabledSubstitutions: ESNextSubstitutionFlags;
let enclosingFunctionFlags: FunctionFlags;
let parametersWithPrecedingObjectRestOrSpread: Set<ParameterDeclaration> | undefined;
let enclosingSuperContainerFlags: NodeCheckFlags = 0;
let hierarchyFacts: HierarchyFacts = 0;
@ -785,7 +786,24 @@ namespace ts {
);
}
function parameterVisitor(node: Node) {
Debug.assertNode(node, isParameter);
return visitParameter(node);
}
function visitParameter(node: ParameterDeclaration): ParameterDeclaration {
if (parametersWithPrecedingObjectRestOrSpread?.has(node)) {
return factory.updateParameterDeclaration(
node,
/*decorators*/ undefined,
/*modifiers*/ undefined,
node.dotDotDotToken,
isBindingPattern(node.name) ? factory.getGeneratedNameForNode(node) : node.name,
/*questionToken*/ undefined,
/*type*/ undefined,
/*initializer*/ undefined
);
}
if (node.transformFlags & TransformFlags.ContainsObjectRestOrSpread) {
// Binding patterns are converted into a generated name and are
// evaluated inside the function body.
@ -803,54 +821,78 @@ namespace ts {
return visitEachChild(node, visitor, context);
}
function collectParametersWithPrecedingObjectRestOrSpread(node: SignatureDeclaration) {
let parameters: Set<ParameterDeclaration> | undefined;
for (const parameter of node.parameters) {
if (parameters) {
parameters.add(parameter);
}
else if (parameter.transformFlags & TransformFlags.ContainsObjectRestOrSpread) {
parameters = new Set();
}
}
return parameters;
}
function visitConstructorDeclaration(node: ConstructorDeclaration) {
const savedEnclosingFunctionFlags = enclosingFunctionFlags;
enclosingFunctionFlags = FunctionFlags.Normal;
const savedParametersWithPrecedingObjectRestOrSpread = parametersWithPrecedingObjectRestOrSpread;
enclosingFunctionFlags = getFunctionFlags(node);
parametersWithPrecedingObjectRestOrSpread = collectParametersWithPrecedingObjectRestOrSpread(node);
const updated = factory.updateConstructorDeclaration(
node,
/*decorators*/ undefined,
node.modifiers,
visitParameterList(node.parameters, visitor, context),
visitParameterList(node.parameters, parameterVisitor, context),
transformFunctionBody(node)
);
enclosingFunctionFlags = savedEnclosingFunctionFlags;
parametersWithPrecedingObjectRestOrSpread = savedParametersWithPrecedingObjectRestOrSpread;
return updated;
}
function visitGetAccessorDeclaration(node: GetAccessorDeclaration) {
const savedEnclosingFunctionFlags = enclosingFunctionFlags;
enclosingFunctionFlags = FunctionFlags.Normal;
const savedParametersWithPrecedingObjectRestOrSpread = parametersWithPrecedingObjectRestOrSpread;
enclosingFunctionFlags = getFunctionFlags(node);
parametersWithPrecedingObjectRestOrSpread = collectParametersWithPrecedingObjectRestOrSpread(node);
const updated = factory.updateGetAccessorDeclaration(
node,
/*decorators*/ undefined,
node.modifiers,
visitNode(node.name, visitor, isPropertyName),
visitParameterList(node.parameters, visitor, context),
visitParameterList(node.parameters, parameterVisitor, context),
/*type*/ undefined,
transformFunctionBody(node)
);
enclosingFunctionFlags = savedEnclosingFunctionFlags;
parametersWithPrecedingObjectRestOrSpread = savedParametersWithPrecedingObjectRestOrSpread;
return updated;
}
function visitSetAccessorDeclaration(node: SetAccessorDeclaration) {
const savedEnclosingFunctionFlags = enclosingFunctionFlags;
enclosingFunctionFlags = FunctionFlags.Normal;
const savedParametersWithPrecedingObjectRestOrSpread = parametersWithPrecedingObjectRestOrSpread;
enclosingFunctionFlags = getFunctionFlags(node);
parametersWithPrecedingObjectRestOrSpread = collectParametersWithPrecedingObjectRestOrSpread(node);
const updated = factory.updateSetAccessorDeclaration(
node,
/*decorators*/ undefined,
node.modifiers,
visitNode(node.name, visitor, isPropertyName),
visitParameterList(node.parameters, visitor, context),
visitParameterList(node.parameters, parameterVisitor, context),
transformFunctionBody(node)
);
enclosingFunctionFlags = savedEnclosingFunctionFlags;
parametersWithPrecedingObjectRestOrSpread = savedParametersWithPrecedingObjectRestOrSpread;
return updated;
}
function visitMethodDeclaration(node: MethodDeclaration) {
const savedEnclosingFunctionFlags = enclosingFunctionFlags;
const savedParametersWithPrecedingObjectRestOrSpread = parametersWithPrecedingObjectRestOrSpread;
enclosingFunctionFlags = getFunctionFlags(node);
parametersWithPrecedingObjectRestOrSpread = collectParametersWithPrecedingObjectRestOrSpread(node);
const updated = factory.updateMethodDeclaration(
node,
/*decorators*/ undefined,
@ -863,19 +905,22 @@ namespace ts {
visitNode(node.name, visitor, isPropertyName),
visitNode<Token<SyntaxKind.QuestionToken>>(/*questionToken*/ undefined, visitor, isToken),
/*typeParameters*/ undefined,
visitParameterList(node.parameters, visitor, context),
visitParameterList(node.parameters, parameterVisitor, context),
/*type*/ undefined,
enclosingFunctionFlags & FunctionFlags.Async && enclosingFunctionFlags & FunctionFlags.Generator
? transformAsyncGeneratorFunctionBody(node)
: transformFunctionBody(node)
);
enclosingFunctionFlags = savedEnclosingFunctionFlags;
parametersWithPrecedingObjectRestOrSpread = savedParametersWithPrecedingObjectRestOrSpread;
return updated;
}
function visitFunctionDeclaration(node: FunctionDeclaration) {
const savedEnclosingFunctionFlags = enclosingFunctionFlags;
const savedParametersWithPrecedingObjectRestOrSpread = parametersWithPrecedingObjectRestOrSpread;
enclosingFunctionFlags = getFunctionFlags(node);
parametersWithPrecedingObjectRestOrSpread = collectParametersWithPrecedingObjectRestOrSpread(node);
const updated = factory.updateFunctionDeclaration(
node,
/*decorators*/ undefined,
@ -887,35 +932,41 @@ namespace ts {
: node.asteriskToken,
node.name,
/*typeParameters*/ undefined,
visitParameterList(node.parameters, visitor, context),
visitParameterList(node.parameters, parameterVisitor, context),
/*type*/ undefined,
enclosingFunctionFlags & FunctionFlags.Async && enclosingFunctionFlags & FunctionFlags.Generator
? transformAsyncGeneratorFunctionBody(node)
: transformFunctionBody(node)
);
enclosingFunctionFlags = savedEnclosingFunctionFlags;
parametersWithPrecedingObjectRestOrSpread = savedParametersWithPrecedingObjectRestOrSpread;
return updated;
}
function visitArrowFunction(node: ArrowFunction) {
const savedEnclosingFunctionFlags = enclosingFunctionFlags;
const savedParametersWithPrecedingObjectRestOrSpread = parametersWithPrecedingObjectRestOrSpread;
enclosingFunctionFlags = getFunctionFlags(node);
parametersWithPrecedingObjectRestOrSpread = collectParametersWithPrecedingObjectRestOrSpread(node);
const updated = factory.updateArrowFunction(
node,
node.modifiers,
/*typeParameters*/ undefined,
visitParameterList(node.parameters, visitor, context),
visitParameterList(node.parameters, parameterVisitor, context),
/*type*/ undefined,
node.equalsGreaterThanToken,
transformFunctionBody(node),
);
enclosingFunctionFlags = savedEnclosingFunctionFlags;
parametersWithPrecedingObjectRestOrSpread = savedParametersWithPrecedingObjectRestOrSpread;
return updated;
}
function visitFunctionExpression(node: FunctionExpression) {
const savedEnclosingFunctionFlags = enclosingFunctionFlags;
const savedParametersWithPrecedingObjectRestOrSpread = parametersWithPrecedingObjectRestOrSpread;
enclosingFunctionFlags = getFunctionFlags(node);
parametersWithPrecedingObjectRestOrSpread = collectParametersWithPrecedingObjectRestOrSpread(node);
const updated = factory.updateFunctionExpression(
node,
enclosingFunctionFlags & FunctionFlags.Generator
@ -926,13 +977,14 @@ namespace ts {
: node.asteriskToken,
node.name,
/*typeParameters*/ undefined,
visitParameterList(node.parameters, visitor, context),
visitParameterList(node.parameters, parameterVisitor, context),
/*type*/ undefined,
enclosingFunctionFlags & FunctionFlags.Async && enclosingFunctionFlags & FunctionFlags.Generator
? transformAsyncGeneratorFunctionBody(node)
: transformFunctionBody(node)
);
enclosingFunctionFlags = savedEnclosingFunctionFlags;
parametersWithPrecedingObjectRestOrSpread = savedParametersWithPrecedingObjectRestOrSpread;
return updated;
}
@ -1007,6 +1059,7 @@ namespace ts {
statementOffset = factory.copyPrologue(body.statements, statements, /*ensureUseStrict*/ false, visitor);
}
addRange(statements, appendObjectRestAssignmentsIfNeeded(/*statements*/ undefined, node));
const leadingStatements = endLexicalEnvironment();
if (statementOffset > 0 || some(statements) || some(leadingStatements)) {
const block = factory.converters.convertToFunctionBlock(body, /*multiLine*/ true);
@ -1018,25 +1071,86 @@ namespace ts {
}
function appendObjectRestAssignmentsIfNeeded(statements: Statement[] | undefined, node: FunctionLikeDeclaration): Statement[] | undefined {
let containsPrecedingObjectRestOrSpread = false;
for (const parameter of node.parameters) {
if (parameter.transformFlags & TransformFlags.ContainsObjectRestOrSpread) {
const temp = factory.getGeneratedNameForNode(parameter);
if (containsPrecedingObjectRestOrSpread) {
if (isBindingPattern(parameter.name)) {
// In cases where a binding pattern is simply '[]' or '{}',
// we usually don't want to emit a var declaration; however, in the presence
// of an initializer, we must emit that expression to preserve side effects.
//
// NOTE: see `insertDefaultValueAssignmentForBindingPattern` in es2015.ts
if (parameter.name.elements.length > 0) {
const declarations = flattenDestructuringBinding(
parameter,
visitor,
context,
FlattenLevel.All,
factory.getGeneratedNameForNode(parameter));
if (some(declarations)) {
const declarationList = factory.createVariableDeclarationList(declarations);
const statement = factory.createVariableStatement(/*modifiers*/ undefined, declarationList);
setEmitFlags(statement, EmitFlags.CustomPrologue);
statements = append(statements, statement);
}
}
else if (parameter.initializer) {
const name = factory.getGeneratedNameForNode(parameter);
const initializer = visitNode(parameter.initializer, visitor, isExpression);
const assignment = factory.createAssignment(name, initializer);
const statement = factory.createExpressionStatement(assignment);
setEmitFlags(statement, EmitFlags.CustomPrologue);
statements = append(statements, statement);
}
}
else if (parameter.initializer) {
// Converts a parameter initializer into a function body statement, i.e.:
//
// function f(x = 1) { }
//
// becomes
//
// function f(x) {
// if (typeof x === "undefined") { x = 1; }
// }
const name = factory.cloneNode(parameter.name);
setTextRange(name, parameter.name);
setEmitFlags(name, EmitFlags.NoSourceMap);
const initializer = visitNode(parameter.initializer, visitor, isExpression);
addEmitFlags(initializer, EmitFlags.NoSourceMap | EmitFlags.NoComments);
const assignment = factory.createAssignment(name, initializer);
setTextRange(assignment, parameter);
setEmitFlags(assignment, EmitFlags.NoComments);
const block = factory.createBlock([factory.createExpressionStatement(assignment)]);
setTextRange(block, parameter);
setEmitFlags(block, EmitFlags.SingleLine | EmitFlags.NoTrailingSourceMap | EmitFlags.NoTokenSourceMaps | EmitFlags.NoComments);
const typeCheck = factory.createTypeCheck(factory.cloneNode(parameter.name), "undefined");
const statement = factory.createIfStatement(typeCheck, block);
startOnNewLine(statement);
setTextRange(statement, parameter);
setEmitFlags(statement, EmitFlags.NoTokenSourceMaps | EmitFlags.NoTrailingSourceMap | EmitFlags.CustomPrologue | EmitFlags.NoComments);
statements = append(statements, statement);
}
}
else if (parameter.transformFlags & TransformFlags.ContainsObjectRestOrSpread) {
containsPrecedingObjectRestOrSpread = true;
const declarations = flattenDestructuringBinding(
parameter,
visitor,
context,
FlattenLevel.ObjectRest,
temp,
factory.getGeneratedNameForNode(parameter),
/*doNotRecordTempVariablesInLine*/ false,
/*skipInitializer*/ true,
);
if (some(declarations)) {
const statement = factory.createVariableStatement(
/*modifiers*/ undefined,
factory.createVariableDeclarationList(
declarations
)
);
const declarationList = factory.createVariableDeclarationList(declarations);
const statement = factory.createVariableStatement(/*modifiers*/ undefined, declarationList);
setEmitFlags(statement, EmitFlags.CustomPrologue);
statements = append(statements, statement);
}

View File

@ -0,0 +1,35 @@
//// [functionParameterObjectRestAndInitializers.ts]
// https://github.com/microsoft/TypeScript/issues/47079
function f({a, ...x}, b = a) {
return b;
}
function g({a, ...x}, b = ({a}, b = a) => {}) {
return b;
}
//// [functionParameterObjectRestAndInitializers.js]
// https://github.com/microsoft/TypeScript/issues/47079
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;
};
function f(_a, b) {
var { a } = _a, x = __rest(_a, ["a"]);
if (b === void 0) { b = a; }
return b;
}
function g(_a, b) {
var { a } = _a, x = __rest(_a, ["a"]);
if (b === void 0) { b = ({ a }, b = a) => { }; }
return b;
}

View File

@ -0,0 +1,11 @@
// @target: es2015
// @noTypesAndSymbols: true
// https://github.com/microsoft/TypeScript/issues/47079
function f({a, ...x}, b = a) {
return b;
}
function g({a, ...x}, b = ({a}, b = a) => {}) {
return b;
}