diff --git a/ChangeLog.md b/ChangeLog.md index 8765fc451bc..0b507acf561 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -195,6 +195,7 @@ These artifacts include extensions for the types available in the latter JDKs, s - Show "Variables and values captured in a closure" highlighting only for usages - [`KT-13838`](https://youtrack.jetbrains.com/issue/KT-13838) Add file name to the presentation of private top-level declaration (Go to symbol, etc.) - [`KT-14096`](https://youtrack.jetbrains.com/issue/KT-14096) Rename: When renaming Kotlin file outside of source root do not rename its namesake in a source root +- [`KT-13928`](https://youtrack.jetbrains.com/issue/KT-13928) Move Inner Class to Upper Level: Fix replacement of outer class instances used in inner class constructor calls #### Intention actions, inspections and quickfixes diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/KotlinImplicitThisToParameterUsage.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/KotlinImplicitThisToParameterUsage.kt index 4d3a7dddaa3..e24ee00a104 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/KotlinImplicitThisToParameterUsage.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/KotlinImplicitThisToParameterUsage.kt @@ -61,16 +61,18 @@ class KotlinImplicitThisUsage( callElement: KtElement, val targetDescriptor: DeclarationDescriptor ): KotlinImplicitReceiverUsage(callElement) { - override fun getNewReceiverText(): String { - val name = targetDescriptor.name - return when { - name.isSpecial -> "this" - DescriptorUtils.isCompanionObject(targetDescriptor) -> IdeDescriptorRenderers.SOURCE_CODE.renderClassifierName(targetDescriptor as ClassifierDescriptor) - else -> "this@${name.asString()}" - } - } + override fun getNewReceiverText() = explicateReceiverOf(targetDescriptor) override fun processReplacedElement(element: KtElement) { element.addToShorteningWaitSet(Options(removeThisLabels = true, removeThis = true)) } +} + +fun explicateReceiverOf(descriptor: DeclarationDescriptor): String { + val name = descriptor.name + return when { + name.isSpecial -> "this" + DescriptorUtils.isCompanionObject(descriptor) -> IdeDescriptorRenderers.SOURCE_CODE.renderClassifierName(descriptor as ClassifierDescriptor) + else -> "this@${name.asString()}" + } } \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/MoveJavaInnerClassKotlinUsagesHandler.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/MoveJavaInnerClassKotlinUsagesHandler.kt index b23654cf847..3edaceb07c2 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/MoveJavaInnerClassKotlinUsagesHandler.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/MoveJavaInnerClassKotlinUsagesHandler.kt @@ -21,42 +21,28 @@ import com.intellij.psi.PsiWhiteSpace import com.intellij.psi.util.PsiTreeUtil import com.intellij.refactoring.move.moveInner.MoveInnerClassUsagesHandler import com.intellij.usageView.UsageInfo -import org.jetbrains.kotlin.idea.references.mainReference -import org.jetbrains.kotlin.idea.references.matchesTarget import org.jetbrains.kotlin.psi.KtCallExpression import org.jetbrains.kotlin.psi.KtPsiFactory -import org.jetbrains.kotlin.psi.KtQualifiedExpression -import org.jetbrains.kotlin.psi.KtSimpleNameExpression -import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElementSelector -import java.util.* +import org.jetbrains.kotlin.psi.KtValueArgumentList +import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelector class MoveJavaInnerClassKotlinUsagesHandler: MoveInnerClassUsagesHandler { override fun correctInnerClassUsage(usage: UsageInfo, outerClass: PsiClass) { val innerCall = usage.element?.parent as? KtCallExpression ?: return - - val receiver = (innerCall.parent as? KtQualifiedExpression)?.receiverExpression ?: return - val outerClassRefExpr = when (receiver) { - is KtCallExpression -> receiver.calleeExpression - is KtQualifiedExpression -> receiver.getQualifiedElementSelector() - else -> null - } as? KtSimpleNameExpression - val outerClassRef = outerClassRefExpr?.mainReference ?: return - if (!outerClassRef.matchesTarget(outerClass)) return + val receiverExpression = innerCall.getQualifiedExpressionForSelector()?.receiverExpression val psiFactory = KtPsiFactory(usage.project) - val argumentList = innerCall.valueArgumentList - if (argumentList != null) { - val newArguments = ArrayList() - newArguments.add(receiver.text!!) - argumentList.arguments.mapTo(newArguments) { it.text!! } - argumentList.replace(psiFactory.createCallArguments(newArguments.joinToString(prefix = "(", postfix = ")"))) - } - else { - innerCall.lambdaArguments.firstOrNull()?.let { lambdaArg -> - val anchor = PsiTreeUtil.skipSiblingsBackward(lambdaArg, PsiWhiteSpace::class.java) - innerCall.addAfter(psiFactory.createCallArguments("(${receiver.text})"), anchor) - } - } + val argumentToAdd = psiFactory.createArgument(receiverExpression ?: psiFactory.createExpression("this")) + + val argumentList = + innerCall.valueArgumentList + ?: (innerCall.lambdaArguments.firstOrNull()?.let { lambdaArg -> + val anchor = PsiTreeUtil.skipSiblingsBackward(lambdaArg, PsiWhiteSpace::class.java) + innerCall.addAfter(psiFactory.createCallArguments("()"), anchor) + } as KtValueArgumentList?) + ?: return + + argumentList.addArgumentAfter(argumentToAdd, null) } } \ No newline at end of file diff --git a/idea/testData/refactoring/move/kotlin/moveNestedClass/innerToTopLevelAddInstanceParam/after/InnerClass.kt b/idea/testData/refactoring/move/kotlin/moveNestedClass/innerToTopLevelAddInstanceParam/after/InnerClass.kt new file mode 100644 index 00000000000..13fb42971c4 --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveNestedClass/innerToTopLevelAddInstanceParam/after/InnerClass.kt @@ -0,0 +1,3 @@ +class InnerClass(private val test: Test) { + fun fun1() {} +} \ No newline at end of file diff --git a/idea/testData/refactoring/move/kotlin/moveNestedClass/innerToTopLevelAddInstanceParam/after/test.kt b/idea/testData/refactoring/move/kotlin/moveNestedClass/innerToTopLevelAddInstanceParam/after/test.kt new file mode 100644 index 00000000000..3155f9d7118 --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveNestedClass/innerToTopLevelAddInstanceParam/after/test.kt @@ -0,0 +1,14 @@ +class Test { + + val test = InnerClass(this) +} + +fun main(args: Array) { + val test = Test() + val innerClass = InnerClass(test) + innerClass.fun1() +} + +fun Test.foo() { + InnerClass(this) +} \ No newline at end of file diff --git a/idea/testData/refactoring/move/kotlin/moveNestedClass/innerToTopLevelAddInstanceParam/before/test.kt b/idea/testData/refactoring/move/kotlin/moveNestedClass/innerToTopLevelAddInstanceParam/before/test.kt new file mode 100644 index 00000000000..987688da02a --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveNestedClass/innerToTopLevelAddInstanceParam/before/test.kt @@ -0,0 +1,17 @@ +class Test { + inner class InnerClass { + fun fun1() {} + } + + val test = InnerClass() +} + +fun main(args: Array) { + val test = Test() + val innerClass = test.InnerClass() + innerClass.fun1() +} + +fun Test.foo() { + InnerClass() +} \ No newline at end of file diff --git a/idea/testData/refactoring/move/kotlin/moveNestedClass/innerToTopLevelAddInstanceParam/innerToTopLevelAddInstanceParam.test b/idea/testData/refactoring/move/kotlin/moveNestedClass/innerToTopLevelAddInstanceParam/innerToTopLevelAddInstanceParam.test new file mode 100644 index 00000000000..196c5326c68 --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveNestedClass/innerToTopLevelAddInstanceParam/innerToTopLevelAddInstanceParam.test @@ -0,0 +1,6 @@ +{ + "mainFile": "test.kt", + "type": "MOVE_KOTLIN_NESTED_CLASS", + "withRuntime": "true", + "outerInstanceParameter": "test" +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/move/MoveTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/refactoring/move/MoveTestGenerated.java index 91b11bb8a21..95d17104bb5 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/move/MoveTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/move/MoveTestGenerated.java @@ -317,6 +317,12 @@ public class MoveTestGenerated extends AbstractMoveTest { doTest(fileName); } + @TestMetadata("kotlin/moveNestedClass/innerToTopLevelAddInstanceParam/innerToTopLevelAddInstanceParam.test") + public void testKotlin_moveNestedClass_innerToTopLevelAddInstanceParam_InnerToTopLevelAddInstanceParam() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/move/kotlin/moveNestedClass/innerToTopLevelAddInstanceParam/innerToTopLevelAddInstanceParam.test"); + doTest(fileName); + } + @TestMetadata("kotlin/moveNestedClass/innerToTopLevelNoThis/innerToTopLevelNoThis.test") public void testKotlin_moveNestedClass_innerToTopLevelNoThis_InnerToTopLevelNoThis() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/move/kotlin/moveNestedClass/innerToTopLevelNoThis/innerToTopLevelNoThis.test");