From d05fc1b5c970a16d8b07202aeb5470ba64217f7c Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Mon, 6 Aug 2018 18:32:39 +0700 Subject: [PATCH] TypeConversionUtil#erasurePreservingIntersection; used in DfaPsiType#normalizeType --- .../dataFlow/value/DfaPsiType.java | 10 +-- .../intellij/psi/util/TypeConversionUtil.java | 82 +++++++++++-------- .../dataFlow/fixture/ExactInstanceOf.java | 4 + 3 files changed, 53 insertions(+), 43 deletions(-) diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaPsiType.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaPsiType.java index 9af42cfad4ef..e1808689c237 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaPsiType.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaPsiType.java @@ -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(); } diff --git a/java/java-psi-api/src/com/intellij/psi/util/TypeConversionUtil.java b/java/java-psi-api/src/com/intellij/psi/util/TypeConversionUtil.java index bc355ec54bd4..fdc1ceb74c80 100644 --- a/java/java-psi-api/src/com/intellij/psi/util/TypeConversionUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/util/TypeConversionUtil.java @@ -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 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 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 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 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() { @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(); } diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/ExactInstanceOf.java b/java/java-tests/testData/inspection/dataFlow/fixture/ExactInstanceOf.java index f542d403130d..fe778f452bcd 100644 --- a/java/java-tests/testData/inspection/dataFlow/fixture/ExactInstanceOf.java +++ b/java/java-tests/testData/inspection/dataFlow/fixture/ExactInstanceOf.java @@ -16,6 +16,10 @@ class Main { } } + + T test() { + return (T)new One(); + } } class One { }