Check extension receiver properly for property references
Without this, the unrelated type specified on the LHS of a property reference literal was considered to be an extension receiver of the candidate, and the resolution was erroneously successul. This is only reproducible for properties, because if we're trying to resolve an extension, we consider all properties from the scope, even non-extensions, because there may be a property of an extension-functional type (T.() -> R). (We don't do this for functions.) #KT-7430 Fixed #KT-7945 Fixed
This commit is contained in:
@@ -41,10 +41,6 @@ import org.jetbrains.kotlin.lexer.JetTokens;
|
|||||||
import org.jetbrains.kotlin.name.Name;
|
import org.jetbrains.kotlin.name.Name;
|
||||||
import org.jetbrains.kotlin.psi.*;
|
import org.jetbrains.kotlin.psi.*;
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilPackage;
|
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilPackage;
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
|
||||||
import org.jetbrains.kotlin.resolve.BindingContextUtils;
|
|
||||||
import org.jetbrains.kotlin.resolve.BindingTrace;
|
|
||||||
import org.jetbrains.kotlin.resolve.CompileTimeConstantUtils;
|
|
||||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer;
|
import org.jetbrains.kotlin.renderer.DescriptorRenderer;
|
||||||
import org.jetbrains.kotlin.resolve.*;
|
import org.jetbrains.kotlin.resolve.*;
|
||||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.BindingContextUtilPackage;
|
import org.jetbrains.kotlin.resolve.bindingContextUtil.BindingContextUtilPackage;
|
||||||
@@ -277,6 +273,8 @@ public class JetControlFlowProcessor {
|
|||||||
);
|
);
|
||||||
if (!status.isSuccess()) continue;
|
if (!status.isSuccess()) continue;
|
||||||
|
|
||||||
|
if ((candidate.getExtensionReceiverParameter() == null) == candidateCall.getExtensionReceiver().exists()) continue;
|
||||||
|
|
||||||
Map<ValueParameterDescriptor, ResolvedValueArgument> candidateArgumentMap = candidateCall.getValueArguments();
|
Map<ValueParameterDescriptor, ResolvedValueArgument> candidateArgumentMap = candidateCall.getValueArguments();
|
||||||
List<? extends ValueArgument> callArguments = call.getValueArguments();
|
List<? extends ValueArgument> callArguments = call.getValueArguments();
|
||||||
for (int i = 0; i < callArguments.size(); i++) {
|
for (int i = 0; i < callArguments.size(); i++) {
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||||
import org.jetbrains.kotlin.descriptors.*;
|
import org.jetbrains.kotlin.descriptors.*;
|
||||||
import org.jetbrains.kotlin.psi.*;
|
import org.jetbrains.kotlin.psi.*;
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilPackage;
|
|
||||||
import org.jetbrains.kotlin.resolve.*;
|
import org.jetbrains.kotlin.resolve.*;
|
||||||
import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilPackage;
|
import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilPackage;
|
||||||
import org.jetbrains.kotlin.resolve.calls.context.*;
|
import org.jetbrains.kotlin.resolve.calls.context.*;
|
||||||
@@ -104,18 +103,16 @@ public class CandidateResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (task.checkArguments == CheckValueArgumentsMode.ENABLED) {
|
if (task.checkArguments == CheckValueArgumentsMode.ENABLED) {
|
||||||
Set<ValueArgument> unmappedArguments = Sets.newLinkedHashSet();
|
|
||||||
ValueArgumentsToParametersMapper.Status argumentMappingStatus = ValueArgumentsToParametersMapper.mapValueArgumentsToParameters(
|
ValueArgumentsToParametersMapper.Status argumentMappingStatus = ValueArgumentsToParametersMapper.mapValueArgumentsToParameters(
|
||||||
context.call, context.tracing, candidateCall, unmappedArguments);
|
context.call, context.tracing, candidateCall, Sets.<ValueArgument>newLinkedHashSet()
|
||||||
|
);
|
||||||
if (!argumentMappingStatus.isSuccess()) {
|
if (!argumentMappingStatus.isSuccess()) {
|
||||||
if (argumentMappingStatus == ValueArgumentsToParametersMapper.Status.STRONG_ERROR) {
|
candidateCall.addStatus(OTHER_ERROR);
|
||||||
candidateCall.addStatus(RECEIVER_PRESENCE_ERROR);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
candidateCall.addStatus(OTHER_ERROR);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkExtensionReceiver(context);
|
||||||
|
|
||||||
if (!checkDispatchReceiver(context)) {
|
if (!checkDispatchReceiver(context)) {
|
||||||
candidateCall.addStatus(OTHER_ERROR);
|
candidateCall.addStatus(OTHER_ERROR);
|
||||||
}
|
}
|
||||||
@@ -174,6 +171,25 @@ public class CandidateResolver {
|
|||||||
checkNonExtensionCalledWithReceiver(context);
|
checkNonExtensionCalledWithReceiver(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static <D extends CallableDescriptor> void checkExtensionReceiver(@NotNull CallCandidateResolutionContext<D> context) {
|
||||||
|
MutableResolvedCall<D> candidateCall = context.candidateCall;
|
||||||
|
ReceiverParameterDescriptor receiverParameter = candidateCall.getCandidateDescriptor().getExtensionReceiverParameter();
|
||||||
|
ReceiverValue receiverArgument = candidateCall.getExtensionReceiver();
|
||||||
|
if (receiverParameter != null &&!receiverArgument.exists()) {
|
||||||
|
context.tracing.missingReceiver(candidateCall.getTrace(), receiverParameter);
|
||||||
|
candidateCall.addStatus(OTHER_ERROR);
|
||||||
|
}
|
||||||
|
if (receiverParameter == null && receiverArgument.exists()) {
|
||||||
|
context.tracing.noReceiverAllowed(candidateCall.getTrace());
|
||||||
|
if (context.call.getCalleeExpression() instanceof JetSimpleNameExpression) {
|
||||||
|
candidateCall.addStatus(RECEIVER_PRESENCE_ERROR);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
candidateCall.addStatus(OTHER_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean checkDispatchReceiver(@NotNull CallCandidateResolutionContext<?> context) {
|
private static boolean checkDispatchReceiver(@NotNull CallCandidateResolutionContext<?> context) {
|
||||||
MutableResolvedCall<? extends CallableDescriptor> candidateCall = context.candidateCall;
|
MutableResolvedCall<? extends CallableDescriptor> candidateCall = context.candidateCall;
|
||||||
CallableDescriptor candidateDescriptor = candidateCall.getCandidateDescriptor();
|
CallableDescriptor candidateDescriptor = candidateCall.getCandidateDescriptor();
|
||||||
|
|||||||
+1
-27
@@ -23,7 +23,6 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor;
|
import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor;
|
||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor;
|
import org.jetbrains.kotlin.descriptors.CallableDescriptor;
|
||||||
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor;
|
|
||||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor;
|
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor;
|
||||||
import org.jetbrains.kotlin.diagnostics.Diagnostic;
|
import org.jetbrains.kotlin.diagnostics.Diagnostic;
|
||||||
import org.jetbrains.kotlin.name.Name;
|
import org.jetbrains.kotlin.name.Name;
|
||||||
@@ -31,7 +30,6 @@ import org.jetbrains.kotlin.psi.*;
|
|||||||
import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilPackage;
|
import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilPackage;
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.*;
|
import org.jetbrains.kotlin.resolve.calls.model.*;
|
||||||
import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy;
|
import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy;
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -46,14 +44,13 @@ import static org.jetbrains.kotlin.resolve.calls.ValueArgumentsToParametersMappe
|
|||||||
public class ValueArgumentsToParametersMapper {
|
public class ValueArgumentsToParametersMapper {
|
||||||
|
|
||||||
public enum Status {
|
public enum Status {
|
||||||
STRONG_ERROR(false),
|
|
||||||
ERROR(false),
|
ERROR(false),
|
||||||
WEAK_ERROR(false),
|
WEAK_ERROR(false),
|
||||||
OK(true);
|
OK(true);
|
||||||
|
|
||||||
private final boolean success;
|
private final boolean success;
|
||||||
|
|
||||||
private Status(boolean success) {
|
Status(boolean success) {
|
||||||
this.success = success;
|
this.success = success;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,7 +59,6 @@ public class ValueArgumentsToParametersMapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Status compose(Status other) {
|
public Status compose(Status other) {
|
||||||
if (this == STRONG_ERROR || other == STRONG_ERROR) return STRONG_ERROR;
|
|
||||||
if (this == ERROR || other == ERROR) return ERROR;
|
if (this == ERROR || other == ERROR) return ERROR;
|
||||||
if (this == WEAK_ERROR || other == WEAK_ERROR) return WEAK_ERROR;
|
if (this == WEAK_ERROR || other == WEAK_ERROR) return WEAK_ERROR;
|
||||||
return this;
|
return this;
|
||||||
@@ -223,7 +219,6 @@ public class ValueArgumentsToParametersMapper {
|
|||||||
|
|
||||||
processFunctionLiteralArguments();
|
processFunctionLiteralArguments();
|
||||||
reportUnmappedParameters();
|
reportUnmappedParameters();
|
||||||
checkReceiverArgument();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processFunctionLiteralArguments() {
|
private void processFunctionLiteralArguments() {
|
||||||
@@ -265,7 +260,6 @@ public class ValueArgumentsToParametersMapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void reportUnmappedParameters() {
|
private void reportUnmappedParameters() {
|
||||||
|
|
||||||
List<ValueParameterDescriptor> valueParameters = candidateCall.getCandidateDescriptor().getValueParameters();
|
List<ValueParameterDescriptor> valueParameters = candidateCall.getCandidateDescriptor().getValueParameters();
|
||||||
for (ValueParameterDescriptor valueParameter : valueParameters) {
|
for (ValueParameterDescriptor valueParameter : valueParameters) {
|
||||||
if (!usedParameters.contains(valueParameter)) {
|
if (!usedParameters.contains(valueParameter)) {
|
||||||
@@ -283,26 +277,6 @@ public class ValueArgumentsToParametersMapper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkReceiverArgument() {
|
|
||||||
D candidate = candidateCall.getCandidateDescriptor();
|
|
||||||
|
|
||||||
ReceiverParameterDescriptor receiverParameter = candidate.getExtensionReceiverParameter();
|
|
||||||
ReceiverValue receiverArgument = candidateCall.getExtensionReceiver();
|
|
||||||
if (receiverParameter != null &&!receiverArgument.exists()) {
|
|
||||||
tracing.missingReceiver(candidateCall.getTrace(), receiverParameter);
|
|
||||||
setStatus(ERROR);
|
|
||||||
}
|
|
||||||
if (receiverParameter == null && receiverArgument.exists()) {
|
|
||||||
tracing.noReceiverAllowed(candidateCall.getTrace());
|
|
||||||
if (call.getCalleeExpression() instanceof JetSimpleNameExpression) {
|
|
||||||
setStatus(STRONG_ERROR);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
setStatus(ERROR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void putVararg(
|
private void putVararg(
|
||||||
ValueParameterDescriptor valueParameterDescriptor,
|
ValueParameterDescriptor valueParameterDescriptor,
|
||||||
ValueArgument valueArgument
|
ValueArgument valueArgument
|
||||||
|
|||||||
Vendored
+4
-4
@@ -4,12 +4,12 @@ class A {
|
|||||||
fun A.extA(x: String) = x
|
fun A.extA(x: String) = x
|
||||||
|
|
||||||
fun main() {
|
fun main() {
|
||||||
::<!EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED!>extInt<!>
|
::<!MISSING_RECEIVER, EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED!>extInt<!>
|
||||||
::<!EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED!>extA<!>
|
::<!EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED!>extA<!>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun main() {
|
fun main() {
|
||||||
A::<!EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED!>extInt<!>
|
A::<!MISSING_RECEIVER, EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED!>extInt<!>
|
||||||
A::<!EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED!>extA<!>
|
A::<!MISSING_RECEIVER, EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED!>extA<!>
|
||||||
}
|
}
|
||||||
|
|||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
// !DIAGNOSTICS: -UNUSED_EXPRESSION
|
||||||
|
|
||||||
|
class Unrelated()
|
||||||
|
|
||||||
|
class Test(val name: String = "") {
|
||||||
|
init {
|
||||||
|
Unrelated::<!UNRESOLVED_REFERENCE!>name<!>
|
||||||
|
Unrelated::<!UNRESOLVED_REFERENCE!>foo<!>
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo() {}
|
||||||
|
}
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
internal final class Test {
|
||||||
|
public constructor Test(/*0*/ name: kotlin.String = ...)
|
||||||
|
internal final val name: kotlin.String
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
internal final fun foo(): kotlin.Unit
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
internal final class Unrelated {
|
||||||
|
public constructor Unrelated()
|
||||||
|
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
|
||||||
|
}
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||||
|
|
||||||
|
import kotlin.reflect.KMemberProperty
|
||||||
|
|
||||||
|
class TestClass(var prop: Int)
|
||||||
|
open class OtherClass
|
||||||
|
fun OtherClass.test(prop: KMemberProperty<TestClass, Int>): Unit = throw Exception()
|
||||||
|
class OtherClass2: OtherClass() {
|
||||||
|
val result = test(TestClass::<!UNRESOLVED_REFERENCE!>result<!>)
|
||||||
|
}
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
internal fun OtherClass.test(/*0*/ prop: kotlin.reflect.KMemberProperty<TestClass, kotlin.Int>): kotlin.Unit
|
||||||
|
|
||||||
|
internal open class OtherClass {
|
||||||
|
public constructor OtherClass()
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
internal final class OtherClass2 : OtherClass {
|
||||||
|
public constructor OtherClass2()
|
||||||
|
internal final val result: kotlin.Unit
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
internal final class TestClass {
|
||||||
|
public constructor TestClass(/*0*/ prop: kotlin.Int)
|
||||||
|
internal final var prop: kotlin.Int
|
||||||
|
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
|
||||||
|
}
|
||||||
@@ -1122,6 +1122,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
|
|||||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/callableReference"), Pattern.compile("^(.+)\\.kt$"), true);
|
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/callableReference"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt7430_wrongClassOnLHS.kt")
|
||||||
|
public void testKt7430_wrongClassOnLHS() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/callableReference/kt7430_wrongClassOnLHS.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("unused.kt")
|
@TestMetadata("unused.kt")
|
||||||
public void testUnused() throws Exception {
|
public void testUnused() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/callableReference/unused.kt");
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/callableReference/unused.kt");
|
||||||
@@ -1505,6 +1511,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt7945_unrelatedClass.kt")
|
||||||
|
public void testKt7945_unrelatedClass() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/callableReference/property/kt7945_unrelatedClass.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("localVariable.kt")
|
@TestMetadata("localVariable.kt")
|
||||||
public void testLocalVariable() throws Exception {
|
public void testLocalVariable() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/callableReference/property/localVariable.kt");
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/callableReference/property/localVariable.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user