diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/ComparatorCombinatorsInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/ComparatorCombinatorsInspection.java index fd8056a9fe12..e8f0efca53bd 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/ComparatorCombinatorsInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/ComparatorCombinatorsInspection.java @@ -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) { diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/ConvertCompareToToEqualsIntention.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/ConvertCompareToToEqualsIntention.java index a6f4fbb0f0e1..bba5c08bd123 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/ConvertCompareToToEqualsIntention.java +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/ConvertCompareToToEqualsIntention.java @@ -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 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 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)"; } } \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/convertCompareToToEquals/noQualifier.java b/java/java-tests/testData/codeInsight/convertCompareToToEquals/noQualifier.java new file mode 100644 index 000000000000..4b20575d4dac --- /dev/null +++ b/java/java-tests/testData/codeInsight/convertCompareToToEquals/noQualifier.java @@ -0,0 +1,9 @@ +class X implements Comparable { + boolean m(X x) { + return compareTo(x) == ((int)0.0); + } + + public int compareTo(X x) { + return 0; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/convertCompareToToEquals/noQualifier_after.java b/java/java-tests/testData/codeInsight/convertCompareToToEquals/noQualifier_after.java new file mode 100644 index 000000000000..c0b7f15b9deb --- /dev/null +++ b/java/java-tests/testData/codeInsight/convertCompareToToEquals/noQualifier_after.java @@ -0,0 +1,9 @@ +class X implements Comparable { + boolean m(X x) { + return equals(x); + } + + public int compareTo(X x) { + return 0; + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/inlays/ToggleInlineHintsActionTest.kt b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/inlays/ToggleInlineHintsActionTest.kt new file mode 100644 index 000000000000..132087a8626d --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/inlays/ToggleInlineHintsActionTest.kt @@ -0,0 +1,82 @@ +/* + * 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. + * 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.daemon.inlays + +import com.intellij.codeInsight.hints.isPossibleHintNearOffset +import com.intellij.openapi.editor.ex.EditorSettingsExternalizable +import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase +import org.assertj.core.api.Assertions.assertThat + +class ToggleInlineHintsActionTest : LightCodeInsightFixtureTestCase() { + + private var before: Boolean = false + + override fun setUp() { + super.setUp() + before = EditorSettingsExternalizable.getInstance().isShowParameterNameHints + EditorSettingsExternalizable.getInstance().isShowParameterNameHints = false + } + + override fun tearDown() { + EditorSettingsExternalizable.getInstance().isShowParameterNameHints = before + super.tearDown() + } + + fun `test is enabled near method with possible hints`() { + myFixture.configureByText("A.java", """" +class Test { + Test(int time) {} + void initialize(int loadTime) {} + static void s_test() { + Test test = new Test(10); + test.initialize(10000); + } +} +""") + + val file = myFixture.file + val caretModel = myFixture.editor.caretModel + + assertThat(caretModel.caretCount).isEqualTo(4) + + editor.caretModel.allCarets.forEach { + val hintCanBeAtCaret = isPossibleHintNearOffset(file, it.offset) + assertThat(hintCanBeAtCaret).isTrue() + } + } + + fun `test is disabled in random places`() { + myFixture.configureByText("A.java", """" +class Test { + static void s_test() { + int a = 2; + List list = null; + } +} +""") + + val file = myFixture.file + val caretModel = myFixture.editor.caretModel + + assertThat(caretModel.caretCount).isEqualTo(3) + + editor.caretModel.allCarets.forEach { + val hintCanBeAtCaret = isPossibleHintNearOffset(file, it.offset) + assertThat(hintCanBeAtCaret).isFalse() + } + } + +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/intention/ConvertCompareToToEqualsTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/intention/ConvertCompareToToEqualsTest.java index 3945e414078b..70c784d97df7 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/intention/ConvertCompareToToEqualsTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/intention/ConvertCompareToToEqualsTest.java @@ -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)")); } } diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/ex/InspectionProfileTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/ex/InspectionProfileTest.java index b209a4534582..096795247e23 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/ex/InspectionProfileTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/ex/InspectionProfileTest.java @@ -402,6 +402,15 @@ public class InspectionProfileTest extends LightIdeaTestCase { ""); } + public void testMergedCallToSuspiciousStringMethodInspections() throws Exception { + checkMergedNoChanges("\n" + + " " ); + } + public void testMergedMisspelledInspections() throws Exception { checkMergedNoChanges("\n" + "