diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/callableReferences/CallableReferencesResolutionUtils.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/callableReferences/CallableReferencesResolutionUtils.kt index a633c09e521..a966b099e77 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/callableReferences/CallableReferencesResolutionUtils.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/callableReferences/CallableReferencesResolutionUtils.kt @@ -41,6 +41,7 @@ import org.jetbrains.kotlin.resolve.scopes.BaseLexicalScope import org.jetbrains.kotlin.resolve.scopes.LexicalScopeKind import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.resolve.scopes.ScopeUtils +import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver import org.jetbrains.kotlin.resolve.scopes.utils.memberScopeAsImportingScope @@ -48,6 +49,7 @@ import org.jetbrains.kotlin.resolve.source.toSourceElement import org.jetbrains.kotlin.types.FunctionPlaceholders import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeUtils +import org.jetbrains.kotlin.types.expressions.DoubleColonLHS import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils import org.jetbrains.kotlin.utils.Printer @@ -68,7 +70,7 @@ private fun resolvePossiblyAmbiguousCallableReference( callResolver: CallResolver ): OverloadResolutionResults { val call = CallMaker.makeCall(reference, receiver, null, reference, emptyList()) - val temporaryTrace = TemporaryTraceAndCache.create(context, "trace to resolve ::${reference.getReferencedName()} as function", reference) + val temporaryTrace = TemporaryTraceAndCache.create(context, "resolve callable reference as function", reference) val newContext = if (resolutionMode == ResolveArgumentsMode.SHAPE_FUNCTION_ARGUMENTS) context.replaceTraceAndCache(temporaryTrace).replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE) else @@ -84,12 +86,12 @@ private fun OverloadResolutionResults<*>.isSomething(): Boolean = !isNothing fun resolvePossiblyAmbiguousCallableReference( callableReferenceExpression: KtCallableReferenceExpression, - lhsType: KotlinType?, + lhs: DoubleColonLHS?, context: ResolutionContext<*>, resolutionMode: ResolveArgumentsMode, callResolver: CallResolver ): OverloadResolutionResults? { - val reference = callableReferenceExpression.getCallableReference() + val reference = callableReferenceExpression.callableReference fun resolveInScope(traceTitle: String, classifier: ClassifierDescriptor, staticScope: MemberScope): OverloadResolutionResults { @@ -124,27 +126,36 @@ fun resolvePossiblyAmbiguousCallableReference( return results } - if (lhsType == null) { - return resolvePossiblyAmbiguousCallableReference(reference, null, context, resolutionMode, callResolver) + val lhsType = lhs?.type ?: return resolvePossiblyAmbiguousCallableReference(reference, null, context, resolutionMode, callResolver) + + when (lhs) { + is DoubleColonLHS.Type -> { + val classifier = lhsType.constructor.declarationDescriptor + if (classifier !is ClassDescriptor) { + context.trace.report(CALLABLE_REFERENCE_LHS_NOT_A_CLASS.on(callableReferenceExpression)) + return null + } + + val possibleStatic = resolveInScope("resolve unbound callable reference in static scope", classifier, classifier.staticScope) + if (possibleStatic.isSomething()) return possibleStatic + + val possibleNested = resolveInScope( + "resolve unbound callable reference in static nested classes scope", classifier, + ScopeUtils.getStaticNestedClassesScope(classifier) + ) + if (possibleNested.isSomething()) return possibleNested + + val possibleWithReceiver = resolveWithReceiver("resolve unbound callable reference with receiver", TransientReceiver(lhsType)) + if (possibleWithReceiver.isSomething()) return possibleWithReceiver + } + is DoubleColonLHS.Expression -> { + val result = resolveWithReceiver("resolve bound callable reference", ExpressionReceiver.create( + callableReferenceExpression.receiverExpression!!, lhsType, context.trace.bindingContext + )) + if (result.isSomething()) return result + } } - val classifier = lhsType.constructor.declarationDescriptor - if (classifier !is ClassDescriptor) { - context.trace.report(CALLABLE_REFERENCE_LHS_NOT_A_CLASS.on(callableReferenceExpression)) - return null - } - - val possibleStatic = resolveInScope("trace to resolve ::${reference.getReferencedName()} in static scope", classifier, classifier.staticScope) - if (possibleStatic.isSomething()) return possibleStatic - - val possibleNested = resolveInScope("trace to resolve ::${reference.getReferencedName()} in static nested classes scope", - classifier, ScopeUtils.getStaticNestedClassesScope(classifier)) - if (possibleNested.isSomething()) return possibleNested - - val possibleWithReceiver = resolveWithReceiver("trace to resolve ::${reference.getReferencedName()} with receiver", - TransientReceiver(lhsType)) - if (possibleWithReceiver.isSomething()) return possibleWithReceiver - return null } 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 fdf5f7d574f..b541428aa18 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt @@ -307,9 +307,8 @@ class DoubleColonExpressionResolver( ): Pair?> { val lhsResult = expression.receiverExpression?.let { resolveDoubleColonLHS(it, expression, context) } - val resolutionResults = resolvePossiblyAmbiguousCallableReference( - expression, lhsResult?.type, context, resolveArgumentsMode, callResolver - ) + val resolutionResults = + resolvePossiblyAmbiguousCallableReference(expression, lhsResult, context, resolveArgumentsMode, callResolver) return lhsResult to resolutionResults } diff --git a/compiler/testData/diagnostics/tests/callableReference/bound/innerNested.kt b/compiler/testData/diagnostics/tests/callableReference/bound/innerNested.kt new file mode 100644 index 00000000000..da7d41b32b6 --- /dev/null +++ b/compiler/testData/diagnostics/tests/callableReference/bound/innerNested.kt @@ -0,0 +1,9 @@ +class Outer { + class Nested + inner class Inner +} + +fun test() { + Outer()::Inner + Outer()::Nested +} diff --git a/compiler/testData/diagnostics/tests/callableReference/bound/innerNested.txt b/compiler/testData/diagnostics/tests/callableReference/bound/innerNested.txt new file mode 100644 index 00000000000..54073083fca --- /dev/null +++ b/compiler/testData/diagnostics/tests/callableReference/bound/innerNested.txt @@ -0,0 +1,24 @@ +package + +public fun test(): kotlin.Unit + +public final class Outer { + public constructor Outer() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + public final inner class Inner { + public constructor Inner() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + public final class Nested { + public constructor Nested() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } +} diff --git a/compiler/testData/diagnostics/tests/callableReference/bound/privateToThis.kt b/compiler/testData/diagnostics/tests/callableReference/bound/privateToThis.kt new file mode 100644 index 00000000000..eb34fd2c487 --- /dev/null +++ b/compiler/testData/diagnostics/tests/callableReference/bound/privateToThis.kt @@ -0,0 +1,24 @@ +// !DIAGNOSTICS: -UNUSED_VARIABLE + +class Foo(name: T) { + private var prop: T = name + private set + + fun testProp() { + val ok1 = this::prop + val ok2 = this@Foo::prop + val ok3 = object { val y: Any = this@Foo::prop } + + val fail1 = Foo(prop)::prop + } + + fun testFunc() { + val ok1 = this::func + val ok2 = this@Foo::func + val ok3 = object { val y: Any = this@Foo::func } + + val fail1 = Foo(prop)::func + } + + private fun func(t: T): T = t +} diff --git a/compiler/testData/diagnostics/tests/callableReference/bound/privateToThis.txt b/compiler/testData/diagnostics/tests/callableReference/bound/privateToThis.txt new file mode 100644 index 00000000000..7b583c858d4 --- /dev/null +++ b/compiler/testData/diagnostics/tests/callableReference/bound/privateToThis.txt @@ -0,0 +1,12 @@ +package + +public final class Foo { + public constructor Foo(/*0*/ name: T) + private/*private to this*/ final var prop: T + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + private/*private to this*/ final fun func(/*0*/ t: T): T + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public final fun testFunc(): kotlin.Unit + public final fun testProp(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/callableReference/bound/referenceToStaticMethodOnInstance.kt b/compiler/testData/diagnostics/tests/callableReference/bound/referenceToStaticMethodOnInstance.kt new file mode 100644 index 00000000000..1cedd8e2e99 --- /dev/null +++ b/compiler/testData/diagnostics/tests/callableReference/bound/referenceToStaticMethodOnInstance.kt @@ -0,0 +1,14 @@ +// FILE: A.java + +public class A { + public static void test() {} +} + +// FILE: test.kt + +enum class E { EN } + +fun test() { + A()::test + E.EN::valueOf +} diff --git a/compiler/testData/diagnostics/tests/callableReference/bound/referenceToStaticMethodOnInstance.txt b/compiler/testData/diagnostics/tests/callableReference/bound/referenceToStaticMethodOnInstance.txt new file mode 100644 index 00000000000..da7b9aacb80 --- /dev/null +++ b/compiler/testData/diagnostics/tests/callableReference/bound/referenceToStaticMethodOnInstance.txt @@ -0,0 +1,32 @@ +package + +public fun test(): kotlin.Unit + +public open class A { + public constructor A() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + // Static members + public open fun test(): kotlin.Unit +} + +public final enum class E : kotlin.Enum { + enum entry EN + + private constructor E() + public final override /*1*/ /*fake_override*/ val name: kotlin.String + public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int + protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: E): kotlin.Int + public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + protected/*protected and package*/ final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun finalize(): kotlin.Unit + public final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun getDeclaringClass(): java.lang.Class! + public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + // Static members + public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): E + public final /*synthesized*/ fun values(): kotlin.Array +} diff --git a/compiler/testData/diagnostics/tests/callableReference/bound/valueOfTypeParameterType.kt b/compiler/testData/diagnostics/tests/callableReference/bound/valueOfTypeParameterType.kt new file mode 100644 index 00000000000..928a186a85c --- /dev/null +++ b/compiler/testData/diagnostics/tests/callableReference/bound/valueOfTypeParameterType.kt @@ -0,0 +1,3 @@ +fun get(t: T): () -> String { + return t::toString +} diff --git a/compiler/testData/diagnostics/tests/callableReference/bound/valueOfTypeParameterType.txt b/compiler/testData/diagnostics/tests/callableReference/bound/valueOfTypeParameterType.txt new file mode 100644 index 00000000000..6c7217adda9 --- /dev/null +++ b/compiler/testData/diagnostics/tests/callableReference/bound/valueOfTypeParameterType.txt @@ -0,0 +1,3 @@ +package + +public fun get(/*0*/ t: T): () -> kotlin.String diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 8cf0899931f..7f75f914c1e 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -1751,11 +1751,35 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("innerNested.kt") + public void testInnerNested() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/callableReference/bound/innerNested.kt"); + doTest(fileName); + } + @TestMetadata("object.kt") public void testObject() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/callableReference/bound/object.kt"); doTest(fileName); } + + @TestMetadata("privateToThis.kt") + public void testPrivateToThis() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/callableReference/bound/privateToThis.kt"); + doTest(fileName); + } + + @TestMetadata("referenceToStaticMethodOnInstance.kt") + public void testReferenceToStaticMethodOnInstance() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/callableReference/bound/referenceToStaticMethodOnInstance.kt"); + doTest(fileName); + } + + @TestMetadata("valueOfTypeParameterType.kt") + public void testValueOfTypeParameterType() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/callableReference/bound/valueOfTypeParameterType.kt"); + doTest(fileName); + } } @TestMetadata("compiler/testData/diagnostics/tests/callableReference/function")