Move Inner Class to Upper Level: Fix replacement of outer class instances used in inner class constructor calls
#KT-13928 Fixed
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
+10
-8
@@ -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()}"
|
||||
}
|
||||
}
|
||||
+14
-28
@@ -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<String>()
|
||||
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)
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
class InnerClass(private val test: Test) {
|
||||
fun fun1() {}
|
||||
}
|
||||
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
class Test {
|
||||
|
||||
val test = InnerClass(this)
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val test = Test()
|
||||
val innerClass = InnerClass(test)
|
||||
innerClass.fun1()
|
||||
}
|
||||
|
||||
fun Test.foo() {
|
||||
InnerClass(this)
|
||||
}
|
||||
idea/testData/refactoring/move/kotlin/moveNestedClass/innerToTopLevelAddInstanceParam/before/test.kt
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
class Test {
|
||||
inner class <caret>InnerClass {
|
||||
fun fun1() {}
|
||||
}
|
||||
|
||||
val test = InnerClass()
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val test = Test()
|
||||
val innerClass = test.InnerClass()
|
||||
innerClass.fun1()
|
||||
}
|
||||
|
||||
fun Test.foo() {
|
||||
InnerClass()
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"mainFile": "test.kt",
|
||||
"type": "MOVE_KOTLIN_NESTED_CLASS",
|
||||
"withRuntime": "true",
|
||||
"outerInstanceParameter": "test"
|
||||
}
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user