@NotNull
 private KotlinTypeInfo getTypeInfoWhenOnlyOneBranchIsPresent(
     @NotNull KtExpression presentBranch,
     @NotNull LexicalWritableScope presentScope,
     @NotNull DataFlowInfo presentInfo,
     @NotNull DataFlowInfo otherInfo,
     @NotNull ExpressionTypingContext context,
     @NotNull KtIfExpression ifExpression,
     boolean isStatement) {
   ExpressionTypingContext newContext =
       context
           .replaceDataFlowInfo(presentInfo)
           .replaceExpectedType(NO_EXPECTED_TYPE)
           .replaceContextDependency(INDEPENDENT);
   KotlinTypeInfo typeInfo =
       components.expressionTypingServices.getBlockReturnedTypeWithWritableScope(
           presentScope,
           Collections.singletonList(presentBranch),
           CoercionStrategy.NO_COERCION,
           newContext);
   KotlinType type = typeInfo.getType();
   DataFlowInfo dataFlowInfo;
   if (type != null && KotlinBuiltIns.isNothing(type)) {
     dataFlowInfo = otherInfo;
   } else {
     dataFlowInfo = typeInfo.getDataFlowInfo().or(otherInfo);
   }
   return components
       .dataFlowAnalyzer
       .checkImplicitCast(
           components.dataFlowAnalyzer.checkType(
               typeInfo.replaceType(components.builtIns.getUnitType()), ifExpression, context),
           ifExpression,
           context,
           isStatement)
       .replaceDataFlowInfo(dataFlowInfo);
 }
  /**
   * Visits block statements propagating data flow information from the first to the last.
   * Determines block returned type and data flow information at the end of the block AND at the
   * nearest jump point from the block beginning.
   */
  /*package*/ KotlinTypeInfo getBlockReturnedTypeWithWritableScope(
      @NotNull LexicalWritableScope scope,
      @NotNull List<? extends KtElement> block,
      @NotNull CoercionStrategy coercionStrategyForLastExpression,
      @NotNull ExpressionTypingContext context) {
    if (block.isEmpty()) {
      return TypeInfoFactoryKt.createTypeInfo(
          expressionTypingComponents.builtIns.getUnitType(), context);
    }

    ExpressionTypingInternals blockLevelVisitor =
        new ExpressionTypingVisitorDispatcher.ForBlock(
            expressionTypingComponents, annotationChecker, scope);
    ExpressionTypingContext newContext =
        context.replaceScope(scope).replaceExpectedType(NO_EXPECTED_TYPE);

    KotlinTypeInfo result = TypeInfoFactoryKt.noTypeInfo(context);
    // Jump point data flow info
    DataFlowInfo beforeJumpInfo = newContext.dataFlowInfo;
    boolean jumpOutPossible = false;
    for (Iterator<? extends KtElement> iterator = block.iterator(); iterator.hasNext(); ) {
      KtElement statement = iterator.next();
      if (!(statement instanceof KtExpression)) {
        continue;
      }
      KtExpression statementExpression = (KtExpression) statement;
      if (!iterator.hasNext()) {
        result =
            getTypeOfLastExpressionInBlock(
                statementExpression,
                newContext.replaceExpectedType(context.expectedType),
                coercionStrategyForLastExpression,
                blockLevelVisitor);
        if (result.getType() != null
            && statementExpression.getParent() instanceof KtBlockExpression) {
          DataFlowValue lastExpressionValue =
              DataFlowValueFactory.createDataFlowValue(
                  statementExpression, result.getType(), context);
          DataFlowValue blockExpressionValue =
              DataFlowValueFactory.createDataFlowValue(
                  (KtBlockExpression) statementExpression.getParent(), result.getType(), context);
          result =
              result.replaceDataFlowInfo(
                  result.getDataFlowInfo().assign(blockExpressionValue, lastExpressionValue));
        }
      } else {
        result =
            blockLevelVisitor.getTypeInfo(
                statementExpression,
                newContext.replaceContextDependency(ContextDependency.INDEPENDENT),
                true);
      }

      DataFlowInfo newDataFlowInfo = result.getDataFlowInfo();
      // If jump is not possible, we take new data flow info before jump
      if (!jumpOutPossible) {
        beforeJumpInfo = result.getJumpFlowInfo();
        jumpOutPossible = result.getJumpOutPossible();
      }
      if (newDataFlowInfo != context.dataFlowInfo) {
        newContext = newContext.replaceDataFlowInfo(newDataFlowInfo);
        // We take current data flow info if jump there is not possible
      }
      blockLevelVisitor =
          new ExpressionTypingVisitorDispatcher.ForBlock(
              expressionTypingComponents, annotationChecker, scope);
    }
    return result.replaceJumpOutPossible(jumpOutPossible).replaceJumpFlowInfo(beforeJumpInfo);
  }
  public KotlinTypeInfo visitForExpression(
      KtForExpression expression,
      ExpressionTypingContext contextWithExpectedType,
      boolean isStatement) {
    if (!isStatement)
      return components.dataFlowAnalyzer.illegalStatementType(
          expression, contextWithExpectedType, facade);

    ExpressionTypingContext context =
        contextWithExpectedType
            .replaceExpectedType(NO_EXPECTED_TYPE)
            .replaceContextDependency(INDEPENDENT);
    // Preliminary analysis
    PreliminaryLoopVisitor loopVisitor = PreliminaryLoopVisitor.visitLoop(expression);
    context =
        context.replaceDataFlowInfo(
            loopVisitor.clearDataFlowInfoForAssignedLocalVariables(context.dataFlowInfo));

    KtExpression loopRange = expression.getLoopRange();
    KotlinType expectedParameterType = null;
    KotlinTypeInfo loopRangeInfo;
    if (loopRange != null) {
      ExpressionReceiver loopRangeReceiver =
          getExpressionReceiver(facade, loopRange, context.replaceScope(context.scope));
      loopRangeInfo = facade.getTypeInfo(loopRange, context);
      if (loopRangeReceiver != null) {
        expectedParameterType =
            components.forLoopConventionsChecker.checkIterableConvention(
                loopRangeReceiver, context);
      }
    } else {
      loopRangeInfo = TypeInfoFactoryKt.noTypeInfo(context);
    }

    LexicalWritableScope loopScope = newWritableScopeImpl(context, "Scope with for-loop index");

    KtParameter loopParameter = expression.getLoopParameter();
    if (loopParameter != null) {
      VariableDescriptor variableDescriptor =
          createLoopParameterDescriptor(loopParameter, expectedParameterType, context);
      components
          .modifiersChecker
          .withTrace(context.trace)
          .checkModifiersForLocalDeclaration(loopParameter, variableDescriptor);
      components.identifierChecker.checkDeclaration(loopParameter, context.trace);

      loopScope.addVariableDescriptor(variableDescriptor);
    } else {
      KtMultiDeclaration multiParameter = expression.getMultiParameter();
      if (multiParameter != null && loopRange != null) {
        KotlinType elementType =
            expectedParameterType == null
                ? ErrorUtils.createErrorType("Loop range has no type")
                : expectedParameterType;
        TransientReceiver iteratorNextAsReceiver = new TransientReceiver(elementType);
        components.annotationResolver.resolveAnnotationsWithArguments(
            loopScope, multiParameter.getModifierList(), context.trace);
        components.multiDeclarationResolver.defineLocalVariablesFromMultiDeclaration(
            loopScope, multiParameter, iteratorNextAsReceiver, loopRange, context);
        components
            .modifiersChecker
            .withTrace(context.trace)
            .checkModifiersForMultiDeclaration(multiParameter);
        components
            .modifiersChecker
            .withTrace(context.trace)
            .checkParameterHasNoValOrVar(multiParameter, VAL_OR_VAR_ON_LOOP_MULTI_PARAMETER);
        components.identifierChecker.checkDeclaration(multiParameter, context.trace);
      }
    }

    KtExpression body = expression.getBody();
    KotlinTypeInfo bodyTypeInfo;
    if (body != null) {
      bodyTypeInfo =
          components.expressionTypingServices.getBlockReturnedTypeWithWritableScope(
              loopScope,
              Collections.singletonList(body),
              CoercionStrategy.NO_COERCION,
              context.replaceDataFlowInfo(loopRangeInfo.getDataFlowInfo()));
    } else {
      bodyTypeInfo = loopRangeInfo;
    }

    return components
        .dataFlowAnalyzer
        .checkType(
            bodyTypeInfo.replaceType(components.builtIns.getUnitType()),
            expression,
            contextWithExpectedType)
        .replaceDataFlowInfo(loopRangeInfo.getDataFlowInfo());
  }
  public KotlinTypeInfo visitDoWhileExpression(
      KtDoWhileExpression expression,
      ExpressionTypingContext contextWithExpectedType,
      boolean isStatement) {
    if (!isStatement)
      return components.dataFlowAnalyzer.illegalStatementType(
          expression, contextWithExpectedType, facade);

    ExpressionTypingContext context =
        contextWithExpectedType
            .replaceExpectedType(NO_EXPECTED_TYPE)
            .replaceContextDependency(INDEPENDENT);
    KtExpression body = expression.getBody();
    LexicalScope conditionScope = context.scope;
    // Preliminary analysis
    PreliminaryLoopVisitor loopVisitor = PreliminaryLoopVisitor.visitLoop(expression);
    context =
        context.replaceDataFlowInfo(
            loopVisitor.clearDataFlowInfoForAssignedLocalVariables(context.dataFlowInfo));
    // Here we must record data flow information at the end of the body (or at the first jump, to be
    // precise) and
    // .and it with entrance data flow information, because do-while body is executed at least once
    // See KT-6283
    KotlinTypeInfo bodyTypeInfo;
    if (body instanceof KtFunctionLiteralExpression) {
      // As a matter of fact, function literal is always unused at this point
      bodyTypeInfo = facade.getTypeInfo(body, context.replaceScope(context.scope));
    } else if (body != null) {
      LexicalWritableScope writableScope = newWritableScopeImpl(context, "do..while body scope");
      conditionScope = writableScope;
      List<KtExpression> block;
      if (body instanceof KtBlockExpression) {
        block = ((KtBlockExpression) body).getStatements();
      } else {
        block = Collections.singletonList(body);
      }
      bodyTypeInfo =
          components.expressionTypingServices.getBlockReturnedTypeWithWritableScope(
              writableScope, block, CoercionStrategy.NO_COERCION, context);
    } else {
      bodyTypeInfo = TypeInfoFactoryKt.noTypeInfo(context);
    }
    KtExpression condition = expression.getCondition();
    DataFlowInfo conditionDataFlowInfo = checkCondition(conditionScope, condition, context);
    DataFlowInfo dataFlowInfo;
    // Without jumps out, condition is entered and false, with jumps out, we know nothing about it
    if (!containsJumpOutOfLoop(expression, context)) {
      dataFlowInfo =
          components
              .dataFlowAnalyzer
              .extractDataFlowInfoFromCondition(condition, false, context)
              .and(conditionDataFlowInfo);
    } else {
      dataFlowInfo = context.dataFlowInfo;
    }
    // Here we must record data flow information at the end of the body (or at the first jump, to be
    // precise) and
    // .and it with entrance data flow information, because do-while body is executed at least once
    // See KT-6283
    // NB: it's really important to do it for non-empty body which is not a function literal
    // If it's a function literal, it appears always unused so it's no matter what we do at this
    // point
    if (body != null) {
      // We should take data flow info from the first jump point,
      // but without affecting changing variables
      dataFlowInfo =
          dataFlowInfo.and(
              loopVisitor.clearDataFlowInfoForAssignedLocalVariables(
                  bodyTypeInfo.getJumpFlowInfo()));
    }
    return components
        .dataFlowAnalyzer
        .checkType(
            bodyTypeInfo.replaceType(components.builtIns.getUnitType()),
            expression,
            contextWithExpectedType)
        .replaceDataFlowInfo(dataFlowInfo);
  }
  public KotlinTypeInfo visitWhileExpression(
      KtWhileExpression expression,
      ExpressionTypingContext contextWithExpectedType,
      boolean isStatement) {
    if (!isStatement)
      return components.dataFlowAnalyzer.illegalStatementType(
          expression, contextWithExpectedType, facade);

    ExpressionTypingContext context =
        contextWithExpectedType
            .replaceExpectedType(NO_EXPECTED_TYPE)
            .replaceContextDependency(INDEPENDENT);
    // Preliminary analysis
    PreliminaryLoopVisitor loopVisitor = PreliminaryLoopVisitor.visitLoop(expression);
    context =
        context.replaceDataFlowInfo(
            loopVisitor.clearDataFlowInfoForAssignedLocalVariables(context.dataFlowInfo));

    KtExpression condition = expression.getCondition();
    // Extract data flow info from condition itself without taking value into account
    DataFlowInfo dataFlowInfo = checkCondition(context.scope, condition, context);

    KtExpression body = expression.getBody();
    KotlinTypeInfo bodyTypeInfo;
    DataFlowInfo conditionInfo =
        components
            .dataFlowAnalyzer
            .extractDataFlowInfoFromCondition(condition, true, context)
            .and(dataFlowInfo);
    if (body != null) {
      LexicalWritableScope scopeToExtend =
          newWritableScopeImpl(context, "Scope extended in while's condition");
      bodyTypeInfo =
          components.expressionTypingServices.getBlockReturnedTypeWithWritableScope(
              scopeToExtend,
              Collections.singletonList(body),
              CoercionStrategy.NO_COERCION,
              context.replaceDataFlowInfo(conditionInfo));
    } else {
      bodyTypeInfo = TypeInfoFactoryKt.noTypeInfo(conditionInfo);
    }

    // Condition is false at this point only if there is no jumps outside
    if (!containsJumpOutOfLoop(expression, context)) {
      dataFlowInfo =
          components
              .dataFlowAnalyzer
              .extractDataFlowInfoFromCondition(condition, false, context)
              .and(dataFlowInfo);
    }

    // Special case: while (true)
    // In this case we must record data flow information at the nearest break / continue and
    // .and it with entrance data flow information, because while body until break is executed at
    // least once in this case
    // See KT-6284
    if (body != null && KtPsiUtil.isTrueConstant(condition)) {
      // We should take data flow info from the first jump point,
      // but without affecting changing variables
      dataFlowInfo =
          dataFlowInfo.and(
              loopVisitor.clearDataFlowInfoForAssignedLocalVariables(
                  bodyTypeInfo.getJumpFlowInfo()));
    }
    return components
        .dataFlowAnalyzer
        .checkType(
            bodyTypeInfo.replaceType(components.builtIns.getUnitType()),
            expression,
            contextWithExpectedType)
        .replaceDataFlowInfo(dataFlowInfo);
  }