IDEA-187130 Inspection: Comparison of primitives with compareTo()

This commit is contained in:
Tagir Valeev
2018-03-01 12:33:56 +07:00
parent b65896dd2f
commit c4da8fdb79
8 changed files with 193 additions and 4 deletions
@@ -0,0 +1,7 @@
<html>
<body>
Reports comparisons like <b>Integer.compare(a, b) == 0</b> which could be replaced simply with <b>a == b</b>.
<!-- tooltip end -->
<p><small>New in 2018.2</small></p>
</body>
</html>
@@ -0,0 +1,39 @@
// "Fix all" "true"
class CompareTest {
int testInteger(int a, int b) {
if(a < b) return 1;
if(a > b) return 2;
if(a >= b) return 3;
if(1 <= Integer.compare(a, b)) return 4;
if(a != b) return 5;
return 0;
}
int testShort(short a, byte b) {
if(a < b) return 1;
if(a >= b) return 2;
if(1 <= Short.compare(a, b)) return 4;
return 0;
}
int testByte(int a, int b) {
if((byte) a < (byte) b) return 1;
if(Byte.compare((byte)a, (byte)b) >= 1) return 2;
return 0;
}
int testLong(long a, long b, long c, int d) {
if(a + b < c + d) return 1;
/*2*/
/*3*/
/*4*/
/*5*/
/*8*/
/*9*/
/*10*/
/*11*/
/*12*/
if(/*1*/(d /*6*/ > 0 ? /*7*/a : b) < c/*13*/) return 1;
return 0;
}
}
@@ -0,0 +1,30 @@
// "Fix all 'Redundant 'compare' method call' problems in file" "true"
class CompareTest {
int testInteger(int a, int b) {
if(Integer.c<caret>ompare(a, b) < 0) return 1;
if(Integer.compare(a, b) > 0) return 2;
if(0 <= Integer.compare(a, b)) return 3;
if(1 <= Integer.compare(a, b)) return 4;
if(0 != Integer.compare(a, b)) return 5;
return 0;
}
int testShort(short a, byte b) {
if(Short.compare(a, b) < 0) return 1;
if(Short.compare(a, b) >= 0) return 2;
if(1 <= Short.compare(a, b)) return 4;
return 0;
}
int testByte(int a, int b) {
if(Byte.compare((byte)a, (byte)b) < 0) return 1;
if(Byte.compare((byte)a, (byte)b) >= 1) return 2;
return 0;
}
int testLong(long a, long b, long c, int d) {
if(Long.compare(a + b, c + d) < 0) return 1;
if(/*1*/Long/*2*/./*3*/compare/*4*/(/*5*/d /*6*/> 0 ? /*7*/a : b/*8*/, /*9*/c/*10*/) /*11*/< /*12*/0/*13*/) return 1;
return 0;
}
}
@@ -0,0 +1,25 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.java.codeInsight.daemon.quickFix;
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.RedundantCompareCallInspection;
import org.jetbrains.annotations.NotNull;
public class RedundantCompareCallInspectionTest extends LightQuickFixParameterizedTestCase {
@NotNull
@Override
protected LocalInspectionTool[] configureLocalInspectionTools() {
return new LocalInspectionTool[]{
new RedundantCompareCallInspection()
};
}
public void test() { doAllTests(); }
@Override
protected String getBasePath() {
return "/codeInsight/daemonCodeAnalyzer/quickFix/redundantCompareCall";
}
}
@@ -1673,7 +1673,7 @@ if.may.be.conditional.problem.descriptor=<code>#ref</code> could be replaced wit
if.may.be.conditional.quickfix=Replace with conditional expression
if.may.be.conditional.report.method.calls.option=Report if statements containing method calls
redundant.string.format.call.display.name=Redundant call to 'String.format()'
redundant.string.format.call.problem.descriptor=Redundant call to <code>#ref()</code> #loc
redundant.call.problem.descriptor=Redundant call to <code>#ref()</code> #loc
redundant.string.format.call.quickfix=Remove redundant call to 'String.format()'
replace.printf.with.print.quickfix=Replace 'printf()' with 'print()'
junit4.test.method.in.class.extending.junit3.testcase.display.name=JUnit 4 test method in class extending JUnit 3 TestCase
@@ -2267,3 +2267,6 @@ inspection.constant.expression.message=Constant expression can be evaluated to '
inspection.constant.expression.fix.name=Compute constant value of ''{0}''
inspection.constant.expression.fix.name.with.value=Replace ''{0}'' with constant value ''{1}''
inspection.constant.expression.fix.family.name=Compute constant value
inspection.redundant.compare.call.display.name=Redundant 'compare' method call
inspection.redundant.compare.call.fix.name=Inline 'compare' call
@@ -41,7 +41,7 @@ public class RedundantStringFormatCallInspection extends BaseInspection {
@Override
@NotNull
protected String buildErrorString(Object... infos) {
return InspectionGadgetsBundle.message("redundant.string.format.call.problem.descriptor");
return InspectionGadgetsBundle.message("redundant.call.problem.descriptor");
}
@Override
@@ -155,7 +155,7 @@ public class RedundantStringFormatCallInspection extends BaseInspection {
}
if (firstType.equalsToText(CommonClassNames.JAVA_LANG_STRING)) {
if (arguments.length == 1 && !containsPercentN(firstArgument)) {
registerMethodCallError(expression, Boolean.valueOf(printf));
registerMethodCallError(expression, printf);
}
}
else if (firstType.equalsToText("java.util.Locale")) {
@@ -170,7 +170,7 @@ public class RedundantStringFormatCallInspection extends BaseInspection {
if (containsPercentN(secondArgument)) {
return;
}
registerMethodCallError(expression, Boolean.valueOf(printf));
registerMethodCallError(expression, printf);
}
}
@@ -2405,6 +2405,11 @@
key="unnecessary.tostring.call.display.name" groupBundle="messages.InspectionsBundle"
groupKey="group.names.code.style.issues" enabledByDefault="false" level="WARNING" cleanupTool="true"
implementationClass="com.siyeh.ig.style.UnnecessaryToStringCallInspection"/>
<localInspection groupPath="Java" language="JAVA" shortName="RedundantCompareCall"
groupBundle="messages.InspectionsBundle" groupKey="group.names.verbose.or.redundant.code.constructs"
enabledByDefault="true" level="WARNING"
key="inspection.redundant.compare.call.display.name" bundle="com.siyeh.InspectionGadgetsBundle"
implementationClass="com.intellij.codeInspection.RedundantCompareCallInspection"/>
<localInspection groupPath="Java" language="JAVA" suppressId="RedundantNoArgConstructor" shortName="UnnecessaryConstructor" bundle="com.siyeh.InspectionGadgetsBundle"
key="unnecessary.constructor.display.name" groupBundle="messages.InspectionsBundle"
groupKey="group.names.code.style.issues" enabledByDefault="false" level="WARNING"
@@ -0,0 +1,80 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInspection;
import com.intellij.codeInspection.dataFlow.value.DfaRelationValue;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ObjectUtils;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.callMatcher.CallMatcher;
import com.siyeh.ig.psiutils.CommentTracker;
import com.siyeh.ig.psiutils.ExpressionUtils;
import com.siyeh.ig.psiutils.ParenthesesUtils;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
public class RedundantCompareCallInspection extends AbstractBaseJavaLocalInspectionTool {
private static final CallMatcher COMPARE_METHODS = CallMatcher.anyOf(
CallMatcher.staticCall(CommonClassNames.JAVA_LANG_INTEGER, "compare").parameterTypes("int", "int"),
CallMatcher.staticCall(CommonClassNames.JAVA_LANG_LONG, "compare").parameterTypes("long", "long"),
CallMatcher.staticCall(CommonClassNames.JAVA_LANG_SHORT, "compare").parameterTypes("short", "short"),
CallMatcher.staticCall(CommonClassNames.JAVA_LANG_CHARACTER, "compare").parameterTypes("char", "char"),
CallMatcher.staticCall(CommonClassNames.JAVA_LANG_BYTE, "compare").parameterTypes("byte", "byte")
);
@NotNull
@Override
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
return new JavaElementVisitor() {
@Override
public void visitMethodCallExpression(PsiMethodCallExpression call) {
if (!COMPARE_METHODS.test(call)) return;
PsiBinaryExpression binOp = ObjectUtils.tryCast(PsiUtil.skipParenthesizedExprUp(call.getParent()), PsiBinaryExpression.class);
if (binOp == null) return;
DfaRelationValue.RelationType type = DfaRelationValue.RelationType.fromElementType(binOp.getOperationTokenType());
if (type == null) return;
if (ExpressionUtils.isZero(binOp.getLOperand())) {
type = type.getFlipped();
if (type == null) return;
} else if (!ExpressionUtils.isZero(binOp.getROperand())) {
return;
}
holder.registerProblem(call, InspectionGadgetsBundle.message("redundant.call.problem.descriptor"), ProblemHighlightType.LIKE_UNUSED_SYMBOL,
new TextRange(0, call.getArgumentList().getStartOffsetInParent()),
new InlineCompareCallFix(type));
}
};
}
private static class InlineCompareCallFix implements LocalQuickFix {
private @NotNull final DfaRelationValue.RelationType myRelationType;
public InlineCompareCallFix(@NotNull DfaRelationValue.RelationType relationType) {
myRelationType = relationType;
}
@Nls
@NotNull
@Override
public String getFamilyName() {
return InspectionGadgetsBundle.message("inspection.redundant.compare.call.fix.name");
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
PsiMethodCallExpression call = ObjectUtils.tryCast(descriptor.getStartElement(), PsiMethodCallExpression.class);
if (call == null) return;
PsiExpression[] args = call.getArgumentList().getExpressions();
if(args.length != 2) return;
PsiBinaryExpression parent = PsiTreeUtil.getParentOfType(call, PsiBinaryExpression.class);
if (parent == null) return;
CommentTracker ct = new CommentTracker();
ct.replaceAndRestoreComments(parent, ct.text(args[0], ParenthesesUtils.EQUALITY_PRECEDENCE) +
myRelationType +
ct.text(args[1], ParenthesesUtils.EQUALITY_PRECEDENCE));
}
}
}