@Nullable
  public PsiType evaluateType(final PsiExpression expr) {
    if (expr == null) return null;
    final LinkedList<PsiType> e = myTypeMap.get(new TypeMigrationUsageInfo(expr));

    if (e != null) {
      return e.getFirst();
    }

    if (expr instanceof PsiArrayAccessExpression arrayAccess) {
      if (evaluateType(arrayAccess.getArrayExpression()) instanceof PsiArrayType arrayType) {
        return arrayType.getComponentType();
      }
    }
    else if (expr instanceof PsiAssignmentExpression assignment) {
      return evaluateType(assignment.getLExpression());
    }
    else if (expr instanceof PsiMethodCallExpression call) {
      final JavaResolveResult resolveResult = call.resolveMethodGenerics();
      final PsiMethod method = (PsiMethod)resolveResult.getElement();

      if (method != null) {
        final PsiParameter[] parameters = method.getParameterList().getParameters();
        final PsiExpression[] actualParms = call.getArgumentList().getExpressions();
        return PsiUtil.captureToplevelWildcards(createMethodSubstitution(parameters, actualParms, method, call, resolveResult.getSubstitutor(), false).substitute(evaluateType(call.getMethodExpression())), expr);
      }
    }
    else if (expr instanceof PsiPolyadicExpression polyadic) {
      final PsiExpression[] operands = polyadic.getOperands();
      final IElementType sign = polyadic.getOperationTokenType();
      PsiType lType = operands.length > 0 ? evaluateType(operands[0]) : null;
      for (int i = 1; i < operands.length; i++) {
        lType = TypeConversionUtil.calcTypeForBinaryExpression(lType, evaluateType(operands[i]), sign, true);
      }
      return lType;
    }
    else if (expr instanceof PsiUnaryExpression unary) {
      return evaluateType(unary.getOperand());
    }
    else if (expr instanceof PsiParenthesizedExpression paren) {
      return evaluateType(paren.getExpression());
    }
    else if (expr instanceof PsiConditionalExpression conditional) {
      final PsiExpression thenExpression = conditional.getThenExpression();
      final PsiExpression elseExpression = conditional.getElseExpression();

      final PsiType thenType = evaluateType(thenExpression);
      final PsiType elseType = evaluateType(elseExpression);

      return switch ((thenType == null ? 0 : 1) + (elseType == null ? 0 : 2)) {
        case 0 -> expr.getType();
        case 1 -> thenType;
        case 2 -> elseType;
        case 3 -> {
          if (TypeConversionUtil.areTypesConvertible(thenType, elseType)) {
            yield thenType;
          }
          else if (TypeConversionUtil.areTypesConvertible(elseType, thenType)) {
            yield elseType;
          }
          else {
            yield switch ((thenType.equals(thenExpression.getType()) ? 0 : 1) + (elseType.equals(elseExpression.getType()) ? 0 : 2)) {
              case 0, 3 -> expr.getType();
              case 1 -> thenType;
              case 2 -> elseType;
              default -> throw new AssertionError();
            };
          }
        }
        default -> throw new AssertionError();
      };
    }
    else if (expr instanceof PsiNewExpression newExpression) {
      final PsiExpression qualifier = newExpression.getQualifier();

      if (qualifier != null) {
        final PsiClassType.ClassResolveResult qualifierResult = resolveType(evaluateType(qualifier));

        if (qualifierResult.getElement() != null) {
          final PsiSubstitutor qualifierSubs = qualifierResult.getSubstitutor();
          final PsiClassType.ClassResolveResult result = resolveType(expr.getType());

          if (result.getElement() != null) {
            final PsiClass aClass = result.getElement();

            return JavaPsiFacade.getElementFactory(aClass.getProject())
                .createType(aClass, result.getSubstitutor().putAll(qualifierSubs));
          }
        }
      }
    }
    else if (expr instanceof PsiFunctionalExpression functionalExpression) {
      final PsiType functionalInterfaceType = functionalExpression.getFunctionalInterfaceType();
      if (functionalInterfaceType != null) {
        return functionalInterfaceType;
      }
    }
    else if (expr instanceof PsiReferenceExpression) {
      final PsiType type = evaluateReferenceExpressionType(expr);
      if (type != null) {
        return PsiImplUtil.normalizeWildcardTypeByPosition(type, expr);
      }
    }
    else if (expr instanceof PsiSuperExpression) {
      final PsiClass psiClass = PsiTreeUtil.getParentOfType(expr, PsiClass.class);
      if (psiClass != null) {
        final PsiClass superClass = psiClass.getSuperClass();
        if (superClass != null) {
          return getType(new TypeMigrationUsageInfo(superClass));
        }
      }
    }

    return getType(expr);
  }
@Nullable
  public PsiType evaluateType(final PsiExpression expr)