Refactor FakeCallResolver, make error reporting simpler

This commit is contained in:
Mikhail Zarechenskiy
2017-08-31 14:31:51 +03:00
parent ff1c5ad793
commit 17b4874c37
5 changed files with 90 additions and 42 deletions
@@ -170,6 +170,17 @@ public class CallResolver {
NewResolutionOldInference.ResolutionKind.Function.INSTANCE);
}
@NotNull
public OverloadResolutionResults<FunctionDescriptor> 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<FunctionDescriptor> resolveCallForInvoke(
@NotNull BasicCallResolutionContext context,
@@ -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 <D : CallableDescriptor?> ambiguity(trace: BindingTrace, descriptors: MutableCollection<out ResolvedCall<D>>) {
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 <D : CallableDescriptor?> ambiguity(trace: BindingTrace, descriptors: MutableCollection<out ResolvedCall<D>>) {
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<KtExpression>,
name: Name,
callElement: KtExpression,
onComplete: (KtSimpleNameExpression, Boolean) -> Unit = { _, _ -> }
realExpression: RealExpression? = null,
onComplete: (KtSimpleNameExpression) -> Unit = { _ -> }
): Pair<Call, OverloadResolutionResults<FunctionDescriptor>> {
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)
}
@@ -10,7 +10,7 @@ class MyClass2 {}
<!CONFLICTING_OVERLOADS!>fun MyClass2.component1()<!> = 1.3
fun test(mc1: MyClass, mc2: MyClass2) {
val (a, b) = <!COMPONENT_FUNCTION_MISSING, COMPONENT_FUNCTION_MISSING!>mc1<!>
val (<!OPERATOR_MODIFIER_REQUIRED!>a<!>, b) = <!COMPONENT_FUNCTION_MISSING, COMPONENT_FUNCTION_MISSING!>mc1<!>
val (c) = <!COMPONENT_FUNCTION_AMBIGUITY!>mc2<!>
//a,b,c are error types
@@ -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");
@@ -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");