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:
Alexander Udalov
2016-06-15 19:24:46 +03:00
parent 6562a2db19
commit 456ba79793
11 changed files with 180 additions and 25 deletions
@@ -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
}
@@ -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
}
@@ -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
}
@@ -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<!>
}
@@ -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>
}
@@ -0,0 +1,3 @@
fun <T: Any> get(t: T): () -> String {
return t::toString
}
@@ -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")