From 0593b833b5b4a2dc55a778859b868e4e311c7ea5 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Mon, 22 Jun 2015 19:53:29 +0300 Subject: [PATCH] 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 --- .../kotlin/cfg/JetControlFlowProcessor.java | 6 ++-- .../resolve/calls/CandidateResolver.java | 34 ++++++++++++++----- .../ValueArgumentsToParametersMapper.java | 28 +-------------- .../function/extensionInClassDisallowed.kt | 8 ++--- .../kt7430_wrongClassOnLHS.kt | 12 +++++++ .../kt7430_wrongClassOnLHS.txt | 17 ++++++++++ .../property/kt7945_unrelatedClass.kt | 10 ++++++ .../property/kt7945_unrelatedClass.txt | 26 ++++++++++++++ .../checkers/JetDiagnosticsTestGenerated.java | 12 +++++++ 9 files changed, 109 insertions(+), 44 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/callableReference/kt7430_wrongClassOnLHS.kt create mode 100644 compiler/testData/diagnostics/tests/callableReference/kt7430_wrongClassOnLHS.txt create mode 100644 compiler/testData/diagnostics/tests/callableReference/property/kt7945_unrelatedClass.kt create mode 100644 compiler/testData/diagnostics/tests/callableReference/property/kt7945_unrelatedClass.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/JetControlFlowProcessor.java b/compiler/frontend/src/org/jetbrains/kotlin/cfg/JetControlFlowProcessor.java index ed174d69e55..f7d478e6a3b 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/JetControlFlowProcessor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/JetControlFlowProcessor.java @@ -41,10 +41,6 @@ import org.jetbrains.kotlin.lexer.JetTokens; import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.psi.*; 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.resolve.*; import org.jetbrains.kotlin.resolve.bindingContextUtil.BindingContextUtilPackage; @@ -277,6 +273,8 @@ public class JetControlFlowProcessor { ); if (!status.isSuccess()) continue; + if ((candidate.getExtensionReceiverParameter() == null) == candidateCall.getExtensionReceiver().exists()) continue; + Map candidateArgumentMap = candidateCall.getValueArguments(); List callArguments = call.getValueArguments(); for (int i = 0; i < callArguments.size(); i++) { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.java index b433ecfbd99..1d507a75c77 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.java @@ -26,7 +26,6 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.builtins.KotlinBuiltIns; import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.psi.*; -import org.jetbrains.kotlin.psi.psiUtil.PsiUtilPackage; import org.jetbrains.kotlin.resolve.*; import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilPackage; import org.jetbrains.kotlin.resolve.calls.context.*; @@ -104,18 +103,16 @@ public class CandidateResolver { } if (task.checkArguments == CheckValueArgumentsMode.ENABLED) { - Set unmappedArguments = Sets.newLinkedHashSet(); ValueArgumentsToParametersMapper.Status argumentMappingStatus = ValueArgumentsToParametersMapper.mapValueArgumentsToParameters( - context.call, context.tracing, candidateCall, unmappedArguments); + context.call, context.tracing, candidateCall, Sets.newLinkedHashSet() + ); if (!argumentMappingStatus.isSuccess()) { - if (argumentMappingStatus == ValueArgumentsToParametersMapper.Status.STRONG_ERROR) { - candidateCall.addStatus(RECEIVER_PRESENCE_ERROR); - } - else { - candidateCall.addStatus(OTHER_ERROR); - } + candidateCall.addStatus(OTHER_ERROR); } } + + checkExtensionReceiver(context); + if (!checkDispatchReceiver(context)) { candidateCall.addStatus(OTHER_ERROR); } @@ -174,6 +171,25 @@ public class CandidateResolver { checkNonExtensionCalledWithReceiver(context); } + private static void checkExtensionReceiver(@NotNull CallCandidateResolutionContext context) { + MutableResolvedCall 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) { MutableResolvedCall candidateCall = context.candidateCall; CallableDescriptor candidateDescriptor = candidateCall.getCandidateDescriptor(); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/ValueArgumentsToParametersMapper.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/ValueArgumentsToParametersMapper.java index 636ae99dd21..5aa7d4b7b91 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/ValueArgumentsToParametersMapper.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/ValueArgumentsToParametersMapper.java @@ -23,7 +23,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor; import org.jetbrains.kotlin.descriptors.CallableDescriptor; -import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor; import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor; import org.jetbrains.kotlin.diagnostics.Diagnostic; 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.model.*; import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy; -import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue; import java.util.List; import java.util.Map; @@ -46,14 +44,13 @@ import static org.jetbrains.kotlin.resolve.calls.ValueArgumentsToParametersMappe public class ValueArgumentsToParametersMapper { public enum Status { - STRONG_ERROR(false), ERROR(false), WEAK_ERROR(false), OK(true); private final boolean success; - private Status(boolean success) { + Status(boolean success) { this.success = success; } @@ -62,7 +59,6 @@ public class ValueArgumentsToParametersMapper { } public Status compose(Status other) { - if (this == STRONG_ERROR || other == STRONG_ERROR) return STRONG_ERROR; if (this == ERROR || other == ERROR) return ERROR; if (this == WEAK_ERROR || other == WEAK_ERROR) return WEAK_ERROR; return this; @@ -223,7 +219,6 @@ public class ValueArgumentsToParametersMapper { processFunctionLiteralArguments(); reportUnmappedParameters(); - checkReceiverArgument(); } private void processFunctionLiteralArguments() { @@ -265,7 +260,6 @@ public class ValueArgumentsToParametersMapper { } private void reportUnmappedParameters() { - List valueParameters = candidateCall.getCandidateDescriptor().getValueParameters(); for (ValueParameterDescriptor valueParameter : valueParameters) { 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( ValueParameterDescriptor valueParameterDescriptor, ValueArgument valueArgument diff --git a/compiler/testData/diagnostics/tests/callableReference/function/extensionInClassDisallowed.kt b/compiler/testData/diagnostics/tests/callableReference/function/extensionInClassDisallowed.kt index 82fd291a47e..03ce8655046 100644 --- a/compiler/testData/diagnostics/tests/callableReference/function/extensionInClassDisallowed.kt +++ b/compiler/testData/diagnostics/tests/callableReference/function/extensionInClassDisallowed.kt @@ -4,12 +4,12 @@ class A { fun A.extA(x: String) = x fun main() { - ::extInt + ::extInt ::extA } } fun main() { - A::extInt - A::extA -} \ No newline at end of file + A::extInt + A::extA +} diff --git a/compiler/testData/diagnostics/tests/callableReference/kt7430_wrongClassOnLHS.kt b/compiler/testData/diagnostics/tests/callableReference/kt7430_wrongClassOnLHS.kt new file mode 100644 index 00000000000..0abc63d8894 --- /dev/null +++ b/compiler/testData/diagnostics/tests/callableReference/kt7430_wrongClassOnLHS.kt @@ -0,0 +1,12 @@ +// !DIAGNOSTICS: -UNUSED_EXPRESSION + +class Unrelated() + +class Test(val name: String = "") { + init { + Unrelated::name + Unrelated::foo + } + + fun foo() {} +} diff --git a/compiler/testData/diagnostics/tests/callableReference/kt7430_wrongClassOnLHS.txt b/compiler/testData/diagnostics/tests/callableReference/kt7430_wrongClassOnLHS.txt new file mode 100644 index 00000000000..042bc3596c8 --- /dev/null +++ b/compiler/testData/diagnostics/tests/callableReference/kt7430_wrongClassOnLHS.txt @@ -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 +} diff --git a/compiler/testData/diagnostics/tests/callableReference/property/kt7945_unrelatedClass.kt b/compiler/testData/diagnostics/tests/callableReference/property/kt7945_unrelatedClass.kt new file mode 100644 index 00000000000..f9c917d5be8 --- /dev/null +++ b/compiler/testData/diagnostics/tests/callableReference/property/kt7945_unrelatedClass.kt @@ -0,0 +1,10 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER + +import kotlin.reflect.KMemberProperty + +class TestClass(var prop: Int) +open class OtherClass +fun OtherClass.test(prop: KMemberProperty): Unit = throw Exception() +class OtherClass2: OtherClass() { + val result = test(TestClass::result) +} diff --git a/compiler/testData/diagnostics/tests/callableReference/property/kt7945_unrelatedClass.txt b/compiler/testData/diagnostics/tests/callableReference/property/kt7945_unrelatedClass.txt new file mode 100644 index 00000000000..d99d7aa2d2c --- /dev/null +++ b/compiler/testData/diagnostics/tests/callableReference/property/kt7945_unrelatedClass.txt @@ -0,0 +1,26 @@ +package + +internal fun OtherClass.test(/*0*/ prop: kotlin.reflect.KMemberProperty): 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 +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java index 74f76731250..c2d72b1543a 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java @@ -1122,6 +1122,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { 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") public void testUnused() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/callableReference/unused.kt"); @@ -1505,6 +1511,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { 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") public void testLocalVariable() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/callableReference/property/localVariable.kt");