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.LexicalScopeKind
|
||||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||||
import org.jetbrains.kotlin.resolve.scopes.ScopeUtils
|
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.ReceiverValue
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver
|
import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver
|
||||||
import org.jetbrains.kotlin.resolve.scopes.utils.memberScopeAsImportingScope
|
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.FunctionPlaceholders
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.kotlin.types.TypeUtils
|
import org.jetbrains.kotlin.types.TypeUtils
|
||||||
|
import org.jetbrains.kotlin.types.expressions.DoubleColonLHS
|
||||||
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils
|
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils
|
||||||
import org.jetbrains.kotlin.utils.Printer
|
import org.jetbrains.kotlin.utils.Printer
|
||||||
|
|
||||||
@@ -68,7 +70,7 @@ private fun resolvePossiblyAmbiguousCallableReference(
|
|||||||
callResolver: CallResolver
|
callResolver: CallResolver
|
||||||
): OverloadResolutionResults<CallableDescriptor> {
|
): OverloadResolutionResults<CallableDescriptor> {
|
||||||
val call = CallMaker.makeCall(reference, receiver, null, reference, emptyList())
|
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)
|
val newContext = if (resolutionMode == ResolveArgumentsMode.SHAPE_FUNCTION_ARGUMENTS)
|
||||||
context.replaceTraceAndCache(temporaryTrace).replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE)
|
context.replaceTraceAndCache(temporaryTrace).replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE)
|
||||||
else
|
else
|
||||||
@@ -84,12 +86,12 @@ private fun OverloadResolutionResults<*>.isSomething(): Boolean = !isNothing
|
|||||||
|
|
||||||
fun resolvePossiblyAmbiguousCallableReference(
|
fun resolvePossiblyAmbiguousCallableReference(
|
||||||
callableReferenceExpression: KtCallableReferenceExpression,
|
callableReferenceExpression: KtCallableReferenceExpression,
|
||||||
lhsType: KotlinType?,
|
lhs: DoubleColonLHS?,
|
||||||
context: ResolutionContext<*>,
|
context: ResolutionContext<*>,
|
||||||
resolutionMode: ResolveArgumentsMode,
|
resolutionMode: ResolveArgumentsMode,
|
||||||
callResolver: CallResolver
|
callResolver: CallResolver
|
||||||
): OverloadResolutionResults<CallableDescriptor>? {
|
): OverloadResolutionResults<CallableDescriptor>? {
|
||||||
val reference = callableReferenceExpression.getCallableReference()
|
val reference = callableReferenceExpression.callableReference
|
||||||
|
|
||||||
fun resolveInScope(traceTitle: String, classifier: ClassifierDescriptor, staticScope: MemberScope): OverloadResolutionResults<CallableDescriptor> {
|
fun resolveInScope(traceTitle: String, classifier: ClassifierDescriptor, staticScope: MemberScope): OverloadResolutionResults<CallableDescriptor> {
|
||||||
|
|
||||||
@@ -124,27 +126,36 @@ fun resolvePossiblyAmbiguousCallableReference(
|
|||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lhsType == null) {
|
val lhsType = lhs?.type ?: return resolvePossiblyAmbiguousCallableReference(reference, null, context, resolutionMode, callResolver)
|
||||||
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
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-3
@@ -307,9 +307,8 @@ class DoubleColonExpressionResolver(
|
|||||||
): Pair<DoubleColonLHS?, OverloadResolutionResults<*>?> {
|
): Pair<DoubleColonLHS?, OverloadResolutionResults<*>?> {
|
||||||
val lhsResult = expression.receiverExpression?.let { resolveDoubleColonLHS(it, expression, context) }
|
val lhsResult = expression.receiverExpression?.let { resolveDoubleColonLHS(it, expression, context) }
|
||||||
|
|
||||||
val resolutionResults = resolvePossiblyAmbiguousCallableReference(
|
val resolutionResults =
|
||||||
expression, lhsResult?.type, context, resolveArgumentsMode, callResolver
|
resolvePossiblyAmbiguousCallableReference(expression, lhsResult, context, resolveArgumentsMode, callResolver)
|
||||||
)
|
|
||||||
|
|
||||||
return lhsResult to resolutionResults
|
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);
|
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")
|
@TestMetadata("object.kt")
|
||||||
public void testObject() throws Exception {
|
public void testObject() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/callableReference/bound/object.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/callableReference/bound/object.kt");
|
||||||
doTest(fileName);
|
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")
|
@TestMetadata("compiler/testData/diagnostics/tests/callableReference/function")
|
||||||
|
|||||||
Reference in New Issue
Block a user