blob: 5e13c8f780fc52f1044451ac7f7735178555b258 [file] [log] [blame]
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi.util;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.containers.HashSet;
import java.util.Set;
/**
* User: anna
* Date: Aug 12, 2010
*/
public class TypesDistinctProver {
private TypesDistinctProver() {
}
public static boolean provablyDistinct(PsiType type1, PsiType type2) {
return provablyDistinct(type1, type2, 0);
}
protected static boolean provablyDistinct(PsiType type1, PsiType type2, int level) {
if (type1 instanceof PsiWildcardType) {
if (type2 instanceof PsiWildcardType) {
return provablyDistinct((PsiWildcardType)type1, (PsiWildcardType)type2, true, level);
}
if (level > 1) return true;
if (type2 instanceof PsiCapturedWildcardType) {
return provablyDistinct((PsiWildcardType)type1, ((PsiCapturedWildcardType)type2).getWildcard(), false, level);
}
if (type2 instanceof PsiClassType) {
final PsiClass psiClass2 = PsiUtil.resolveClassInType(type2);
if (psiClass2 == null) return false;
if (((PsiWildcardType)type1).isExtends()) {
final PsiType extendsBound = ((PsiWildcardType)type1).getExtendsBound();
if (extendsBound instanceof PsiArrayType &&
proveArrayTypeDistinct(((PsiWildcardType)type1).getManager().getProject(), (PsiArrayType)extendsBound, type2)) return true;
final PsiClass boundClass1 = PsiUtil.resolveClassInType(extendsBound);
if (boundClass1 == null) return false;
if (CommonClassNames.JAVA_LANG_OBJECT.equals(psiClass2.getQualifiedName()) && !(boundClass1 instanceof PsiTypeParameter)) {
return !CommonClassNames.JAVA_LANG_OBJECT.equals(boundClass1.getQualifiedName());
}
return proveExtendsBoundsDistinct(type1, type2, boundClass1, psiClass2);
}
if (((PsiWildcardType)type1).isSuper()) {
final PsiType superBound = ((PsiWildcardType)type1).getSuperBound();
if (superBound instanceof PsiArrayType &&
proveArrayTypeDistinct(((PsiWildcardType)type1).getManager().getProject(), (PsiArrayType)superBound, type2)) return true;
final PsiClass boundClass1 = PsiUtil.resolveClassInType(superBound);
if (boundClass1 == null) return false;
if (boundClass1 instanceof PsiTypeParameter) {
final PsiClassType[] extendsListTypes = boundClass1.getExtendsListTypes();
for (PsiClassType classType : extendsListTypes) {
final PsiClass psiClass = classType.resolve();
if (InheritanceUtil.isInheritorOrSelf(psiClass, psiClass2, true) || InheritanceUtil.isInheritorOrSelf(psiClass2, psiClass, true)) return false;
}
return extendsListTypes.length > 0;
}
return !InheritanceUtil.isInheritorOrSelf(boundClass1, psiClass2, true);
}
final PsiType bound = ((PsiWildcardType)type1).getBound();
return bound != null && !bound.equals(psiClass2);
}
if (type2 instanceof PsiArrayType) {
return proveArrayTypeDistinct(((PsiWildcardType)type1).getManager().getProject(), (PsiArrayType)type2, type1);
}
} else {
if (type2 instanceof PsiWildcardType) return provablyDistinct(type2, type1, level);
if (type1 instanceof PsiCapturedWildcardType) return provablyDistinct(((PsiCapturedWildcardType)type1).getWildcard(), type2, level);
if (type2 instanceof PsiCapturedWildcardType) return provablyDistinct(type2, type1, level);
}
final PsiClassType.ClassResolveResult classResolveResult1 = PsiUtil.resolveGenericsClassInType(type1);
final PsiClassType.ClassResolveResult classResolveResult2 = PsiUtil.resolveGenericsClassInType(type2);
if (Comparing.equal(TypeConversionUtil.erasure(type1), TypeConversionUtil.erasure(type2))) {
final PsiSubstitutor substitutor1 = classResolveResult1.getSubstitutor();
final PsiSubstitutor substitutor2 = classResolveResult2.getSubstitutor();
for (PsiTypeParameter parameter : substitutor1.getSubstitutionMap().keySet()) {
final PsiType substitutedType1 = substitutor1.substitute(parameter);
final PsiType substitutedType2 = substitutor2.substitute(parameter);
if (substitutedType1 == null && substitutedType2 == null){
continue;
}
if (substitutedType1 == null) {
if (type2 instanceof PsiClassType && ((PsiClassType)type2).hasParameters()) return true;
}
else if (substitutedType2 == null) {
if (type1 instanceof PsiClassType && ((PsiClassType)type1).hasParameters()) return true;
} else {
if (provablyDistinct(substitutedType1, substitutedType2, level + 1)) return true;
if (substitutedType1 instanceof PsiWildcardType && !((PsiWildcardType)substitutedType1).isBounded()) return true;
}
}
if (level < 2) return false;
}
final PsiClass boundClass1 = classResolveResult1.getElement();
final PsiClass boundClass2 = classResolveResult2.getElement();
if (boundClass1 instanceof PsiTypeParameter && level < 2) {
if (!distinguishFromTypeParam((PsiTypeParameter)boundClass1, boundClass2, type1)) return false;
}
if (boundClass2 instanceof PsiTypeParameter && level < 2) {
if (!distinguishFromTypeParam((PsiTypeParameter)boundClass2, boundClass1, type2)) return false;
}
return type2 != null && type1 != null && !type1.equals(type2) &&
(!InheritanceUtil.isInheritorOrSelf(boundClass1, boundClass2, true) ||
!InheritanceUtil.isInheritorOrSelf(boundClass2, boundClass1, true));
}
private static boolean distinguishFromTypeParam(PsiTypeParameter typeParam, PsiClass boundClass, PsiType type1) {
final PsiClassType[] paramBounds = typeParam.getExtendsListTypes();
if (paramBounds.length == 0 && type1 instanceof PsiClassType) return false;
for (PsiClassType classType : paramBounds) {
final PsiClass paramBound = classType.resolve();
if (paramBound != null &&
(InheritanceUtil.isInheritorOrSelf(paramBound, boundClass, true) ||
InheritanceUtil.isInheritorOrSelf(boundClass, paramBound, true))) {
return false;
}
}
return true;
}
public static boolean provablyDistinct(PsiWildcardType type1, PsiWildcardType type2, boolean rejectInconsistentRaw, int level) {
if (type1.isSuper() && type2.isSuper()) return false;
if (type1.isExtends() && type2.isExtends()) {
final PsiType extendsBound1 = type1.getExtendsBound();
final PsiType extendsBound2 = type2.getExtendsBound();
if (extendsBound1 instanceof PsiArrayType && proveArrayTypeDistinct(type1.getManager().getProject(), (PsiArrayType)extendsBound1, extendsBound2) ||
extendsBound2 instanceof PsiArrayType && proveArrayTypeDistinct(type1.getManager().getProject(), (PsiArrayType)extendsBound2, extendsBound1)) return true;
final PsiClass boundClass1 = PsiUtil.resolveClassInType(extendsBound1);
final PsiClass boundClass2 = PsiUtil.resolveClassInType(extendsBound2);
if (boundClass1 != null && boundClass2 != null) {
if (rejectInconsistentRaw &&
extendsBound1 instanceof PsiClassType && extendsBound2 instanceof PsiClassType &&
(((PsiClassType)extendsBound1).isRaw() ^ ((PsiClassType)extendsBound2).isRaw())) return true;
return proveExtendsBoundsDistinct(type1, type2, boundClass1, boundClass2);
}
return provablyDistinct(extendsBound1, extendsBound2, 1);
}
if (type2.isExtends()) return provablyDistinct(type2, type1, rejectInconsistentRaw, level);
if (type1.isExtends() && !type2.isBounded() && level > 1) return PsiUtil.resolveClassInType(type1.getExtendsBound()) instanceof PsiTypeParameter;
if (type1.isExtends() && type2.isSuper()) {
final PsiType extendsBound = type1.getExtendsBound();
final PsiType superBound = type2.getSuperBound();
if (extendsBound instanceof PsiArrayType && proveArrayTypeDistinct(type1.getManager().getProject(), (PsiArrayType)extendsBound, superBound) ||
superBound instanceof PsiArrayType && proveArrayTypeDistinct(type1.getManager().getProject(), (PsiArrayType)superBound, extendsBound)) return true;
final PsiClass extendsBoundClass = PsiUtil.resolveClassInType(extendsBound);
final PsiClass superBoundClass = PsiUtil.resolveClassInType(superBound);
if (extendsBoundClass != null && superBoundClass != null) {
if (extendsBoundClass instanceof PsiTypeParameter) {
return try2ProveTypeParameterDistinct(type2, extendsBoundClass);
}
if (superBoundClass instanceof PsiTypeParameter) return false;
return !InheritanceUtil.isInheritorOrSelf(superBoundClass, extendsBoundClass, true);
}
return provablyDistinct(extendsBound, superBound);
}
if (!type1.isBounded() || !type2.isBounded()) {
return false;
}
return !type1.equals(type2);
}
public static boolean proveExtendsBoundsDistinct(PsiType type1,
PsiType type2,
PsiClass boundClass1,
PsiClass boundClass2) {
if (boundClass1 == null || boundClass2 == null) {
return false;
}
if (boundClass1.isInterface() && boundClass2.isInterface()) return false;
if (boundClass1.isInterface()) {
return !(boundClass2.hasModifierProperty(PsiModifier.FINAL) ? InheritanceUtil.isInheritorOrSelf(boundClass2, boundClass1, true) : true);
}
if (boundClass2.isInterface()) {
return !(boundClass1.hasModifierProperty(PsiModifier.FINAL) ? InheritanceUtil.isInheritorOrSelf(boundClass1, boundClass2, true) : true);
}
if (boundClass1 instanceof PsiTypeParameter) {
return try2ProveTypeParameterDistinct(type2, boundClass1);
}
if (boundClass2 instanceof PsiTypeParameter) {
return try2ProveTypeParameterDistinct(type1, boundClass2);
}
return !InheritanceUtil.isInheritorOrSelf(boundClass1, boundClass2, true) && !InheritanceUtil.isInheritorOrSelf(boundClass2, boundClass1, true);
}
public static boolean try2ProveTypeParameterDistinct(PsiType type, PsiClass typeParameter) {
final PsiClassType[] types = typeParameter.getExtendsListTypes();
if (types.length == 0) return false;
return provablyDistinct(PsiWildcardType.createExtends(typeParameter.getManager(), types[0]), type);
}
public static boolean proveArrayTypeDistinct(Project project,
PsiArrayType type,
PsiType bound) {
final JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
final GlobalSearchScope searchScope = GlobalSearchScope.allScope(project);
final Set<PsiClass> possibleClasses = new HashSet<PsiClass>();
possibleClasses.add(facade.findClass(CommonClassNames.JAVA_IO_SERIALIZABLE, searchScope));
possibleClasses.add(facade.findClass(CommonClassNames.JAVA_LANG_CLONEABLE, searchScope));
possibleClasses.add(facade.findClass(CommonClassNames.JAVA_LANG_OBJECT, searchScope));
if (type.getArrayDimensions() == bound.getArrayDimensions()) {
final PsiType componentType = type.getComponentType();
final PsiType boundComponentType = ((PsiArrayType)bound).getComponentType();
if (boundComponentType instanceof PsiClassType && componentType instanceof PsiClassType) {
return proveExtendsBoundsDistinct(boundComponentType, componentType, ((PsiClassType)boundComponentType).resolve(), ((PsiClassType)componentType).resolve());
}
else {
return !bound.equals(type);
}
}
else if (bound.getArrayDimensions() + 1 == type.getArrayDimensions() && bound.getDeepComponentType() instanceof PsiClassType) {
return !possibleClasses.contains(((PsiClassType)bound.getDeepComponentType()).resolve());
}
else if (bound.getArrayDimensions() == type.getArrayDimensions() + 1 && type.getDeepComponentType() instanceof PsiClassType) {
return !possibleClasses.contains(((PsiClassType)type.getDeepComponentType()).resolve());
}
else if (bound instanceof PsiClassType) {
return !possibleClasses.contains(((PsiClassType)bound).resolve());
}
else if (bound instanceof PsiWildcardType) {
final PsiType boundBound = ((PsiWildcardType)bound).getBound();
return boundBound != null && !boundBound.equals(type);
}
return true;
}
}