private TracingStrategy createTracingForSpecialConstruction()

in compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingUtils.java [406:575]


    private TracingStrategy createTracingForSpecialConstruction(
            @NotNull Call call,
            @NotNull String constructionName,
            @NotNull ExpressionTypingContext context
    ) {
        class CheckTypeContext {
            public BindingTrace trace;
            public KotlinType expectedType;

            CheckTypeContext(@NotNull BindingTrace trace, @NotNull KotlinType expectedType) {
                this.trace = trace;
                this.expectedType = expectedType;
            }

            CheckTypeContext makeTypeNullable() {
                if (TypeUtils.noExpectedType(expectedType)) return this;
                return new CheckTypeContext(trace, TypeUtils.makeNullable(expectedType));
            }
        }

        KtVisitor<Boolean, CheckTypeContext> checkTypeVisitor = new KtVisitor<Boolean, CheckTypeContext>() {
            private boolean checkExpressionType(@NotNull KtExpression expression, CheckTypeContext c) {
                KotlinTypeInfo typeInfo = BindingContextUtils.getRecordedTypeInfo(expression, c.trace.getBindingContext());
                if (typeInfo == null) return false;

                Ref<Boolean> hasError = Ref.create();
                dataFlowAnalyzer.checkType(
                        typeInfo.getType(),
                        expression,
                        context
                                .replaceExpectedType(c.expectedType)
                                .replaceDataFlowInfo(typeInfo.getDataFlowInfo())
                                .replaceBindingTrace(c.trace),
                        hasError,
                        true
                );
                return hasError.get();
            }

            private boolean checkExpressionTypeRecursively(@Nullable KtExpression expression, CheckTypeContext c) {
                if (expression == null) return false;
                return expression.accept(this, c);
            }

            private boolean checkSubExpressions(
                    KtExpression firstSub, KtExpression secondSub, KtExpression expression,
                    CheckTypeContext firstContext, CheckTypeContext secondContext, CheckTypeContext context
            ) {
                boolean errorWasReported = checkExpressionTypeRecursively(firstSub, firstContext);
                errorWasReported |= checkExpressionTypeRecursively(secondSub, secondContext);
                return errorWasReported || checkExpressionType(expression, context);
            }

            @Override
            public Boolean visitWhenExpression(@NotNull KtWhenExpression whenExpression, CheckTypeContext c) {
                boolean errorWasReported = false;
                for (KtWhenEntry whenEntry : whenExpression.getEntries()) {
                    KtExpression entryExpression = whenEntry.getExpression();
                    if (entryExpression != null) {
                        errorWasReported |= checkExpressionTypeRecursively(entryExpression, c);
                    }
                }
                errorWasReported |= checkExpressionType(whenExpression, c);
                return errorWasReported;
            }

            @Override
            public Boolean visitIfExpression(@NotNull KtIfExpression ifExpression, CheckTypeContext c) {
                KtExpression thenBranch = ifExpression.getThen();
                KtExpression elseBranch = ifExpression.getElse();
                if (thenBranch == null || elseBranch == null) {
                    return checkExpressionType(ifExpression, c);
                }
                return checkSubExpressions(thenBranch, elseBranch, ifExpression, c, c, c);
            }

            @Override
            public Boolean visitBlockExpression(@NotNull KtBlockExpression expression, CheckTypeContext c) {
                if (expression.getStatements().isEmpty()) {
                    return checkExpressionType(expression, c);
                }
                KtExpression lastStatement = KtPsiUtil.getLastStatementInABlock(expression);
                if (lastStatement != null) {
                    return checkExpressionTypeRecursively(lastStatement, c);
                }
                return false;
            }

            @Override
            public Boolean visitPostfixExpression(@NotNull KtPostfixExpression expression, CheckTypeContext c) {
                if (expression.getOperationReference().getReferencedNameElementType() == KtTokens.EXCLEXCL) {
                    return checkExpressionTypeRecursively(expression.getBaseExpression(), c.makeTypeNullable());
                }
                return super.visitPostfixExpression(expression, c);
            }

            @Override
            public Boolean visitBinaryExpression(@NotNull KtBinaryExpression expression, CheckTypeContext c) {
                if (expression.getOperationReference().getReferencedNameElementType() == KtTokens.ELVIS) {

                    return checkSubExpressions(expression.getLeft(), expression.getRight(), expression, c.makeTypeNullable(), c, c);
                }
                return super.visitBinaryExpression(expression, c);
            }

            @Override
            public Boolean visitExpression(@NotNull KtExpression expression, CheckTypeContext c) {
                return checkExpressionType(expression, c);
            }
        };

        return new ThrowingOnErrorTracingStrategy("resolve " + constructionName + " as a call") {
            @Override
            public <D extends CallableDescriptor> void bindReference(
                    @NotNull BindingTrace trace, @NotNull ResolvedCall<D> resolvedCall
            ) {
                //do nothing
            }

            @Override
            public void bindCall(@NotNull BindingTrace trace, @NotNull Call call) {
                trace.record(CALL, call.getCalleeExpression(), call);
            }

            @Override
            public <D extends CallableDescriptor> void bindResolvedCall(
                    @NotNull BindingTrace trace, @NotNull ResolvedCall<D> resolvedCall
            ) {
                trace.record(RESOLVED_CALL, call, resolvedCall);
            }

            @Override
            public void typeInferenceFailed(
                    @NotNull ResolutionContext<?> context, @NotNull InferenceErrorData data
            ) {
                ConstraintSystem constraintSystem = data.constraintSystem;
                ConstraintSystemStatus status = constraintSystem.getStatus();
                assert !status.isSuccessful() : "Report error only for not successful constraint system";

                if (status.hasErrorInConstrainingTypes() || status.hasUnknownParameters()) {
                    return;
                }
                KtExpression expression = (KtExpression) call.getCallElement();
                if (status.hasOnlyErrorsDerivedFrom(EXPECTED_TYPE_POSITION) || status.hasConflictingConstraints()
                        || status.hasTypeInferenceIncorporationError()) { // todo after KT-... remove this line
                    if (noTypeCheckingErrorsInExpression(expression, context.trace, data.expectedType)) {
                        KtExpression calleeExpression = call.getCalleeExpression();
                        if (calleeExpression instanceof KtWhenExpression || calleeExpression instanceof KtIfExpression) {
                            if (status.hasConflictingConstraints() || status.hasTypeInferenceIncorporationError()) {
                                // TODO provide comprehensible error report for hasConflictingConstraints() case (if possible)
                                context.trace.report(TYPE_INFERENCE_FAILED_ON_SPECIAL_CONSTRUCT.on(expression));
                            }
                        }
                    }
                    return;
                }
                KtDeclaration parentDeclaration = PsiTreeUtil.getParentOfType(expression, KtNamedDeclaration.class);
                logError("Expression: " + (parentDeclaration != null ? parentDeclaration.getText() : expression.getText()) +
                         "\nConstraint system status: \n" + ConstraintsUtil.getDebugMessageForStatus(status));
            }

            private boolean noTypeCheckingErrorsInExpression(
                    KtExpression expression,
                    @NotNull BindingTrace trace,
                    @NotNull KotlinType expectedType
            ) {
                return Boolean.TRUE != expression.accept(checkTypeVisitor, new CheckTypeContext(trace, expectedType));
            }
        };
    }