Refactor InconvertibleTypesChecker

GitOrigin-RevId: e2833d187bf6ce3c3ebd49e7efaaa30bef8a099d
This commit is contained in:
Tagir Valeev
2020-05-05 07:15:24 +00:00
committed by intellij-monorepo-bot
parent 462856d81c
commit e813c64ce2
4 changed files with 142 additions and 111 deletions
@@ -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());
}
}
}
}
@@ -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<Couple<PsiType>> 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);
}
}
}
@@ -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<Couple<PsiType>> 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;
}
}
}
@@ -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());
}
}
}
}