Consider callable reference's LHS when resolving RHS
Previous resolution sequence (static scope, nested classes scope, receiver) and a check against type parameters only made sense when there's a type, not an expression, on the LHS of a callable reference. Also TransientReceiver is incorrect in such case because private-to-this visibility check only works for ExpressionReceiver values
This commit is contained in:
+33
-22
@@ -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<CallableDescriptor> {
|
||||
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<CallableDescriptor>? {
|
||||
val reference = callableReferenceExpression.getCallableReference()
|
||||
val reference = callableReferenceExpression.callableReference
|
||||
|
||||
fun resolveInScope(traceTitle: String, classifier: ClassifierDescriptor, staticScope: MemberScope): OverloadResolutionResults<CallableDescriptor> {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
+2
-3
@@ -307,9 +307,8 @@ class DoubleColonExpressionResolver(
|
||||
): Pair<DoubleColonLHS?, OverloadResolutionResults<*>?> {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
class Outer {
|
||||
class Nested
|
||||
inner class Inner
|
||||
}
|
||||
|
||||
fun test() {
|
||||
Outer()::Inner
|
||||
Outer()::<!UNRESOLVED_REFERENCE!>Nested<!>
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// !DIAGNOSTICS: -UNUSED_VARIABLE
|
||||
|
||||
class Foo<out T>(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)::<!INVISIBLE_MEMBER!>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)::<!INVISIBLE_MEMBER!>func<!>
|
||||
}
|
||||
|
||||
private fun func(t: T): T = t
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package
|
||||
|
||||
public final class Foo</*0*/ out T> {
|
||||
public constructor Foo</*0*/ out T>(/*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
|
||||
}
|
||||
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
// FILE: A.java
|
||||
|
||||
public class A {
|
||||
public static void test() {}
|
||||
}
|
||||
|
||||
// FILE: test.kt
|
||||
|
||||
enum class E { EN }
|
||||
|
||||
fun test() {
|
||||
A()::<!UNRESOLVED_REFERENCE!>test<!>
|
||||
E.EN::<!UNRESOLVED_REFERENCE!>valueOf<!>
|
||||
}
|
||||
Vendored
+32
@@ -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<E> {
|
||||
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<E!>!
|
||||
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<E>
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fun <T: Any> get(t: T): () -> String {
|
||||
return t::toString
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
package
|
||||
|
||||
public fun </*0*/ T : kotlin.Any> get(/*0*/ t: T): () -> kotlin.String
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user