boolean (@NotNull PsiType left, @NotNull PsiType right, boolean allowUncheckedConversion, boolean capture) { if (left == right) return true; if (left instanceof PsiClassType && left.equalsToText(JAVA_LANG_OBJECT)) { if (right instanceof PsiMethodReferenceType || right instanceof PsiLambdaExpressionType) return false; } if (left.equals(right)) return true; if (isNullType(right)) { return !(left instanceof PsiPrimitiveType) || isNullType(left); } if (right instanceof PsiMethodReferenceType) { final PsiMethodReferenceExpression methodReferenceExpression = ((PsiMethodReferenceType)right).getExpression(); if (left instanceof PsiLambdaExpressionType) { final PsiType rType = methodReferenceExpression.getFunctionalInterfaceType(); final PsiType lType = ((PsiLambdaExpressionType)left).getExpression().getFunctionalInterfaceType(); return Comparing.equal(rType, lType); } if (left instanceof PsiMethodReferenceType) { final PsiType rType = methodReferenceExpression.getFunctionalInterfaceType(); final PsiType lType = ((PsiMethodReferenceType)left).getExpression().getFunctionalInterfaceType(); return Comparing.equal(rType, lType); } return !(left instanceof PsiArrayType) && methodReferenceExpression.isAcceptable(left); } if (right instanceof PsiLambdaExpressionType) { final PsiLambdaExpression rLambdaExpression = ((PsiLambdaExpressionType)right).getExpression(); if (left instanceof PsiLambdaExpressionType) { final PsiLambdaExpression lLambdaExpression = ((PsiLambdaExpressionType)left).getExpression(); final PsiType rType = rLambdaExpression.getFunctionalInterfaceType(); final PsiType lType = lLambdaExpression.getFunctionalInterfaceType(); return Comparing.equal(rType, lType); } return !(left instanceof PsiArrayType) && rLambdaExpression.isAcceptable(left); } if (left instanceof PsiIntersectionType) { PsiType[] conjuncts = ((PsiIntersectionType)left).getConjuncts(); for (PsiType conjunct : conjuncts) { if (!isAssignable(conjunct, right, allowUncheckedConversion, capture)) return false; } return true; } if (right instanceof PsiIntersectionType) { PsiType[] conjuncts = ((PsiIntersectionType)right).getConjuncts(); for (PsiType conjunct : conjuncts) { if (isAssignable(left, conjunct, allowUncheckedConversion, capture)) return true; } return false; } if (right instanceof PsiCapturedWildcardType) { return isAssignable(left, ((PsiCapturedWildcardType)right).getUpperBound(capture), allowUncheckedConversion, capture); } if (left instanceof PsiCapturedWildcardType) { return left.equals(right) || isAssignable(((PsiCapturedWildcardType)left).getLowerBound(), right, allowUncheckedConversion, capture); } if (left instanceof PsiWildcardType) { return isAssignableToWildcard((PsiWildcardType)left, right); } if (right instanceof PsiWildcardType) { return isAssignableFromWildcard(left, (PsiWildcardType)right); } if (right instanceof PsiArrayType) { if (!(left instanceof PsiArrayType)) { if (left instanceof PsiPrimitiveType || PsiUtil.resolveClassInType(left) == null) return false; PsiClass lClass = PsiUtil.resolveClassInType(left); if (lClass == null) return false; if (lClass.isInterface()) { final String qualifiedName = lClass.getQualifiedName(); return "java.io.Serializable".equals(qualifiedName) || "java.lang.Cloneable".equals(qualifiedName); } else { return left.equalsToText(JAVA_LANG_OBJECT); } } PsiType lCompType = ((PsiArrayType)left).getComponentType(); PsiType rCompType = ((PsiArrayType)right).getComponentType(); if (lCompType instanceof PsiPrimitiveType) { return lCompType.equals(rCompType); } return !(rCompType instanceof PsiPrimitiveType) && isAssignable(lCompType, rCompType, allowUncheckedConversion, capture); } if (left instanceof PsiDisjunctionType) { for (PsiType type : ((PsiDisjunctionType)left).getDisjunctions()) { if (isAssignable(type, right, allowUncheckedConversion, capture)) return true; } return false; } if (right instanceof PsiDisjunctionType) { return isAssignable(left, ((PsiDisjunctionType)right).getLeastUpperBound(), allowUncheckedConversion, capture); } if (left instanceof PsiArrayType) { if (right instanceof PsiClassType) { PsiClass aClass = ((PsiClassType)right).resolve(); if (aClass instanceof PsiTypeParameter) { PsiType upperBound = getInferredUpperBoundForSynthetic((PsiTypeParameter)aClass); return upperBound != null && isAssignable(left, upperBound, allowUncheckedConversion, capture); } } return false; } if (right instanceof PsiPrimitiveType) { if (isVoidType(right)) return false; if (!(left instanceof PsiPrimitiveType)) { return left instanceof PsiClassType && isBoxable((PsiClassType)left, (PsiPrimitiveType)right); } int l = TYPE_TO_RANK_MAP.get(left); int r = TYPE_TO_RANK_MAP.get(right); int leftTypeIndex = (l==-1?0:l) - 1; int rightTypeIndex = (r==-1?0:r) - 1; return leftTypeIndex >= 0 && rightTypeIndex >= 0 && rightTypeIndex < IS_ASSIGNABLE_BIT_SET.length && leftTypeIndex < IS_ASSIGNABLE_BIT_SET.length && IS_ASSIGNABLE_BIT_SET[rightTypeIndex][leftTypeIndex]; } if (!(right instanceof PsiClassType)) { return false; // must be TypeCook's PsiTypeVariable } if (left instanceof PsiPrimitiveType) { return isUnboxable((PsiPrimitiveType)left, (PsiClassType)right, new HashSet<>()); } final PsiClassType.ClassResolveResult leftResult = PsiUtil.resolveGenericsClassInType(left); final PsiClassType.ClassResolveResult rightResult = PsiUtil.resolveGenericsClassInType(right); PsiClass leftResultElement = leftResult.getElement(); PsiClass rightResultElement = rightResult.getElement(); if (leftResultElement == null || rightResultElement == null) { if (leftResultElement == null && rightResultElement != null && left instanceof PsiClassType && left.equalsToText(JAVA_LANG_OBJECT)) { return true; } if (leftResultElement != rightResultElement) return false; // let's suppose 2 unknown classes, which could be the same to be the same String lText = left.getPresentableText(); String rText = right.getPresentableText(); if (lText.equals(rText)) return true; if (lText.length() > rText.length() && lText.endsWith(rText) && lText.charAt(lText.length() - rText.length() - 1) == '.') { return true; } return rText.length() > lText.length() && rText.endsWith(lText) && rText.charAt(rText.length() - lText.length() - 1) == '.'; } return isClassAssignable(leftResult, rightResult, allowUncheckedConversion, left.getResolveScope(), capture); }