boolean (@NotNull PsiType fromType, @NotNull PsiType toType) { toType = uncapture(toType); fromType = uncapture(fromType); if (toType instanceof PsiPrimitiveType || fromType instanceof PsiPrimitiveType) return fromType.equals(toType); //Done with primitives if (toType instanceof PsiDiamondType || fromType instanceof PsiDiamondType) return false; if (toType instanceof PsiArrayType && !(fromType instanceof PsiArrayType)) { if (fromType instanceof PsiClassType) { final PsiClass resolved = ((PsiClassType)fromType).resolve(); if (resolved instanceof PsiTypeParameter) { for (final PsiClassType boundType : resolved.getExtendsListTypes()) { if (!isNarrowingReferenceConversionAllowed(boundType, toType)) return false; } return true; } } return isAssignable(fromType, toType); } if (fromType instanceof PsiArrayType) { if (toType instanceof PsiClassType) { final PsiClass resolved = ((PsiClassType)toType).resolve(); if (resolved instanceof PsiTypeParameter) { for (final PsiClassType boundType : resolved.getExtendsListTypes()) { if (!areTypesConvertible(fromType, boundType)) return false; } return true; } } return toType instanceof PsiArrayType && isNarrowingReferenceConversionAllowed(((PsiArrayType)fromType).getComponentType(), ((PsiArrayType)toType).getComponentType()); } //Done with array types if (fromType instanceof PsiIntersectionType) { final PsiType[] conjuncts = ((PsiIntersectionType)fromType).getConjuncts(); for (PsiType conjunct : conjuncts) { if (isNarrowingReferenceConversionAllowed(conjunct, toType)) return true; } return false; } if (toType instanceof PsiIntersectionType) { if (fromType instanceof PsiClassType && ((PsiClassType)fromType).getLanguageLevel().isAtLeast(LanguageLevel.JDK_1_8)) { for (PsiType conjunct : ((PsiIntersectionType)toType).getConjuncts()) { if (!isNarrowingReferenceConversionAllowed(fromType, conjunct)) return false; } return true; } return false; } if (fromType instanceof PsiDisjunctionType) { return isNarrowingReferenceConversionAllowed(((PsiDisjunctionType)fromType).getLeastUpperBound(), toType); } if (toType instanceof PsiDisjunctionType) { return false; } if (fromType instanceof PsiWildcardType) { final PsiWildcardType fromWildcard = (PsiWildcardType)fromType; final PsiType bound = fromWildcard.getBound(); if (bound == null) return true; if (fromWildcard.isSuper()) { return isAssignable(toType, bound); } return isNarrowingReferenceConversionAllowed(bound, toType); } if (toType instanceof PsiWildcardType) { final PsiWildcardType toWildcard = (PsiWildcardType)toType; if (toWildcard.isSuper()) return false; final PsiType bound = toWildcard.getBound(); return bound == null || isNarrowingReferenceConversionAllowed(fromType, bound); } if (isAssignable(fromType, toType)) return true; if (!(fromType instanceof PsiClassType) || !(toType instanceof PsiClassType)) return false; PsiClassType fromClassType = (PsiClassType)fromType; PsiClassType toClassType = (PsiClassType)toType; PsiClassType.ClassResolveResult fromResult = fromClassType.resolveGenerics(); final PsiClass fromClass = fromResult.getElement(); if (fromClass == null) return false; if (fromClass instanceof PsiTypeParameter) { return isNarrowingReferenceConversionAllowed(obtainSafeSuperType((PsiTypeParameter)fromClass), toType); } PsiClassType.ClassResolveResult toResult = toClassType.resolveGenerics(); final PsiClass toClass = toResult.getElement(); if (toClass == null) return false; if (toClass instanceof PsiTypeParameter) { return isNarrowingReferenceConversionAllowed(fromType, obtainSafeSuperType((PsiTypeParameter)toClass)); } //Done with type parameters PsiManager manager = fromClass.getManager(); final LanguageLevel languageLevel = toClassType.getLanguageLevel(); // jep-397 if (languageLevel.isAtLeast(LanguageLevel.JDK_17)) { if (fromClass.isInterface() || toClass.isInterface()) { if (fromClass.hasModifierProperty(PsiModifier.SEALED)) { if (!canConvertSealedTo(fromClass, toClass)) return false; } else if (toClass.hasModifierProperty(PsiModifier.SEALED)) { if (!canConvertSealedTo(toClass, fromClass)) return false; } } } if (!fromClass.isInterface()) { if (toClass.isInterface()) { return (!fromClass.hasModifierProperty(PsiModifier.FINAL) || fromClass.isInheritor(toClass, true)) && checkSuperTypesWithDifferentTypeArguments(toResult, fromClass, manager, fromResult.getSubstitutor(), null, languageLevel); } else { if (manager.areElementsEquivalent(fromClass, toClass)) { return areSameParameterTypes(fromClassType, toClassType); } if (toClass.isInheritor(fromClass, true)) { return checkSuperTypesWithDifferentTypeArguments(fromResult, toClass, manager, toResult.getSubstitutor(), null, languageLevel); } if (fromClass.isInheritor(toClass, true)) { return checkSuperTypesWithDifferentTypeArguments(toResult, fromClass, manager, fromResult.getSubstitutor(), null, languageLevel); } return false; } } else if (!toClass.isInterface()) { if (!toClass.hasModifierProperty(PsiModifier.FINAL)) { return checkSuperTypesWithDifferentTypeArguments(fromResult, toClass, manager, toResult.getSubstitutor(), null, languageLevel); } else { PsiSubstitutor toSubstitutor = getMaybeSuperClassSubstitutor(fromClass, toClass, toResult.getSubstitutor()); return toSubstitutor != null && areSameArgumentTypes(fromClass, fromResult.getSubstitutor(), toSubstitutor); } } else if (languageLevel.compareTo(LanguageLevel.JDK_1_5) < 0) { //In jls2 check for method in both interfaces with the same signature but different return types. Collection<HierarchicalMethodSignature> fromClassMethodSignatures = fromClass.getVisibleSignatures(); Collection<HierarchicalMethodSignature> toClassMethodSignatures = toClass.getVisibleSignatures(); for (HierarchicalMethodSignature fromMethodSignature : fromClassMethodSignatures) { for (HierarchicalMethodSignature toMethodSignature : toClassMethodSignatures) { if (fromMethodSignature.equals(toMethodSignature)) { final PsiType fromClassReturnType = fromMethodSignature.getMethod().getReturnType(); final PsiType toClassReturnType = toMethodSignature.getMethod().getReturnType(); if (fromClassReturnType != null && toClassReturnType != null && !fromClassReturnType.equals(toClassReturnType)) { return false; } } } } return true; } else { //In jls3 check for super interface with distinct type arguments PsiClassType.ClassResolveResult baseResult; PsiClass derived; PsiSubstitutor derivedSubstitutor; if (toClass.isInheritor(fromClass, true)) { baseResult = fromResult; derived = toClass; derivedSubstitutor = toResult.getSubstitutor(); } else { baseResult = toResult; derived = fromClass; derivedSubstitutor = fromResult.getSubstitutor(); } return checkSuperTypesWithDifferentTypeArguments(baseResult, derived, manager, derivedSubstitutor, null, languageLevel); } }