diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/ExtractSetFromComparisonChainAction.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/ExtractSetFromComparisonChainAction.java index e8720ac10d30..f9c22941ade4 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/ExtractSetFromComparisonChainAction.java +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/ExtractSetFromComparisonChainAction.java @@ -38,13 +38,11 @@ import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.refactoring.rename.inplace.MemberInplaceRenamer; -import com.intellij.util.ArrayUtil; import com.intellij.util.IncorrectOperationException; import com.intellij.util.ThrowableRunnable; -import com.siyeh.ig.callMatcher.CallMatcher; import com.siyeh.ig.psiutils.ClassUtils; +import com.siyeh.ig.psiutils.EqualityCheck; import com.siyeh.ig.psiutils.ExpressionUtils; -import com.siyeh.ig.psiutils.MethodCallUtils; import one.util.streamex.IntStreamEx; import one.util.streamex.MoreCollectors; import one.util.streamex.StreamEx; @@ -61,10 +59,6 @@ import static com.intellij.util.ObjectUtils.tryCast; * @author Tagir Valeev */ public class ExtractSetFromComparisonChainAction extends PsiElementBaseIntentionAction { - private static final CallMatcher OBJECT_EQUALS = CallMatcher.anyOf( - CallMatcher.staticCall("java.util.Objects", "equals").parameterCount(2), - CallMatcher.staticCall("com.google.common.base.Objects", "equal").parameterCount(2)); - private static final String GUAVA_IMMUTABLE_SET = "com.google.common.collect.ImmutableSet"; private static final String INITIALIZER_FORMAT_GUAVA = GUAVA_IMMUTABLE_SET + ".of({0})"; private static final String INITIALIZER_FORMAT_JAVA2 = @@ -346,18 +340,9 @@ public class ExtractSetFromComparisonChainAction extends PsiElementBaseIntention static ExpressionToConstantComparison create(PsiExpression candidate) { candidate = PsiUtil.skipParenthesizedExprDown(candidate); - PsiMethodCallExpression call = tryCast(candidate, PsiMethodCallExpression.class); - if (call != null) { - if (MethodCallUtils.isEqualsCall(call)) { - PsiExpression qualifier = call.getMethodExpression().getQualifierExpression(); - PsiExpression argument = ArrayUtil.getFirstElement(call.getArgumentList().getExpressions()); - return fromComparison(candidate, qualifier, argument); - } - if (OBJECT_EQUALS.test(call)) { - PsiExpression[] arguments = call.getArgumentList().getExpressions(); - return fromComparison(candidate, arguments[0], arguments[1]); - } - return null; + EqualityCheck check = EqualityCheck.from(candidate); + if (check != null) { + return fromComparison(candidate, check.getLeft(), check.getRight()); } PsiBinaryExpression binOp = tryCast(candidate, PsiBinaryExpression.class); if (binOp != null && JavaTokenType.EQEQ.equals(binOp.getOperationTokenType())) { diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/bigDecimalEquals/afterBigDecimalEquals.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/bigDecimalEquals/afterBigDecimalEquals.java new file mode 100644 index 000000000000..5aecbc5914ef --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/bigDecimalEquals/afterBigDecimalEquals.java @@ -0,0 +1,22 @@ +// "Fix all ''equals()' called on 'java.math.BigDecimal'' problems in file" "true" +import java.math.BigDecimal; +import java.util.Objects; + +class BigDecimalEquals { + public void foo(BigDecimal qux) + { + final BigDecimal foo = new BigDecimal(3); + final BigDecimal bar = new BigDecimal(3); + foo.equals(bar); + if(foo.compareTo(bar) == 0) + { + return; + } + if(!(foo.compareTo(bar) == 0)) { + System.out.println("not equals"); + } + if(qux != null && qux.compareTo(bar) == 0) { + System.out.println("equals"); + } + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/bigDecimalEquals/beforeBigDecimalEquals.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/bigDecimalEquals/beforeBigDecimalEquals.java new file mode 100644 index 000000000000..91981c71408e --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/bigDecimalEquals/beforeBigDecimalEquals.java @@ -0,0 +1,22 @@ +// "Fix all ''equals()' called on 'java.math.BigDecimal'' problems in file" "true" +import java.math.BigDecimal; +import java.util.Objects; + +class BigDecimalEquals { + public void foo(BigDecimal qux) + { + final BigDecimal foo = new BigDecimal(3); + final BigDecimal bar = new BigDecimal(3); + foo.equals(bar); + if(foo.equals(bar)) + { + return; + } + if(!Objects.equals(foo, bar)) { + System.out.println("not equals"); + } + if(Objects.equals(qux, bar)) { + System.out.println("equals"); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/PsiReplacementUtil.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/PsiReplacementUtil.java index a23ba7756f66..818c94ad57c7 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/PsiReplacementUtil.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/PsiReplacementUtil.java @@ -3,7 +3,6 @@ */ package com.siyeh.ig; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleManager; @@ -16,7 +15,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class PsiReplacementUtil { - private static final Logger LOG = Logger.getInstance(PsiReplacementUtil.class); /** * Consider to use {@link #replaceExpression(PsiExpression, String, CommentTracker)} to preserve comments @@ -32,7 +30,7 @@ public class PsiReplacementUtil { } /** - * @param commentTracker ensure to {@link CommentTracker#markUnchanged(PsiElement)} expressions used as getText in newExpressionText + * @param tracker ensure to {@link CommentTracker#markUnchanged(PsiElement)} expressions used as getText in newExpressionText */ public static void replaceExpression(@NotNull PsiExpression expression, @NotNull @NonNls String newExpressionText, CommentTracker tracker) { final Project project = expression.getProject(); @@ -62,7 +60,7 @@ public class PsiReplacementUtil { } /** - * Consider to use {@link #replaceStatement(PsiExpression, String, CommentTracker)} to preserve comments + * Consider to use {@link #replaceStatement(PsiStatement, String, CommentTracker)} to preserve comments */ public static PsiElement replaceStatement(@NotNull PsiStatement statement, @NotNull @NonNls String newStatementText) { final Project project = statement.getProject(); diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/fixes/EqualsToEqualityFix.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/fixes/EqualsToEqualityFix.java index 75b1e4e1eab1..276ec6470cc7 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/fixes/EqualsToEqualityFix.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/fixes/EqualsToEqualityFix.java @@ -8,13 +8,13 @@ import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiExpression; import com.intellij.psi.PsiMethodCallExpression; -import com.intellij.psi.PsiReferenceExpression; -import com.intellij.psi.util.PsiUtil; +import com.intellij.psi.util.PsiTreeUtil; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.InspectionGadgetsFix; import com.siyeh.ig.PsiReplacementUtil; import com.siyeh.ig.psiutils.BoolUtils; import com.siyeh.ig.psiutils.CommentTracker; +import com.siyeh.ig.psiutils.EqualityCheck; import com.siyeh.ig.psiutils.ParenthesesUtils; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; @@ -41,19 +41,11 @@ public class EqualsToEqualityFix extends InspectionGadgetsFix { @Override protected void doFix(Project project, ProblemDescriptor descriptor) { - final PsiMethodCallExpression call = (PsiMethodCallExpression)descriptor.getPsiElement().getParent().getParent(); - if (call == null) { - return; - } - final PsiReferenceExpression methodExpression = call.getMethodExpression(); - final PsiExpression lhs = PsiUtil.deparenthesizeExpression(methodExpression.getQualifierExpression()); - if (lhs == null) { - return; - } - final PsiExpression rhs = PsiUtil.deparenthesizeExpression(call.getArgumentList().getExpressions()[0]); - if (rhs == null) { - return; - } + final PsiMethodCallExpression call = PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), PsiMethodCallExpression.class); + EqualityCheck check = EqualityCheck.from(call); + if (check == null) return; + PsiExpression lhs = check.getLeft(); + PsiExpression rhs = check.getRight(); final PsiElement parent = ParenthesesUtils.getParentSkipParentheses(call); final CommentTracker commentTracker = new CommentTracker(); final String lhsText = commentTracker.text(lhs, ParenthesesUtils.EQUALITY_PRECEDENCE); diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/numeric/BigDecimalEqualsInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/numeric/BigDecimalEqualsInspection.java index 6b836abc46a8..967b2181bee6 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/numeric/BigDecimalEqualsInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/numeric/BigDecimalEqualsInspection.java @@ -16,16 +16,23 @@ package com.siyeh.ig.numeric; import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.codeInspection.dataFlow.Nullness; +import com.intellij.codeInspection.dataFlow.NullnessUtil; import com.intellij.openapi.project.Project; -import com.intellij.psi.*; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiExpression; +import com.intellij.psi.PsiExpressionStatement; +import com.intellij.psi.PsiMethodCallExpression; +import com.intellij.psi.util.PsiTreeUtil; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.InspectionGadgetsFix; import com.siyeh.ig.PsiReplacementUtil; import com.siyeh.ig.psiutils.CommentTracker; +import com.siyeh.ig.psiutils.EqualityCheck; import com.siyeh.ig.psiutils.ExpressionUtils; -import com.siyeh.ig.psiutils.MethodCallUtils; +import com.siyeh.ig.psiutils.ParenthesesUtils; import org.jetbrains.annotations.NotNull; public class BigDecimalEqualsInspection extends BaseInspection { @@ -56,21 +63,17 @@ public class BigDecimalEqualsInspection extends BaseInspection { @Override public void doFix(Project project, ProblemDescriptor descriptor) { - final PsiIdentifier name = (PsiIdentifier)descriptor.getPsiElement(); - final PsiReferenceExpression expression = (PsiReferenceExpression)name.getParent(); - assert expression != null; - final PsiMethodCallExpression call = (PsiMethodCallExpression)expression.getParent(); - final PsiExpression qualifier = expression.getQualifierExpression(); - if (qualifier == null) { - return; - } + PsiMethodCallExpression call = PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), PsiMethodCallExpression.class); + EqualityCheck check = EqualityCheck.from(call); + if (check == null) return; CommentTracker commentTracker = new CommentTracker(); - final String qualifierText = commentTracker.text(qualifier); - assert call != null; - final PsiExpressionList argumentList = call.getArgumentList(); - final PsiExpression[] args = argumentList.getExpressions(); - final String argText = commentTracker.text(args[0]); - PsiReplacementUtil.replaceExpression(call, qualifierText + ".compareTo(" + argText + ")==0", commentTracker); + final String qualifierText = commentTracker.text(check.getLeft(), ParenthesesUtils.METHOD_CALL_PRECEDENCE); + final String argText = commentTracker.text(check.getRight()); + String replacement = qualifierText + ".compareTo(" + argText + ")==0"; + if (!check.isLeftDereferenced() && NullnessUtil.getExpressionNullness(check.getLeft(), true) != Nullness.NOT_NULL) { + replacement = commentTracker.text(check.getLeft(), ParenthesesUtils.EQUALITY_PRECEDENCE) + "!=null && " + replacement; + } + PsiReplacementUtil.replaceExpression(call, replacement, commentTracker); } } @@ -84,23 +87,12 @@ public class BigDecimalEqualsInspection extends BaseInspection { @Override public void visitMethodCallExpression(@NotNull PsiMethodCallExpression expression) { super.visitMethodCallExpression(expression); - if (!MethodCallUtils.isEqualsCall(expression)) { - return; - } - final PsiReferenceExpression methodExpression = expression.getMethodExpression(); - final PsiExpressionList argumentList = expression.getArgumentList(); - final PsiExpression[] arguments = argumentList.getExpressions(); - if (arguments.length == 0) { - return; - } - final PsiExpression arg = arguments[0]; - if (!ExpressionUtils.hasType(arg, "java.math.BigDecimal")) { - return; - } - final PsiExpression qualifier = methodExpression.getQualifierExpression(); - if (!ExpressionUtils.hasType(qualifier, "java.math.BigDecimal")) { - return; - } + EqualityCheck check = EqualityCheck.from(expression); + if (check == null) return; + PsiExpression left = check.getLeft(); + PsiExpression right = check.getRight(); + if (!ExpressionUtils.hasType(left, "java.math.BigDecimal")) return; + if (!ExpressionUtils.hasType(right, "java.math.BigDecimal")) return; final PsiElement context = expression.getParent(); if (context instanceof PsiExpressionStatement) { //cheesy, but necessary, because otherwise the quickfix will diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/performance/ObjectEqualsCanBeEqualityInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/performance/ObjectEqualsCanBeEqualityInspection.java index a7f2157bcff3..3f4f81529b97 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/performance/ObjectEqualsCanBeEqualityInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/performance/ObjectEqualsCanBeEqualityInspection.java @@ -12,7 +12,7 @@ import com.siyeh.ig.InspectionGadgetsFix; import com.siyeh.ig.fixes.EqualsToEqualityFix; import com.siyeh.ig.psiutils.BoolUtils; import com.siyeh.ig.psiutils.ClassUtils; -import com.siyeh.ig.psiutils.MethodCallUtils; +import com.siyeh.ig.psiutils.EqualityCheck; import com.siyeh.ig.psiutils.ParenthesesUtils; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; @@ -55,24 +55,19 @@ public class ObjectEqualsCanBeEqualityInspection extends BaseInspection { @Override public void visitMethodCallExpression(PsiMethodCallExpression expression) { super.visitMethodCallExpression(expression); - if (!MethodCallUtils.isEqualsCall(expression)) { - return; - } - final PsiReferenceExpression methodExpression = expression.getMethodExpression(); - final PsiExpression qualifier = methodExpression.getQualifierExpression(); - if (qualifier == null) { - return; - } - final PsiExpression[] expressions = expression.getArgumentList().getExpressions(); - if (expressions.length != 1) { - return; - } - final PsiExpression argument = expressions[0]; - if (!TypeConversionUtil.isBinaryOperatorApplicable(JavaTokenType.EQEQ, qualifier, argument, false)) { + EqualityCheck check = EqualityCheck.from(expression); + if (check == null) return; + PsiExpression left = check.getLeft(); + PsiExpression right = check.getRight(); + if (!TypeConversionUtil.isBinaryOperatorApplicable(JavaTokenType.EQEQ, left, right, false)) { // replacing with == or != will generate uncompilable code return; } - final PsiClass aClass = PsiUtil.resolveClassInClassTypeOnly(qualifier.getType()); + final PsiClass aClass = PsiUtil.resolveClassInClassTypeOnly(left.getType()); + if (aClass != null && aClass.isEnum()) { + // Enums are reported by separate EqualsCalledOnEnumConstantInspection + return; + } final ProblemHighlightType highlightType; if (ClassUtils.isFinalClassWithDefaultEquals(aClass)) { highlightType = ProblemHighlightType.GENERIC_ERROR_OR_WARNING; @@ -83,9 +78,9 @@ public class ObjectEqualsCanBeEqualityInspection extends BaseInspection { } final PsiElement parent = ParenthesesUtils.getParentSkipParentheses(expression); final boolean negated = parent instanceof PsiExpression && BoolUtils.isNegation((PsiExpression)parent); - final PsiElement nameToken = methodExpression.getReferenceNameElement(); + final PsiElement nameToken = expression.getMethodExpression().getReferenceNameElement(); assert nameToken != null; - registerError(nameToken, highlightType, Boolean.valueOf(negated)); + registerError(nameToken, highlightType, negated); } } } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/EqualityCheck.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/EqualityCheck.java new file mode 100644 index 000000000000..596e75ffba52 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/EqualityCheck.java @@ -0,0 +1,69 @@ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.siyeh.ig.psiutils; + +import com.intellij.psi.PsiExpression; +import com.intellij.psi.PsiMethodCallExpression; +import com.intellij.psi.util.PsiUtil; +import com.intellij.util.ArrayUtil; +import com.intellij.util.ObjectUtils; +import com.siyeh.ig.callMatcher.CallMatcher; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a model of PsiExpression which checks whether two expressions are equal (via Object.equals directly or indirectly). + */ +public class EqualityCheck { + private static final CallMatcher OBJECT_EQUALS = CallMatcher.anyOf( + CallMatcher.staticCall("java.util.Objects", "equals").parameterCount(2), + CallMatcher.staticCall("com.google.common.base.Objects", "equal").parameterCount(2)); + private final @NotNull PsiExpression myLeft; + private final @NotNull PsiExpression myRight; + private final boolean myLeftDereferenced; + + private EqualityCheck(@NotNull PsiExpression left, @NotNull PsiExpression right, boolean leftDereferenced) { + myLeft = left; + myRight = right; + myLeftDereferenced = leftDereferenced; + } + + /** + * @param expression to create an {@link EqualityCheck} from + * @return an {@link EqualityCheck} which represents an equality check performed on given expression; null if equality check + * was not found in given expression. + */ + @Nullable + @Contract("null -> null") + public static EqualityCheck from(PsiExpression expression) { + PsiMethodCallExpression call = ObjectUtils.tryCast(PsiUtil.skipParenthesizedExprDown(expression), PsiMethodCallExpression.class); + if (call == null) { + return null; + } + if (MethodCallUtils.isEqualsCall(call)) { + PsiExpression left = call.getMethodExpression().getQualifierExpression(); + PsiExpression right = ArrayUtil.getFirstElement(call.getArgumentList().getExpressions()); + if (left == null || right == null) return null; + return new EqualityCheck(left, right, true); + } + if (OBJECT_EQUALS.test(call)) { + PsiExpression[] args = call.getArgumentList().getExpressions(); + return new EqualityCheck(args[0], args[1], false); + } + return null; + } + + @NotNull + public PsiExpression getLeft() { + return myLeft; + } + + @NotNull + public PsiExpression getRight() { + return myRight; + } + + public boolean isLeftDereferenced() { + return myLeftDereferenced; + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/EqualsCalledOnEnumConstantInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/EqualsCalledOnEnumConstantInspection.java index b69be8f9283a..33b9d6aa3a0f 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/EqualsCalledOnEnumConstantInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/EqualsCalledOnEnumConstantInspection.java @@ -15,18 +15,16 @@ */ package com.siyeh.ig.style; -import com.intellij.codeInspection.ProblemDescriptor; -import com.intellij.openapi.project.Project; import com.intellij.psi.*; -import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.TypeConversionUtil; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.InspectionGadgetsFix; -import com.siyeh.ig.PsiReplacementUtil; -import com.siyeh.ig.psiutils.CommentTracker; -import com.siyeh.ig.psiutils.MethodCallUtils; +import com.siyeh.ig.fixes.EqualsToEqualityFix; +import com.siyeh.ig.psiutils.BoolUtils; +import com.siyeh.ig.psiutils.EqualityCheck; +import com.siyeh.ig.psiutils.ParenthesesUtils; import com.siyeh.ig.psiutils.TypeUtils; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; @@ -49,64 +47,12 @@ public class EqualsCalledOnEnumConstantInspection extends BaseInspection { @Override protected InspectionGadgetsFix buildFix(Object... infos) { final PsiElement element = (PsiElement)infos[0]; + final boolean negated = (boolean)infos[1]; final PsiElement parent = element.getParent(); if (parent instanceof PsiExpressionStatement) { return null; } - return new EqualsCalledOnEnumValueFix(); - } - - private static class EqualsCalledOnEnumValueFix extends InspectionGadgetsFix { - - @Override - @NotNull - public String getFamilyName() { - return InspectionGadgetsBundle.message("equals.called.on.enum.constant.quickfix"); - } - - @Override - protected void doFix(Project project, ProblemDescriptor descriptor) { - final PsiElement element = descriptor.getPsiElement(); - final PsiElement parent = element.getParent(); - if (parent == null) { - return; - } - final PsiElement grandParent = parent.getParent(); - if (!(grandParent instanceof PsiMethodCallExpression)) { - return; - } - final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)grandParent; - final PsiExpressionList argumentList = methodCallExpression.getArgumentList(); - final PsiExpression[] arguments = argumentList.getExpressions(); - if (arguments.length > 1) { - return; - } - final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression(); - final PsiExpression qualifier = methodExpression.getQualifierExpression(); - if (qualifier == null) { - return; - } - final StringBuilder newExpression = new StringBuilder(); - final PsiElement greatGrandParent = grandParent.getParent(); - final boolean not; - final PsiPrefixExpression prefixExpression; - if (greatGrandParent instanceof PsiPrefixExpression) { - prefixExpression = (PsiPrefixExpression)greatGrandParent; - final IElementType tokenType = prefixExpression.getOperationTokenType(); - not = JavaTokenType.EXCL == tokenType; - } - else { - prefixExpression = null; - not = false; - } - CommentTracker commentTracker = new CommentTracker(); - newExpression.append(commentTracker.text(qualifier)); - newExpression.append(not ? "!=" : "=="); - if (arguments.length == 1) { - newExpression.append(commentTracker.text(arguments[0])); - } - PsiReplacementUtil.replaceExpression(not ? prefixExpression : methodCallExpression, newExpression.toString(), commentTracker); - } + return new EqualsToEqualityFix(negated); } @Override @@ -119,25 +65,21 @@ public class EqualsCalledOnEnumConstantInspection extends BaseInspection { @Override public void visitMethodCallExpression(@NotNull PsiMethodCallExpression expression) { super.visitMethodCallExpression(expression); - if (!MethodCallUtils.isEqualsCall(expression)) { + EqualityCheck check = EqualityCheck.from(expression); + if (check == null) return; + final PsiExpression left = check.getLeft(); + if (!TypeUtils.expressionHasTypeOrSubtype(left, CommonClassNames.JAVA_LANG_ENUM)) return; + final PsiExpression right = check.getRight(); + + final PsiType comparedTypeErasure = TypeConversionUtil.erasure(left.getType()); + final PsiType comparisonTypeErasure = TypeConversionUtil.erasure(right.getType()); + if (comparedTypeErasure == null || comparisonTypeErasure == null || + !TypeConversionUtil.areTypesConvertible(comparedTypeErasure, comparisonTypeErasure)) { return; } - final PsiReferenceExpression methodExpression = expression.getMethodExpression(); - final PsiExpression qualifier = methodExpression.getQualifierExpression(); - if (!TypeUtils.expressionHasTypeOrSubtype(qualifier, CommonClassNames.JAVA_LANG_ENUM)) { - return; - } - final PsiExpressionList argumentList = expression.getArgumentList(); - final PsiExpression[] arguments = argumentList.getExpressions(); - if (arguments.length > 0) { - final PsiType comparedTypeErasure = TypeConversionUtil.erasure(qualifier.getType()); - final PsiType comparisonTypeErasure = TypeConversionUtil.erasure(arguments[0].getType()); - if (comparedTypeErasure == null || comparisonTypeErasure == null || - !TypeConversionUtil.areTypesConvertible(comparedTypeErasure, comparisonTypeErasure)) { - return; - } - } - registerMethodCallError(expression, expression); + final PsiElement parent = ParenthesesUtils.getParentSkipParentheses(expression); + final boolean negated = parent instanceof PsiExpression && BoolUtils.isNegation((PsiExpression)parent); + registerMethodCallError(expression, expression, negated); } } } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/testFrameworks/SimplifiableAssertionInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/testFrameworks/SimplifiableAssertionInspection.java index b64834b14f39..d1593e3f9a20 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/testFrameworks/SimplifiableAssertionInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/testFrameworks/SimplifiableAssertionInspection.java @@ -26,8 +26,8 @@ import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.InspectionGadgetsFix; import com.siyeh.ig.PsiReplacementUtil; import com.siyeh.ig.psiutils.ComparisonUtils; +import com.siyeh.ig.psiutils.EqualityCheck; import com.siyeh.ig.psiutils.ImportUtils; -import com.siyeh.ig.psiutils.MethodCallUtils; import com.siyeh.ig.psiutils.TypeUtils; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -102,15 +102,7 @@ public abstract class SimplifiableAssertionInspection extends BaseInspection { final PsiType type = lhs.getType(); return type != null && TypeConversionUtil.isPrimitiveAndNotNullOrWrapper(type); } - else if (expression instanceof PsiMethodCallExpression) { - final PsiMethodCallExpression call = (PsiMethodCallExpression)expression; - if (!MethodCallUtils.isEqualsCall(call)) { - return false; - } - final PsiReferenceExpression methodExpression = call.getMethodExpression(); - return methodExpression.getQualifierExpression() != null; - } - return false; + return EqualityCheck.from(expression) != null; } private static boolean isIdentityComparison(PsiExpression expression) { @@ -235,13 +227,12 @@ public abstract class SimplifiableAssertionInspection extends BaseInspection { lhs = binaryExpression.getLOperand(); rhs = binaryExpression.getROperand(); } - else if (position instanceof PsiMethodCallExpression) { - final PsiMethodCallExpression call = (PsiMethodCallExpression)position; - final PsiReferenceExpression equalityMethodExpression = call.getMethodExpression(); - final PsiExpressionList equalityArgumentList = call.getArgumentList(); - final PsiExpression[] equalityArgs = equalityArgumentList.getExpressions(); - rhs = equalityArgs[0]; - lhs = equalityMethodExpression.getQualifierExpression(); + else { + EqualityCheck check = EqualityCheck.from(position); + if (check != null) { + lhs = check.getLeft(); + rhs = check.getRight(); + } } if (!(lhs instanceof PsiLiteralExpression) && rhs instanceof PsiLiteralExpression) { final PsiExpression temp = lhs; diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/junit/simplifiable_junit_assertion/ObjectEqualsToEquals.after.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/junit/simplifiable_junit_assertion/ObjectEqualsToEquals.after.java new file mode 100644 index 000000000000..589c2f70c7cd --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/junit/simplifiable_junit_assertion/ObjectEqualsToEquals.after.java @@ -0,0 +1,15 @@ +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.Objects; + +class ObjectEqualsToEquals { + + @Test + public void testObjectsEquals() { + assertEquals(getFoo(), getBar()); + } + + String getFoo() { return "foo"; } + String getBar() { return "foo"; } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/junit/simplifiable_junit_assertion/ObjectEqualsToEquals.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/junit/simplifiable_junit_assertion/ObjectEqualsToEquals.java new file mode 100644 index 000000000000..9e3dc6443afc --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/junit/simplifiable_junit_assertion/ObjectEqualsToEquals.java @@ -0,0 +1,14 @@ +import static org.junit.Assert.assertTrue; + +import java.util.Objects; + +class ObjectEqualsToEquals { + + @Test + public void testObjectsEquals() { + assertTrue(Objects.equals(getFoo(), getBar())); + } + + String getFoo() { return "foo"; } + String getBar() { return "foo"; } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/BigDecimalEqualsInspection.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/BigDecimalEqualsInspection.java deleted file mode 100644 index a1411963c9ce..000000000000 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/BigDecimalEqualsInspection.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.siyeh.igtest.bugs; - -import java.math.BigDecimal; - -public class BigDecimalEqualsInspection { - public void foo() - { - final BigDecimal foo = new BigDecimal(3); - final BigDecimal bar = new BigDecimal(3); - foo.equals(bar); - if(foo.equals(bar)) - { - return; - } - } -} diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/junit/simplifiable_junit_assertion/SimplifiableJUnitAssertion.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/junit/simplifiable_junit_assertion/SimplifiableJUnitAssertion.java index f554833cefee..674bd51c4704 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/junit/simplifiable_junit_assertion/SimplifiableJUnitAssertion.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/junit/simplifiable_junit_assertion/SimplifiableJUnitAssertion.java @@ -3,6 +3,7 @@ package com.siyeh.igtest.junit; import junit.framework.TestCase; import java.util.Collection; +import java.util.Objects; public class SimplifiableJUnitAssertion extends TestCase{ public void test() @@ -15,6 +16,10 @@ public class SimplifiableJUnitAssertion extends TestCase{ assertTrue(collection.size() == 2); } + public void testObjectEquals() { + assertTrue(Objects.equals("foo", "bar")); + } + static class IDEABugTest extends TestCase { public static enum Input { value1 } diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/style/equals_called_on_enum_constant/EqualsCalled.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/style/equals_called_on_enum_constant/EqualsCalled.java index 61cb3fe4d1f9..5fd01b68f1fc 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/style/equals_called_on_enum_constant/EqualsCalled.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/style/equals_called_on_enum_constant/EqualsCalled.java @@ -9,10 +9,16 @@ public class EqualsCalled { void one() { E.A.equals(E.C); E.B.equals(new Object()); - E.C.equals(); + E.C.equals(); final Object A = new Object(); A.equals(1); } + + void objectsEquals(E a, E b) { + if(java.util.Objects.equals(a, b)) { + System.out.println("equals"); + } + } } class Main { enum Suit { diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/junit/SimplifiableJUnitAssertionFixTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/junit/SimplifiableJUnitAssertionFixTest.java index 61b2972423cf..71e232111290 100644 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/junit/SimplifiableJUnitAssertionFixTest.java +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/junit/SimplifiableJUnitAssertionFixTest.java @@ -15,6 +15,8 @@ */ package com.siyeh.ig.fixes.junit; +import com.intellij.testFramework.IdeaTestUtil; +import com.intellij.testFramework.builders.JavaModuleFixtureBuilder; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.IGQuickFixesTestCase; import com.siyeh.ig.junit.SimplifiableJUnitAssertionInspection; @@ -34,6 +36,13 @@ public class SimplifiableJUnitAssertionFixTest extends IGQuickFixesTestCase { public void testTrueToEqualsJUnit5() { doTest(); } public void testTrueToEqualsBetweenIncompatibleTypes() { doTest(); } public void testFalseToNotEqualsJUnit4() { doTest(); } + public void testObjectEqualsToEquals() { doTest(); } + + @Override + protected void tuneFixture(JavaModuleFixtureBuilder builder) throws Exception { + super.tuneFixture(builder); + builder.addJdk(IdeaTestUtil.getMockJdk18Path().getPath()); + } @Override protected void setUp() throws Exception { @@ -43,7 +52,7 @@ public class SimplifiableJUnitAssertionFixTest extends IGQuickFixesTestCase { myDefaultHint = InspectionGadgetsBundle.message("simplify.junit.assertion.simplify.quickfix"); myFixture.addClass("package junit.framework;" + - "public abstract class TestCase extends Assert {" + + " /** @noinspection RedundantThrows*/ public abstract class TestCase extends Assert {" + " protected void setUp() throws Exception {}" + " protected void tearDown() throws Exception {}" + "}"); diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/junit/SimplifiableJUnitAssertionInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/junit/SimplifiableJUnitAssertionInspectionTest.java index 68542990521a..9e74502da7fb 100644 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/junit/SimplifiableJUnitAssertionInspectionTest.java +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/junit/SimplifiableJUnitAssertionInspectionTest.java @@ -16,7 +16,9 @@ package com.siyeh.ig.junit; import com.intellij.codeInspection.InspectionProfileEntry; +import com.intellij.testFramework.LightProjectDescriptor; import com.siyeh.ig.LightInspectionTestCase; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** @@ -32,6 +34,12 @@ public class SimplifiableJUnitAssertionInspectionTest extends LightInspectionTes doTest(); } + @NotNull + @Override + protected LightProjectDescriptor getProjectDescriptor() { + return JAVA_8; + } + @Nullable @Override protected InspectionProfileEntry getInspection() { @@ -42,7 +50,7 @@ public class SimplifiableJUnitAssertionInspectionTest extends LightInspectionTes protected String[] getEnvironmentClasses() { return new String[] { "package junit.framework;" + - "public abstract class TestCase extends Assert {" + + " /** @noinspection ALL*/ public abstract class TestCase extends Assert {" + " protected void setUp() throws Exception {}" + " protected void tearDown() throws Exception {}" + " public static void assertTrue(boolean condition) {" + diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/numeric/BigDecimalEqualsInspectionFixTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/numeric/BigDecimalEqualsInspectionFixTest.java new file mode 100644 index 000000000000..2ea0a74ca166 --- /dev/null +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/numeric/BigDecimalEqualsInspectionFixTest.java @@ -0,0 +1,30 @@ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.siyeh.ig.numeric; + +import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase; +import com.intellij.codeInspection.LocalInspectionTool; +import com.intellij.testFramework.LightProjectDescriptor; +import org.jetbrains.annotations.NotNull; + +import static com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase.JAVA_8; + +public class BigDecimalEqualsInspectionFixTest extends LightQuickFixParameterizedTestCase { + @NotNull + @Override + protected LocalInspectionTool[] configureLocalInspectionTools() { + return new LocalInspectionTool[]{new BigDecimalEqualsInspection()}; + } + + @NotNull + @Override + protected LightProjectDescriptor getProjectDescriptor() { + return JAVA_8; + } + + @Override + protected String getBasePath() { + return "/codeInsight/daemonCodeAnalyzer/quickFix/bigDecimalEquals"; + } + + public void test() { doAllTests(); } +} diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/performance/ObjectEqualsCanBeEqualityInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/performance/ObjectEqualsCanBeEqualityInspectionTest.java index 4f91898b4e38..047a041e6ae3 100644 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/performance/ObjectEqualsCanBeEqualityInspectionTest.java +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/performance/ObjectEqualsCanBeEqualityInspectionTest.java @@ -16,7 +16,9 @@ package com.siyeh.ig.performance; import com.intellij.codeInspection.InspectionProfileEntry; +import com.intellij.testFramework.LightProjectDescriptor; import com.siyeh.ig.LightInspectionTestCase; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** @@ -32,6 +34,14 @@ public class ObjectEqualsCanBeEqualityInspectionTest extends LightInspectionTest "}"); } + public void testClassObjects() { + doTest("class X {" + + " boolean m(Class c1, Class c2) {" + + " return java.util.Objects./*'equals()' can be replaced with '=='*/equals/**/(c1, c2);" + + " }" + + "}"); + } + public void testObject() { doTest("class X {" + " boolean m(Object o1, Object o2) {" + @@ -72,6 +82,12 @@ public class ObjectEqualsCanBeEqualityInspectionTest extends LightInspectionTest "}"); } + @NotNull + @Override + protected LightProjectDescriptor getProjectDescriptor() { + return JAVA_8; + } + @Nullable @Override protected InspectionProfileEntry getInspection() { diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/style/EqualsCalledOnEnumConstantInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/style/EqualsCalledOnEnumConstantInspectionTest.java index dbebc48acd99..ef18120aa4ff 100644 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/style/EqualsCalledOnEnumConstantInspectionTest.java +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/style/EqualsCalledOnEnumConstantInspectionTest.java @@ -1,7 +1,9 @@ package com.siyeh.ig.style; import com.intellij.codeInspection.InspectionProfileEntry; +import com.intellij.testFramework.LightProjectDescriptor; import com.siyeh.ig.LightInspectionTestCase; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class EqualsCalledOnEnumConstantInspectionTest extends LightInspectionTestCase { @@ -10,6 +12,12 @@ public class EqualsCalledOnEnumConstantInspectionTest extends LightInspectionTes doTest(); } + @NotNull + @Override + protected LightProjectDescriptor getProjectDescriptor() { + return JAVA_8; + } + @Nullable @Override protected InspectionProfileEntry getInspection() {