TypeConversionUtil#erasurePreservingIntersection; used in DfaPsiType#normalizeType

This commit is contained in:
Tagir Valeev
2018-08-06 18:36:02 +07:00
parent f3309a17c3
commit d05fc1b5c9
3 changed files with 53 additions and 43 deletions
@@ -17,10 +17,9 @@ package com.intellij.codeInspection.dataFlow.value;
import com.intellij.codeInspection.dataFlow.TypeConstraint;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.PsiCapturedWildcardType;
import com.intellij.psi.PsiClassType;
import com.intellij.psi.PsiType;
import com.intellij.psi.PsiWildcardType;
import com.intellij.psi.util.TypeConversionUtil;
import org.jetbrains.annotations.NotNull;
/**
@@ -78,15 +77,10 @@ public class DfaPsiType {
public static PsiType normalizeType(@NotNull PsiType psiType) {
int dimensions = psiType.getArrayDimensions();
psiType = psiType.getDeepComponentType();
if (psiType instanceof PsiCapturedWildcardType) {
psiType = ((PsiCapturedWildcardType)psiType).getUpperBound();
}
if (psiType instanceof PsiWildcardType) {
psiType = ((PsiWildcardType)psiType).getExtendsBound();
}
if (psiType instanceof PsiClassType) {
psiType = ((PsiClassType)psiType).rawType();
}
psiType = TypeConversionUtil.erasurePreservingIntersection(psiType);
while (dimensions-- > 0) {
psiType = psiType.createArrayType();
}
@@ -19,6 +19,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.stream.Stream;
public class TypeConversionUtil {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.util.TypeConversionUtil");
@@ -1191,49 +1192,43 @@ public class TypeConversionUtil {
}
public static PsiType typeParameterErasure(@NotNull PsiTypeParameter typeParameter) {
return typeParameterErasure(typeParameter, PsiSubstitutor.EMPTY);
return typeParameterErasure(typeParameter, new THashSet<>(), PsiSubstitutor.EMPTY, false);
}
private static PsiType typeParameterErasure(@NotNull PsiTypeParameter typeParameter, @NotNull PsiSubstitutor beforeSubstitutor) {
private static PsiType typeParameterErasure(PsiTypeParameter typeParameter,
Set<? super PsiClass> visited,
PsiSubstitutor beforeSubstitutor, boolean preserveIntersection) {
final PsiClassType[] extendsList = typeParameter.getExtendsList().getReferencedTypes();
if (extendsList.length > 0) {
final PsiClass psiClass = extendsList[0].resolve();
if (psiClass instanceof PsiTypeParameter) {
Set<PsiClass> visited = new THashSet<>();
visited.add(psiClass);
final PsiTypeParameter boundTypeParameter = (PsiTypeParameter)psiClass;
if (preserveIntersection) {
PsiType[] types = Stream.of(extendsList).map(PsiClassType::resolve).filter(Objects::nonNull)
.map(psiClass -> getClassErasedType(psiClass, visited, beforeSubstitutor, typeParameter, true))
.toArray(PsiType[]::new);
return PsiIntersectionType.createIntersection(true, types);
}
return getClassErasedType(extendsList[0].resolve(), visited, beforeSubstitutor, typeParameter, false);
}
return PsiType.getJavaLangObject(typeParameter.getManager(), typeParameter.getResolveScope());
}
@NotNull
private static PsiType getClassErasedType(PsiClass psiClass,
Set<? super PsiClass> visited,
PsiSubstitutor beforeSubstitutor,
PsiElement context, boolean preserveIntersection) {
if (psiClass instanceof PsiTypeParameter) {
if (visited.add(psiClass)) {
PsiTypeParameter boundTypeParameter = (PsiTypeParameter)psiClass;
if (beforeSubstitutor.getSubstitutionMap().containsKey(boundTypeParameter)) {
return erasure(beforeSubstitutor.substitute(boundTypeParameter));
return erasure(Objects.requireNonNull(beforeSubstitutor.substitute(boundTypeParameter)));
}
return typeParameterErasureInner(boundTypeParameter, visited, beforeSubstitutor);
}
else if (psiClass != null) {
return JavaPsiFacade.getInstance(typeParameter.getProject()).getElementFactory().createType(psiClass);
return typeParameterErasure(boundTypeParameter, visited, beforeSubstitutor, preserveIntersection);
}
}
return PsiType.getJavaLangObject(typeParameter.getManager(), typeParameter.getResolveScope());
}
private static PsiClassType typeParameterErasureInner(PsiTypeParameter typeParameter,
Set<? super PsiClass> visited,
PsiSubstitutor beforeSubstitutor) {
final PsiClassType[] extendsList = typeParameter.getExtendsList().getReferencedTypes();
if (extendsList.length > 0) {
final PsiClass psiClass = extendsList[0].resolve();
if (psiClass instanceof PsiTypeParameter) {
if (!visited.contains(psiClass)) {
visited.add(psiClass);
if (beforeSubstitutor.getSubstitutionMap().containsKey(psiClass)) {
return (PsiClassType)erasure(beforeSubstitutor.substitute((PsiTypeParameter)psiClass));
}
return typeParameterErasureInner((PsiTypeParameter)psiClass, visited, beforeSubstitutor);
}
}
else if (psiClass != null) {
return JavaPsiFacade.getInstance(typeParameter.getProject()).getElementFactory().createType(psiClass);
}
else if (psiClass != null) {
return JavaPsiFacade.getInstance(context.getProject()).getElementFactory().createType(psiClass);
}
return PsiType.getJavaLangObject(typeParameter.getManager(), typeParameter.getResolveScope());
return PsiType.getJavaLangObject(context.getManager(), context.getResolveScope());
}
@Contract("null -> null")
@@ -1241,8 +1236,25 @@ public class TypeConversionUtil {
return erasure(type, PsiSubstitutor.EMPTY);
}
/**
* Erase type, but preserve intersections
*
* @param type type to erase
* @return erased type (probably a {@link PsiIntersectionType})
*/
public static PsiType erasurePreservingIntersection(@Nullable PsiType type) {
return erasure(type, PsiSubstitutor.EMPTY, true);
}
@Contract("null, _ -> null")
public static PsiType erasure(@Nullable final PsiType type, @NotNull final PsiSubstitutor beforeSubstitutor) {
return erasure(type, beforeSubstitutor, false);
}
@Contract("null, _, _ -> null")
private static PsiType erasure(@Nullable final PsiType type,
@NotNull final PsiSubstitutor beforeSubstitutor,
boolean preserveIntersection) {
if (type == null) return null;
return type.accept(new PsiTypeVisitor<PsiType>() {
@Nullable
@@ -1255,7 +1267,7 @@ public class TypeConversionUtil {
public PsiType visitClassType(PsiClassType classType) {
final PsiClass aClass = classType.resolve();
if (aClass instanceof PsiTypeParameter && !isFreshVariable((PsiTypeParameter)aClass)) {
return typeParameterErasure((PsiTypeParameter)aClass, beforeSubstitutor);
return typeParameterErasure((PsiTypeParameter)aClass, new THashSet<>(), beforeSubstitutor, preserveIntersection);
}
return classType.rawType();
}
@@ -16,6 +16,10 @@ class Main {
}
}
<T> T test() {
return (T)new One();
}
}
class One { }