Skip to content

Commit 0b303ff

Browse files
committed
Fix some rest cases and handling of unused results
1 parent 28c4f32 commit 0b303ff

File tree

24 files changed

+4596
-4700
lines changed

24 files changed

+4596
-4700
lines changed

src/compiler/factory/nodeFactory.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2663,12 +2663,14 @@ namespace ts {
26632663
node.transformFlags |=
26642664
TransformFlags.ContainsES2015 |
26652665
TransformFlags.ContainsES2018 |
2666-
TransformFlags.ContainsDestructuringAssignment;
2666+
TransformFlags.ContainsDestructuringAssignment |
2667+
propagateAssignmentPatternFlags(node.left);
26672668
}
26682669
else if (isArrayLiteralExpression(node.left)) {
26692670
node.transformFlags |=
26702671
TransformFlags.ContainsES2015 |
2671-
TransformFlags.ContainsDestructuringAssignment;
2672+
TransformFlags.ContainsDestructuringAssignment |
2673+
propagateAssignmentPatternFlags(node.left);
26722674
}
26732675
}
26742676
else if (operatorKind === SyntaxKind.AsteriskAsteriskToken || operatorKind === SyntaxKind.AsteriskAsteriskEqualsToken) {
@@ -2680,6 +2682,27 @@ namespace ts {
26802682
return node;
26812683
}
26822684

2685+
function propagateAssignmentPatternFlags(node: AssignmentPattern): TransformFlags {
2686+
if (node.transformFlags & TransformFlags.ContainsObjectRestOrSpread) return TransformFlags.ContainsObjectRestOrSpread;
2687+
if (node.transformFlags & TransformFlags.ContainsES2018) {
2688+
// check for nested spread assignments, otherwise '{ x: { a, ...b } = foo } = c'
2689+
// will not be correctly interpreted by the ES2018 transformer
2690+
for (const element of getElementsOfBindingOrAssignmentPattern(node)) {
2691+
const target = getTargetOfBindingOrAssignmentElement(element);
2692+
if (target && isAssignmentPattern(target)) {
2693+
if (target.transformFlags & TransformFlags.ContainsObjectRestOrSpread) {
2694+
return TransformFlags.ContainsObjectRestOrSpread;
2695+
}
2696+
if (target.transformFlags & TransformFlags.ContainsES2018) {
2697+
const flags = propagateAssignmentPatternFlags(target);
2698+
if (flags) return flags;
2699+
}
2700+
}
2701+
}
2702+
}
2703+
return TransformFlags.None;
2704+
}
2705+
26832706
// @api
26842707
function updateBinaryExpression(node: BinaryExpression, left: Expression, operator: BinaryOperatorToken, right: Expression) {
26852708
return node.left !== left

src/compiler/transformers/es2015.ts

Lines changed: 73 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -355,12 +355,11 @@ namespace ts {
355355
}
356356

357357
function visitor(node: Node): VisitResult<Node> {
358-
if (shouldVisitNode(node)) {
359-
return visitJavaScript(node);
360-
}
361-
else {
362-
return node;
363-
}
358+
return shouldVisitNode(node) ? visitorWorker(node, /*expressionResultIsUnused*/ false) : node;
359+
}
360+
361+
function visitorWithUnusedExpressionResult(node: Node): VisitResult<Node> {
362+
return shouldVisitNode(node) ? visitorWorker(node, /*expressionResultIsUnused*/ true) : node;
364363
}
365364

366365
function callExpressionVisitor(node: Node): VisitResult<Node> {
@@ -370,7 +369,7 @@ namespace ts {
370369
return visitor(node);
371370
}
372371

373-
function visitJavaScript(node: Node): VisitResult<Node> {
372+
function visitorWorker(node: Node, expressionResultIsUnused: boolean): VisitResult<Node> {
374373
switch (node.kind) {
375374
case SyntaxKind.StaticKeyword:
376375
return undefined; // elide static keyword
@@ -456,10 +455,13 @@ namespace ts {
456455
return visitNewExpression(<NewExpression>node);
457456

458457
case SyntaxKind.ParenthesizedExpression:
459-
return visitParenthesizedExpression(<ParenthesizedExpression>node, /*needsDestructuringValue*/ true);
458+
return visitParenthesizedExpression(<ParenthesizedExpression>node, expressionResultIsUnused);
460459

461460
case SyntaxKind.BinaryExpression:
462-
return visitBinaryExpression(<BinaryExpression>node, /*needsDestructuringValue*/ true);
461+
return visitBinaryExpression(<BinaryExpression>node, expressionResultIsUnused);
462+
463+
case SyntaxKind.CommaListExpression:
464+
return visitCommaListExpression(<CommaListExpression>node, expressionResultIsUnused);
463465

464466
case SyntaxKind.NoSubstitutionTemplateLiteral:
465467
case SyntaxKind.TemplateHead:
@@ -507,6 +509,9 @@ namespace ts {
507509
case SyntaxKind.ReturnStatement:
508510
return visitReturnStatement(<ReturnStatement>node);
509511

512+
case SyntaxKind.VoidExpression:
513+
return visitVoidExpression(node as VoidExpression);
514+
510515
default:
511516
return visitEachChild(node, visitor, context);
512517
}
@@ -596,6 +601,10 @@ namespace ts {
596601
return node;
597602
}
598603

604+
function visitVoidExpression(node: VoidExpression): Expression {
605+
return visitEachChild(node, visitorWithUnusedExpressionResult, context);
606+
}
607+
599608
function visitIdentifier(node: Identifier): Identifier {
600609
if (!convertedLoopState) {
601610
return node;
@@ -1975,59 +1984,69 @@ namespace ts {
19751984
* @param node An ExpressionStatement node.
19761985
*/
19771986
function visitExpressionStatement(node: ExpressionStatement): Statement {
1978-
// If we are here it is most likely because our expression is a destructuring assignment.
1979-
switch (node.expression.kind) {
1980-
case SyntaxKind.ParenthesizedExpression:
1981-
return factory.updateExpressionStatement(node, visitParenthesizedExpression(<ParenthesizedExpression>node.expression, /*needsDestructuringValue*/ false));
1982-
case SyntaxKind.BinaryExpression:
1983-
return factory.updateExpressionStatement(node, visitBinaryExpression(<BinaryExpression>node.expression, /*needsDestructuringValue*/ false));
1984-
}
1985-
return visitEachChild(node, visitor, context);
1987+
return visitEachChild(node, visitorWithUnusedExpressionResult, context);
19861988
}
19871989

19881990
/**
19891991
* Visits a ParenthesizedExpression that may contain a destructuring assignment.
19901992
*
19911993
* @param node A ParenthesizedExpression node.
1992-
* @param needsDestructuringValue A value indicating whether we need to hold onto the rhs
1993-
* of a destructuring assignment.
1994+
* @param expressionResultIsUnused Indicates the result of an expression is unused by the parent node (i.e., the left side of a comma or the
1995+
* expression of an `ExpressionStatement`).
19941996
*/
1995-
function visitParenthesizedExpression(node: ParenthesizedExpression, needsDestructuringValue: boolean): ParenthesizedExpression {
1996-
// If we are here it is most likely because our expression is a destructuring assignment.
1997-
if (!needsDestructuringValue) {
1998-
// By default we always emit the RHS at the end of a flattened destructuring
1999-
// expression. If we are in a state where we do not need the destructuring value,
2000-
// we pass that information along to the children that care about it.
2001-
switch (node.expression.kind) {
2002-
case SyntaxKind.ParenthesizedExpression:
2003-
return factory.updateParenthesizedExpression(node, visitParenthesizedExpression(<ParenthesizedExpression>node.expression, /*needsDestructuringValue*/ false));
2004-
case SyntaxKind.BinaryExpression:
2005-
return factory.updateParenthesizedExpression(node, visitBinaryExpression(<BinaryExpression>node.expression, /*needsDestructuringValue*/ false));
2006-
}
2007-
}
2008-
return visitEachChild(node, visitor, context);
1997+
function visitParenthesizedExpression(node: ParenthesizedExpression, expressionResultIsUnused: boolean): ParenthesizedExpression {
1998+
return visitEachChild(node, expressionResultIsUnused ? visitorWithUnusedExpressionResult : visitor, context);
20091999
}
20102000

20112001
/**
20122002
* Visits a BinaryExpression that contains a destructuring assignment.
20132003
*
20142004
* @param node A BinaryExpression node.
2015-
* @param needsDestructuringValue A value indicating whether we need to hold onto the rhs
2016-
* of a destructuring assignment.
2005+
* @param expressionResultIsUnused Indicates the result of an expression is unused by the parent node (i.e., the left side of a comma or the
2006+
* expression of an `ExpressionStatement`).
20172007
*/
2018-
function visitBinaryExpression(node: BinaryExpression, needsDestructuringValue: boolean): Expression {
2008+
function visitBinaryExpression(node: BinaryExpression, expressionResultIsUnused: boolean): Expression {
20192009
// If we are here it is because this is a destructuring assignment.
20202010
if (isDestructuringAssignment(node)) {
20212011
return flattenDestructuringAssignment(
20222012
node,
20232013
visitor,
20242014
context,
20252015
FlattenLevel.All,
2026-
needsDestructuringValue);
2016+
!expressionResultIsUnused);
2017+
}
2018+
if (node.operatorToken.kind === SyntaxKind.CommaToken) {
2019+
return factory.updateBinaryExpression(
2020+
node,
2021+
visitNode(node.left, visitorWithUnusedExpressionResult, isExpression),
2022+
node.operatorToken,
2023+
visitNode(node.right, expressionResultIsUnused ? visitorWithUnusedExpressionResult : visitor, isExpression)
2024+
);
20272025
}
20282026
return visitEachChild(node, visitor, context);
20292027
}
20302028

2029+
/**
2030+
* @param expressionResultIsUnused Indicates the result of an expression is unused by the parent node (i.e., the left side of a comma or the
2031+
* expression of an `ExpressionStatement`).
2032+
*/
2033+
function visitCommaListExpression(node: CommaListExpression, expressionResultIsUnused: boolean): Expression {
2034+
if (expressionResultIsUnused) {
2035+
return visitEachChild(node, visitorWithUnusedExpressionResult, context);
2036+
}
2037+
let result: Expression[] | undefined;
2038+
for (let i = 0; i < node.elements.length; i++) {
2039+
const element = node.elements[i];
2040+
const visited = visitNode(element, i < node.elements.length - 1 ? visitorWithUnusedExpressionResult : visitor, isExpression);
2041+
if (result || visited !== element) {
2042+
result ||= node.elements.slice(0, i);
2043+
result.push(visited);
2044+
}
2045+
}
2046+
const elements = result ? setTextRange(factory.createNodeArray(result), node.elements) : node.elements;
2047+
return factory.updateCommaListExpression(node, elements);
2048+
}
2049+
20312050
function isVariableStatementOfTypeScriptClassWrapper(node: VariableStatement) {
20322051
return node.declarationList.declarations.length === 1
20332052
&& !!node.declarationList.declarations[0].initializer
@@ -2288,6 +2307,16 @@ namespace ts {
22882307
outermostLabeledStatement);
22892308
}
22902309

2310+
function visitEachChildOfForStatement(node: ForStatement) {
2311+
return factory.updateForStatement(
2312+
node,
2313+
visitNode(node.initializer, visitorWithUnusedExpressionResult, isForInitializer),
2314+
visitNode(node.condition, visitor, isExpression),
2315+
visitNode(node.incrementor, visitorWithUnusedExpressionResult, isExpression),
2316+
visitNode(node.statement, visitor, isStatement, factory.liftToBlock)
2317+
);
2318+
}
2319+
22912320
function visitForInStatement(node: ForInStatement, outermostLabeledStatement: LabeledStatement | undefined) {
22922321
return visitIterationStatementWithFacts(
22932322
HierarchyFacts.ForInOrForOfStatementExcludes,
@@ -2371,7 +2400,7 @@ namespace ts {
23712400
// evaluated on every iteration.
23722401
const assignment = factory.createAssignment(initializer, boundValue);
23732402
if (isDestructuringAssignment(assignment)) {
2374-
statements.push(factory.createExpressionStatement(visitBinaryExpression(assignment, /*needsDestructuringValue*/ false)));
2403+
statements.push(factory.createExpressionStatement(visitBinaryExpression(assignment, /*expressionResultIsUnused*/ true)));
23752404
}
23762405
else {
23772406
setTextRangeEnd(assignment, initializer.end);
@@ -2714,7 +2743,10 @@ namespace ts {
27142743

27152744
const result = convert
27162745
? convert(node, outermostLabeledStatement, /*convertedLoopBodyStatements*/ undefined, ancestorFacts)
2717-
: factory.restoreEnclosingLabel(visitEachChild(node, visitor, context), outermostLabeledStatement, convertedLoopState && resetLabel);
2746+
: factory.restoreEnclosingLabel(
2747+
isForStatement(node) ? visitEachChildOfForStatement(node) : visitEachChild(node, visitor, context),
2748+
outermostLabeledStatement,
2749+
convertedLoopState && resetLabel);
27182750

27192751
if (convertedLoopState) {
27202752
convertedLoopState.allowedNonLabeledJumps = saveAllowedNonLabeledJumps;
@@ -2777,9 +2809,9 @@ namespace ts {
27772809
const shouldConvertIncrementor = shouldConvertCondition || node.incrementor && shouldConvertPartOfIterationStatement(node.incrementor);
27782810
return factory.updateForStatement(
27792811
node,
2780-
visitNode(initializerFunction ? initializerFunction.part : node.initializer, visitor, isForInitializer),
2812+
visitNode(initializerFunction ? initializerFunction.part : node.initializer, visitorWithUnusedExpressionResult, isForInitializer),
27812813
visitNode(shouldConvertCondition ? undefined : node.condition, visitor, isExpression),
2782-
visitNode(shouldConvertIncrementor ? undefined : node.incrementor, visitor, isExpression),
2814+
visitNode(shouldConvertIncrementor ? undefined : node.incrementor, visitorWithUnusedExpressionResult, isExpression),
27832815
convertedLoopBody
27842816
);
27852817
}

0 commit comments

Comments
 (0)