From 2c7f68ff712d3ce3e29995a57f7b3cf002b7bbd8 Mon Sep 17 00:00:00 2001 From: Yan Zhulanow Date: Tue, 22 Sep 2015 16:50:22 +0300 Subject: [PATCH] Member and extension functions with 'operator' have now higher priority --- .../kotlin/resolve/calls/CallResolverUtil.kt | 18 +++++---- .../resolve/calls/tasks/TaskPrioritizer.kt | 35 +++++++++++++---- .../resolve/validation/OperatorValidator.kt | 2 +- .../modifiers/operator/LocalFunctions.kt | 33 ++++++++++++++++ .../modifiers/operator/LocalFunctions.txt | 17 +++++++++ .../modifiers/operator/MemberFunctions.kt | 36 ++++++++++++++++++ .../modifiers/operator/MemberFunctions.txt | 21 ++++++++++ .../tests/modifiers/operator/Simple.kt | 38 +++++++++++++++++++ .../tests/modifiers/operator/Simple.txt | 35 +++++++++++++++++ .../checkers/JetDiagnosticsTestGenerated.java | 27 +++++++++++++ 10 files changed, 246 insertions(+), 16 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/modifiers/operator/LocalFunctions.kt create mode 100644 compiler/testData/diagnostics/tests/modifiers/operator/LocalFunctions.txt create mode 100644 compiler/testData/diagnostics/tests/modifiers/operator/MemberFunctions.kt create mode 100644 compiler/testData/diagnostics/tests/modifiers/operator/MemberFunctions.txt create mode 100644 compiler/testData/diagnostics/tests/modifiers/operator/Simple.kt create mode 100644 compiler/testData/diagnostics/tests/modifiers/operator/Simple.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolverUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolverUtil.kt index abd936a2340..24835336bd0 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolverUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolverUtil.kt @@ -18,24 +18,19 @@ package org.jetbrains.kotlin.resolve.calls.callResolverUtil import com.google.common.collect.Lists import com.intellij.util.containers.ContainerUtil -import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.ReflectionTypes import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor -import org.jetbrains.kotlin.psi.Call -import org.jetbrains.kotlin.psi.JetSimpleNameExpression -import org.jetbrains.kotlin.psi.JetSuperExpression -import org.jetbrains.kotlin.psi.ValueArgument +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.calls.CallTransformer import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystem import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemImpl import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.EXPECTED_TYPE_POSITION import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.TypeUtils.DONT_CARE -import org.jetbrains.kotlin.types.typeUtil.getNestedArguments -import java.util.* public enum class ResolveArgumentsMode { RESOLVE_FUNCTION_ARGUMENTS, @@ -116,6 +111,15 @@ public fun isOrOverridesSynthesized(descriptor: CallableMemberDescriptor): Boole return false } + +fun isConventionCall(call: Call): Boolean { + if (call is CallTransformer.CallForImplicitInvoke) return true + val callElement = call.callElement + if (callElement is JetArrayAccessExpression || callElement is JetMultiDeclarationEntry) return true + val calleeExpression = call.calleeExpression as? JetOperationReferenceExpression ?: return false + return calleeExpression.getNameForConventionalOperation() != null +} + public fun isInvokeCallOnVariable(call: Call): Boolean { if (call.getCallType() !== Call.CallType.INVOKE) return false val dispatchReceiver = call.getDispatchReceiver() diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/TaskPrioritizer.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/TaskPrioritizer.kt index 48b91512940..072e5174388 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/TaskPrioritizer.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/TaskPrioritizer.kt @@ -21,10 +21,10 @@ import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.incremental.KotlinLookupLocation import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus -import org.jetbrains.kotlin.psi.Call -import org.jetbrains.kotlin.psi.JetFile -import org.jetbrains.kotlin.psi.doNotAnalyze +import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.calls.CallTransformer +import org.jetbrains.kotlin.resolve.calls.callResolverUtil.isConventionCall import org.jetbrains.kotlin.resolve.calls.callResolverUtil.isOrOverridesSynthesized import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext @@ -135,9 +135,20 @@ public class TaskPrioritizer( c: TaskPrioritizerContext, isExplicit: Boolean ) { + val explicitReceiverTypeIsDynamic = explicitReceiver.value.type.isDynamic() + + // Members and extensions with 'operator' modifier have higher priority + if (isConventionCall(c.context.call)) { + val filter = { d: CallableDescriptor -> (d is FunctionDescriptor && d.isOperator) || ErrorUtils.isError(d) } + addMembers(explicitReceiver, c, staticMembers = false, isExplicit = isExplicit, filter = filter) + if (!explicitReceiverTypeIsDynamic) { + addExtensionCandidates(explicitReceiver, implicitReceivers, c, isExplicit, filter) + } + } + addMembers(explicitReceiver, c, staticMembers = false, isExplicit = isExplicit) - if (explicitReceiver.value.type.isDynamic()) { + if (explicitReceiverTypeIsDynamic) { addCandidatesForDynamicReceiver(explicitReceiver, implicitReceivers, c, isExplicit) } else { @@ -149,7 +160,8 @@ public class TaskPrioritizer( explicitReceiver: ReceiverWithTypes, implicitReceivers: Collection, c: TaskPrioritizerContext, - isExplicit: Boolean + isExplicit: Boolean, + filter: ((CallableDescriptor) -> Boolean)? = null ) { for (callableDescriptorCollector in c.callableDescriptorCollectors) { //member extensions @@ -164,10 +176,14 @@ public class TaskPrioritizer( } //extensions c.result.addCandidates { + val extensions = callableDescriptorCollector.getExtensionsByName( + c.scope.asJetScope(), c.name, explicitReceiver.types, createLookupLocation(c)) + val filteredExtensions = if (filter == null) extensions else extensions.filter(filter) + convertWithImpliedThis( c.scope, explicitReceiver.value, - callableDescriptorCollector.getExtensionsByName(c.scope.asJetScope(), c.name, explicitReceiver.types, createLookupLocation(c)), + filteredExtensions, createKind(EXTENSION_RECEIVER, isExplicit), c.context.call ) @@ -179,7 +195,8 @@ public class TaskPrioritizer( explicitReceiver: ReceiverWithTypes, c: TaskPrioritizerContext, staticMembers: Boolean, - isExplicit: Boolean + isExplicit: Boolean, + filter: ((CallableDescriptor) -> Boolean)? = null ) { for (callableDescriptorCollector in c.callableDescriptorCollectors) { c.result.addCandidates { @@ -191,8 +208,10 @@ public class TaskPrioritizer( else { callableDescriptorCollector.getMembersByName(type, c.name, createLookupLocation(c)) } + val filteredMembers = if (filter == null) membersForThisVariant else membersForThisVariant.filter(filter) + convertWithReceivers( - membersForThisVariant, + filteredMembers, explicitReceiver.value, NO_RECEIVER, members, diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/validation/OperatorValidator.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/validation/OperatorValidator.kt index 631a198b8a0..084c2da7f9d 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/validation/OperatorValidator.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/validation/OperatorValidator.kt @@ -65,7 +65,7 @@ public class OperatorValidator : SymbolUsageValidator { return } - if (isVariableAsFunctionCall() || isMultiDeclaration() || isConventionOperator() || isArrayAccessExpression()) { + if (isVariableAsFunctionCall() || isConventionOperator() || isArrayAccessExpression()) { if (!functionDescriptor.isOperator) { report(jetElement, functionDescriptor, trace) } diff --git a/compiler/testData/diagnostics/tests/modifiers/operator/LocalFunctions.kt b/compiler/testData/diagnostics/tests/modifiers/operator/LocalFunctions.kt new file mode 100644 index 00000000000..36961845e23 --- /dev/null +++ b/compiler/testData/diagnostics/tests/modifiers/operator/LocalFunctions.kt @@ -0,0 +1,33 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER + +class Example + +fun Example.plus(other: Example) = 0 +operator fun Example.minus(other: Example) = 0 + +operator fun Example.times(other: Example) = 0 +fun Example.div(other: Example) = 0 + +fun a() { + with (Example()) { + operator fun Example.plus(other: Example) = "" + fun Example.minus(other: Example) = "" + + operator fun Example.times(other: Example) = "" + fun Example.div(other: Example) = "" + + with (Example()) { + val a = Example() + val b = Example() + consumeString(a + b) + consumeInt(a - b) + a * b + a / b + } + } +} + +public fun with(receiver: T, f: T.() -> R): R = receiver.f() + +fun consumeInt(i: Int) {} +fun consumeString(s: String) {} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/modifiers/operator/LocalFunctions.txt b/compiler/testData/diagnostics/tests/modifiers/operator/LocalFunctions.txt new file mode 100644 index 00000000000..15d6aef6922 --- /dev/null +++ b/compiler/testData/diagnostics/tests/modifiers/operator/LocalFunctions.txt @@ -0,0 +1,17 @@ +package + +public fun a(): kotlin.Unit +public fun consumeInt(/*0*/ i: kotlin.Int): kotlin.Unit +public fun consumeString(/*0*/ s: kotlin.String): kotlin.Unit +public fun with(/*0*/ receiver: T, /*1*/ f: T.() -> R): R +public fun Example.div(/*0*/ other: Example): kotlin.Int +public operator fun Example.minus(/*0*/ other: Example): kotlin.Int +public fun Example.plus(/*0*/ other: Example): kotlin.Int +public operator fun Example.times(/*0*/ other: Example): kotlin.Int + +public final class Example { + public constructor Example() + 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/modifiers/operator/MemberFunctions.kt b/compiler/testData/diagnostics/tests/modifiers/operator/MemberFunctions.kt new file mode 100644 index 00000000000..61736577102 --- /dev/null +++ b/compiler/testData/diagnostics/tests/modifiers/operator/MemberFunctions.kt @@ -0,0 +1,36 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER + +class Example { + operator fun plus(other: Example) = 0 + fun minus(other: Example) = 0 + + operator fun times(other: Example) = 0 + fun div(other: Example) = 0 +} + +fun Example.plus(other: Example) = "" +operator fun Example.minus(other: Example) = "" + +operator fun Example.times(other: Example) = "" +fun Example.div(other: Example) = "" + +fun a() { + val a = Example() + val b = Example() + a + b + a - b + a * b + a / b + + with (Example()) { + consumeInt(this + a) + consumeString(this - b) + consumeInt(this * a) + consumeInt(this / b) + } +} + +public fun with(receiver: T, f: T.() -> R): R = receiver.f() + +fun consumeInt(i: Int) {} +fun consumeString(s: String) {} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/modifiers/operator/MemberFunctions.txt b/compiler/testData/diagnostics/tests/modifiers/operator/MemberFunctions.txt new file mode 100644 index 00000000000..25f82655b67 --- /dev/null +++ b/compiler/testData/diagnostics/tests/modifiers/operator/MemberFunctions.txt @@ -0,0 +1,21 @@ +package + +public fun a(): kotlin.Unit +public fun consumeInt(/*0*/ i: kotlin.Int): kotlin.Unit +public fun consumeString(/*0*/ s: kotlin.String): kotlin.Unit +public fun with(/*0*/ receiver: T, /*1*/ f: T.() -> R): R +public fun Example.div(/*0*/ other: Example): kotlin.String +public operator fun Example.minus(/*0*/ other: Example): kotlin.String +public fun Example.plus(/*0*/ other: Example): kotlin.String +public operator fun Example.times(/*0*/ other: Example): kotlin.String + +public final class Example { + public constructor Example() + public final fun div(/*0*/ other: Example): 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 final fun minus(/*0*/ other: Example): kotlin.Int + public final operator fun plus(/*0*/ other: Example): kotlin.Int + public final operator fun times(/*0*/ other: Example): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/modifiers/operator/Simple.kt b/compiler/testData/diagnostics/tests/modifiers/operator/Simple.kt new file mode 100644 index 00000000000..62f7e6f603d --- /dev/null +++ b/compiler/testData/diagnostics/tests/modifiers/operator/Simple.kt @@ -0,0 +1,38 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER + +open class Example { + fun invoke() = 0 + fun get(i: Int) = 0 + + fun component1() = 0 + fun component2() = 0 + + fun inc() = Example() +} + +class Example2 : Example() + +operator fun Example.invoke() = "" +operator fun Example.get(i: Int) = "" + +operator fun Example.component1() = "" +operator fun Example.component2() = "" + +operator fun Example.inc() = Example2() + +fun a() { + var a = Example() + + consumeString(a()) + consumeString(a[1]) + + val (x, y) = Example() + consumeString(x) + consumeString(y) + + consumeExample2(++a) +} + +fun consumeInt(i: Int) {} +fun consumeString(s: String) {} +fun consumeExample2(e: Example2) {} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/modifiers/operator/Simple.txt b/compiler/testData/diagnostics/tests/modifiers/operator/Simple.txt new file mode 100644 index 00000000000..e0157555e0e --- /dev/null +++ b/compiler/testData/diagnostics/tests/modifiers/operator/Simple.txt @@ -0,0 +1,35 @@ +package + +public fun a(): kotlin.Unit +public fun consumeExample2(/*0*/ e: Example2): kotlin.Unit +public fun consumeInt(/*0*/ i: kotlin.Int): kotlin.Unit +public fun consumeString(/*0*/ s: kotlin.String): kotlin.Unit +public operator fun Example.component1(): kotlin.String +public operator fun Example.component2(): kotlin.String +public operator fun Example.get(/*0*/ i: kotlin.Int): kotlin.String +public operator fun Example.inc(): Example2 +public operator fun Example.invoke(): kotlin.String + +public open class Example { + public constructor Example() + public final fun component1(): kotlin.Int + public final fun component2(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun get(/*0*/ i: kotlin.Int): kotlin.Int + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public final fun inc(): Example + public final fun invoke(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class Example2 : Example { + public constructor Example2() + public final override /*1*/ /*fake_override*/ fun component1(): kotlin.Int + public final override /*1*/ /*fake_override*/ fun component2(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final override /*1*/ /*fake_override*/ fun get(/*0*/ i: kotlin.Int): kotlin.Int + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public final override /*1*/ /*fake_override*/ fun inc(): Example + public final override /*1*/ /*fake_override*/ fun invoke(): 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 17e3925f304..1ee8ee8f908 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java @@ -9188,6 +9188,33 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } } + + @TestMetadata("compiler/testData/diagnostics/tests/modifiers/operator") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Operator extends AbstractJetDiagnosticsTest { + public void testAllFilesPresentInOperator() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/modifiers/operator"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("LocalFunctions.kt") + public void testLocalFunctions() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/modifiers/operator/LocalFunctions.kt"); + doTest(fileName); + } + + @TestMetadata("MemberFunctions.kt") + public void testMemberFunctions() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/modifiers/operator/MemberFunctions.kt"); + doTest(fileName); + } + + @TestMetadata("Simple.kt") + public void testSimple() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/modifiers/operator/Simple.kt"); + doTest(fileName); + } + } } @TestMetadata("compiler/testData/diagnostics/tests/multimodule")