Allow "Convert 'compareTo()' expression to 'equals()' call" intention in more cases

This commit is contained in:
Bas Leijdekkers
2016-10-28 13:32:24 +02:00
parent eb6d8cf0be
commit 50d645e256
8 changed files with 103 additions and 94 deletions

View File

@@ -25,7 +25,7 @@ import com.intellij.psi.codeStyle.SuggestedNameInfo;
import com.intellij.psi.codeStyle.VariableKind;
import com.intellij.psi.util.PsiTreeUtil;
import com.siyeh.ig.psiutils.EquivalenceChecker;
import com.siyeh.ig.psiutils.MethodUtils;
import com.siyeh.ig.psiutils.MethodCallUtils;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
@@ -55,8 +55,11 @@ public class ComparatorCombinatorsInspection extends BaseJavaBatchLocalInspectio
if (body instanceof PsiMethodCallExpression) {
PsiMethodCallExpression methodCall = (PsiMethodCallExpression)body;
PsiExpression[] args = methodCall.getArgumentList().getExpressions();
if (args.length == 1 && MethodUtils.isCompareToCall(methodCall)) {
if (args.length == 1 && MethodCallUtils.isCompareToCall(methodCall)) {
PsiExpression left = methodCall.getMethodExpression().getQualifierExpression();
if (left == null) {
return;
}
PsiExpression right = args[0];
if (left instanceof PsiReferenceExpression && right instanceof PsiReferenceExpression) {
PsiElement leftElement = ((PsiReferenceExpression)left).resolve();
@@ -163,7 +166,7 @@ public class ComparatorCombinatorsInspection extends BaseJavaBatchLocalInspectio
String methodName = null;
if (body instanceof PsiMethodCallExpression) {
PsiMethodCallExpression methodCall = (PsiMethodCallExpression)body;
if (MethodUtils.isCompareToCall(methodCall)) {
if (MethodCallUtils.isCompareToCall(methodCall)) {
methodName = "comparing";
keyExtractor = methodCall.getMethodExpression().getQualifierExpression();
if (keyExtractor instanceof PsiReferenceExpression) {

View File

@@ -19,12 +19,12 @@ import com.intellij.codeInsight.FileModificationService;
import com.intellij.codeInsight.intention.BaseElementAtCaretIntentionAction;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.*;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import com.siyeh.ig.psiutils.MethodUtils;
import com.siyeh.ig.psiutils.ExpressionUtils;
import com.siyeh.ig.psiutils.MethodCallUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -32,110 +32,100 @@ import org.jetbrains.annotations.Nullable;
* @author Dmitry Batkovich
*/
public class ConvertCompareToToEqualsIntention extends BaseElementAtCaretIntentionAction {
public static final String TEXT = "Convert '.compareTo()' method to '.equals()' (may change semantics)";
@Override
public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException {
if (!FileModificationService.getInstance().preparePsiElementsForWrite(element)) {
return;
}
final ResolveResult resolveResult = findCompareTo(element);
assert resolveResult != null;
final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(project).getElementFactory();
final Pair<PsiExpression, PsiExpression> qualifierAndParameter = getQualifierAndParameter(resolveResult.getCompareToCall());
final PsiExpression newExpression =
elementFactory.createExpressionFromText(String.format((resolveResult.isEqEq() ? "" : "!") + "%s.equals(%s)", qualifierAndParameter.getFirst().getText(), qualifierAndParameter.getSecond().getText()), null);
final PsiElement result = resolveResult.getBinaryExpression().replace(newExpression);
final CompareToResult compareToResult = CompareToResult.findCompareTo(element);
assert compareToResult != null;
final PsiExpression qualifier = compareToResult.getQualifier();
final PsiExpression argument = compareToResult.getArgument();
final StringBuilder text = new StringBuilder();
if (!compareToResult.isEqEq()) {
text.append('!');
}
if (qualifier != null) {
text.append(qualifier.getText()).append('.');
}
text.append("equals(").append(argument.getText()).append(')');
final PsiExpression newExpression = JavaPsiFacade.getElementFactory(project).createExpressionFromText(text.toString(), null);
final PsiElement result = compareToResult.getBinaryExpression().replace(newExpression);
editor.getCaretModel().moveToOffset(result.getTextOffset() + result.getTextLength());
}
@Override
public boolean isAvailable(@NotNull final Project project, final Editor editor, @NotNull final PsiElement element) {
return findCompareTo(element) != null;
return CompareToResult.findCompareTo(element) != null;
}
private static Pair<PsiExpression, PsiExpression> getQualifierAndParameter(PsiMethodCallExpression methodCallExpression) {
final PsiExpression qualifier = methodCallExpression.getMethodExpression().getQualifierExpression();
assert qualifier != null;
final PsiExpression parameter = methodCallExpression.getArgumentList().getExpressions()[0];
return Pair.create(qualifier, parameter);
}
private static class CompareToResult {
@Nullable
private static ResolveResult findCompareTo(PsiElement element) {
final PsiBinaryExpression binaryExpression = PsiTreeUtil.getParentOfType(element, PsiBinaryExpression.class);
if (binaryExpression == null) {
return null;
}
final PsiJavaToken operationSign = binaryExpression.getOperationSign();
boolean isEqEq;
if (JavaTokenType.NE.equals(operationSign.getTokenType())) {
isEqEq = false;
} else if (JavaTokenType.EQEQ.equals(operationSign.getTokenType())) {
isEqEq = true;
} else {
return null;
}
PsiMethodCallExpression compareToExpression = null;
boolean hasZero = false;
for (PsiExpression psiExpression : binaryExpression.getOperands()) {
if (compareToExpression == null && MethodUtils.isCompareToCall(psiExpression)) {
compareToExpression = (PsiMethodCallExpression)psiExpression;
continue;
}
if (!hasZero && detectZero(psiExpression)) {
hasZero = true;
}
}
if (!hasZero || compareToExpression == null) {
return null;
}
getQualifierAndParameter(compareToExpression);
return new ResolveResult(binaryExpression, compareToExpression, isEqEq);
}
private static boolean detectZero(final @NotNull PsiExpression expression) {
if (!(expression instanceof PsiLiteralExpression)) {
return false;
}
final Object value = ((PsiLiteralExpression)expression).getValue();
return Comparing.equal(value, 0);
}
private static class ResolveResult {
private final PsiBinaryExpression myBinaryExpression;
private final PsiMethodCallExpression myCompareToCall;
private final boolean myEqEq;
private ResolveResult(PsiBinaryExpression binaryExpression, PsiMethodCallExpression compareToCall, boolean eqEq) {
private CompareToResult(PsiBinaryExpression binaryExpression, PsiMethodCallExpression compareToCall) {
myBinaryExpression = binaryExpression;
myCompareToCall = compareToCall;
myEqEq = eqEq;
}
public PsiBinaryExpression getBinaryExpression() {
return myBinaryExpression;
}
public PsiMethodCallExpression getCompareToCall() {
return myCompareToCall;
public boolean isEqEq() {
return JavaTokenType.EQEQ.equals(myBinaryExpression.getOperationTokenType());
}
public boolean isEqEq() {
return myEqEq;
public PsiExpression getArgument() {
return myCompareToCall.getArgumentList().getExpressions()[0];
}
public PsiExpression getQualifier() {
return myCompareToCall.getMethodExpression().getQualifierExpression();
}
@Nullable
static CompareToResult findCompareTo(PsiElement element) {
final PsiBinaryExpression binaryExpression = PsiTreeUtil.getParentOfType(element, PsiBinaryExpression.class);
if (binaryExpression == null) {
return null;
}
final IElementType tokenType = binaryExpression.getOperationTokenType();
if (!JavaTokenType.NE.equals(tokenType) && !JavaTokenType.EQEQ.equals(tokenType)) {
return null;
}
PsiMethodCallExpression compareToExpression;
final PsiExpression lhs = binaryExpression.getLOperand();
final PsiExpression rhs = binaryExpression.getROperand();
if (lhs instanceof PsiMethodCallExpression) {
compareToExpression = (PsiMethodCallExpression)lhs;
if (!MethodCallUtils.isCompareToCall(compareToExpression) || !ExpressionUtils.isZero(rhs)) {
return null;
}
} else if (rhs instanceof PsiMethodCallExpression) {
compareToExpression = (PsiMethodCallExpression)rhs;
if (!ExpressionUtils.isZero(lhs) || !MethodCallUtils.isCompareToCall(compareToExpression)) {
return null;
}
} else {
return null;
}
return new CompareToResult(binaryExpression, compareToExpression);
}
}
@NotNull
@Override
public String getFamilyName() {
return TEXT;
return "Convert 'compareTo()' expression to 'equals()' call";
}
@NotNull
@Override
public String getText() {
return getFamilyName();
return "Convert 'compareTo()' expression to 'equals()' call (may change semantics)";
}
}

View File

@@ -0,0 +1,9 @@
class X implements Comparable<X> {
boolean m(X x) {
return <caret>compareTo(x) == ((int)0.0);
}
public int compareTo(X x) {
return 0;
}
}

View File

@@ -0,0 +1,9 @@
class X implements Comparable<X> {
boolean m(X x) {
return equals(x);
}
public int compareTo(X x) {
return 0;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,7 +16,6 @@
package com.intellij.codeInsight.intention;
import com.intellij.JavaTestUtil;
import com.intellij.codeInsight.intention.impl.ConvertCompareToToEqualsIntention;
import com.intellij.testFramework.fixtures.CodeInsightTestUtil;
import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase;
@@ -37,18 +36,22 @@ public class ConvertCompareToToEqualsTest extends JavaCodeInsightFixtureTestCase
doTest();
}
public void testNoQualifier() {
doTest();
}
public void testNotAvailable() {
doTestNotAvailable();;
doTestNotAvailable();
}
private void doTest() {
final String name = getTestName(true);
CodeInsightTestUtil.doIntentionTest(myFixture, ConvertCompareToToEqualsIntention.TEXT, name + ".java", name + "_after.java");
CodeInsightTestUtil.doIntentionTest(myFixture, "Convert 'compareTo()' expression to 'equals()' call (may change semantics)", name + ".java", name + "_after.java");
}
private void doTestNotAvailable() {
myFixture.configureByFile(getTestName(true) + ".java");
assertEmpty(myFixture.filterAvailableIntentions(ConvertCompareToToEqualsIntention.TEXT));
assertEmpty(myFixture.filterAvailableIntentions("Convert 'compareTo()' expression to 'equals()' call (may change semantics)"));
}
}

View File

@@ -55,14 +55,23 @@ public class MethodCallUtils {
@Nullable
public static PsiType getTargetType(@NotNull PsiMethodCallExpression expression) {
final PsiReferenceExpression method = expression.getMethodExpression();
final PsiExpression qualifierExpression = method.getQualifierExpression();
final PsiReferenceExpression methodExpression = expression.getMethodExpression();
final PsiExpression qualifierExpression = methodExpression.getQualifierExpression();
if (qualifierExpression == null) {
return null;
}
return qualifierExpression.getType();
}
public static boolean isCompareToCall(@NotNull PsiMethodCallExpression expression) {
final PsiReferenceExpression methodExpression = expression.getMethodExpression();
if (!HardcodedMethodConstants.COMPARE_TO.equals(methodExpression.getReferenceName())) {
return false;
}
final PsiMethod method = expression.resolveMethod();
return MethodUtils.isCompareTo(method);
}
public static boolean isEqualsCall(PsiMethodCallExpression expression) {
final PsiReferenceExpression methodExpression = expression.getMethodExpression();
final String name = methodExpression.getReferenceName();

View File

@@ -363,18 +363,4 @@ public class MethodUtils {
final PsiExpression returnValue = returnStatement.getReturnValue();
return returnValue instanceof PsiThisExpression;
}
@Contract("null -> false")
public static boolean isCompareToCall(final @Nullable PsiExpression expression) {
if (!(expression instanceof PsiMethodCallExpression)) {
return false;
}
final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)expression;
if (methodCallExpression.getMethodExpression().getQualifierExpression() == null ||
!HardcodedMethodConstants.COMPARE_TO.equals(methodCallExpression.getMethodExpression().getReferenceName())) {
return false;
}
final PsiMethod psiMethod = methodCallExpression.resolveMethod();
return isCompareTo(psiMethod);
}
}

View File

@@ -15,6 +15,6 @@
-->
<html>
<body>
Converts call if <b>compareTo</b> method to call of <b>equals</b> method.
Converts a <b>compareTo()</b> method call expression to an <b>equals()</b> call.
</body>
</html>