@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));
    }
  }
  /**
   * 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);
  }