Member and extension functions with 'operator' have now higher priority
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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<D, F>,
|
||||
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<ReceiverValue>,
|
||||
c: TaskPrioritizerContext<D, F>,
|
||||
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<D, F>,
|
||||
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,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 <!OVERLOAD_RESOLUTION_AMBIGUITY!>*<!> b
|
||||
a <!OVERLOAD_RESOLUTION_AMBIGUITY!>/<!> b
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public fun <T, R> with(receiver: T, f: T.() -> R): R = receiver.f()
|
||||
|
||||
fun consumeInt(i: Int) {}
|
||||
fun consumeString(s: String) {}
|
||||
@@ -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 </*0*/ T, /*1*/ R> 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
|
||||
}
|
||||
@@ -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 <!OPERATOR_MODIFIER_REQUIRED!>/<!> b
|
||||
|
||||
with (Example()) {
|
||||
consumeInt(this + a)
|
||||
consumeString(this - b)
|
||||
consumeInt(this * a)
|
||||
consumeInt(this <!OPERATOR_MODIFIER_REQUIRED!>/<!> b)
|
||||
}
|
||||
}
|
||||
|
||||
public fun <T, R> with(receiver: T, f: T.() -> R): R = receiver.f()
|
||||
|
||||
fun consumeInt(i: Int) {}
|
||||
fun consumeString(s: String) {}
|
||||
@@ -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 </*0*/ T, /*1*/ R> 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
|
||||
}
|
||||
@@ -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) {}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user