Fix resolution of callable references
When there is unsuccessful (e.g invisible) result of one kind (static/non-static) and there is a successful candidate for another kind, choose the latter one. Note, that we have to postpone commiting trace until we choose one of the results, otherwise errors of unsuccessful results are reported TODO: Maybe it makes sense to report all results when all of them are unsuccessful (NONE_APPLICABLE or something like this) #KT-16278 Fixed
This commit is contained in:
+74
-47
@@ -58,9 +58,11 @@ import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNullable
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import org.jetbrains.kotlin.utils.yieldIfNotNull
|
||||
import java.lang.UnsupportedOperationException
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
import kotlin.coroutines.experimental.buildSequence
|
||||
|
||||
sealed class DoubleColonLHS(val type: KotlinType) {
|
||||
/**
|
||||
@@ -626,15 +628,18 @@ class DoubleColonExpressionResolver(
|
||||
}
|
||||
}
|
||||
|
||||
private class ResolutionResultsAndTraceCommitCallback(
|
||||
val results: OverloadResolutionResults<CallableDescriptor>,
|
||||
val commitTrace: () -> Unit
|
||||
)
|
||||
|
||||
private fun tryResolveRHSWithReceiver(
|
||||
traceTitle: String,
|
||||
receiver: Receiver?,
|
||||
reference: KtSimpleNameExpression,
|
||||
outerContext: ResolutionContext<*>,
|
||||
resolutionMode: ResolveArgumentsMode
|
||||
): OverloadResolutionResults<CallableDescriptor>? {
|
||||
checkReservedYield(reference, outerContext.trace)
|
||||
|
||||
): ResolutionResultsAndTraceCommitCallback? {
|
||||
// we should preserve information about `call` because callable references are analyzed two times,
|
||||
// otherwise there will be not completed calls in trace
|
||||
val call = outerContext.trace[BindingContext.CALL, reference] ?: CallMaker.makeCall(reference, receiver, null, reference, emptyList())
|
||||
@@ -652,12 +657,15 @@ class DoubleColonExpressionResolver(
|
||||
reference, BasicCallResolutionContext.create(newContext, call, CheckArgumentTypesMode.CHECK_CALLABLE_TYPE)
|
||||
)
|
||||
|
||||
val shouldCommitTrace =
|
||||
if (resolutionMode == ResolveArgumentsMode.SHAPE_FUNCTION_ARGUMENTS) resolutionResults.isSingleResult
|
||||
else !resolutionResults.isNothing
|
||||
if (shouldCommitTrace) temporaryTrace.commit()
|
||||
|
||||
return if (resolutionResults.isNothing) null else resolutionResults
|
||||
return when {
|
||||
resolutionResults.isNothing -> null
|
||||
else -> ResolutionResultsAndTraceCommitCallback(resolutionResults) {
|
||||
checkReservedYield(reference, outerContext.trace)
|
||||
if (resolutionMode != ResolveArgumentsMode.SHAPE_FUNCTION_ARGUMENTS || resolutionResults.isSingleResult) {
|
||||
temporaryTrace.commit()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun resolveCallableReferenceRHS(
|
||||
@@ -668,52 +676,71 @@ class DoubleColonExpressionResolver(
|
||||
): OverloadResolutionResults<CallableDescriptor>? {
|
||||
val reference = expression.callableReference
|
||||
|
||||
val lhsType = lhs?.type ?:
|
||||
return tryResolveRHSWithReceiver("resolve callable reference with empty LHS", null, reference, c, mode)
|
||||
val lhsType =
|
||||
lhs?.type ?:
|
||||
return tryResolveRHSWithReceiver("resolve callable reference with empty LHS", null, reference, c, mode)
|
||||
?.apply { commitTrace() }?.results
|
||||
|
||||
when (lhs) {
|
||||
is DoubleColonLHS.Type -> {
|
||||
val classifier = lhsType.constructor.declarationDescriptor
|
||||
if (classifier !is ClassDescriptor) {
|
||||
c.trace.report(CALLABLE_REFERENCE_LHS_NOT_A_CLASS.on(expression))
|
||||
return null
|
||||
}
|
||||
|
||||
val qualifier = c.trace.get(BindingContext.QUALIFIER, expression.receiverExpression!!)
|
||||
if (qualifier is ClassQualifier) {
|
||||
val possibleStatic = tryResolveRHSWithReceiver(
|
||||
"resolve unbound callable reference in static scope", qualifier, reference, c, mode
|
||||
)
|
||||
if (possibleStatic != null) return possibleStatic
|
||||
}
|
||||
|
||||
val possibleWithReceiver = tryResolveRHSWithReceiver(
|
||||
"resolve unbound callable reference with receiver", TransientReceiver(lhsType), reference, c, mode
|
||||
)
|
||||
if (possibleWithReceiver != null) return possibleWithReceiver
|
||||
}
|
||||
is DoubleColonLHS.Expression -> {
|
||||
val expressionReceiver = ExpressionReceiver.create(expression.receiverExpression!!, lhsType, c.trace.bindingContext)
|
||||
val result = tryResolveRHSWithReceiver(
|
||||
"resolve bound callable reference", expressionReceiver, reference, c, mode
|
||||
)
|
||||
if (result != null) return result
|
||||
|
||||
if (lhs.isObjectQualifier) {
|
||||
val resultSequence = buildSequence {
|
||||
when (lhs) {
|
||||
is DoubleColonLHS.Type -> {
|
||||
val classifier = lhsType.constructor.declarationDescriptor
|
||||
val calleeExpression = expression.receiverExpression?.getCalleeExpressionIfAny()
|
||||
if (calleeExpression is KtSimpleNameExpression && classifier is ClassDescriptor) {
|
||||
val qualifier = ClassQualifier(calleeExpression, classifier)
|
||||
val possibleStatic = tryResolveRHSWithReceiver(
|
||||
"resolve object callable reference in static scope", qualifier, reference, c, mode
|
||||
if (classifier !is ClassDescriptor) {
|
||||
c.trace.report(CALLABLE_REFERENCE_LHS_NOT_A_CLASS.on(expression))
|
||||
return@buildSequence
|
||||
}
|
||||
|
||||
val qualifier = c.trace.get(BindingContext.QUALIFIER, expression.receiverExpression!!)
|
||||
if (qualifier is ClassQualifier) {
|
||||
yieldIfNotNull(
|
||||
tryResolveRHSWithReceiver(
|
||||
"resolve unbound callable reference in static scope", qualifier, reference, c, mode
|
||||
)
|
||||
)
|
||||
if (possibleStatic != null) return possibleStatic
|
||||
}
|
||||
|
||||
yieldIfNotNull(
|
||||
tryResolveRHSWithReceiver(
|
||||
"resolve unbound callable reference with receiver", TransientReceiver(lhsType), reference, c, mode
|
||||
)
|
||||
)
|
||||
}
|
||||
is DoubleColonLHS.Expression -> {
|
||||
val expressionReceiver = ExpressionReceiver.create(expression.receiverExpression!!, lhsType, c.trace.bindingContext)
|
||||
yieldIfNotNull(
|
||||
tryResolveRHSWithReceiver(
|
||||
"resolve bound callable reference", expressionReceiver, reference, c, mode
|
||||
)
|
||||
)
|
||||
|
||||
if (lhs.isObjectQualifier) {
|
||||
val classifier = lhsType.constructor.declarationDescriptor
|
||||
val calleeExpression = expression.receiverExpression?.getCalleeExpressionIfAny()
|
||||
if (calleeExpression is KtSimpleNameExpression && classifier is ClassDescriptor) {
|
||||
val qualifier = ClassQualifier(calleeExpression, classifier)
|
||||
yieldIfNotNull(
|
||||
tryResolveRHSWithReceiver(
|
||||
"resolve object callable reference in static scope", qualifier, reference, c, mode
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
// TODO: Maybe it makes sense to report all results when all of them are unsuccessful (NONE_APPLICABLE or something like this)
|
||||
var resultToCommit: ResolutionResultsAndTraceCommitCallback? = null
|
||||
for (result in resultSequence) {
|
||||
resultToCommit = result
|
||||
if (result.results.isSuccess) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return resultToCommit?.let {
|
||||
it.commitTrace()
|
||||
it.results
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
// SKIP_TXT
|
||||
// FILE: A.java
|
||||
class A {
|
||||
private static void foo() {}
|
||||
public String foo(int x) {}
|
||||
}
|
||||
// FILE: main.kt
|
||||
fun main() {
|
||||
(A::foo)(A(), 1)
|
||||
}
|
||||
@@ -2199,6 +2199,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("privateStaticAndPublicMember.kt")
|
||||
public void testPrivateStaticAndPublicMember() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/callableReference/function/privateStaticAndPublicMember.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("renameOnImport.kt")
|
||||
public void testRenameOnImport() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/callableReference/function/renameOnImport.kt");
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.jetbrains.kotlin.utils
|
||||
|
||||
import java.util.*
|
||||
import kotlin.coroutines.experimental.SequenceBuilder
|
||||
|
||||
fun <K, V> Iterable<K>.keysToMap(value: (K) -> V): Map<K, V> {
|
||||
return associateBy({ it }, value)
|
||||
@@ -63,6 +64,8 @@ fun <T: Any> MutableCollection<T>.addIfNotNull(t: T?) {
|
||||
if (t != null) add(t)
|
||||
}
|
||||
|
||||
suspend fun <T: Any> SequenceBuilder<in T>.yieldIfNotNull(t: T?) = if (t != null) yield(t) else Unit
|
||||
|
||||
fun <K, V> newHashMapWithExpectedSize(expectedSize: Int): HashMap<K, V> =
|
||||
HashMap(capacity(expectedSize))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user