teach "Replace 'if' with 'switch'" about polyadic expressions, do not offer to replace with switch when expression has long type

This commit is contained in:
Bas Leijdekkers
2013-03-27 20:47:28 +01:00
parent 4a75446789
commit bb5daaefcd
8 changed files with 221 additions and 251 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2003-2005 Dave Griffith
* Copyright 2003-2013 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.
@@ -15,10 +15,7 @@
*/
package com.siyeh.ipp.switchtoif;
import com.intellij.psi.JavaRecursiveElementWalkingVisitor;
import com.intellij.psi.PsiIdentifier;
import com.intellij.psi.PsiLabeledStatement;
import com.intellij.psi.PsiReferenceExpression;
import com.intellij.psi.*;
class LabelSearchVisitor extends JavaRecursiveElementWalkingVisitor {
@@ -26,12 +23,15 @@ class LabelSearchVisitor extends JavaRecursiveElementWalkingVisitor {
private boolean m_used = false;
LabelSearchVisitor(String name) {
super();
m_labelName = name;
}
@Override
public void visitReferenceExpression(PsiReferenceExpression expression) {
public void visitElement(PsiElement element) {
if (m_used) {
return;
}
super.visitElement(element);
}
@Override
@@ -1,45 +0,0 @@
/*
* Copyright 2003-2005 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 com.siyeh.ipp.switchtoif;
import com.intellij.psi.JavaRecursiveElementWalkingVisitor;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiLocalVariable;
import com.intellij.psi.PsiReferenceExpression;
class LocalVariableUsageVisitor extends JavaRecursiveElementWalkingVisitor {
private final PsiLocalVariable m_var;
private boolean m_used = false;
LocalVariableUsageVisitor(PsiLocalVariable name) {
super();
m_var = name;
}
@Override
public void visitReferenceExpression(PsiReferenceExpression expression) {
final PsiElement reference = expression.resolve();
if (m_var.equals(reference)) {
m_used = true;
}
super.visitReferenceElement(expression);
}
public boolean isUsed() {
return m_used;
}
}
@@ -223,20 +223,22 @@ public class ReplaceIfWithSwitchIntention extends Intention {
values.addCaseExpression(argument);
}
}
else if (expression instanceof PsiBinaryExpression) {
final PsiBinaryExpression binaryExpression = (PsiBinaryExpression)expression;
final PsiExpression lhs = binaryExpression.getLOperand();
final PsiExpression rhs = binaryExpression.getROperand();
final IElementType tokenType = binaryExpression.getOperationTokenType();
else if (expression instanceof PsiPolyadicExpression) {
final PsiPolyadicExpression polyadicExpression = (PsiPolyadicExpression)expression;
final PsiExpression[] operands = polyadicExpression.getOperands();
final IElementType tokenType = polyadicExpression.getOperationTokenType();
if (JavaTokenType.OROR.equals(tokenType)) {
extractCaseExpressions(lhs, switchExpression, values);
extractCaseExpressions(rhs, switchExpression, values);
for (PsiExpression operand : operands) {
extractCaseExpressions(operand, switchExpression, values);
}
}
else {
else if (JavaTokenType.EQEQ.equals(tokenType) && operands.length == 2) {
final PsiExpression lhs = operands[0];
final PsiExpression rhs = operands[1];
if (EquivalenceChecker.expressionsAreEquivalent(switchExpression, rhs)) {
values.addCaseExpression(lhs);
}
else {
else if (EquivalenceChecker.expressionsAreEquivalent(switchExpression, lhs)){
values.addCaseExpression(rhs);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2003-2011 Dave Griffith, Bas Leijdekkers
* Copyright 2003-2013 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.
@@ -21,60 +21,181 @@ import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.siyeh.ipp.psiutils.EquivalenceChecker;
import com.siyeh.ipp.psiutils.ParenthesesUtils;
import com.siyeh.ipp.psiutils.SideEffectChecker;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;
import java.util.List;
class SwitchUtils {
private SwitchUtils() {
private SwitchUtils() {}
@Nullable
public static PsiExpression getSwitchExpression(PsiIfStatement statement) {
final PsiExpression condition = statement.getCondition();
final LanguageLevel languageLevel = PsiUtil.getLanguageLevel(statement);
final PsiExpression possibleSwitchExpression = determinePossibleSwitchExpressions(condition, languageLevel);
if (!canBeSwitchExpression(possibleSwitchExpression, languageLevel)) {
return null;
}
while (true) {
if (!canBeMadeIntoCase(statement.getCondition(), possibleSwitchExpression, languageLevel)) {
break;
}
final PsiStatement elseBranch = statement.getElseBranch();
if (!(elseBranch instanceof PsiIfStatement)) {
return possibleSwitchExpression;
}
statement = (PsiIfStatement)elseBranch;
}
return null;
}
private static boolean canBeCaseLabel(PsiExpression expression,
LanguageLevel languageLevel) {
if (expression == null) {
return false;
}
if (languageLevel.compareTo(LanguageLevel.JDK_1_5) >= 0
&& expression instanceof PsiReferenceExpression) {
final PsiElement referent = ((PsiReference)expression).resolve();
if (referent instanceof PsiEnumConstant) {
private static boolean canBeMadeIntoCase(PsiExpression expression, PsiExpression switchExpression, LanguageLevel languageLevel) {
expression = ParenthesesUtils.stripParentheses(expression);
if (languageLevel.isAtLeast(LanguageLevel.JDK_1_7)) {
final PsiExpression stringSwitchExpression = determinePossibleStringSwitchExpression(expression);
if (EquivalenceChecker.expressionsAreEquivalent(switchExpression, stringSwitchExpression)) {
return true;
}
}
final PsiType type = expression.getType();
return type != null &&
(type.equals(PsiType.INT) ||
type.equals(PsiType.CHAR) ||
type.equals(PsiType.LONG) ||
type.equals(PsiType.SHORT)) &&
PsiUtil.isConstantExpression(expression);
if (!(expression instanceof PsiPolyadicExpression)) {
return false;
}
final PsiPolyadicExpression polyadicExpression = (PsiPolyadicExpression)expression;
final IElementType operation = polyadicExpression.getOperationTokenType();
final PsiExpression[] operands = polyadicExpression.getOperands();
if (operation.equals(JavaTokenType.OROR)) {
for (PsiExpression operand : operands) {
if (!canBeMadeIntoCase(operand, switchExpression, languageLevel)) {
return false;
}
}
return true;
}
else if (operation.equals(JavaTokenType.EQEQ) && operands.length == 2) {
return (canBeCaseLabel(operands[0], languageLevel) && EquivalenceChecker.expressionsAreEquivalent(switchExpression, operands[1])) ||
(canBeCaseLabel(operands[1], languageLevel) && EquivalenceChecker.expressionsAreEquivalent(switchExpression, operands[0]));
}
else {
return false;
}
}
public static boolean isUsedByStatementList(PsiLocalVariable variable,
List<PsiElement> elements) {
for (PsiElement element : elements) {
if (isUsedByStatement(variable, element)) {
private static boolean canBeSwitchExpression(PsiExpression expression, LanguageLevel languageLevel) {
if (expression == null || SideEffectChecker.mayHaveSideEffects(expression)) {
return false;
}
final PsiType type = expression.getType();
if (PsiType.CHAR.equals(type) || PsiType.BYTE.equals(type) || PsiType.SHORT.equals(type) || PsiType.INT.equals(type)) {
return true;
}
else if (type instanceof PsiClassType) {
if (type.equalsToText(CommonClassNames.JAVA_LANG_CHARACTER) || type.equalsToText(CommonClassNames.JAVA_LANG_BYTE) ||
type.equalsToText(CommonClassNames.JAVA_LANG_SHORT) || type.equalsToText(CommonClassNames.JAVA_LANG_INTEGER)) {
return true;
}
if (languageLevel.isAtLeast(LanguageLevel.JDK_1_5)) {
final PsiClassType classType = (PsiClassType)type;
final PsiClass aClass = classType.resolve();
if (aClass != null && aClass.isEnum()) {
return true;
}
}
if (languageLevel.isAtLeast(LanguageLevel.JDK_1_7) && type.equalsToText(CommonClassNames.JAVA_LANG_STRING)) {
return true;
}
}
return false;
}
private static boolean isUsedByStatement(PsiLocalVariable variable,
PsiElement statement) {
final LocalVariableUsageVisitor visitor =
new LocalVariableUsageVisitor(variable);
statement.accept(visitor);
return visitor.isUsed();
private static PsiExpression determinePossibleSwitchExpressions(PsiExpression expression, LanguageLevel languageLevel) {
expression = ParenthesesUtils.stripParentheses(expression);
if (expression == null) {
return null;
}
if (languageLevel.isAtLeast(LanguageLevel.JDK_1_7)) {
final PsiExpression jdk17Expression = determinePossibleStringSwitchExpression(expression);
if (jdk17Expression != null) {
return jdk17Expression;
}
}
if (!(expression instanceof PsiPolyadicExpression)) {
return null;
}
final PsiPolyadicExpression polyadicExpression = (PsiPolyadicExpression)expression;
final IElementType operation = polyadicExpression.getOperationTokenType();
final PsiExpression[] operands = polyadicExpression.getOperands();
if (operation.equals(JavaTokenType.OROR) && operands.length > 0) {
return determinePossibleSwitchExpressions(operands[0], languageLevel);
}
else if (operation.equals(JavaTokenType.EQEQ) && operands.length == 2) {
final PsiExpression lhs = operands[0];
final PsiExpression rhs = operands[1];
if (canBeCaseLabel(lhs, languageLevel)) {
return rhs;
}
else if (canBeCaseLabel(rhs, languageLevel)) {
return lhs;
}
}
return null;
}
public static String findUniqueLabelName(PsiStatement statement,
@NonNls String baseName) {
final PsiElement ancestor =
PsiTreeUtil.getParentOfType(statement, PsiMember.class);
private static PsiExpression determinePossibleStringSwitchExpression(PsiExpression expression) {
if (!(expression instanceof PsiMethodCallExpression)) {
return null;
}
final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)expression;
final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression();
@NonNls final String referenceName = methodExpression.getReferenceName();
if (!"equals".equals(referenceName)) {
return null;
}
final PsiExpression qualifierExpression = methodExpression.getQualifierExpression();
if (qualifierExpression == null) {
return null;
}
final PsiType type = qualifierExpression.getType();
if (type == null || !type.equalsToText(CommonClassNames.JAVA_LANG_STRING)) {
return null;
}
final PsiExpressionList argumentList = methodCallExpression.getArgumentList();
final PsiExpression[] arguments = argumentList.getExpressions();
if (arguments.length != 1) {
return null;
}
final PsiExpression argument = arguments[0];
final PsiType argumentType = argument.getType();
if (argumentType == null || !argumentType.equalsToText(CommonClassNames.JAVA_LANG_STRING)) {
return null;
}
if (PsiUtil.isConstantExpression(qualifierExpression)) {
return argument;
}
else if (PsiUtil.isConstantExpression(argument)) {
return qualifierExpression;
}
return null;
}
private static boolean canBeCaseLabel(PsiExpression expression, LanguageLevel languageLevel) {
if (expression == null) {
return false;
}
if (languageLevel.isAtLeast(LanguageLevel.JDK_1_5) && expression instanceof PsiReferenceExpression) {
final PsiElement referent = ((PsiReference)expression).resolve();
if (referent instanceof PsiEnumConstant) {
return true;
}
}
final PsiType type = expression.getType();
return (PsiType.INT.equals(type) || PsiType.SHORT.equals(type) || PsiType.BYTE.equals(type) || PsiType.CHAR.equals(type)) &&
PsiUtil.isConstantExpression(expression);
}
public static String findUniqueLabelName(PsiStatement statement, @NonNls String baseName) {
final PsiElement ancestor = PsiTreeUtil.getParentOfType(statement, PsiMember.class);
if (!checkForLabel(baseName, ancestor)) {
return baseName;
}
@@ -93,157 +214,4 @@ class SwitchUtils {
ancestor.accept(visitor);
return visitor.isUsed();
}
@Nullable
public static PsiExpression getSwitchExpression(PsiIfStatement statement) {
final PsiExpression condition = statement.getCondition();
final LanguageLevel languageLevel =
PsiUtil.getLanguageLevel(statement);
final PsiExpression possibleSwitchExpression =
determinePossibleSwitchExpressions(condition, languageLevel);
if (possibleSwitchExpression == null) {
return null;
}
if (SideEffectChecker.mayHaveSideEffects(possibleSwitchExpression)) {
return null;
}
while (true) {
final PsiExpression caseCondition = statement.getCondition();
if (!canBeMadeIntoCase(caseCondition, possibleSwitchExpression,
languageLevel)) {
break;
}
final PsiStatement elseBranch = statement.getElseBranch();
if (!(elseBranch instanceof PsiIfStatement)) {
return possibleSwitchExpression;
}
statement = (PsiIfStatement)elseBranch;
}
return null;
}
private static PsiExpression determinePossibleSwitchExpressions(
PsiExpression expression, LanguageLevel languageLevel) {
while (expression instanceof PsiParenthesizedExpression) {
final PsiParenthesizedExpression parenthesizedExpression =
(PsiParenthesizedExpression)expression;
expression = parenthesizedExpression.getExpression();
}
if (expression == null) {
return null;
}
if (languageLevel.compareTo(LanguageLevel.JDK_1_7) >= 0) {
final PsiExpression jdk17Expression =
determinePossibleStringSwitchExpression(expression);
if (jdk17Expression != null) {
return jdk17Expression;
}
}
if (!(expression instanceof PsiBinaryExpression)) {
return null;
}
final PsiBinaryExpression binaryExpression =
(PsiBinaryExpression)expression;
final IElementType operation = binaryExpression.getOperationTokenType();
final PsiExpression lhs = binaryExpression.getLOperand();
final PsiExpression rhs = binaryExpression.getROperand();
if (operation.equals(JavaTokenType.OROR)) {
return determinePossibleSwitchExpressions(lhs, languageLevel);
}
else if (operation.equals(JavaTokenType.EQEQ)) {
if (canBeCaseLabel(lhs, languageLevel)) {
return rhs;
}
else if (canBeCaseLabel(rhs, languageLevel)) {
return lhs;
}
}
return null;
}
private static PsiExpression determinePossibleStringSwitchExpression(
PsiExpression expression) {
if (!(expression instanceof PsiMethodCallExpression)) {
return null;
}
final PsiMethodCallExpression methodCallExpression =
(PsiMethodCallExpression)expression;
final PsiReferenceExpression methodExpression =
methodCallExpression.getMethodExpression();
@NonNls final String referenceName =
methodExpression.getReferenceName();
if (!"equals".equals(referenceName)) {
return null;
}
final PsiExpression qualifierExpression =
methodExpression.getQualifierExpression();
if (qualifierExpression == null) {
return null;
}
final PsiType type = qualifierExpression.getType();
if (type == null || !type.equalsToText("java.lang.String")) {
return null;
}
final PsiExpressionList argumentList =
methodCallExpression.getArgumentList();
final PsiExpression[] arguments = argumentList.getExpressions();
if (arguments.length != 1) {
return null;
}
final PsiExpression argument = arguments[0];
final PsiType argumentType = argument.getType();
if (argumentType == null ||
!argumentType.equalsToText("java.lang.String")) {
return null;
}
if (PsiUtil.isConstantExpression(qualifierExpression)) {
return argument;
}
else if (PsiUtil.isConstantExpression(argument)) {
return qualifierExpression;
}
return null;
}
private static boolean canBeMadeIntoCase(
PsiExpression expression, PsiExpression caseExpression,
LanguageLevel languageLevel) {
while (expression instanceof PsiParenthesizedExpression) {
final PsiParenthesizedExpression parenthesizedExpression =
(PsiParenthesizedExpression)expression;
expression = parenthesizedExpression.getExpression();
}
if (languageLevel.compareTo(LanguageLevel.JDK_1_7) >= 0) {
final PsiExpression stringCaseExpression =
determinePossibleStringSwitchExpression(expression);
if (EquivalenceChecker.expressionsAreEquivalent(caseExpression,
stringCaseExpression)) {
return true;
}
}
if (!(expression instanceof PsiBinaryExpression)) {
return false;
}
final PsiBinaryExpression binaryExpression =
(PsiBinaryExpression)expression;
final IElementType operation = binaryExpression.getOperationTokenType();
final PsiExpression lOperand = binaryExpression.getLOperand();
final PsiExpression rhs = binaryExpression.getROperand();
if (operation.equals(JavaTokenType.OROR)) {
return canBeMadeIntoCase(lOperand, caseExpression, languageLevel) &&
canBeMadeIntoCase(rhs, caseExpression, languageLevel);
}
else if (operation.equals(JavaTokenType.EQEQ)) {
return (canBeCaseLabel(lOperand, languageLevel) &&
EquivalenceChecker.expressionsAreEquivalent(
caseExpression, rhs))
||
(canBeCaseLabel(rhs, languageLevel) &&
EquivalenceChecker.expressionsAreEquivalent(
caseExpression, lOperand));
}
else {
return false;
}
}
}
@@ -0,0 +1,10 @@
package com.siyeh.ipp.switchtoif.replace_if_with_switch;
public class Long {
void x(long l) {
<caret>if (l == 1) {
} else if (l == 2) {
} else if (l == 3) {
} else if (l == 4) {}
}
}
@@ -0,0 +1,13 @@
package com.siyeh.ipp.switchtoif.replace_if_with_switch;
public class Polyadic {
void x(int i) {
<caret>if (i == 1 || i == 2 || i == 3) {
} else if (i == 5) {
} else {
}
}
}
@@ -0,0 +1,19 @@
package com.siyeh.ipp.switchtoif.replace_if_with_switch;
public class Polyadic {
void x(int i) {
switch (i) {
case 1:
case 2:
case 3:
break;
case 5:
break;
default:
break;
}
}
}
@@ -18,11 +18,14 @@ package com.siyeh.ipp.switchtoif;
import com.siyeh.IntentionPowerPackBundle;
import com.siyeh.ipp.IPPTestCase;
/**
* @see ReplaceIfWithSwitchIntention
*/
public class ReplaceIfWithSwitchlIntentionTest extends IPPTestCase {
public void testComments() {
doTest();
}
public void testComments() { doTest(); }
public void testLong() { assertIntentionNotAvailable(); }
public void testPolyadic() { doTest(); }
@Override
protected String getIntentionName() {