@SuppressWarnings({"unchecked"}) @NotNull public static <T extends PsiType> T originalize(@NotNull T type) { if (!type.isValid()) { return type; } T result = new PsiTypeMapper() { private final Set<PsiClassType> myVisited = ContainerUtil.newIdentityTroveSet(); @Override public PsiType visitClassType(final PsiClassType classType) { if (!myVisited.add(classType)) return classType; final PsiClassType.ClassResolveResult classResolveResult = classType.resolveGenerics(); final PsiClass psiClass = classResolveResult.getElement(); final PsiSubstitutor substitutor = classResolveResult.getSubstitutor(); if (psiClass == null) return classType; return new PsiImmediateClassType( CompletionUtil.getOriginalOrSelf(psiClass), originalizeSubstitutor(substitutor)); } private PsiSubstitutor originalizeSubstitutor(final PsiSubstitutor substitutor) { PsiSubstitutor originalSubstitutor = PsiSubstitutor.EMPTY; for (final Map.Entry<PsiTypeParameter, PsiType> entry : substitutor.getSubstitutionMap().entrySet()) { final PsiType value = entry.getValue(); originalSubstitutor = originalSubstitutor.put( CompletionUtil.getOriginalOrSelf(entry.getKey()), value == null ? null : mapType(value)); } return originalSubstitutor; } @Override public PsiType visitType(PsiType type) { return type; } }.mapType(type); if (result == null) { throw new AssertionError("Null result for type " + type + " of class " + type.getClass()); } return result; }
@Nullable public <T extends PsiExpression> PsiType getType( @NotNull T expr, @NotNull Function<T, PsiType> f) { PsiType type = getCachedType(expr); if (type == null) { final RecursionGuard.StackStamp dStackStamp = PsiDiamondType.ourDiamondGuard.markStack(); final RecursionGuard.StackStamp gStackStamp = PsiResolveHelper.ourGraphGuard.markStack(); type = f.fun(expr); if (!dStackStamp.mayCacheNow() || !gStackStamp.mayCacheNow()) { return type; } if (type == null) type = TypeConversionUtil.NULL_TYPE; Reference<PsiType> ref = new SoftReference<PsiType>(type); myCalculatedTypes.put(expr, ref); if (type instanceof PsiClassReferenceType) { // convert reference-based class type to the PsiImmediateClassType, since the reference may // become invalid PsiClassType.ClassResolveResult result = ((PsiClassReferenceType) type).resolveGenerics(); PsiClass psiClass = result.getElement(); type = psiClass == null ? type // for type with unresolved reference, leave it in the cache // for clients still might be able to retrieve its getCanonicalText() from the // reference text : new PsiImmediateClassType( psiClass, result.getSubstitutor(), ((PsiClassReferenceType) type).getLanguageLevel(), type.getAnnotations()); } } if (!type.isValid()) { if (expr.isValid()) { PsiJavaCodeReferenceElement refInside = type instanceof PsiClassReferenceType ? ((PsiClassReferenceType) type).getReference() : null; @NonNls String typeinfo = type + " (" + type.getClass() + ")" + (refInside == null ? "" : "; ref inside: " + refInside + " (" + refInside.getClass() + ") valid:" + refInside.isValid()); LOG.error( "Type is invalid: " + typeinfo + "; expr: '" + expr + "' (" + expr.getClass() + ") is valid"); } else { LOG.error("Expression: '" + expr + "' is invalid, must not be used for getType()"); } } return type == TypeConversionUtil.NULL_TYPE ? null : type; }