@Override
    public PsiType visitClassType(PsiClassType classType) {
      final PsiClassType.ClassResolveResult resolveResult = classType.resolveGenerics();
      final PsiClass aClass = resolveResult.getElement();
      if (aClass == null) return classType;

      PsiUtilCore.ensureValid(aClass);
      if (aClass instanceof PsiTypeParameter) {
        final PsiTypeParameter typeParameter = (PsiTypeParameter) aClass;
        if (containsInMap(typeParameter)) {
          PsiType result = substituteTypeParameter(typeParameter);
          if (result != null) {
            PsiUtil.ensureValidType(result);
          }
          return result;
        }
        return classType;
      }
      final Map<PsiTypeParameter, PsiType> hashMap = new HashMap<PsiTypeParameter, PsiType>(2);
      if (!processClass(aClass, resolveResult.getSubstitutor(), hashMap)) {
        return null;
      }
      PsiClassType result =
          JavaPsiFacade.getElementFactory(aClass.getProject())
              .createType(aClass, createSubstitutor(hashMap), classType.getLanguageLevel());
      PsiUtil.ensureValidType(result);
      return result;
    }
 @Override
 public void ensureValid() {
   for (PsiType type : mySubstitutionMap.values()) {
     if (type != null) {
       PsiUtil.ensureValidType(type);
     }
   }
 }
 @Override
 public PsiType substitute(PsiType type) {
   if (type == null) {
     //noinspection ConstantConditions
     return null;
   }
   PsiUtil.ensureValidType(type);
   PsiType substituted = type.accept(mySimpleSubstitutionVisitor);
   return correctExternalSubstitution(substituted, type);
 }
 private DfaVariableValue(
     @NotNull PsiModifierListOwner variable,
     @Nullable PsiType varType,
     boolean isNegated,
     DfaValueFactory factory,
     @Nullable DfaVariableValue qualifier) {
   super(factory);
   myVariable = variable;
   myIsNegated = isNegated;
   myQualifier = qualifier;
   myVarType = varType;
   DfaValue typeValue = myFactory.createTypeValue(varType, Nullness.UNKNOWN);
   myTypeValue = typeValue instanceof DfaTypeValue ? (DfaTypeValue) typeValue : null;
   if (varType != null && !varType.isValid()) {
     PsiUtil.ensureValidType(
         varType, "Variable: " + variable + " of class " + variable.getClass());
   }
 }
  private static ExpectedTypeMatching getExpectedTypeMatching(
      LookupElement item, ExpectedTypeInfo[] expectedInfos) {
    PsiType itemType = JavaCompletionUtil.getLookupElementType(item);

    if (itemType != null) {
      PsiUtil.ensureValidType(itemType);

      for (final ExpectedTypeInfo expectedInfo : expectedInfos) {
        final PsiType defaultType = expectedInfo.getDefaultType();
        final PsiType expectedType = expectedInfo.getType();

        assert expectedType.isValid();
        assert defaultType.isValid();

        if (defaultType != expectedType && defaultType.isAssignableFrom(itemType)) {
          return ExpectedTypeMatching.ofDefaultType;
        }
        if (expectedType.isAssignableFrom(itemType)) {
          return ExpectedTypeMatching.expected;
        }
      }
    }

    if (hasNonVoid(expectedInfos)) {
      if (item.getObject() instanceof PsiKeyword) {
        String keyword = ((PsiKeyword) item.getObject()).getText();
        if (PsiKeyword.NEW.equals(keyword) || PsiKeyword.NULL.equals(keyword)) {
          return ExpectedTypeMatching.maybeExpected;
        }
      }
    } else if (expectedInfos.length > 0) {
      return ExpectedTypeMatching.unexpected;
    }

    return ExpectedTypeMatching.normal;
  }
  /** Types should be proceed by the callers themselves */
  @Deprecated
  public static PsiType normalizeWildcardTypeByPosition(
      @NotNull PsiType type, @NotNull PsiExpression expression) {
    PsiUtilCore.ensureValid(expression);
    PsiUtil.ensureValidType(type);

    PsiExpression topLevel = expression;
    while (topLevel.getParent() instanceof PsiArrayAccessExpression
        && ((PsiArrayAccessExpression) topLevel.getParent()).getArrayExpression() == topLevel) {
      topLevel = (PsiExpression) topLevel.getParent();
    }

    if (topLevel instanceof PsiArrayAccessExpression && !PsiUtil.isAccessedForWriting(topLevel)) {
      return PsiUtil.captureToplevelWildcards(type, expression);
    }

    final PsiType normalized = doNormalizeWildcardByPosition(type, expression, topLevel);
    LOG.assertTrue(normalized.isValid(), type);
    if (normalized instanceof PsiClassType && !PsiUtil.isAccessedForWriting(topLevel)) {
      return PsiUtil.captureToplevelWildcards(normalized, expression);
    }

    return normalized;
  }
  private void buildText(
      @NotNull PsiClass aClass,
      @NotNull PsiSubstitutor substitutor,
      @NotNull StringBuilder buffer,
      @NotNull TextType textType,
      boolean annotated) {
    if (aClass instanceof PsiAnonymousClass) {
      ClassResolveResult baseResolveResult =
          ((PsiAnonymousClass) aClass).getBaseClassType().resolveGenerics();
      PsiClass baseClass = baseResolveResult.getElement();
      if (baseClass != null) {
        buildText(baseClass, baseResolveResult.getSubstitutor(), buffer, textType, false);
      }
      return;
    }

    boolean qualified = textType != TextType.PRESENTABLE;

    PsiClass enclosingClass = null;
    if (!aClass.hasModifierProperty(PsiModifier.STATIC)) {
      PsiElement parent = aClass.getParent();
      if (parent instanceof PsiClass && !(parent instanceof PsiAnonymousClass)) {
        enclosingClass = (PsiClass) parent;
      }
    }
    if (enclosingClass != null) {
      buildText(enclosingClass, substitutor, buffer, textType, false);
      buffer.append('.');
    } else if (qualified) {
      String fqn = aClass.getQualifiedName();
      if (fqn != null) {
        String prefix = StringUtil.getPackageName(fqn);
        if (!StringUtil.isEmpty(prefix)) {
          buffer.append(prefix);
          buffer.append('.');
        }
      }
    }

    if (annotated) {
      PsiNameHelper.appendAnnotations(buffer, getAnnotations(), qualified);
    }

    buffer.append(aClass.getName());

    PsiTypeParameter[] typeParameters = aClass.getTypeParameters();
    if (typeParameters.length > 0) {
      int pos = buffer.length();
      buffer.append('<');

      for (int i = 0; i < typeParameters.length; i++) {
        PsiTypeParameter typeParameter = typeParameters[i];
        PsiUtilCore.ensureValid(typeParameter);

        if (i > 0) {
          buffer.append(',');
          if (textType == TextType.PRESENTABLE) buffer.append(' ');
        }

        PsiType substitutionResult = substitutor.substitute(typeParameter);
        if (substitutionResult == null) {
          buffer.setLength(pos);
          pos = -1;
          break;
        }
        PsiUtil.ensureValidType(substitutionResult);

        if (textType == TextType.PRESENTABLE) {
          buffer.append(substitutionResult.getPresentableText());
        } else if (textType == TextType.CANONICAL) {
          buffer.append(substitutionResult.getCanonicalText(annotated));
        } else {
          buffer.append(substitutionResult.getInternalCanonicalText());
        }
      }

      if (pos >= 0) {
        buffer.append('>');
      }
    }
  }