@Override
  public KotlinTypeInfo visitTryExpression(
      @NotNull KtTryExpression expression, ExpressionTypingContext typingContext) {
    ExpressionTypingContext context = typingContext.replaceContextDependency(INDEPENDENT);
    KtExpression tryBlock = expression.getTryBlock();
    List<KtCatchClause> catchClauses = expression.getCatchClauses();
    KtFinallySection finallyBlock = expression.getFinallyBlock();
    List<KotlinType> types = new ArrayList<KotlinType>();
    for (KtCatchClause catchClause : catchClauses) {
      KtParameter catchParameter = catchClause.getCatchParameter();
      KtExpression catchBody = catchClause.getCatchBody();
      if (catchParameter != null) {
        components.identifierChecker.checkDeclaration(catchParameter, context.trace);
        ModifiersChecker.ModifiersCheckingProcedure modifiersChecking =
            components.modifiersChecker.withTrace(context.trace);
        modifiersChecking.checkParameterHasNoValOrVar(
            catchParameter, VAL_OR_VAR_ON_CATCH_PARAMETER);
        ModifierCheckerCore.INSTANCE$.check(catchParameter, context.trace, null);

        VariableDescriptor variableDescriptor =
            components.descriptorResolver.resolveLocalVariableDescriptor(
                context.scope, catchParameter, context.trace);
        KotlinType throwableType = components.builtIns.getThrowable().getDefaultType();
        components.dataFlowAnalyzer.checkType(
            variableDescriptor.getType(),
            catchParameter,
            context.replaceExpectedType(throwableType));
        if (catchBody != null) {
          LexicalWritableScope catchScope = newWritableScopeImpl(context, "Catch scope");
          catchScope.addVariableDescriptor(variableDescriptor);
          KotlinType type =
              facade.getTypeInfo(catchBody, context.replaceScope(catchScope)).getType();
          if (type != null) {
            types.add(type);
          }
        }
      }
    }

    KotlinTypeInfo result = TypeInfoFactoryKt.noTypeInfo(context);
    if (finallyBlock != null) {
      result =
          facade.getTypeInfo(
              finallyBlock.getFinalExpression(), context.replaceExpectedType(NO_EXPECTED_TYPE));
    }

    KotlinType type = facade.getTypeInfo(tryBlock, context).getType();
    if (type != null) {
      types.add(type);
    }
    if (types.isEmpty()) {
      return result.clearType();
    } else {
      return result.replaceType(CommonSupertypes.commonSupertype(types));
    }
  }
  @NotNull
  private DataFlowInfo checkCondition(
      @NotNull LexicalScope scope,
      @Nullable KtExpression condition,
      ExpressionTypingContext context) {
    if (condition != null) {
      KotlinTypeInfo typeInfo =
          facade.getTypeInfo(
              condition,
              context
                  .replaceScope(scope)
                  .replaceExpectedType(components.builtIns.getBooleanType())
                  .replaceContextDependency(INDEPENDENT));
      KotlinType conditionType = typeInfo.getType();

      if (conditionType != null && !components.builtIns.isBooleanOrSubtype(conditionType)) {
        context.trace.report(TYPE_MISMATCH_IN_CONDITION.on(condition, conditionType));
      }

      return typeInfo.getDataFlowInfo();
    }
    return context.dataFlowInfo;
  }
  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);
  }