blob: 29bc9dd6182a1642b09c8575a2a717be2dba20a8 [file] [log] [blame]
/*
* Copyright 2003-2014 Dave Griffith, Bas Leijdekkers
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.siyeh.ig.psiutils;
import com.intellij.psi.*;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Map;
public class ParenthesesUtils {
private ParenthesesUtils() {}
public static final int PARENTHESIZED_PRECEDENCE = 0;
public static final int LITERAL_PRECEDENCE = 0;
public static final int METHOD_CALL_PRECEDENCE = 1;
public static final int POSTFIX_PRECEDENCE = 2;
public static final int PREFIX_PRECEDENCE = 3;
public static final int TYPE_CAST_PRECEDENCE = 4;
public static final int MULTIPLICATIVE_PRECEDENCE = 5;
public static final int ADDITIVE_PRECEDENCE = 6;
public static final int SHIFT_PRECEDENCE = 7;
public static final int RELATIONAL_PRECEDENCE = 8;
public static final int EQUALITY_PRECEDENCE = 9;
public static final int BINARY_AND_PRECEDENCE = 10;
public static final int BINARY_XOR_PRECEDENCE = 11;
public static final int BINARY_OR_PRECEDENCE = 12;
public static final int AND_PRECEDENCE = 13;
public static final int OR_PRECEDENCE = 14;
public static final int CONDITIONAL_PRECEDENCE = 15;
public static final int ASSIGNMENT_PRECEDENCE = 16;
public static final int NUM_PRECEDENCES = 17;
private static final Map<IElementType, Integer> s_binaryOperatorPrecedence = new HashMap<IElementType, Integer>(NUM_PRECEDENCES);
static {
s_binaryOperatorPrecedence.put(JavaTokenType.PLUS, ADDITIVE_PRECEDENCE);
s_binaryOperatorPrecedence.put(JavaTokenType.MINUS, ADDITIVE_PRECEDENCE);
s_binaryOperatorPrecedence.put(JavaTokenType.ASTERISK, MULTIPLICATIVE_PRECEDENCE);
s_binaryOperatorPrecedence.put(JavaTokenType.DIV, MULTIPLICATIVE_PRECEDENCE);
s_binaryOperatorPrecedence.put(JavaTokenType.PERC, MULTIPLICATIVE_PRECEDENCE);
s_binaryOperatorPrecedence.put(JavaTokenType.ANDAND, AND_PRECEDENCE);
s_binaryOperatorPrecedence.put(JavaTokenType.OROR, OR_PRECEDENCE);
s_binaryOperatorPrecedence.put(JavaTokenType.AND, BINARY_AND_PRECEDENCE);
s_binaryOperatorPrecedence.put(JavaTokenType.OR, BINARY_OR_PRECEDENCE);
s_binaryOperatorPrecedence.put(JavaTokenType.XOR, BINARY_XOR_PRECEDENCE);
s_binaryOperatorPrecedence.put(JavaTokenType.LTLT, SHIFT_PRECEDENCE);
s_binaryOperatorPrecedence.put(JavaTokenType.GTGT, SHIFT_PRECEDENCE);
s_binaryOperatorPrecedence.put(JavaTokenType.GTGTGT, SHIFT_PRECEDENCE);
s_binaryOperatorPrecedence.put(JavaTokenType.GT, RELATIONAL_PRECEDENCE);
s_binaryOperatorPrecedence.put(JavaTokenType.GE, RELATIONAL_PRECEDENCE);
s_binaryOperatorPrecedence.put(JavaTokenType.LT, RELATIONAL_PRECEDENCE);
s_binaryOperatorPrecedence.put(JavaTokenType.LE, RELATIONAL_PRECEDENCE);
s_binaryOperatorPrecedence.put(JavaTokenType.EQEQ, EQUALITY_PRECEDENCE);
s_binaryOperatorPrecedence.put(JavaTokenType.NE, EQUALITY_PRECEDENCE);
}
@Nullable public static PsiElement getParentSkipParentheses(PsiElement element) {
PsiElement parent = element.getParent();
while (parent instanceof PsiParenthesizedExpression || parent instanceof PsiTypeCastExpression) {
parent = parent.getParent();
}
return parent;
}
@Nullable
public static PsiExpression stripParentheses(@Nullable PsiExpression expression) {
while (expression instanceof PsiParenthesizedExpression) {
final PsiParenthesizedExpression parenthesizedExpression = (PsiParenthesizedExpression)expression;
expression = parenthesizedExpression.getExpression();
}
return expression;
}
public static boolean isCommutativeOperator(@NotNull IElementType token) {
return !(token.equals(JavaTokenType.MINUS) ||
token.equals(JavaTokenType.DIV) ||
token.equals(JavaTokenType.PERC) ||
token.equals(JavaTokenType.LTLT) ||
token.equals(JavaTokenType.GTGT) ||
token.equals(JavaTokenType.GTGTGT));
}
public static boolean isAssociativeOperation(PsiPolyadicExpression expression) {
final IElementType tokenType = expression.getOperationTokenType();
final PsiType type = expression.getType();
final PsiPrimitiveType primitiveType;
if (type instanceof PsiClassType) {
primitiveType = PsiPrimitiveType.getUnboxedType(type);
if (primitiveType == null) {
return false;
}
} else if (type instanceof PsiPrimitiveType) {
primitiveType = (PsiPrimitiveType)type;
} else {
return false;
}
if (JavaTokenType.PLUS == tokenType || JavaTokenType.ASTERISK == tokenType) {
return primitiveType != PsiType.FLOAT && primitiveType != PsiType.DOUBLE;
} else if (JavaTokenType.EQEQ == tokenType || JavaTokenType.NE == tokenType) {
return primitiveType == PsiType.BOOLEAN;
} else if (JavaTokenType.AND == tokenType || JavaTokenType.OR == tokenType || JavaTokenType.XOR == tokenType) {
return true;
} else if (JavaTokenType.OROR == tokenType || JavaTokenType.ANDAND == tokenType) {
return true;
}
return false;
}
public static int getPrecedence(PsiExpression expression) {
if (expression instanceof PsiThisExpression ||
expression instanceof PsiLiteralExpression ||
expression instanceof PsiSuperExpression ||
expression instanceof PsiClassObjectAccessExpression ||
expression instanceof PsiArrayAccessExpression ||
expression instanceof PsiArrayInitializerExpression) {
return LITERAL_PRECEDENCE;
}
if (expression instanceof PsiReferenceExpression) {
final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)expression;
if (referenceExpression.getQualifier() != null) {
return METHOD_CALL_PRECEDENCE;
}
else {
return LITERAL_PRECEDENCE;
}
}
if (expression instanceof PsiMethodCallExpression || expression instanceof PsiNewExpression) {
return METHOD_CALL_PRECEDENCE;
}
if (expression instanceof PsiTypeCastExpression) {
return TYPE_CAST_PRECEDENCE;
}
if (expression instanceof PsiPrefixExpression) {
return PREFIX_PRECEDENCE;
}
if (expression instanceof PsiPostfixExpression) {
return POSTFIX_PRECEDENCE;
}
if (expression instanceof PsiPolyadicExpression) {
final PsiPolyadicExpression polyadicExpression = (PsiPolyadicExpression)expression;
return getPrecedenceForOperator(polyadicExpression.getOperationTokenType());
}
if (expression instanceof PsiInstanceOfExpression) {
return RELATIONAL_PRECEDENCE;
}
if (expression instanceof PsiConditionalExpression) {
return CONDITIONAL_PRECEDENCE;
}
if (expression instanceof PsiAssignmentExpression) {
return ASSIGNMENT_PRECEDENCE;
}
if (expression instanceof PsiParenthesizedExpression) {
return PARENTHESIZED_PRECEDENCE;
}
return -1;
}
public static int getPrecedenceForOperator(@NotNull IElementType operator) {
final Integer precedence = s_binaryOperatorPrecedence.get(operator);
if (precedence == null) {
throw new IllegalArgumentException("unknown operator: " + operator);
}
return precedence.intValue();
}
public static void removeParentheses(@NotNull PsiExpression expression, boolean ignoreClarifyingParentheses) {
if (expression instanceof PsiMethodCallExpression) {
final PsiMethodCallExpression methodCall = (PsiMethodCallExpression)expression;
removeParensFromMethodCallExpression(methodCall, ignoreClarifyingParentheses);
}
if (expression instanceof PsiReferenceExpression) {
final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)expression;
removeParensFromReferenceExpression(referenceExpression, ignoreClarifyingParentheses);
}
if (expression instanceof PsiNewExpression) {
final PsiNewExpression newExpression = (PsiNewExpression)expression;
removeParensFromNewExpression(newExpression, ignoreClarifyingParentheses);
}
if (expression instanceof PsiAssignmentExpression) {
final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)expression;
removeParensFromAssignmentExpression(assignmentExpression, ignoreClarifyingParentheses);
}
if (expression instanceof PsiArrayInitializerExpression) {
final PsiArrayInitializerExpression arrayInitializerExpression = (PsiArrayInitializerExpression)expression;
removeParensFromArrayInitializerExpression(arrayInitializerExpression, ignoreClarifyingParentheses);
}
if (expression instanceof PsiTypeCastExpression) {
final PsiTypeCastExpression typeCastExpression = (PsiTypeCastExpression)expression;
removeParensFromTypeCastExpression(typeCastExpression, ignoreClarifyingParentheses);
}
if (expression instanceof PsiArrayAccessExpression) {
final PsiArrayAccessExpression arrayAccessExpression = (PsiArrayAccessExpression)expression;
removeParensFromArrayAccessExpression(arrayAccessExpression, ignoreClarifyingParentheses);
}
if (expression instanceof PsiPrefixExpression) {
final PsiPrefixExpression prefixExpression = (PsiPrefixExpression)expression;
removeParensFromPrefixExpression(prefixExpression, ignoreClarifyingParentheses);
}
if (expression instanceof PsiPostfixExpression) {
final PsiPostfixExpression postfixExpression = (PsiPostfixExpression)expression;
removeParensFromPostfixExpression(postfixExpression, ignoreClarifyingParentheses);
}
if (expression instanceof PsiPolyadicExpression) {
final PsiPolyadicExpression polyadicExpression = (PsiPolyadicExpression)expression;
removeParensFromPolyadicExpression(polyadicExpression, ignoreClarifyingParentheses);
}
if (expression instanceof PsiInstanceOfExpression) {
final PsiInstanceOfExpression instanceofExpression = (PsiInstanceOfExpression)expression;
removeParensFromInstanceOfExpression(instanceofExpression, ignoreClarifyingParentheses);
}
if (expression instanceof PsiConditionalExpression) {
final PsiConditionalExpression conditionalExpression = (PsiConditionalExpression)expression;
removeParensFromConditionalExpression(conditionalExpression, ignoreClarifyingParentheses);
}
if (expression instanceof PsiParenthesizedExpression) {
final PsiParenthesizedExpression parenthesizedExpression = (PsiParenthesizedExpression)expression;
removeParensFromParenthesizedExpression(parenthesizedExpression, ignoreClarifyingParentheses);
}
}
private static void removeParensFromReferenceExpression(@NotNull PsiReferenceExpression referenceExpression,
boolean ignoreClarifyingParentheses) {
final PsiExpression qualifier = referenceExpression.getQualifierExpression();
if (qualifier != null) {
removeParentheses(qualifier, ignoreClarifyingParentheses);
}
}
private static void removeParensFromParenthesizedExpression(@NotNull PsiParenthesizedExpression parenthesizedExpression,
boolean ignoreClarifyingParentheses) {
final PsiExpression body = parenthesizedExpression.getExpression();
if (body == null) {
parenthesizedExpression.delete();
return;
}
final PsiElement parent = parenthesizedExpression.getParent();
if (!(parent instanceof PsiExpression) || parent instanceof PsiParenthesizedExpression ||
parent instanceof PsiArrayInitializerExpression) {
final PsiExpression newExpression = (PsiExpression)parenthesizedExpression.replace(body);
removeParentheses(newExpression, ignoreClarifyingParentheses);
return;
}
else if (parent instanceof PsiArrayAccessExpression) {
final PsiArrayAccessExpression arrayAccessExpression = (PsiArrayAccessExpression)parent;
if (parenthesizedExpression == arrayAccessExpression.getIndexExpression()) {
// use addAfter() + delete() instead of replace() to
// workaround automatic insertion of parentheses by psi
final PsiExpression newExpression = (PsiExpression)parent.addAfter(body, parenthesizedExpression);
parenthesizedExpression.delete();
removeParentheses(newExpression, ignoreClarifyingParentheses);
return;
}
}
final PsiExpression parentExpression = (PsiExpression)parent;
final int parentPrecedence = getPrecedence(parentExpression);
final int childPrecedence = getPrecedence(body);
if (parentPrecedence < childPrecedence) {
final PsiElement bodyParent = body.getParent();
final PsiParenthesizedExpression newParenthesizedExpression = (PsiParenthesizedExpression)parenthesizedExpression.replace(bodyParent);
final PsiExpression expression = newParenthesizedExpression.getExpression();
if (expression != null) {
removeParentheses(expression, ignoreClarifyingParentheses);
}
}
else if (parentPrecedence == childPrecedence) {
if (parentExpression instanceof PsiPolyadicExpression && body instanceof PsiPolyadicExpression) {
final PsiPolyadicExpression parentPolyadicExpression = (PsiPolyadicExpression)parentExpression;
final IElementType parentOperator = parentPolyadicExpression.getOperationTokenType();
final PsiPolyadicExpression bodyPolyadicExpression = (PsiPolyadicExpression)body;
final IElementType bodyOperator = bodyPolyadicExpression.getOperationTokenType();
final PsiType parentType = parentPolyadicExpression.getType();
final PsiType bodyType = body.getType();
if (parentType != null && parentType.equals(bodyType) && parentOperator.equals(bodyOperator)) {
final PsiExpression[] parentOperands = parentPolyadicExpression.getOperands();
if (PsiTreeUtil.isAncestor(parentOperands[0], body, true) || isCommutativeOperator(bodyOperator)) {
// use addAfter() + delete() instead of replace() to
// workaround automatic insertion of parentheses by psi
final PsiExpression newExpression = (PsiExpression)parent.addAfter(body, parenthesizedExpression);
parenthesizedExpression.delete();
removeParentheses(newExpression, ignoreClarifyingParentheses);
return;
}
}
if (ignoreClarifyingParentheses) {
if (parentOperator.equals(bodyOperator)) {
removeParentheses(body, ignoreClarifyingParentheses);
}
}
else {
final PsiExpression newExpression = (PsiExpression)parenthesizedExpression.replace(body);
removeParentheses(newExpression, ignoreClarifyingParentheses);
}
}
else {
final PsiExpression newExpression = (PsiExpression)parenthesizedExpression.replace(body);
removeParentheses(newExpression, ignoreClarifyingParentheses);
}
}
else {
if (ignoreClarifyingParentheses && parent instanceof PsiPolyadicExpression &&
(body instanceof PsiPolyadicExpression || body instanceof PsiInstanceOfExpression)) {
removeParentheses(body, ignoreClarifyingParentheses);
}
else {
final PsiExpression newExpression = (PsiExpression)parenthesizedExpression.replace(body);
removeParentheses(newExpression, ignoreClarifyingParentheses);
}
}
}
private static void removeParensFromConditionalExpression(@NotNull PsiConditionalExpression conditionalExpression,
boolean ignoreClarifyingParentheses) {
final PsiExpression condition = conditionalExpression.getCondition();
removeParentheses(condition, ignoreClarifyingParentheses);
final PsiExpression thenBranch = conditionalExpression.getThenExpression();
if (thenBranch != null) {
removeParentheses(thenBranch, ignoreClarifyingParentheses);
}
final PsiExpression elseBranch = conditionalExpression.getElseExpression();
if (elseBranch != null) {
removeParentheses(elseBranch, ignoreClarifyingParentheses);
}
}
private static void removeParensFromInstanceOfExpression(@NotNull PsiInstanceOfExpression instanceofExpression,
boolean ignoreClarifyingParentheses) {
final PsiExpression operand = instanceofExpression.getOperand();
removeParentheses(operand, ignoreClarifyingParentheses);
}
private static void removeParensFromPolyadicExpression(@NotNull PsiPolyadicExpression polyadicExpression,
boolean ignoreClarifyingParentheses) {
for (PsiExpression operand : polyadicExpression.getOperands()) {
removeParentheses(operand, ignoreClarifyingParentheses);
}
}
private static void removeParensFromPostfixExpression(@NotNull PsiPostfixExpression postfixExpression,
boolean ignoreClarifyingParentheses) {
final PsiExpression operand = postfixExpression.getOperand();
removeParentheses(operand, ignoreClarifyingParentheses);
}
private static void removeParensFromPrefixExpression(@NotNull PsiPrefixExpression prefixExpression, boolean ignoreClarifyingParentheses) {
final PsiExpression operand = prefixExpression.getOperand();
if (operand != null) {
removeParentheses(operand, ignoreClarifyingParentheses);
}
}
private static void removeParensFromArrayAccessExpression(@NotNull PsiArrayAccessExpression arrayAccessExpression,
boolean ignoreClarifyingParentheses) {
final PsiExpression arrayExpression = arrayAccessExpression.getArrayExpression();
removeParentheses(arrayExpression, ignoreClarifyingParentheses);
final PsiExpression indexExpression = arrayAccessExpression.getIndexExpression();
if (indexExpression != null) {
removeParentheses(indexExpression, ignoreClarifyingParentheses);
}
}
private static void removeParensFromTypeCastExpression(@NotNull PsiTypeCastExpression typeCastExpression,
boolean ignoreClarifyingParentheses) {
final PsiExpression operand = typeCastExpression.getOperand();
if (operand != null) {
removeParentheses(operand, ignoreClarifyingParentheses);
}
}
private static void removeParensFromArrayInitializerExpression(@NotNull PsiArrayInitializerExpression arrayInitializerExpression,
boolean ignoreClarifyingParentheses) {
final PsiExpression[] initializers = arrayInitializerExpression.getInitializers();
for (final PsiExpression initializer : initializers) {
removeParentheses(initializer, ignoreClarifyingParentheses);
}
}
private static void removeParensFromAssignmentExpression(@NotNull PsiAssignmentExpression assignment,
boolean ignoreClarifyingParentheses) {
final PsiExpression lhs = assignment.getLExpression();
final PsiExpression rhs = assignment.getRExpression();
removeParentheses(lhs, ignoreClarifyingParentheses);
if (rhs != null) {
removeParentheses(rhs, ignoreClarifyingParentheses);
}
}
private static void removeParensFromNewExpression(@NotNull PsiNewExpression newExpression, boolean ignoreClarifyingParentheses) {
final PsiExpression[] dimensions = newExpression.getArrayDimensions();
for (PsiExpression dimension : dimensions) {
removeParentheses(dimension, ignoreClarifyingParentheses);
}
final PsiExpression qualifier = newExpression.getQualifier();
if (qualifier != null) {
removeParentheses(qualifier, ignoreClarifyingParentheses);
}
final PsiExpression arrayInitializer = newExpression.getArrayInitializer();
if (arrayInitializer != null) {
removeParentheses(arrayInitializer, ignoreClarifyingParentheses);
}
final PsiExpressionList argumentList = newExpression.getArgumentList();
if (argumentList != null) {
final PsiExpression[] arguments = argumentList.getExpressions();
for (PsiExpression argument : arguments) {
removeParentheses(argument, ignoreClarifyingParentheses);
}
}
}
private static void removeParensFromMethodCallExpression(@NotNull PsiMethodCallExpression methodCallExpression,
boolean ignoreClarifyingParentheses) {
final PsiReferenceExpression target = methodCallExpression.getMethodExpression();
final PsiExpressionList argumentList = methodCallExpression.getArgumentList();
final PsiExpression[] arguments = argumentList.getExpressions();
removeParentheses(target, ignoreClarifyingParentheses);
for (final PsiExpression argument : arguments) {
removeParentheses(argument, ignoreClarifyingParentheses);
}
}
public static boolean areParenthesesNeeded(PsiParenthesizedExpression expression, boolean ignoreClarifyingParentheses) {
final PsiElement parent = expression.getParent();
if (!(parent instanceof PsiExpression)) {
return false;
}
final PsiExpression child = expression.getExpression();
if (child == null) {
return true;
}
if (parent instanceof PsiArrayAccessExpression) {
final PsiArrayAccessExpression arrayAccessExpression = (PsiArrayAccessExpression)parent;
final PsiExpression indexExpression = arrayAccessExpression.getIndexExpression();
if (expression == indexExpression) {
return false;
}
}
return areParenthesesNeeded(child, (PsiExpression)parent, ignoreClarifyingParentheses);
}
public static boolean areParenthesesNeeded(PsiExpression expression, PsiExpression parentExpression,
boolean ignoreClarifyingParentheses) {
if (parentExpression instanceof PsiParenthesizedExpression || parentExpression instanceof PsiArrayInitializerExpression) {
return false;
}
final int parentPrecedence = getPrecedence(parentExpression);
final int childPrecedence = getPrecedence(expression);
if (parentPrecedence > childPrecedence) {
if (ignoreClarifyingParentheses) {
if (expression instanceof PsiPolyadicExpression) {
if (parentExpression instanceof PsiPolyadicExpression ||
parentExpression instanceof PsiConditionalExpression ||
parentExpression instanceof PsiInstanceOfExpression) {
return true;
}
}
else if (expression instanceof PsiInstanceOfExpression) {
return true;
}
}
return false;
}
if (parentExpression instanceof PsiPolyadicExpression && expression instanceof PsiPolyadicExpression) {
final PsiPolyadicExpression parentPolyadicExpression = (PsiPolyadicExpression)parentExpression;
final PsiType parentType = parentPolyadicExpression.getType();
if (parentType == null) {
return true;
}
final PsiPolyadicExpression childPolyadicExpression = (PsiPolyadicExpression)expression;
final PsiType childType = childPolyadicExpression.getType();
if (!parentType.equals(childType)) {
return true;
}
if (childType.equalsToText(CommonClassNames.JAVA_LANG_STRING) &&
!PsiTreeUtil.isAncestor(parentPolyadicExpression.getOperands()[0], childPolyadicExpression, true)) {
final PsiExpression[] operands = childPolyadicExpression.getOperands();
for (PsiExpression operand : operands) {
if (!childType.equals(operand.getType())) {
return true;
}
}
}
else if (childType.equals(PsiType.BOOLEAN)) {
final PsiExpression[] operands = childPolyadicExpression.getOperands();
for (PsiExpression operand : operands) {
if (!PsiType.BOOLEAN.equals(operand.getType())) {
return true;
}
}
}
final IElementType parentOperator = parentPolyadicExpression.getOperationTokenType();
final IElementType childOperator = childPolyadicExpression.getOperationTokenType();
if (ignoreClarifyingParentheses) {
if (!childOperator.equals(parentOperator)) {
return true;
}
}
final PsiExpression[] parentOperands = parentPolyadicExpression.getOperands();
if (!PsiTreeUtil.isAncestor(parentOperands[0], expression, false)) {
if (!isAssociativeOperation(parentPolyadicExpression) ||
JavaTokenType.DIV == childOperator || JavaTokenType.PERC == childOperator) {
return true;
}
}
}
else if (parentExpression instanceof PsiConditionalExpression && expression instanceof PsiConditionalExpression) {
final PsiConditionalExpression conditionalExpression = (PsiConditionalExpression)parentExpression;
final PsiExpression condition = conditionalExpression.getCondition();
return PsiTreeUtil.isAncestor(condition, expression, true);
}
return parentPrecedence < childPrecedence;
}
}