diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/ConvertEqualsMethodToStaticIntention.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/ConvertEqualsMethodToStaticIntention.java deleted file mode 100644 index cc3f08e45a64..000000000000 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/ConvertEqualsMethodToStaticIntention.java +++ /dev/null @@ -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(); - } -} diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/intention/ConvertEqualsMethodToStaticTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/intention/ConvertEqualsMethodToStaticTest.java deleted file mode 100644 index 5377fe688dc3..000000000000 --- a/java/java-tests/testSrc/com/intellij/codeInsight/intention/ConvertEqualsMethodToStaticTest.java +++ /dev/null @@ -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)); - } -} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/META-INF/InspectionGadgets.xml b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/META-INF/InspectionGadgets.xml index 8beed8aedac4..3c969aec8153 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/META-INF/InspectionGadgets.xml +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/META-INF/InspectionGadgets.xml @@ -1312,7 +1312,7 @@ implementationClass="com.siyeh.ig.migration.BigDecimalLegacyMethodInspection"/> #ref 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=Objects.#ref() on arrays should probably be 'Arrays.equals()' #loc array.objects.deep.equals.problem.descriptor=Objects.#ref() on arrays should probably be 'Arrays.deepEquals()' #loc diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/EqualsReplaceableByObjectsCallInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/EqualsReplaceableByObjectsCallInspection.java index 75b59118fee7..e95e536ceb23 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/EqualsReplaceableByObjectsCallInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/EqualsReplaceableByObjectsCallInspection.java @@ -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; diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ExpressionUtils.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ExpressionUtils.java index 7d234614067f..c72f1991f3b9 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ExpressionUtils.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ExpressionUtils.java @@ -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; diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/migration/equals_replaceable_by_objects_call/EqualsReplaceableByObjectsCall.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/migration/equals_replaceable_by_objects_call/EqualsReplaceableByObjectsCall.java index fa187d730513..49b9afbef78f 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/migration/equals_replaceable_by_objects_call/EqualsReplaceableByObjectsCall.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/migration/equals_replaceable_by_objects_call/EqualsReplaceableByObjectsCall.java @@ -4,4 +4,8 @@ class EqualsReplaceableByObjectsCall { boolean d = (a != b) && (a == null || !a.equals(b)); boolean e = ((a) == (b)) || ((a) != (null) && (a).equals((b))); } + + void ignoreNullityCheck(Object a, Object b) { + boolean c = a.equals(b); + } } \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/migration/equals_replaceable_by_objects_call/EqualsReplaceableByObjectsCallCheckNull.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/migration/equals_replaceable_by_objects_call/EqualsReplaceableByObjectsCallCheckNull.java new file mode 100644 index 000000000000..5d4f1fdbe924 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/migration/equals_replaceable_by_objects_call/EqualsReplaceableByObjectsCallCheckNull.java @@ -0,0 +1,11 @@ +class EqualsReplaceableByObjectsCall { + void yyy(Object a, Object b) { + boolean c = (a != null) && a.equals(b); + boolean d = (a != b) && (a == null || !a.equals(b)); + boolean e = ((a) == (b)) || ((a) != (null) && (a).equals((b))); + } + + void ignoreNullityCheck(Object a, Object b) { + boolean c = a.equals(b); + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/migration/EqualsReplaceableByObjectsCallInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/migration/EqualsReplaceableByObjectsCallInspectionTest.java index 38e5b432ddbc..27d6202dbcab 100644 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/migration/EqualsReplaceableByObjectsCallInspectionTest.java +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/migration/EqualsReplaceableByObjectsCallInspectionTest.java @@ -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; } } \ No newline at end of file diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index fb6a351caaf8..ad80bf08d27d 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -882,10 +882,6 @@ com.intellij.codeInsight.intention.impl.ConvertCompareToToEqualsIntention Java/Control Flow - - com.intellij.codeInsight.intention.impl.ConvertEqualsMethodToStaticIntention - Java/Control Flow - com.intellij.codeInsight.intention.impl.CreateFieldFromParameterAction