ConvertEqualsMethodToStaticIntention merged with EqualsReplaceableByObjectsCallInspection to become a informational level inspection IDEA-157727

This commit is contained in:
Dmitry Batkovich
2016-07-20 17:54:12 +03:00
parent ab0db9b789
commit 4bf9dd514b
10 changed files with 85 additions and 186 deletions
@@ -1,102 +0,0 @@
/*
* Copyright 2000-2015 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.
* 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.intellij.codeInsight.intention.impl;
import com.intellij.codeInsight.FileModificationService;
import com.intellij.codeInsight.intention.BaseElementAtCaretIntentionAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
/**
* @author Dmitry Batkovich
*/
public class ConvertEqualsMethodToStaticIntention extends BaseElementAtCaretIntentionAction {
private static final Logger LOG = Logger.getInstance(ConvertEqualsMethodToStaticIntention.class);
private static final String REPLACE_TEMPLATE = "java.util.Objects.equals(%s, %s)";
public static final String TEXT = "Convert '.equals()' to 'java.util.Objects.equals()'";
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) {
if (!(element instanceof PsiIdentifier)) {
return false;
}
if (!PsiUtil.isLanguageLevel7OrHigher(element)) {
return false;
}
final PsiElement referenceExpression = element.getParent();
if (!(referenceExpression instanceof PsiReferenceExpression)) {
return false;
}
if (!"equals".equals(((PsiReferenceExpression)referenceExpression).getReferenceName())) {
return false;
}
final PsiElement methodCallExpression = referenceExpression.getParent();
if (!(methodCallExpression instanceof PsiMethodCallExpression)) {
return false;
}
final int argumentsCount = ((PsiMethodCallExpression)methodCallExpression).getArgumentList().getExpressions().length;
if (argumentsCount != 1) {
return false;
}
final PsiMethod method = ((PsiMethodCallExpression)methodCallExpression).resolveMethod();
if (method == null) {
return false;
}
PsiClass javaLangObject = JavaPsiFacade.getInstance(project).findClass(CommonClassNames.JAVA_LANG_OBJECT, element.getResolveScope());
if (javaLangObject == null) {
return false;
}
if (javaLangObject.isEquivalentTo(method.getContainingClass())) {
return true;
}
final PsiMethod[] superMethods = method.findSuperMethods(javaLangObject);
return superMethods.length == 1;
}
@Override
public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException {
if (!FileModificationService.getInstance().preparePsiElementsForWrite(element)) {
return;
}
final PsiElement parent = element.getParent().getParent();
LOG.assertTrue(parent instanceof PsiMethodCallExpression);
PsiMethodCallExpression methodCall = (PsiMethodCallExpression) parent;
final PsiExpression qualifier = methodCall.getMethodExpression().getQualifierExpression();
final String qualifierText = qualifier == null ? PsiKeyword.THIS : qualifier.getText();
final PsiExpression parameter = methodCall.getArgumentList().getExpressions()[0];
final String expressionText = String.format(REPLACE_TEMPLATE, qualifierText, parameter.getText());
methodCall.replace(JavaPsiFacade.getElementFactory(project).createExpressionFromText(expressionText, null));
}
@Nls
@NotNull
@Override
public String getFamilyName() {
return TEXT;
}
@NotNull
@Override
public String getText() {
return getFamilyName();
}
}
@@ -1,62 +0,0 @@
/*
* Copyright 2000-2015 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.
* 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.intellij.codeInsight.intention;
import com.intellij.JavaTestUtil;
import com.intellij.codeInsight.intention.impl.ConvertCompareToToEqualsIntention;
import com.intellij.codeInsight.intention.impl.ConvertEqualsMethodToStaticIntention;
import com.intellij.openapi.roots.LanguageLevelProjectExtension;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.testFramework.fixtures.CodeInsightTestUtil;
import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase;
/**
* @author Dmitry Batkovich
*/
public class ConvertEqualsMethodToStaticTest extends JavaCodeInsightFixtureTestCase {
@Override
protected String getTestDataPath() {
return JavaTestUtil.getJavaTestDataPath() + "/codeInsight/convertEqualsMethodToStatic/";
}
@Override
protected void setUp() throws Exception {
super.setUp();
LanguageLevelProjectExtension.getInstance(getProject()).setLanguageLevel(LanguageLevel.JDK_1_7);
}
public void testSimple() {
doTest();
}
public void testComplexQualifierAndArgument() {
doTest();
}
public void testNotAvailable() {
doTestNotAvailable();
}
private void doTest() {
final String name = getTestName(true);
CodeInsightTestUtil.doIntentionTest(myFixture, ConvertEqualsMethodToStaticIntention.TEXT, name + ".java", name + "_after.java");
}
private void doTestNotAvailable() {
myFixture.configureByFile(getTestName(true) + ".java");
assertEmpty(myFixture.filterAvailableIntentions(ConvertEqualsMethodToStaticIntention.TEXT));
}
}
@@ -1312,7 +1312,7 @@
implementationClass="com.siyeh.ig.migration.BigDecimalLegacyMethodInspection"/>
<localInspection groupPath="Java" language="JAVA" shortName="EqualsReplaceableByObjectsCall" bundle="com.siyeh.InspectionGadgetsBundle"
key="equals.replaceable.by.objects.call.display.name" groupBundle="messages.InspectionsBundle"
groupKey="group.names.language.level.specific.issues.and.migration.aids" enabledByDefault="false" level="WARNING"
groupKey="group.names.language.level.specific.issues.and.migration.aids" enabledByDefault="true" level="INFORMATION"
implementationClass="com.siyeh.ig.migration.EqualsReplaceableByObjectsCallInspection"/>
<localInspection groupPath="Java" language="JAVA" shortName="EnumerationCanBeIteration" bundle="com.siyeh.InspectionGadgetsBundle"
key="enumeration.can.be.iteration.display.name" groupBundle="messages.InspectionsBundle"
@@ -2129,6 +2129,7 @@ dangling.javadoc.delete.quickfix=Remove dangling comment
equals.replaceable.by.objects.call.display.name='equals()' expression replaceable by 'Objects.equals()' expression
equals.replaceable.by.objects.call.problem.descriptor=<code>#ref</code> replaceable by 'Objects.equals()' expression #loc
equals.replaceable.by.objects.call.quickfix=Replace with 'Objects.equals()' expression
equals.replaceable.by.objects.check.not.null.option=Report only null safe 'equals' calls
array.objects.equals.display.name='Objects.equals()' called on arrays
array.objects.equals.problem.descriptor=<code>Objects.#ref()</code> on arrays should probably be 'Arrays.equals()' #loc
array.objects.deep.equals.problem.descriptor=<code>Objects.#ref()</code> on arrays should probably be 'Arrays.deepEquals()' #loc
@@ -16,6 +16,7 @@
package com.siyeh.ig.migration;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.codeInspection.ui.SingleCheckboxOptionsPanel;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.tree.IElementType;
@@ -34,10 +35,19 @@ import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
/**
* @author Bas Leijdekkers
*/
public class EqualsReplaceableByObjectsCallInspection extends BaseInspection {
public boolean checkNotNull;
@NotNull
@Override
public JComponent createOptionsPanel() {
return new SingleCheckboxOptionsPanel(InspectionGadgetsBundle.message("equals.replaceable.by.objects.check.not.null.option"), this, "checkNotNull");
}
@Nls
@NotNull
@@ -86,10 +96,10 @@ public class EqualsReplaceableByObjectsCallInspection extends BaseInspection {
@Override
protected void doFix(Project project, ProblemDescriptor descriptor) {
final PsiElement element = descriptor.getPsiElement();
if (!(element instanceof PsiBinaryExpression)) {
if (!(element instanceof PsiBinaryExpression || element instanceof PsiMethodCallExpression)) {
return;
}
final PsiBinaryExpression expression = (PsiBinaryExpression)element;
final PsiExpression expression = (PsiExpression)element;
if (myEquals) {
PsiReplacementUtil.replaceExpressionAndShorten(expression, "java.util.Objects.equals(" + myName1 + "," + myName2 + ")");
}
@@ -109,41 +119,62 @@ public class EqualsReplaceableByObjectsCallInspection extends BaseInspection {
return new EqualsReplaceableByObjectsCallVisitor();
}
private static class EqualsReplaceableByObjectsCallVisitor extends BaseInspectionVisitor {
private class EqualsReplaceableByObjectsCallVisitor extends BaseInspectionVisitor {
@Override
public void visitBinaryExpression(PsiBinaryExpression expression) {
public void visitMethodCallExpression(PsiMethodCallExpression expression) {
final PsiElement maybeBinary = PsiTreeUtil.skipParentsOfType(expression, PsiParenthesizedExpression.class, PsiPrefixExpression.class);
if (maybeBinary instanceof PsiBinaryExpression) {
if (processNotNullCheck((PsiBinaryExpression)maybeBinary)) {
return;
}
}
if (!checkNotNull) {
final PsiVariable variable = ExpressionUtils.getVariable(expression.getMethodExpression().getQualifierExpression());
if (variable == null) {
return;
}
final PsiVariable otherVariable = getArgumentFromEqualsCallOn(expression, variable);
if (otherVariable == null) {
return;
}
registerError(expression, variable.getName(), otherVariable.getName(), true);
}
}
private boolean processNotNullCheck(PsiBinaryExpression expression) {
final IElementType tokenType = expression.getOperationTokenType();
if (JavaTokenType.ANDAND.equals(tokenType)) {
final PsiVariable variable = ExpressionUtils.getVariableFromNullComparison(expression.getLOperand(), false);
if (variable == null) {
return;
return false;
}
final PsiVariable otherVariable = getArgumentFromEqualsCallOn(expression.getROperand(), variable);
if (otherVariable == null) {
return;
return false;
}
checkEqualityBefore(expression, true, variable, otherVariable);
}
else if (JavaTokenType.OROR.equals(tokenType)) {
final PsiVariable variable = ExpressionUtils.getVariableFromNullComparison(expression.getLOperand(), true);
if (variable == null) {
return;
return false;
}
final PsiExpression rhs = ParenthesesUtils.stripParentheses(expression.getROperand());
if (!(rhs instanceof PsiPrefixExpression)) {
return;
return false;
}
final PsiPrefixExpression prefixExpression = (PsiPrefixExpression)rhs;
if (!JavaTokenType.EXCL.equals(prefixExpression.getOperationTokenType())) {
return;
return false;
}
final PsiVariable otherVariable = getArgumentFromEqualsCallOn(prefixExpression.getOperand(), variable);
if (otherVariable == null) {
return;
return false;
}
checkEqualityBefore(expression, false, variable, otherVariable);
}
return true;
}
private void checkEqualityBefore(PsiExpression expression, boolean equals, PsiVariable variable1, PsiVariable variable2) {
@@ -161,7 +192,7 @@ public class EqualsReplaceableByObjectsCallInspection extends BaseInspection {
registerError(expression, variable1.getName(), variable2.getName(), Boolean.valueOf(equals));
}
private static boolean isEquality(PsiExpression expression, boolean equals, PsiVariable variable1, PsiVariable variable2) {
private boolean isEquality(PsiExpression expression, boolean equals, PsiVariable variable1, PsiVariable variable2) {
expression = ParenthesesUtils.stripParentheses(expression);
if (!(expression instanceof PsiBinaryExpression)) {
return false;
@@ -183,7 +214,7 @@ public class EqualsReplaceableByObjectsCallInspection extends BaseInspection {
(VariableAccessUtils.evaluatesToVariable(lhs, variable2) && VariableAccessUtils.evaluatesToVariable(rhs, variable1));
}
private static PsiVariable getArgumentFromEqualsCallOn(PsiExpression expression, @NotNull PsiVariable variable) {
private PsiVariable getArgumentFromEqualsCallOn(PsiExpression expression, PsiVariable variable) {
expression = ParenthesesUtils.stripParentheses(expression);
if (!(expression instanceof PsiMethodCallExpression)) {
return null;
@@ -635,7 +635,7 @@ public class ExpressionUtils {
return null;
}
public static PsiVariable getVariable(PsiExpression expression) {
public static PsiVariable getVariable(@Nullable PsiExpression expression) {
expression = ParenthesesUtils.stripParentheses(expression);
if (!(expression instanceof PsiReferenceExpression)) {
return null;
@@ -4,4 +4,8 @@ class EqualsReplaceableByObjectsCall {
boolean d = <warning descr="'(a != b) && (a == null || !a.equals(b))' replaceable by 'Objects.equals()' expression">(a != b) && (a == null || !a.equals(b))</warning>;
boolean e = <warning descr="'((a) == (b)) || ((a) != (null) && (a).equals((b)))' replaceable by 'Objects.equals()' expression">((a) == (b)) || ((a) != (null) && (a).equals((b)))</warning>;
}
void ignoreNullityCheck(Object a, Object b) {
boolean c = <warning descr="'a.equals(b)' replaceable by 'Objects.equals()' expression">a.equals(b)</warning>;
}
}
@@ -0,0 +1,11 @@
class EqualsReplaceableByObjectsCall {
void yyy(Object a, Object b) {
boolean c = <warning descr="'(a != null) && a.equals(b)' replaceable by 'Objects.equals()' expression">(a != null) && a.equals(b)</warning>;
boolean d = <warning descr="'(a != b) && (a == null || !a.equals(b))' replaceable by 'Objects.equals()' expression">(a != b) && (a == null || !a.equals(b))</warning>;
boolean e = <warning descr="'((a) == (b)) || ((a) != (null) && (a).equals((b)))' replaceable by 'Objects.equals()' expression">((a) == (b)) || ((a) != (null) && (a).equals((b)))</warning>;
}
void ignoreNullityCheck(Object a, Object b) {
boolean c = a.equals(b);
}
}
@@ -15,23 +15,43 @@
*/
package com.siyeh.ig.migration;
import com.intellij.codeInspection.InspectionProfileEntry;
import com.intellij.codeHighlighting.HighlightDisplayLevel;
import com.intellij.codeInsight.daemon.HighlightDisplayKey;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.ex.InspectionProfileImpl;
import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
import com.siyeh.ig.LightInspectionTestCase;
import junit.framework.TestCase;
import org.jetbrains.annotations.Nullable;
/**
* @author Bas Leijdekkers
*/
public class EqualsReplaceableByObjectsCallInspectionTest extends LightInspectionTestCase {
private EqualsReplaceableByObjectsCallInspection myInspection = new EqualsReplaceableByObjectsCallInspection();
@Override
public void setUp() throws Exception {
super.setUp();
final InspectionProfileImpl profile = (InspectionProfileImpl)InspectionProjectProfileManager.getInstance(getProject()).getInspectionProfile();
profile.setErrorLevel(HighlightDisplayKey.find("EqualsReplaceableByObjectsCall"), HighlightDisplayLevel.WARNING, getProject());
}
public void testEqualsReplaceableByObjectsCall() {
doTest();
}
public void testEqualsReplaceableByObjectsCallCheckNull() {
try {
myInspection.checkNotNull = true;
doTest();
} finally {
myInspection.checkNotNull = false;
}
}
@Nullable
@Override
protected InspectionProfileEntry getInspection() {
return new EqualsReplaceableByObjectsCallInspection();
protected LocalInspectionTool getInspection() {
return myInspection;
}
}
-4
View File
@@ -882,10 +882,6 @@
<className>com.intellij.codeInsight.intention.impl.ConvertCompareToToEqualsIntention</className>
<category>Java/Control Flow</category>
</intentionAction>
<intentionAction>
<className>com.intellij.codeInsight.intention.impl.ConvertEqualsMethodToStaticIntention</className>
<category>Java/Control Flow</category>
</intentionAction>
<intentionAction>
<className>com.intellij.codeInsight.intention.impl.CreateFieldFromParameterAction</className>