in scala/scala-impl/src/org/jetbrains/plugins/scala/lang/psi/ScalaPsiUtil.scala [898:1047]
def needParentheses(from: ScExpression, expr: ScExpression): Boolean = {
def infixInInfixParentheses(parent: ScInfixExpr, child: ScInfixExpr): Boolean = {
import org.jetbrains.plugins.scala.lang.parser.util.ParserUtils._
if (parent.left == from) {
val lid = parent.operation.getText
val rid = child.operation.getText
if (priority(lid) < priority(rid)) true
else if (priority(rid) < priority(lid)) false
else if (operatorAssociativity(lid) != operatorAssociativity(rid)) true
else if (operatorAssociativity(lid) == Associativity.Right) true
else false
}
else {
val lid = child.operation.getText
val rid = parent.operation.getText
if (priority(lid) < priority(rid)) false
else if (priority(rid) < priority(lid)) true
else if (operatorAssociativity(lid) != operatorAssociativity(rid)) true
else if (operatorAssociativity(lid) == Associativity.Right) false
else true
}
}
def tupleInInfixNeedParentheses(parent: ScInfixExpr, from: ScExpression): Boolean = {
if (from.getParent != parent) throw new IllegalArgumentException
if (from == parent.left) false
else {
parent.operation.bind() match {
case Some(resolveResult) =>
val fromLength = from.getTextLength
val startInParent = from.getStartOffsetInParent
val endInParent = startInParent + fromLength
val parentText = parent.getText
val modifiedParentText =
parentText.substring(0, startInParent) +
from.getText.substring(1, fromLength - 1) +
parentText.substring(endInParent)
val modifiedParent = createExpressionWithContextFromText(modifiedParentText, parent.getContext)
modifiedParent match {
case ScInfixExpr(_, newOper, _: ScTuple) =>
newOper.bind() match {
case Some(newResolveResult) =>
newResolveResult.getElement == resolveResult.getElement && newResolveResult.tuplingUsed
case _ => true
}
case _ => true
}
case _ => false
}
}
}
def parsedDifferently(from: ScExpression, expr: ScExpression): Boolean = {
if (newLinesEnabled(from)) {
expr.getParent match {
case ScParenthesisedExpr(_) =>
val text = expr.getText
val dummyFile = createScalaFileFromText(text, expr)(expr.getManager)
dummyFile.firstChild match {
case Some(newExpr: ScExpression) => !newExpr.textMatches(text)
case _ => true
}
case _ => false
}
} else false
}
/**
* This method determines whether we're dealing with a case like this:
* {{{
* if (false)
* (if (false) println(1) else if (false) println(2))
* else println(3)
* }}}
*
* More specifically, we are interested in testing whether the parentheses around
* `if (false) println(1) else if (false) println(2)` are necessary.
*
* There are 2 conditions that determine whether removal of these parentheses leads
* to changed semantics:
*
* The first and more obvious condition, is that the outer if-expression must have
* an else-expression. If it does not, one can safely remove the parentheses.
*
* The second condition is less obvious, but becomes clear after considering that
* the parenthesized expression is potentially a chain of if-expressions, linked
* together via the else-construct.
* To figure out whether the total inner if-expression needs its parentheses, we must
* recurse the chain until its end and check that the final if-expression has an
* else-expression.
* If it does, we can remove the parentheses without changing semantics. If it doesn't,
* and the outer if-expression indeed has an else-expression, removing the parentheses
* would associate that else-expression with the formerly parenthesized expression.
*/
def innerIfNeedsParentheses(outerIf: ScIf, innerIf: ScIf): Boolean = {
@tailrec
def ifChainHasTerminatingElse(ifExpr: ScIf): Boolean = ifExpr.elseExpression match {
case Some(nestedIfExpr: ScIf) => ifChainHasTerminatingElse(nestedIfExpr)
case Some(_) => true
case _ => false
}
outerIf.elseExpression.nonEmpty && !ifChainHasTerminatingElse(innerIf)
}
if (parsedDifferently(from, expr)) true
else {
val parent = from.getParent
(parent, expr) match {
//order of these case clauses is important!
case (outerIf: ScIf, innerIf: ScIf) if innerIfNeedsParentheses(outerIf, innerIf) => true
case (_: ScGuard, _: ScMatch) => true
case _ if !parent.is[ScExpression] => false
case _ if expr.textMatches("_") => false
case (_: ScTuple | _: ScNamedTuple | _: ScBlock | _: ScXmlExpr, _) => false
case (infix: ScInfixExpr, call: ScMethodCall) if infix.left == from && call.args.isColonArgs => true
case (infix: ScInfixExpr, nested @ ScParenthesisedExpr(_: ScTuple)) => tupleInInfixNeedParentheses(infix, nested)
case (_: ScSugarCallExpr |
_: ScReferenceExpression |
_: ScTypedExpression, elem: PsiElement)
if from.isInstanceOf[ScParenthesizedElement] && ScUnderScoreSectionUtil.isUnderscoreFunction(elem) => true
case (_, _: ScReferenceExpression | _: ScMethodCall |
_: ScGenericCall | _: ScLiteral | _: ScTuple | _: ScNamedTuple |
_: ScXmlExpr | _: ScParenthesisedExpr | _: ScUnitExpr |
_: ScThisReference | _: ScSuperReference) => false
case (_: ScMethodCall | _: ScUnderscoreSection, _) => true
case (_, _: ScBlock) => false
case (_: ScGenericCall, _) => true
case (_: ScReferenceExpression, _: ScNewTemplateDefinition) =>
val lastChar: Char = expr.getText.last
lastChar != ')' && lastChar != '}' && lastChar != ']'
case (_: ScReferenceExpression, _) => true
case (_, _: ScNewTemplateDefinition |
_: ScUnderscoreSection) => false
case (_: ScPrefixExpr, _) => true
case (_, _: ScPrefixExpr) => false
case (par: ScInfixExpr, child: ScInfixExpr) => infixInInfixParentheses(par, child)
case (_, _: ScInfixExpr) => false
case (_: ScPostfixExpr | _: ScInfixExpr, _) => true
case (_, _: ScPostfixExpr) => false
case (_: ScTypedExpression | _: ScMatch, _) => true
case _ => false
}
}
}