Refactor FakeCallResolver, make error reporting simpler
This commit is contained in:
@@ -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)
|
||||
}
|
||||
|
||||
+1
-1
@@ -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");
|
||||
|
||||
+6
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user