From b5a8ffaddc64b9862d8cf1173183df5e538e4c8e Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Fri, 10 Feb 2017 15:29:57 +0300 Subject: [PATCH] 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 --- .../DoubleColonExpressionResolver.kt | 121 +++++++++++------- .../function/privateStaticAndPublicMember.kt | 10 ++ .../checkers/DiagnosticsTestGenerated.java | 6 + .../org/jetbrains/kotlin/utils/collections.kt | 3 + 4 files changed, 93 insertions(+), 47 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/callableReference/function/privateStaticAndPublicMember.kt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt index 05057a34711..fcddce9cbdc 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt @@ -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, + val commitTrace: () -> Unit + ) + private fun tryResolveRHSWithReceiver( traceTitle: String, receiver: Receiver?, reference: KtSimpleNameExpression, outerContext: ResolutionContext<*>, resolutionMode: ResolveArgumentsMode - ): OverloadResolutionResults? { - 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? { 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 { diff --git a/compiler/testData/diagnostics/tests/callableReference/function/privateStaticAndPublicMember.kt b/compiler/testData/diagnostics/tests/callableReference/function/privateStaticAndPublicMember.kt new file mode 100644 index 00000000000..4a1fcff935d --- /dev/null +++ b/compiler/testData/diagnostics/tests/callableReference/function/privateStaticAndPublicMember.kt @@ -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) +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 26afe498131..a9614505d1a 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -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"); diff --git a/core/util.runtime/src/org/jetbrains/kotlin/utils/collections.kt b/core/util.runtime/src/org/jetbrains/kotlin/utils/collections.kt index c2af8056e65..731768bb4d7 100644 --- a/core/util.runtime/src/org/jetbrains/kotlin/utils/collections.kt +++ b/core/util.runtime/src/org/jetbrains/kotlin/utils/collections.kt @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.utils import java.util.* +import kotlin.coroutines.experimental.SequenceBuilder fun Iterable.keysToMap(value: (K) -> V): Map { return associateBy({ it }, value) @@ -63,6 +64,8 @@ fun MutableCollection.addIfNotNull(t: T?) { if (t != null) add(t) } +suspend fun SequenceBuilder.yieldIfNotNull(t: T?) = if (t != null) yield(t) else Unit + fun newHashMapWithExpectedSize(expectedSize: Int): HashMap = HashMap(capacity(expectedSize))