diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/KotlinChangeSignatureUsageProcessor.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/KotlinChangeSignatureUsageProcessor.kt index d4ab6a935ee8..e956ba0a3019 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/KotlinChangeSignatureUsageProcessor.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/KotlinChangeSignatureUsageProcessor.kt @@ -71,8 +71,10 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.classId import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver +import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue import org.jetbrains.kotlin.resolve.source.getPsi import org.jetbrains.kotlin.util.OperatorNameConventions +import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull import org.jetbrains.kotlin.utils.addToStdlib.safeAs @@ -403,6 +405,33 @@ class KotlinChangeSignatureUsageProcessor : ChangeSignatureUsageProcessor { } } + private fun processThisCall( + expression: KtSimpleNameExpression, + context: BindingContext, + extensionReceiver: ReceiverValue?, + dispatchReceiver: ReceiverValue? + ) { + val thisExpression = expression.parent as? KtThisExpression ?: return + val thisCallExpression = thisExpression.parent as? KtCallExpression ?: return + val usageInfo = when (callableDescriptor) { + dispatchReceiver.getReceiverTargetDescriptor(context) -> { + val extensionReceiverTargetDescriptor = extensionReceiver.getReceiverTargetDescriptor(context) + if (extensionReceiverTargetDescriptor != null) { + KotlinNonQualifiedOuterThisCallUsage( + thisCallExpression, originalReceiverInfo!!, functionUsageInfo, extensionReceiverTargetDescriptor + ) + } else { + KotlinParameterUsage(expression, originalReceiverInfo!!, functionUsageInfo) + } + } + extensionReceiver.getReceiverTargetDescriptor(context) -> { + KotlinParameterUsage(expression, originalReceiverInfo!!, functionUsageInfo) + } + else -> null + } + result.addIfNotNull(usageInfo) + } + override fun visitSimpleNameExpression(expression: KtSimpleNameExpression, context: BindingContext): Void? { val resolvedCall = expression.getResolvedCall(context) ?: return null @@ -412,11 +441,16 @@ class KotlinChangeSignatureUsageProcessor : ChangeSignatureUsageProcessor { return null } - val receiverValue = resolvedCall + val (extensionReceiver, dispatchReceiver) = resolvedCall .let { (it as? VariableAsFunctionResolvedCall)?.variableCall ?: it } - .let { it.extensionReceiver ?: it.dispatchReceiver } - if (receiverValue is ImplicitReceiver) { - processImplicitThis(resolvedCall.call.callElement, receiverValue) + .let { (it.extensionReceiver to it.dispatchReceiver) } + if (extensionReceiver != null || dispatchReceiver != null) { + val receiverValue = extensionReceiver ?: dispatchReceiver + if (receiverValue is ImplicitReceiver) { + processImplicitThis(resolvedCall.call.callElement, receiverValue) + } else { + processThisCall(expression, context, extensionReceiver, dispatchReceiver) + } } return null diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/KotlinParameterUsage.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/KotlinParameterUsage.kt index 856dd44d588b..bc4164911047 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/KotlinParameterUsage.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/KotlinParameterUsage.kt @@ -11,10 +11,8 @@ import org.jetbrains.kotlin.idea.codeInsight.shorten.addToShorteningWaitSet import org.jetbrains.kotlin.idea.core.ShortenReferences.Options import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinChangeInfo import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinParameterInfo -import org.jetbrains.kotlin.psi.KtElement -import org.jetbrains.kotlin.psi.KtPsiFactory -import org.jetbrains.kotlin.psi.KtQualifiedExpression -import org.jetbrains.kotlin.psi.KtThisExpression +import org.jetbrains.kotlin.idea.refactoring.explicateAsTextForReceiver +import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.isIdentifier // Explicit reference to function parameter or outer this @@ -63,4 +61,18 @@ class KotlinNonQualifiedOuterThisUsage( } override fun getReplacementText(changeInfo: KotlinChangeInfo): String = "this@${targetDescriptor.name.asString()}" -} \ No newline at end of file +} + +class KotlinNonQualifiedOuterThisCallUsage( + element: KtCallExpression, + val parameterInfo: KotlinParameterInfo, + val containingCallable: KotlinCallableDefinitionUsage<*>, + val targetDescriptor: DeclarationDescriptor +) : KotlinExplicitReferenceUsage(element) { + override fun getReplacementText(changeInfo: KotlinChangeInfo): String { + val inheritedName = parameterInfo.getInheritedName(containingCallable) + val receiver = targetDescriptor.explicateAsTextForReceiver() + element?.calleeExpression?.replace(KtPsiFactory(changeInfo.context).createExpression(receiver)) + return "with($inheritedName) $inheritedName@{ ${element?.text} }" + } +} diff --git a/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index b6dee92d5f66..0fea76f88dce 100644 --- a/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -6454,6 +6454,11 @@ public abstract class IntentionTestGenerated extends AbstractIntentionTest { runTest("testData/intentions/convertReceiverToParameter/replaceThis.kt"); } + @TestMetadata("thisCall.kt") + public void testThisCall() throws Exception { + runTest("testData/intentions/convertReceiverToParameter/thisCall.kt"); + } + @TestMetadata("topLevelFun.kt") public void testTopLevelFun() throws Exception { runTest("testData/intentions/convertReceiverToParameter/topLevelFun.kt"); diff --git a/plugins/kotlin/idea/tests/testData/intentions/convertReceiverToParameter/callVariableAsFunction.kt b/plugins/kotlin/idea/tests/testData/intentions/convertReceiverToParameter/callVariableAsFunction.kt index 010e014e50fc..d16c0a23d9e8 100644 --- a/plugins/kotlin/idea/tests/testData/intentions/convertReceiverToParameter/callVariableAsFunction.kt +++ b/plugins/kotlin/idea/tests/testData/intentions/convertReceiverToParameter/callVariableAsFunction.kt @@ -9,6 +9,8 @@ class User( val callableLambda: () -> Unit = {} fun checkName() = Unit + + operator fun invoke() = Unit } fun User.extFun() { @@ -16,4 +18,5 @@ fun User.extFun() { checkName() callableLambda() callableObject() + this() } \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/intentions/convertReceiverToParameter/callVariableAsFunction.kt.after b/plugins/kotlin/idea/tests/testData/intentions/convertReceiverToParameter/callVariableAsFunction.kt.after index 3b34ab624a34..6c3354ed182e 100644 --- a/plugins/kotlin/idea/tests/testData/intentions/convertReceiverToParameter/callVariableAsFunction.kt.after +++ b/plugins/kotlin/idea/tests/testData/intentions/convertReceiverToParameter/callVariableAsFunction.kt.after @@ -9,6 +9,8 @@ class User( val callableLambda: () -> Unit = {} fun checkName() = Unit + + operator fun invoke() = Unit } fun extFun(user: User) { @@ -16,4 +18,5 @@ fun extFun(user: User) { user.checkName() user.callableLambda() user.callableObject() + user() } \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/intentions/convertReceiverToParameter/thisCall.kt b/plugins/kotlin/idea/tests/testData/intentions/convertReceiverToParameter/thisCall.kt new file mode 100644 index 000000000000..6127d46f18d4 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/intentions/convertReceiverToParameter/thisCall.kt @@ -0,0 +1,16 @@ +// WITH_RUNTIME +class CallableObject { + operator fun User.invoke() = println("User.invoke") +} + +class User { + operator fun CallableObject.invoke() = println("CallableObject.invoke") +} + +fun User.extFun() { + val callableObject = CallableObject() + with(callableObject) { + this() + this@extFun() + } +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/intentions/convertReceiverToParameter/thisCall.kt.after b/plugins/kotlin/idea/tests/testData/intentions/convertReceiverToParameter/thisCall.kt.after new file mode 100644 index 000000000000..7242598b900f --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/intentions/convertReceiverToParameter/thisCall.kt.after @@ -0,0 +1,16 @@ +// WITH_RUNTIME +class CallableObject { + operator fun User.invoke() = println("User.invoke") +} + +class User { + operator fun CallableObject.invoke() = println("CallableObject.invoke") +} + +fun extFun(user: User) { + val callableObject = CallableObject() + with(callableObject) { + with(user) user@{ this@with() } + user() + } +} \ No newline at end of file