blob: 4be923268b10603705884daff56088ce839144eb [file] [log] [blame]
/*
* Copyright 2007-2008 Dave Griffith
*
* 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 org.jetbrains.plugins.groovy.codeInspection.control;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiType;
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.groovy.codeInspection.BaseInspection;
import org.jetbrains.plugins.groovy.codeInspection.BaseInspectionVisitor;
import org.jetbrains.plugins.groovy.codeInspection.GroovyFix;
import org.jetbrains.plugins.groovy.codeInspection.utils.BoolUtils;
import org.jetbrains.plugins.groovy.codeInspection.utils.EquivalenceChecker;
import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrCondition;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrIfStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrAssignmentExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
public class GroovyTrivialIfInspection extends BaseInspection {
@Override
@NotNull
public String getDisplayName() {
return "Redundant 'if' statement";
}
@Override
@NotNull
public String getGroupDisplayName() {
return CONTROL_FLOW;
}
@NotNull
@Override
public BaseInspectionVisitor buildVisitor() {
return new TrivialIfVisitor();
}
@Override
public boolean isEnabledByDefault() {
return true;
}
@Override
public String buildErrorString(Object... args) {
return "#ref statement can be simplified #loc";
}
@Override
public GroovyFix buildFix(@NotNull PsiElement location) {
return new TrivialIfFix();
}
private static class TrivialIfFix extends GroovyFix {
@Override
@NotNull
public String getName() {
return "Simplify";
}
@Override
public void doFix(Project project, ProblemDescriptor descriptor)
throws IncorrectOperationException {
final PsiElement ifKeywordElement = descriptor.getPsiElement();
final GrIfStatement statement =
(GrIfStatement) ifKeywordElement.getParent();
if (isSimplifiableAssignment(statement)) {
replaceSimplifiableAssignment(statement);
} else if (isSimplifiableReturn(statement)) {
repaceSimplifiableReturn(statement);
} else if (isSimplifiableImplicitReturn(statement)) {
replaceSimplifiableImplicitReturn(statement);
} else if (isSimplifiableAssignmentNegated(statement)) {
replaceSimplifiableAssignmentNegated(statement);
} else if (isSimplifiableReturnNegated(statement)) {
repaceSimplifiableReturnNegated(statement);
} else if (isSimplifiableImplicitReturnNegated(statement)) {
replaceSimplifiableImplicitReturnNegated(statement);
} else if (isSimplifiableImplicitAssignment(statement)) {
replaceSimplifiableImplicitAssignment(statement);
} else if (isSimplifiableImplicitAssignmentNegated(statement)) {
replaceSimplifiableImplicitAssignmentNegated(statement);
}
}
private static void replaceSimplifiableImplicitReturn(GrIfStatement statement)
throws IncorrectOperationException {
final GrCondition condition = statement.getCondition();
final String conditionText = condition.getText();
final PsiElement nextStatement =
PsiTreeUtil.skipSiblingsForward(statement,
PsiWhiteSpace.class);
@NonNls final String newStatement = "return " + conditionText + ';';
replaceStatement(statement, newStatement);
assert nextStatement != null;
nextStatement.delete();
}
private static void repaceSimplifiableReturn(GrIfStatement statement)
throws IncorrectOperationException {
final GrCondition condition = statement.getCondition();
final String conditionText = condition.getText();
@NonNls final String newStatement = "return " + conditionText + ';';
replaceStatement(statement, newStatement);
}
private static void replaceSimplifiableAssignment(GrIfStatement statement)
throws IncorrectOperationException {
final GrCondition condition = statement.getCondition();
final String conditionText = condition.getText();
final GrStatement thenBranch = statement.getThenBranch();
final GrAssignmentExpression assignmentExpression =
(GrAssignmentExpression) ConditionalUtils.stripBraces(thenBranch);
final IElementType operator =
assignmentExpression.getOperationTokenType();
final String operatorText = getTextForOperator(operator);
final GrExpression lhs = assignmentExpression.getLValue();
final String lhsText = lhs.getText();
replaceStatement(statement,
lhsText + operatorText + conditionText + ';');
}
private static void replaceSimplifiableImplicitAssignment(GrIfStatement statement)
throws IncorrectOperationException {
final PsiElement prevStatement =
PsiTreeUtil.skipSiblingsBackward(statement,
PsiWhiteSpace.class);
if (prevStatement == null) {
return;
}
final GrCondition condition = statement.getCondition();
final String conditionText = condition.getText();
final GrStatement thenBranch = statement.getThenBranch();
final GrAssignmentExpression assignmentExpression =
(GrAssignmentExpression) ConditionalUtils.stripBraces(thenBranch);
final IElementType operator =
assignmentExpression.getOperationTokenType();
final GrExpression lhs = assignmentExpression.getLValue();
final String lhsText = lhs.getText();
replaceStatement(statement,
lhsText + operator + conditionText + ';');
prevStatement.delete();
}
private static void replaceSimplifiableImplicitAssignmentNegated(GrIfStatement statement)
throws IncorrectOperationException {
final PsiElement prevStatement =
PsiTreeUtil.skipSiblingsBackward(statement,
PsiWhiteSpace.class);
final GrCondition condition = statement.getCondition();
if (!(condition instanceof GrExpression)) {
return;
}
final GrExpression expression = (GrExpression) condition;
final String conditionText =
BoolUtils.getNegatedExpressionText(expression);
final GrStatement thenBranch = statement.getThenBranch();
final GrAssignmentExpression assignmentExpression =
(GrAssignmentExpression) ConditionalUtils.stripBraces(thenBranch);
final IElementType operator =
assignmentExpression.getOperationTokenType();
final String operatorText = getTextForOperator(operator);
final GrExpression lhs = assignmentExpression.getLValue();
final String lhsText = lhs.getText();
replaceStatement(statement,
lhsText + operatorText + conditionText + ';');
assert prevStatement != null;
prevStatement.delete();
}
private static void replaceSimplifiableImplicitReturnNegated(GrIfStatement statement)
throws IncorrectOperationException {
final GrCondition condition = statement.getCondition();
if (!(condition instanceof GrExpression)) {
return;
}
final GrExpression expression = (GrExpression) condition;
final String conditionText =
BoolUtils.getNegatedExpressionText(expression);
final PsiElement nextStatement =
PsiTreeUtil.skipSiblingsForward(statement,
PsiWhiteSpace.class);
if (nextStatement == null) {
return;
}
@NonNls final String newStatement = "return " + conditionText + ';';
replaceStatement(statement, newStatement);
nextStatement.delete();
}
private static void repaceSimplifiableReturnNegated(GrIfStatement statement)
throws IncorrectOperationException {
final GrCondition condition = statement.getCondition();
if (!(condition instanceof GrExpression)) {
return;
}
final GrExpression expression = (GrExpression) condition;
final String conditionText =
BoolUtils.getNegatedExpressionText(expression);
@NonNls final String newStatement = "return " + conditionText + ';';
replaceStatement(statement, newStatement);
}
private static void replaceSimplifiableAssignmentNegated(GrIfStatement statement)
throws IncorrectOperationException {
final GrCondition condition = statement.getCondition();
if (!(condition instanceof GrExpression)) {
return;
}
final GrExpression expression = (GrExpression) condition;
final String conditionText =
BoolUtils.getNegatedExpressionText(expression);
final GrStatement thenBranch = statement.getThenBranch();
final GrAssignmentExpression assignmentExpression =
(GrAssignmentExpression) ConditionalUtils.stripBraces(thenBranch);
final IElementType operator =
assignmentExpression.getOperationTokenType();
final String operatorText = getTextForOperator(operator);
final GrExpression lhs = assignmentExpression.getLValue();
final String lhsText = lhs.getText();
replaceStatement(statement,
lhsText + operatorText + conditionText + ';');
}
}
private static class TrivialIfVisitor extends BaseInspectionVisitor {
@Override
public void visitIfStatement(@NotNull GrIfStatement ifStatement) {
super.visitIfStatement(ifStatement);
final GrCondition condition = ifStatement.getCondition();
if (!(condition instanceof GrExpression)) {
return;
}
final PsiType type = ((GrExpression)condition).getType();
if (type == null || !(PsiType.BOOLEAN.isAssignableFrom(type))) {
return;
}
if (isSimplifiableAssignment(ifStatement)) {
registerStatementError(ifStatement);
return;
}
if (isSimplifiableReturn(ifStatement)) {
registerStatementError(ifStatement);
return;
}
if (isSimplifiableImplicitReturn(ifStatement)) {
registerStatementError(ifStatement);
return;
}
if (isSimplifiableAssignmentNegated(ifStatement)) {
registerStatementError(ifStatement);
return;
}
if (isSimplifiableReturnNegated(ifStatement)) {
registerStatementError(ifStatement);
return;
}
if (isSimplifiableImplicitReturnNegated(ifStatement)) {
registerStatementError(ifStatement);
return;
}
if (isSimplifiableImplicitAssignment(ifStatement)) {
registerStatementError(ifStatement);
return;
}
if (isSimplifiableImplicitAssignmentNegated(ifStatement)) {
registerStatementError(ifStatement);
}
}
}
public static boolean isSimplifiableImplicitReturn(GrIfStatement ifStatement) {
if (ifStatement.getElseBranch() != null) {
return false;
}
GrStatement thenBranch = ifStatement.getThenBranch();
thenBranch = ConditionalUtils.stripBraces(thenBranch);
final PsiElement nextStatement =
PsiTreeUtil.skipSiblingsForward(ifStatement,
PsiWhiteSpace.class);
if (!(nextStatement instanceof GrStatement)) {
return false;
}
final GrStatement elseBranch = (GrStatement) nextStatement;
return ConditionalUtils.isReturn(thenBranch, "true")
&& ConditionalUtils.isReturn(elseBranch, "false");
}
public static boolean isSimplifiableImplicitReturnNegated(GrIfStatement ifStatement) {
if (ifStatement.getElseBranch() != null) {
return false;
}
GrStatement thenBranch = ifStatement.getThenBranch();
thenBranch = ConditionalUtils.stripBraces(thenBranch);
final PsiElement nextStatement =
PsiTreeUtil.skipSiblingsForward(ifStatement,
PsiWhiteSpace.class);
if (!(nextStatement instanceof GrStatement)) {
return false;
}
final GrStatement elseBranch = (GrStatement) nextStatement;
return ConditionalUtils.isReturn(thenBranch, "false")
&& ConditionalUtils.isReturn(elseBranch, "true");
}
public static boolean isSimplifiableReturn(GrIfStatement ifStatement) {
GrStatement thenBranch = ifStatement.getThenBranch();
thenBranch = ConditionalUtils.stripBraces(thenBranch);
GrStatement elseBranch = ifStatement.getElseBranch();
elseBranch = ConditionalUtils.stripBraces(elseBranch);
return ConditionalUtils.isReturn(thenBranch, "true")
&& ConditionalUtils.isReturn(elseBranch, "false");
}
public static boolean isSimplifiableReturnNegated(GrIfStatement ifStatement) {
GrStatement thenBranch = ifStatement.getThenBranch();
thenBranch = ConditionalUtils.stripBraces(thenBranch);
GrStatement elseBranch = ifStatement.getElseBranch();
elseBranch = ConditionalUtils.stripBraces(elseBranch);
return ConditionalUtils.isReturn(thenBranch, "false")
&& ConditionalUtils.isReturn(elseBranch, "true");
}
public static boolean isSimplifiableAssignment(GrIfStatement ifStatement) {
GrStatement thenBranch = ifStatement.getThenBranch();
thenBranch = ConditionalUtils.stripBraces(thenBranch);
GrStatement elseBranch = ifStatement.getElseBranch();
elseBranch = ConditionalUtils.stripBraces(elseBranch);
if (ConditionalUtils.isAssignment(thenBranch, "true") &&
ConditionalUtils.isAssignment(elseBranch, "false")) {
final GrAssignmentExpression thenExpression =
(GrAssignmentExpression) thenBranch;
final GrAssignmentExpression elseExpression =
(GrAssignmentExpression) elseBranch;
final IElementType thenSign = thenExpression.getOperationTokenType();
final IElementType elseSign = elseExpression.getOperationTokenType();
if (!thenSign.equals(elseSign)) {
return false;
}
final GrExpression thenLhs = thenExpression.getLValue();
final GrExpression elseLhs = elseExpression.getLValue();
return EquivalenceChecker.expressionsAreEquivalent(thenLhs,
elseLhs);
} else {
return false;
}
}
public static boolean isSimplifiableAssignmentNegated(GrIfStatement ifStatement) {
GrStatement thenBranch = ifStatement.getThenBranch();
thenBranch = ConditionalUtils.stripBraces(thenBranch);
GrStatement elseBranch = ifStatement.getElseBranch();
elseBranch = ConditionalUtils.stripBraces(elseBranch);
if (ConditionalUtils.isAssignment(thenBranch, "false") &&
ConditionalUtils.isAssignment(elseBranch, "true")) {
final GrAssignmentExpression thenExpression =
(GrAssignmentExpression) thenBranch;
final GrAssignmentExpression elseExpression =
(GrAssignmentExpression) elseBranch;
final IElementType thenSign = thenExpression.getOperationTokenType();
final IElementType elseSign = elseExpression.getOperationTokenType();
if (!thenSign.equals(elseSign)) {
return false;
}
final GrExpression thenLhs = thenExpression.getLValue();
final GrExpression elseLhs = elseExpression.getLValue();
return EquivalenceChecker.expressionsAreEquivalent(thenLhs,
elseLhs);
} else {
return false;
}
}
public static boolean isSimplifiableImplicitAssignment(GrIfStatement ifStatement) {
if (ifStatement.getElseBranch() != null) {
return false;
}
GrStatement thenBranch = ifStatement.getThenBranch();
thenBranch = ConditionalUtils.stripBraces(thenBranch);
final PsiElement nextStatement =
PsiTreeUtil.skipSiblingsBackward(ifStatement,
PsiWhiteSpace.class);
if (!(nextStatement instanceof GrStatement)) {
return false;
}
GrStatement elseBranch = (GrStatement) nextStatement;
elseBranch = ConditionalUtils.stripBraces(elseBranch);
if (ConditionalUtils.isAssignment(thenBranch, "true") &&
ConditionalUtils.isAssignment(elseBranch, "false")) {
final GrAssignmentExpression thenExpression =
(GrAssignmentExpression) thenBranch;
final GrAssignmentExpression elseExpression =
(GrAssignmentExpression) elseBranch;
final IElementType thenSign = thenExpression.getOperationTokenType();
final IElementType elseSign = elseExpression.getOperationTokenType();
if (!thenSign.equals(elseSign)) {
return false;
}
final GrExpression thenLhs = thenExpression.getLValue();
final GrExpression elseLhs = elseExpression.getLValue();
return EquivalenceChecker.expressionsAreEquivalent(thenLhs,
elseLhs);
} else {
return false;
}
}
public static boolean isSimplifiableImplicitAssignmentNegated(GrIfStatement ifStatement) {
if (ifStatement.getElseBranch() != null) {
return false;
}
GrStatement thenBranch = ifStatement.getThenBranch();
thenBranch = ConditionalUtils.stripBraces(thenBranch);
final PsiElement nextStatement =
PsiTreeUtil.skipSiblingsBackward(ifStatement,
PsiWhiteSpace.class);
if (!(nextStatement instanceof GrStatement)) {
return false;
}
GrStatement elseBranch = (GrStatement) nextStatement;
elseBranch = ConditionalUtils.stripBraces(elseBranch);
if (ConditionalUtils.isAssignment(thenBranch, "false") &&
ConditionalUtils.isAssignment(elseBranch, "true")) {
final GrAssignmentExpression thenExpression =
(GrAssignmentExpression) thenBranch;
final GrAssignmentExpression elseExpression =
(GrAssignmentExpression) elseBranch;
final IElementType thenSign = thenExpression.getOperationTokenType();
final IElementType elseSign = elseExpression.getOperationTokenType();
if (!thenSign.equals(elseSign)) {
return false;
}
final GrExpression thenLhs = thenExpression.getLValue();
final GrExpression elseLhs = elseExpression.getLValue();
return EquivalenceChecker.expressionsAreEquivalent(thenLhs,
elseLhs);
} else {
return false;
}
}
@NonNls
private static String getTextForOperator(IElementType operator) {
if (operator.equals(GroovyTokenTypes.mASSIGN)) {
return "=";
}
if (operator.equals(GroovyTokenTypes.mNOT_EQUAL)) {
return "!=";
}
if (operator.equals(GroovyTokenTypes.mLE)) {
return "<=";
}
if (operator.equals(GroovyTokenTypes.mGE)) {
return ">=";
}
if (operator.equals(GroovyTokenTypes.mLT)) {
return "<=";
}
if (operator.equals(GroovyTokenTypes.mGT)) {
return ">=";
}
if (operator.equals(GroovyTokenTypes.mELVIS)) {
return "==";
}
if (operator.equals(GroovyTokenTypes.mPLUS_ASSIGN)) {
return "+=";
}
if (operator.equals(GroovyTokenTypes.mMINUS_ASSIGN)) {
return "-=";
}
if (operator.equals(GroovyTokenTypes.mSTAR_ASSIGN)) {
return "*=";
}
if (operator.equals(GroovyTokenTypes.mDIV_ASSIGN)) {
return "/=";
}
if (operator.equals(GroovyTokenTypes.mMOD_ASSIGN)) {
return "%=";
}
if (operator.equals(GroovyTokenTypes.mBXOR_ASSIGN)) {
return "^=";
}
if (operator.equals(GroovyTokenTypes.mBAND_ASSIGN)) {
return "&=";
}
if (operator.equals(GroovyTokenTypes.mBOR_ASSIGN)) {
return "|=";
}
return "unknown";
}
}