diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/EqualsBetweenInconvertibleTypesInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/EqualsBetweenInconvertibleTypesInspection.java index db01c1380985..712955f96bca 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/EqualsBetweenInconvertibleTypesInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/EqualsBetweenInconvertibleTypesInspection.java @@ -22,12 +22,12 @@ import com.intellij.psi.util.TypeConversionUtil; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.InconvertibleTypesChecker; import com.siyeh.ig.psiutils.TypeUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; -import java.util.HashSet; public class EqualsBetweenInconvertibleTypesInspection extends BaseInspection { @@ -92,24 +92,28 @@ public class EqualsBetweenInconvertibleTypesInspection extends BaseInspection { !TypeUtils.areConvertible(lhsType, rhsType) /* red code */) { return; } - createInconvertibleTypesChecker().deepCheck(lhsType, rhsType, expression.getOperationSign(), new HashSet<>(), WARN_IF_NO_MUTUAL_SUBCLASS_FOUND, isOnTheFly()); + InconvertibleTypesChecker.TypeMismatch mismatch = + InconvertibleTypesChecker.deepCheck(lhsType, rhsType, getMutualSubclassMode()); + if (mismatch != null) { + registerError(expression.getOperationSign(), mismatch.getLeft(), mismatch.getRight(), mismatch.isConvertible()); + } + } + + private InconvertibleTypesChecker.LookForMutualSubclass getMutualSubclassMode() { + if (!WARN_IF_NO_MUTUAL_SUBCLASS_FOUND) { + return InconvertibleTypesChecker.LookForMutualSubclass.NEVER; + } + return isOnTheFly() + ? InconvertibleTypesChecker.LookForMutualSubclass.IF_CHEAP + : InconvertibleTypesChecker.LookForMutualSubclass.ALWAYS; } @Override public void checkTypes(@NotNull PsiReferenceExpression expression, @NotNull PsiType leftType, @NotNull PsiType rightType) { - createInconvertibleTypesChecker().checkTypes(expression, leftType, rightType, WARN_IF_NO_MUTUAL_SUBCLASS_FOUND, isOnTheFly()); - } - - private InconvertibleTypesChecker createInconvertibleTypesChecker() { - return new InconvertibleTypesChecker() { - @Override - protected void registerEqualsError(PsiElement highlightLocation, - @NotNull PsiType leftType, - @NotNull PsiType rightType, - boolean convertible) { - registerError(highlightLocation, leftType, rightType, convertible); - } - }; + InconvertibleTypesChecker.TypeMismatch mismatch = InconvertibleTypesChecker.checkTypes(leftType, rightType, getMutualSubclassMode()); + if (mismatch != null) { + registerError(expression, mismatch.getLeft(), mismatch.getRight(), mismatch.isConvertible()); + } } } } \ No newline at end of file diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/InconvertibleTypesChecker.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/InconvertibleTypesChecker.java deleted file mode 100644 index 210c2fc9769a..000000000000 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/InconvertibleTypesChecker.java +++ /dev/null @@ -1,86 +0,0 @@ -// 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.bugs; - -import com.intellij.openapi.util.Couple; -import com.intellij.psi.*; -import com.intellij.psi.util.PsiUtil; -import com.siyeh.ig.psiutils.InheritanceUtil; -import com.siyeh.ig.psiutils.TypeUtils; -import org.jetbrains.annotations.NotNull; - -import java.util.HashSet; -import java.util.Set; - -public abstract class InconvertibleTypesChecker { - protected abstract void registerEqualsError(PsiElement highlightLocation, - @NotNull PsiType leftType, - @NotNull PsiType rightType, - boolean convertible); - - public void checkTypes(@NotNull PsiReferenceExpression expression, - @NotNull PsiType leftType, - @NotNull PsiType rightType, - boolean warnIfNoMutualSubclassFound, - boolean onTheFly) { - PsiElement name = expression.getReferenceNameElement(); - if (name == null) { - return; - } - if (TypeUtils.areConvertible(leftType, rightType) || TypeUtils.mayBeEqualByContract(leftType, rightType)) { - deepCheck(leftType, rightType, name, new HashSet<>(), warnIfNoMutualSubclassFound, onTheFly); - return; - } - registerEqualsError(name, leftType, rightType, false); - } - - protected void deepCheck(@NotNull PsiType leftType, - @NotNull PsiType rightType, - PsiElement highlightLocation, - Set> checked, - boolean warnIfNoMutualSubclassFound, - boolean onTheFly) { - if (leftType instanceof PsiCapturedWildcardType) { - leftType = ((PsiCapturedWildcardType)leftType).getUpperBound(); - } - if (rightType instanceof PsiCapturedWildcardType) { - rightType = ((PsiCapturedWildcardType)rightType).getUpperBound(); - } - if (!checked.add(Couple.of(leftType, rightType))) { - return; - } - if (leftType.isAssignableFrom(rightType) || rightType.isAssignableFrom(leftType)) return; - PsiClass leftClass = PsiUtil.resolveClassInClassTypeOnly(leftType); - PsiClass rightClass = PsiUtil.resolveClassInClassTypeOnly(rightType); - if (leftClass == null || rightClass == null) return; - if (!rightClass.isInterface()) { - PsiClass tmp = leftClass; - leftClass = rightClass; - rightClass = tmp; - } - if (leftClass == rightClass || TypeUtils.mayBeEqualByContract(leftType, rightType)) { - // check type parameters - if (leftType instanceof PsiClassType && rightType instanceof PsiClassType) { - final PsiType[] leftParameters = ((PsiClassType)leftType).getParameters(); - final PsiType[] rightParameters = ((PsiClassType)rightType).getParameters(); - if (leftParameters.length == rightParameters.length) { - for (int i = 0, length = leftParameters.length; i < length; i++) { - final PsiType leftParameter = leftParameters[i]; - final PsiType rightParameter = rightParameters[i]; - if (!TypeUtils.areConvertible(leftParameter, rightParameter) && - !TypeUtils.mayBeEqualByContract(leftParameter, rightParameter)) { - registerEqualsError(highlightLocation, leftType, rightType, false); - return; - } - deepCheck(leftParameter, rightParameter, highlightLocation, checked, warnIfNoMutualSubclassFound, onTheFly); - } - } - } - } - else if (TypeUtils.cannotBeEqualByContract(leftType, rightType)) { - registerEqualsError(highlightLocation, leftType, rightType, false); - } - else if (warnIfNoMutualSubclassFound && !InheritanceUtil.existsMutualSubclass(leftClass, rightClass, onTheFly)) { - registerEqualsError(highlightLocation, leftType, rightType, true); - } - } -} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/InconvertibleTypesChecker.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/InconvertibleTypesChecker.java new file mode 100644 index 000000000000..cb36c78a3491 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/InconvertibleTypesChecker.java @@ -0,0 +1,114 @@ +// Copyright 2000-2020 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.openapi.util.Couple; +import com.intellij.psi.PsiCapturedWildcardType; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiClassType; +import com.intellij.psi.PsiType; +import com.intellij.psi.util.PsiUtil; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.HashSet; +import java.util.Set; + +public class InconvertibleTypesChecker { + @Contract(pure = true) + public static @Nullable TypeMismatch checkTypes(@NotNull PsiType leftType, + @NotNull PsiType rightType, + @NotNull LookForMutualSubclass lookForMutualSubclass) { + if (TypeUtils.areConvertible(leftType, rightType) || TypeUtils.mayBeEqualByContract(leftType, rightType)) { + return deepCheck(leftType, rightType, lookForMutualSubclass); + } + return new TypeMismatch(leftType, rightType, false); + } + + public static @Nullable TypeMismatch deepCheck(@NotNull PsiType leftType, + @NotNull PsiType rightType, + @NotNull LookForMutualSubclass lookForMutualSubclass) { + return deepCheck(leftType, rightType, new HashSet<>(), lookForMutualSubclass); + } + + private static @Nullable TypeMismatch deepCheck(@NotNull PsiType leftType, + @NotNull PsiType rightType, + @NotNull Set> checked, + @NotNull LookForMutualSubclass lookForMutualSubclass) { + if (leftType instanceof PsiCapturedWildcardType) { + leftType = ((PsiCapturedWildcardType)leftType).getUpperBound(); + } + if (rightType instanceof PsiCapturedWildcardType) { + rightType = ((PsiCapturedWildcardType)rightType).getUpperBound(); + } + if (!checked.add(Couple.of(leftType, rightType))) { + return null; + } + if (leftType.isAssignableFrom(rightType) || rightType.isAssignableFrom(leftType)) return null; + PsiClass leftClass = PsiUtil.resolveClassInClassTypeOnly(leftType); + PsiClass rightClass = PsiUtil.resolveClassInClassTypeOnly(rightType); + if (leftClass == null || rightClass == null) return null; + if (!rightClass.isInterface()) { + PsiClass tmp = leftClass; + leftClass = rightClass; + rightClass = tmp; + } + if (leftClass == rightClass || TypeUtils.mayBeEqualByContract(leftType, rightType)) { + // check type parameters + if (leftType instanceof PsiClassType && rightType instanceof PsiClassType) { + final PsiType[] leftParameters = ((PsiClassType)leftType).getParameters(); + final PsiType[] rightParameters = ((PsiClassType)rightType).getParameters(); + if (leftParameters.length == rightParameters.length) { + for (int i = 0, length = leftParameters.length; i < length; i++) { + final PsiType leftParameter = leftParameters[i]; + final PsiType rightParameter = rightParameters[i]; + if (!TypeUtils.areConvertible(leftParameter, rightParameter) && + !TypeUtils.mayBeEqualByContract(leftParameter, rightParameter)) { + return new TypeMismatch(leftType, rightType, false); + } + TypeMismatch mismatch = deepCheck(leftParameter, rightParameter, checked, lookForMutualSubclass); + if (mismatch != null) { + return mismatch; + } + } + } + } + } + else if (TypeUtils.cannotBeEqualByContract(leftType, rightType)) { + return new TypeMismatch(leftType, rightType, false); + } + else if (lookForMutualSubclass != LookForMutualSubclass.NEVER && + !InheritanceUtil.existsMutualSubclass(leftClass, rightClass, lookForMutualSubclass == LookForMutualSubclass.IF_CHEAP)) { + return new TypeMismatch(leftType, rightType, true); + } + return null; + } + + public enum LookForMutualSubclass { + NEVER, ALWAYS, IF_CHEAP + } + + public static class TypeMismatch { + private final @NotNull PsiType myLeft; + private final @NotNull PsiType myRight; + private final boolean myConvertible; + + private TypeMismatch(@NotNull PsiType left, @NotNull PsiType right, boolean convertible) { + myLeft = left; + myRight = right; + myConvertible = convertible; + } + + public @NotNull PsiType getLeft() { + return myLeft; + } + + public @NotNull PsiType getRight() { + return myRight; + } + + public boolean isConvertible() { + return myConvertible; + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/testFrameworks/BaseAssertEqualsBetweenInconvertibleTypesInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/testFrameworks/BaseAssertEqualsBetweenInconvertibleTypesInspection.java index 319b419f3908..b4f69aff4df2 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/testFrameworks/BaseAssertEqualsBetweenInconvertibleTypesInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/testFrameworks/BaseAssertEqualsBetweenInconvertibleTypesInspection.java @@ -6,10 +6,12 @@ import com.intellij.psi.*; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; -import com.siyeh.ig.bugs.InconvertibleTypesChecker; +import com.siyeh.ig.psiutils.InconvertibleTypesChecker; import com.siyeh.ig.psiutils.TypeUtils; import org.jetbrains.annotations.NotNull; +import java.util.Objects; + public abstract class BaseAssertEqualsBetweenInconvertibleTypesInspection extends BaseInspection { protected abstract boolean checkTestNG(); @@ -56,15 +58,12 @@ public abstract class BaseAssertEqualsBetweenInconvertibleTypesInspection extend return; } - new InconvertibleTypesChecker() { - @Override - protected void registerEqualsError(PsiElement highlightLocation, - @NotNull PsiType leftType, - @NotNull PsiType rightType, - boolean convertible) { - AssertEqualsBetweenInconvertibleTypesVisitor.this.registerError(highlightLocation, leftType, rightType, convertible); - } - }.checkTypes(expression.getMethodExpression(), type1, type2, true, isOnTheFly()); + InconvertibleTypesChecker.TypeMismatch mismatch = InconvertibleTypesChecker.checkTypes( + type1, type2, isOnTheFly() ? InconvertibleTypesChecker.LookForMutualSubclass.IF_CHEAP : InconvertibleTypesChecker.LookForMutualSubclass.ALWAYS); + if (mismatch != null) { + PsiElement name = Objects.requireNonNull(expression.getMethodExpression().getReferenceNameElement()); + registerError(name, mismatch.getLeft(), mismatch.getRight(), mismatch.isConvertible()); + } } } }