public void migrateExpressionType(PsiExpression expr,
                                    PsiType migrationType,
                                    PsiElement place,
                                    boolean alreadyProcessed,
                                    boolean isCovariant) {
    PsiType originalType = expr.getType();

    if (originalType == null || originalType.equals(migrationType)) return;

    if (originalType.equals(PsiTypes.nullType())) {
      if (migrationType instanceof PsiPrimitiveType) {
        markFailedConversion(migrationType, expr);
        return;
      }
      PsiType expectedType = ExpectedTypeUtils.findExpectedType(expr, false);
      if (expectedType != null &&
          !expectedType.equals(PsiTypes.nullType()) &&
          myRules.shouldConvertNull(expectedType, migrationType, expr)) {
        convertExpression(expr, migrationType, expectedType, isCovariant);
      }
      return;
    }

    if (expr instanceof PsiConditionalExpression condExpr) {
      for (PsiExpression e : new PsiExpression[]{condExpr.getThenExpression(), condExpr.getElseExpression()}) {
        if (e != null) {
          migrateExpressionType(e, migrationType, place, alreadyProcessed, false);
        }
      }
      getTypeEvaluator().setType(new TypeMigrationUsageInfo(expr), migrationType);
      return;
    } else if (expr instanceof PsiClassObjectAccessExpression) {
      if (!TypeConversionUtil.isAssignable(migrationType, expr.getType())) {
        markFailedConversion(migrationType, expr);
        return;
      }
    } else if (expr instanceof PsiArrayInitializerExpression && migrationType instanceof PsiArrayType) {
      final PsiExpression[] initializers = ((PsiArrayInitializerExpression)expr).getInitializers();
      for (PsiExpression initializer : initializers) {
        migrateExpressionType(initializer, ((PsiArrayType)migrationType).getComponentType(), expr, alreadyProcessed, true);
      }
      getTypeEvaluator().setType(new TypeMigrationUsageInfo(expr), migrationType);
      return;
    } else if (expr instanceof PsiArrayAccessExpression) {
      migrateExpressionType(((PsiArrayAccessExpression)expr).getArrayExpression(),
                            migrationType.createArrayType(), place, alreadyProcessed, isCovariant);
      return;
    }
    else if (expr instanceof PsiReferenceExpression) {
      final PsiElement resolved = ((PsiReferenceExpression)expr).resolve();
      if (resolved != null) {
        if (!addMigrationRoot(resolved, migrationType, place, alreadyProcessed, !isCovariant)) {
          convertExpression(expr, migrationType, getTypeEvaluator().evaluateType(expr), isCovariant);
        }
      }
      return;
    }
    else if (expr instanceof PsiMethodCallExpression) {
      final PsiMethod resolved = ((PsiMethodCallExpression)expr).resolveMethod();
      if (resolved != null) {
        if (!addMigrationRoot(resolved, migrationType, place, alreadyProcessed, !isCovariant)) {
          convertExpression(expr, migrationType, getTypeEvaluator().evaluateType(expr), isCovariant);
        }
      }
      return;
    }
    else if (expr instanceof PsiNewExpression) {
      if (originalType.getArrayDimensions() == migrationType.getArrayDimensions()) {
        if (migrationType.getArrayDimensions() > 0) {
          final PsiType elementType = ((PsiArrayType)migrationType).getComponentType();

          final PsiArrayInitializerExpression arrayInitializer = ((PsiNewExpression)expr).getArrayInitializer();

          if (arrayInitializer != null) {
            final PsiExpression[] initializers = arrayInitializer.getInitializers();
            for (int i = initializers.length - 1; i >= 0; i--) {
              migrateExpressionType(initializers[i], elementType, place, alreadyProcessed, true);
            }
          }

          if (isGenericsArrayType(elementType)) {
            markFailedConversion(migrationType, expr);
            return;
          }

          if (!alreadyProcessed) {
            final TypeMigrationUsageInfo usageInfo = new TypeMigrationUsageInfo(expr);
            usageInfo.setOwnerRoot(myCurrentRoot);
            myNewExpressionTypeChange.put(usageInfo, migrationType);
            getTypeEvaluator().setType(new TypeMigrationUsageInfo(expr), migrationType);
          }
          return;
        }
        else if (migrationType instanceof PsiClassType migrationClassType && originalType instanceof PsiClassType originalClassType &&
                 migrationClassType.rawType().isAssignableFrom(originalClassType.rawType())) {
          final PsiClass originalClass = PsiUtil.resolveClassInType(originalType);
          if (originalClass instanceof PsiAnonymousClass anonymousClass) {
            originalClassType = anonymousClass.getBaseClassType();
          }
          final PsiType type = TypeEvaluator.substituteType(migrationClassType, originalClassType, true,
                                                            originalClassType.resolveGenerics().getElement(),
                                                            JavaPsiFacade.getElementFactory(expr.getProject())
                                                              .createType(originalClassType.resolve(), PsiSubstitutor.EMPTY));
          if (type != null) {
            final TypeMigrationUsageInfo usageInfo = new TypeMigrationUsageInfo(expr);
            usageInfo.setOwnerRoot(myCurrentRoot);
            myNewExpressionTypeChange.put(usageInfo, type);
            getTypeEvaluator().setType(new TypeMigrationUsageInfo(expr), type);
            return;
          }
        }
      }

    }
    else if (expr instanceof PsiLambdaExpression) {
      //TODO conversion of lambda expression now works incorrectly [Dmitry Batkovich]
      return;
    }

    convertExpression(expr, migrationType, originalType, isCovariant);
  }
public void migrateExpressionType(PsiExpression expr,
                                    PsiType migrationType,
                                    PsiElement place,
                                    boolean alreadyProcessed,
                                    boolean isCovariant)