ObjectsEqualsCanBeSimplified: replace with == when arguments are primitives

Fixes IDEA-199799 Inspection 'Objects.equals() can be replaced with equals()' should be aware of primitives too
This commit is contained in:
Tagir Valeev
2018-10-03 12:36:46 +07:00
parent b7e05651ce
commit 485e950c2c
4 changed files with 47 additions and 10 deletions
@@ -1,7 +1,7 @@
<html>
<body>
Reports calls to <b>Objects.equals(a, b)</b> where the first argument is statically known to be non-null.
Such a call can be safely replaced with <b>a.equals(b)</b>.
Such a call can be safely replaced with <b>a.equals(b)</b> or <b>a == b</b> if both arguments are primitives.
<!-- tooltip end -->
<p><small>New in 2018.3</small></p>
</body>
@@ -1001,8 +1001,7 @@ inspection.class.getclass.fix.remove.name=Remove 'getClass()' call
inspection.class.getclass.fix.replace.name=Replace with 'Class.class'
redundant.backticks.around.raw.string.literal.display.name=Redundant backticks around raw string literal
inspection.objects.equals.can.be.simplified.display.name=Objects.equals() can be replaced with equals()
inspection.objects.equals.can.be.simplified.message=Can be replaced with 'equals()'
inspection.objects.equals.can.be.simplified.fix.name=Replace 'Objects.equals()' with 'equals()'
inspection.objects.equals.can.be.simplified.message=Can be replaced with ''{0}''
inspection.duplicate.expressions.display.name=Multiple occurrences of the same expression
inspection.duplicate.expressions.message=Multiple occurrences of <code>#ref</code> #loc
@@ -6,9 +6,9 @@ import com.intellij.codeInspection.*;
import com.intellij.codeInspection.dataFlow.NullabilityUtil;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiPrecedenceUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.siyeh.ig.callMatcher.CallMatcher;
import com.siyeh.ig.psiutils.CommentTracker;
import com.siyeh.ig.psiutils.TypeUtils;
@@ -17,6 +17,9 @@ import org.jetbrains.annotations.NotNull;
import java.util.Objects;
import static com.intellij.psi.util.PsiPrecedenceUtil.EQUALITY_PRECEDENCE;
import static com.intellij.psi.util.PsiPrecedenceUtil.METHOD_CALL_PRECEDENCE;
public class ObjectsEqualsCanBeSimplifiedInspection extends AbstractBaseJavaLocalInspectionTool {
private static final CallMatcher OBJECTS_EQUALS = CallMatcher.staticCall(
CommonClassNames.JAVA_UTIL_OBJECTS, "equals").parameterCount(2);
@@ -31,7 +34,11 @@ public class ObjectsEqualsCanBeSimplifiedInspection extends AbstractBaseJavaLoca
@Override
public void visitMethodCallExpression(PsiMethodCallExpression call) {
if (!OBJECTS_EQUALS.test(call)) return;
PsiExpression arg1 = call.getArgumentList().getExpressions()[0];
PsiExpression[] args = call.getArgumentList().getExpressions();
PsiExpression arg1 = args[0];
PsiExpression arg2 = args[1];
PsiElement nameElement = Objects.requireNonNull(call.getMethodExpression().getReferenceNameElement());
if (processPrimitives(nameElement, arg1, arg2)) return;
PsiClass argClass = PsiUtil.resolveClassInClassTypeOnly(arg1.getType());
if (argClass == null) return;
if (NullabilityUtil.getExpressionNullability(arg1, true) == Nullability.NOT_NULL) {
@@ -45,20 +52,36 @@ public class ObjectsEqualsCanBeSimplifiedInspection extends AbstractBaseJavaLoca
return;
}
}
PsiElement nameElement = Objects.requireNonNull(call.getMethodExpression().getReferenceNameElement());
holder.registerProblem(nameElement, InspectionsBundle.message("inspection.objects.equals.can.be.simplified.message"),
new ReplaceWithEqualsFix());
holder.registerProblem(nameElement, InspectionsBundle.message("inspection.objects.equals.can.be.simplified.message", "equals()"),
new ReplaceWithEqualsFix(false));
}
}
private boolean processPrimitives(PsiElement nameElement, PsiExpression arg1, PsiExpression arg2) {
PsiType type1 = arg1.getType();
PsiType type2 = arg2.getType();
if (type1 instanceof PsiPrimitiveType && type1.equals(type2) && !TypeConversionUtil.isFloatOrDoubleType(type1)) {
holder.registerProblem(nameElement, InspectionsBundle.message("inspection.objects.equals.can.be.simplified.message", "=="),
new ReplaceWithEqualsFix(true));
return true;
}
return false;
}
};
}
private static class ReplaceWithEqualsFix implements LocalQuickFix {
final boolean myEquality;
private ReplaceWithEqualsFix(boolean equality) {
myEquality = equality;
}
@Nls(capitalization = Nls.Capitalization.Sentence)
@NotNull
@Override
public String getFamilyName() {
return InspectionsBundle.message("inspection.objects.equals.can.be.simplified.fix.name");
return CommonQuickFixBundle.message("fix.replace.x.with.y", "Objects.equals()", myEquality ? "==" : "equals()");
}
@Override
@@ -68,7 +91,13 @@ public class ObjectsEqualsCanBeSimplifiedInspection extends AbstractBaseJavaLoca
PsiExpression[] args = call.getArgumentList().getExpressions();
if (args.length != 2) return;
CommentTracker ct = new CommentTracker();
String replacement = ct.text(args[0], PsiPrecedenceUtil.METHOD_CALL_PRECEDENCE) + ".equals(" + ct.text(args[1]) + ")";
String replacement;
if (myEquality) {
replacement = ct.text(args[0], EQUALITY_PRECEDENCE) + "==" + ct.text(args[1], EQUALITY_PRECEDENCE);
}
else {
replacement = ct.text(args[0], METHOD_CALL_PRECEDENCE) + ".equals(" + ct.text(args[1]) + ")";
}
ct.replaceAndRestoreComments(call, replacement);
}
}
@@ -13,6 +13,15 @@ class Test {
if(arr1 != null && Objects.equals(arr1, arr2)) System.out.println();
}
void testPrimitives(int a, int b, long c, long d, double e, double f, boolean g, boolean h) {
if(Objects.<warning descr="Can be replaced with '=='">equals</warning>(a, b)) {}
if(Objects.equals(a, c)) {}
if(Objects.<warning descr="Can be replaced with '=='">equals</warning>(c, d)) {}
if(Objects.equals(e, f)) {}
if(Objects.<warning descr="Can be replaced with '=='">equals</warning>(g, h)) {}
if(Objects.equals(a, h)) {}
}
void testOverridden(First f1, First f2, Second s1, Second s2, Third t1, Third t2) {
// No conflicting equals signatures