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))