blob: 08903d476c6f4715062210640e2850278af22d94 [file] [log] [blame]
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
/// Parenthesize the target of the [expressionStatement]'s expression (assumed
/// to [cascadeExpression]) before removing the cascade.
ExpressionStatement fixCascadeByParenthesizingTarget({
required ExpressionStatement expressionStatement,
required CascadeExpression cascadeExpression,
}) {
cascadeExpression as CascadeExpressionImpl;
assert(cascadeExpression.cascadeSections.length == 1);
var newTarget = ParenthesizedExpressionImpl(
leftParenthesis:
Token(TokenType.OPEN_PAREN, 0)
..previous = expressionStatement.beginToken.previous
..next = cascadeExpression.target.beginToken,
expression: cascadeExpression.target,
rightParenthesis:
Token(TokenType.CLOSE_PAREN, 0)
..previous = cascadeExpression.target.endToken
..next = expressionStatement.semicolon,
);
return ExpressionStatementImpl(
expression: CascadeExpressionImpl(
target: newTarget,
cascadeSections: cascadeExpression.cascadeSections,
),
semicolon: expressionStatement.semicolon,
);
}
/// Recursively insert [cascadeTarget] (the LHS of the cascade) into the
/// LHS of the assignment expression that used to be the cascade's RHS.
ExpressionImpl insertCascadeTargetIntoExpression({
required Expression expression,
required Expression cascadeTarget,
}) {
expression as ExpressionImpl;
cascadeTarget as ExpressionImpl;
// Base case: We've recursed as deep as possible.
if (expression == cascadeTarget) return cascadeTarget;
// Otherwise, copy `expression` and recurse into its LHS.
if (expression is AssignmentExpressionImpl) {
return AssignmentExpressionImpl(
leftHandSide: insertCascadeTargetIntoExpression(
expression: expression.leftHandSide,
cascadeTarget: cascadeTarget,
),
operator: expression.operator,
rightHandSide: expression.rightHandSide,
);
} else if (expression is IndexExpressionImpl) {
var expressionTarget = expression.realTarget;
var question = expression.question;
// A null-aware cascade treats the `?` in `?..` as part of the token, but
// for a non-cascade index, it is a separate `?` token.
if (expression.period?.type == TokenType.QUESTION_PERIOD_PERIOD) {
question = _synthesizeToken(TokenType.QUESTION, expression.period!);
}
return IndexExpressionImpl(
target: insertCascadeTargetIntoExpression(
expression: expressionTarget,
cascadeTarget: cascadeTarget,
),
period: null,
question: question,
leftBracket: expression.leftBracket,
index: expression.index,
rightBracket: expression.rightBracket,
);
} else if (expression is MethodInvocationImpl) {
var expressionTarget = expression.realTarget!;
return MethodInvocationImpl(
target: insertCascadeTargetIntoExpression(
expression: expressionTarget,
cascadeTarget: cascadeTarget,
),
// If we've reached the end, replace the `..` operator with `.`
operator:
expressionTarget == cascadeTarget
? _synthesizeToken(TokenType.PERIOD, expression.operator!)
: expression.operator,
methodName: expression.methodName,
typeArguments: expression.typeArguments,
argumentList: expression.argumentList,
);
} else if (expression is PropertyAccessImpl) {
var expressionTarget = expression.realTarget;
return PropertyAccessImpl(
target: insertCascadeTargetIntoExpression(
expression: expressionTarget,
cascadeTarget: cascadeTarget,
),
// If we've reached the end, replace the `..` operator with `.`
operator:
expressionTarget == cascadeTarget
? _synthesizeToken(TokenType.PERIOD, expression.operator)
: expression.operator,
propertyName: expression.propertyName,
);
}
throw UnimplementedError(
'Unhandled ${expression.runtimeType}'
'($expression)',
);
}
/// Synthesize a token with [type] to replace the given [operator].
///
/// Offset, comments, and previous/next links are all preserved.
Token _synthesizeToken(TokenType type, Token operator) =>
Token(type, operator.offset, operator.precedingComments)
..previous = operator.previous
..next = operator.next;