From 17b4874c37326058a1d51357d623964e4aad570c Mon Sep 17 00:00:00 2001 From: Mikhail Zarechenskiy Date: Thu, 31 Aug 2017 14:31:51 +0300 Subject: [PATCH] Refactor `FakeCallResolver`, make error reporting simpler --- .../kotlin/resolve/calls/CallResolver.java | 11 ++ .../types/expressions/FakeCallResolver.kt | 107 +++++++++++------- .../MultiDeclarationErrors.kt | 2 +- .../checkers/DiagnosticsTestGenerated.java | 6 + .../DiagnosticsUsingJavacTestGenerated.java | 6 + 5 files changed, 90 insertions(+), 42 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolver.java index 2fc185bb10e..56ac8c60209 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolver.java @@ -170,6 +170,17 @@ public class CallResolver { NewResolutionOldInference.ResolutionKind.Function.INSTANCE); } + @NotNull + public OverloadResolutionResults resolveCallWithGivenName( + @NotNull ResolutionContext context, + @NotNull Call call, + @NotNull Name name, + @NotNull TracingStrategy tracing + ) { + BasicCallResolutionContext callResolutionContext = BasicCallResolutionContext.create(context, call, CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS); + return computeTasksAndResolveCall(callResolutionContext, name, tracing, NewResolutionOldInference.ResolutionKind.Function.INSTANCE); + } + @NotNull private OverloadResolutionResults resolveCallForInvoke( @NotNull BasicCallResolutionContext context, diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/FakeCallResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/FakeCallResolver.kt index 73eb4fe9238..ef7923059aa 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/FakeCallResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/FakeCallResolver.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2015 JetBrains s.r.o. + * Copyright 2010-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,21 +17,23 @@ package org.jetbrains.kotlin.types.expressions import com.intellij.openapi.project.Project +import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.diagnostics.Severity import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.psi.Call -import org.jetbrains.kotlin.psi.KtExpression -import org.jetbrains.kotlin.psi.KtPsiFactory -import org.jetbrains.kotlin.psi.KtSimpleNameExpression +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.BindingTrace import org.jetbrains.kotlin.resolve.TemporaryBindingTrace import org.jetbrains.kotlin.resolve.calls.CallResolver import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults +import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy +import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategyImpl import org.jetbrains.kotlin.resolve.calls.util.CallMaker import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue +import org.jetbrains.kotlin.types.KotlinType enum class FakeCallKind { ITERATOR, @@ -55,39 +57,18 @@ class FakeCallResolver( val fakeTrace = TemporaryBindingTrace.create(context.trace, "trace to resolve fake call for", name) val fakeBindingTrace = context.replaceBindingTrace(fakeTrace) - var unreportedDiagnostic: Diagnostic? = null - val result = makeAndResolveFakeCallInContext(receiver, fakeBindingTrace, valueArguments, name, callElement) { fake, isSuccess -> - unreportedDiagnostic = fakeTrace.bindingContext.diagnostics.noSuppression().forElement(fake).firstOrNull { it.severity == Severity.ERROR } - - if (isSuccess) { - fakeTrace.commit( - { _, key -> - // excluding all entries related to fake expression - // convert all errors on this expression to ITERATOR_MISSING on callElement - key != fake - }, true - ) - } + var errorIsMissing = false + val realExpression = RealExpression(reportErrorsOn, callKind) + val result = makeAndResolveFakeCallInContext(receiver, fakeBindingTrace, valueArguments, name, callElement, realExpression) { fake -> + errorIsMissing = fakeTrace.bindingContext.diagnostics.noSuppression().forElement(fake).any { it.severity == Severity.ERROR } + fakeTrace.commit({ _, key -> key != fake }, true) } val resolutionResults = result.second - if (!resolutionResults.isSuccess || unreportedDiagnostic != null) { - val isUnsafeCall = unreportedDiagnostic?.factory == Errors.UNSAFE_CALL + if (errorIsMissing) { val diagnostic = when (callKind) { - FakeCallKind.ITERATOR -> - when { - resolutionResults.isAmbiguity -> Errors.ITERATOR_AMBIGUITY.on(reportErrorsOn, resolutionResults.resultingCalls) - isUnsafeCall -> Errors.ITERATOR_ON_NULLABLE.on(reportErrorsOn) - else -> Errors.ITERATOR_MISSING.on(reportErrorsOn) - } - FakeCallKind.COMPONENT -> - when { - resolutionResults.isAmbiguity -> Errors.COMPONENT_FUNCTION_AMBIGUITY.on( - reportErrorsOn, name, resolutionResults.resultingCalls) - isUnsafeCall -> Errors.COMPONENT_FUNCTION_ON_NULLABLE.on(reportErrorsOn, name) - receiver != null -> Errors.COMPONENT_FUNCTION_MISSING.on(reportErrorsOn, name, receiver.type) - else -> null - } + FakeCallKind.ITERATOR -> Errors.ITERATOR_MISSING.on(reportErrorsOn) + FakeCallKind.COMPONENT -> if (receiver != null) Errors.COMPONENT_FUNCTION_MISSING.on(reportErrorsOn, name, receiver.type) else null FakeCallKind.OTHER -> null } @@ -99,6 +80,39 @@ class FakeCallResolver( return resolutionResults } + private fun createTracingStrategyForComponentCall( + fakeExpression: KtReferenceExpression, + reportErrorsOn: KtExpression, + name: Name, + call: Call + ): TracingStrategy { + return object : TracingStrategy by TracingStrategyImpl.create(fakeExpression, call) { + override fun ambiguity(trace: BindingTrace, descriptors: MutableCollection>) { + trace.report(Errors.COMPONENT_FUNCTION_AMBIGUITY.on(reportErrorsOn, name, descriptors)) + } + + override fun unsafeCall(trace: BindingTrace, type: KotlinType, isCallForImplicitInvoke: Boolean) { + trace.report(Errors.COMPONENT_FUNCTION_ON_NULLABLE.on(reportErrorsOn, name)) + } + } + } + + private fun createTracingStrategyForIteratorCall( + fakeExpression: KtReferenceExpression, + reportErrorsOn: KtExpression, + call: Call + ): TracingStrategy { + return object : TracingStrategy by TracingStrategyImpl.create(fakeExpression, call) { + override fun ambiguity(trace: BindingTrace, descriptors: MutableCollection>) { + trace.report(Errors.ITERATOR_AMBIGUITY.on(reportErrorsOn, descriptors)) + } + + override fun unsafeCall(trace: BindingTrace, type: KotlinType, isCallForImplicitInvoke: Boolean) { + trace.report(Errors.ITERATOR_ON_NULLABLE.on(reportErrorsOn)) + } + } + } + @JvmOverloads fun makeAndResolveFakeCallInContext( receiver: ReceiverValue?, @@ -106,16 +120,27 @@ class FakeCallResolver( valueArguments: List, name: Name, callElement: KtExpression, - onComplete: (KtSimpleNameExpression, Boolean) -> Unit = { _, _ -> } + realExpression: RealExpression? = null, + onComplete: (KtSimpleNameExpression) -> Unit = { _ -> } ): Pair> { val fakeCalleeExpression = KtPsiFactory(project, markGenerated = false).createSimpleName(name.asString()) - val call = CallMaker.makeCallWithExpressions( - callElement, receiver, /* callOperationNode = */ null, fakeCalleeExpression, valueArguments - ) - val results = callResolver.resolveCallWithGivenName(context, call, fakeCalleeExpression, name) + val call = CallMaker.makeCallWithExpressions(callElement, receiver, null, fakeCalleeExpression, valueArguments) - onComplete(fakeCalleeExpression, results.isSuccess) + val tracingStrategy = when (realExpression?.callKind) { + FakeCallKind.ITERATOR -> createTracingStrategyForIteratorCall(fakeCalleeExpression, realExpression.expression, call) + FakeCallKind.COMPONENT -> createTracingStrategyForComponentCall(fakeCalleeExpression, realExpression.expression, name, call) + else -> null + } + + val results = if (tracingStrategy != null) + callResolver.resolveCallWithGivenName(context, call, name, tracingStrategy) + else + callResolver.resolveCallWithGivenName(context, call, fakeCalleeExpression, name) + + onComplete(fakeCalleeExpression) return Pair(call, results) } + + class RealExpression(val expression: KtExpression, val callKind: FakeCallKind) } diff --git a/compiler/testData/diagnostics/tests/declarationChecks/MultiDeclarationErrors.kt b/compiler/testData/diagnostics/tests/declarationChecks/MultiDeclarationErrors.kt index 09eb45323e6..5233faf262c 100644 --- a/compiler/testData/diagnostics/tests/declarationChecks/MultiDeclarationErrors.kt +++ b/compiler/testData/diagnostics/tests/declarationChecks/MultiDeclarationErrors.kt @@ -10,7 +10,7 @@ class MyClass2 {} fun MyClass2.component1() = 1.3 fun test(mc1: MyClass, mc2: MyClass2) { - val (a, b) = mc1 + val (a, b) = mc1 val (c) = mc2 //a,b,c are error types diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index f24d1ffc928..b086aa3e152 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -11040,6 +11040,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("typeInferenceFailedOnComponentN.kt") + public void testTypeInferenceFailedOnComponentN() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/inference/reportingImprovements/typeInferenceFailedOnComponentN.kt"); + doTest(fileName); + } + @TestMetadata("wrongArgumentExtensionFunction.kt") public void testWrongArgumentExtensionFunction() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/inference/reportingImprovements/wrongArgumentExtensionFunction.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java index 6e5ecfd8d97..8e57fc675cc 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java @@ -11040,6 +11040,12 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing doTest(fileName); } + @TestMetadata("typeInferenceFailedOnComponentN.kt") + public void testTypeInferenceFailedOnComponentN() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/inference/reportingImprovements/typeInferenceFailedOnComponentN.kt"); + doTest(fileName); + } + @TestMetadata("wrongArgumentExtensionFunction.kt") public void testWrongArgumentExtensionFunction() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/inference/reportingImprovements/wrongArgumentExtensionFunction.kt");