diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/KotlinChangeSignatureProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/KotlinChangeSignatureProcessor.kt index 231acb15331..b777f50ca3e 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/KotlinChangeSignatureProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/KotlinChangeSignatureProcessor.kt @@ -30,6 +30,8 @@ import com.intellij.refactoring.rename.UnresolvableCollisionUsageInfo import com.intellij.usageView.UsageInfo import com.intellij.usageView.UsageViewDescriptor import com.intellij.util.containers.MultiMap +import org.jetbrains.kotlin.idea.refactoring.changeSignature.usages.KotlinFunctionCallUsage +import org.jetbrains.kotlin.idea.refactoring.changeSignature.usages.KotlinImplicitReceiverUsage import org.jetbrains.kotlin.idea.refactoring.changeSignature.usages.KotlinUsageInfo import org.jetbrains.kotlin.idea.refactoring.changeSignature.usages.KotlinWrapperForJavaUsageInfos import java.util.* @@ -87,6 +89,8 @@ class KotlinChangeSignatureProcessor(project: Project, val usageArray = usagesSet.toTypedArray() Arrays.sort(usageArray) { u1, u2 -> + if (u1 is KotlinImplicitReceiverUsage && u2 is KotlinFunctionCallUsage) return@sort -1 + if (u2 is KotlinImplicitReceiverUsage && u1 is KotlinFunctionCallUsage) return@sort 1 val element1 = u1.element val element2 = u2.element val rank1 = if (element1 != null) element1.textOffset else -1 diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/KotlinFunctionCallUsage.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/KotlinFunctionCallUsage.kt index b06b6912446..25d17660c0b 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/KotlinFunctionCallUsage.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/KotlinFunctionCallUsage.kt @@ -298,6 +298,12 @@ class KotlinFunctionCallUsage( } } + private fun ExpressionReceiver.wrapInvalidated(element: KtCallElement): ExpressionReceiver { + return object: ExpressionReceiver by this { + override val expression = element.getQualifiedExpressionForSelector()!!.receiverExpression + } + } + private fun updateArgumentsAndReceiver(changeInfo: KotlinChangeInfo, element: KtCallElement, allUsages: Array) { if (isPropertyJavaUsage) return updateJavaPropertyCall(changeInfo, element) @@ -321,7 +327,12 @@ class KotlinFunctionCallUsage( val (index, param) = it val oldIndex = param.oldIndex val resolvedArgument = if (oldIndex >= 0) getResolvedValueArgument(oldIndex) else null - val receiverValue = if (param == originalReceiverInfo) extensionReceiver else null + var receiverValue = if (param == originalReceiverInfo) extensionReceiver else null + // Workaround for recursive calls where implicit extension receiver is transformed into ordinary value argument + // Receiver expression retained in the original resolved call is no longer valid at this point + if (receiverValue is ExpressionReceiver && !receiverValue.expression.isValid) { + receiverValue = receiverValue.wrapInvalidated(element) + } ArgumentInfo(param, index, resolvedArgument, receiverValue as? ReceiverValue) } diff --git a/idea/testData/refactoring/changeSignature/ImplicitReceiverInRecursiveCallAfter.kt b/idea/testData/refactoring/changeSignature/ImplicitReceiverInRecursiveCallAfter.kt new file mode 100644 index 00000000000..a59df497b09 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/ImplicitReceiverInRecursiveCallAfter.kt @@ -0,0 +1,5 @@ +interface A { + val parent: A +} + +fun ext(a: A): Int = 1 + ext(a.parent) \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/ImplicitReceiverInRecursiveCallBefore.kt b/idea/testData/refactoring/changeSignature/ImplicitReceiverInRecursiveCallBefore.kt new file mode 100644 index 00000000000..a786800f802 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/ImplicitReceiverInRecursiveCallBefore.kt @@ -0,0 +1,5 @@ +interface A { + val parent: A +} + +fun A.ext(): Int = 1 + parent.ext() \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/changeSignature/KotlinChangeSignatureTest.kt b/idea/tests/org/jetbrains/kotlin/idea/refactoring/changeSignature/KotlinChangeSignatureTest.kt index d9a19e2c25f..2ae21081ed6 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/changeSignature/KotlinChangeSignatureTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/changeSignature/KotlinChangeSignatureTest.kt @@ -944,4 +944,9 @@ class KotlinChangeSignatureTest : KotlinCodeInsightTestCase() { psiFactory.createExpression("5"), KotlinValVar.Val)) } + + fun testImplicitReceiverInRecursiveCall() = doTest { + receiverParameterInfo = null + newParameters[0].name = "a" + } } \ No newline at end of file