Move: Fix processing of callable references to nested Kotlin classes
#KT-17827 In Progress
This commit is contained in:
@@ -209,6 +209,7 @@ class KtSimpleNameReference(expression: KtSimpleNameExpression) : KtSimpleRefere
|
|||||||
parent is KtCallableReferenceExpression && parent.callableReference == this -> {
|
parent is KtCallableReferenceExpression && parent.callableReference == this -> {
|
||||||
parentDelimiter = ""
|
parentDelimiter = ""
|
||||||
val callableRefCopy = parent.copied()
|
val callableRefCopy = parent.copied()
|
||||||
|
callableRefCopy.receiverExpression?.delete()
|
||||||
callableRefCopy.callableReference.replace(psiFactory.createSimpleName(shortName)).parent!!.text
|
callableRefCopy.callableReference.replace(psiFactory.createSimpleName(shortName)).parent!!.text
|
||||||
}
|
}
|
||||||
else -> shortName
|
else -> shortName
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.*
|
|||||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver
|
||||||
|
import org.jetbrains.kotlin.types.expressions.DoubleColonLHS
|
||||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@@ -259,6 +260,35 @@ class QualifiableMoveRenameUsageInfo(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface DeferredKotlinMoveUsage : KotlinMoveUsage {
|
||||||
|
fun resolve(newElement: PsiElement): UsageInfo?
|
||||||
|
}
|
||||||
|
|
||||||
|
class CallableReferenceMoveRenameUsageInfo(
|
||||||
|
element: PsiElement,
|
||||||
|
reference: PsiReference,
|
||||||
|
referencedElement: PsiElement,
|
||||||
|
val originalFile: PsiFile,
|
||||||
|
val addImportToOriginalFile: Boolean,
|
||||||
|
override val isInternal: Boolean
|
||||||
|
) : MoveRenameUsageInfo(element, reference, reference.rangeInElement.startOffset, reference.rangeInElement.endOffset, referencedElement, false), DeferredKotlinMoveUsage {
|
||||||
|
override fun refresh(refExpr: KtSimpleNameExpression, referencedElement: PsiElement): UsageInfo? {
|
||||||
|
return CallableReferenceMoveRenameUsageInfo(refExpr, refExpr.mainReference, referencedElement, originalFile, addImportToOriginalFile, isInternal)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun resolve(newElement: PsiElement): UsageInfo? {
|
||||||
|
val target = newElement.unwrapped
|
||||||
|
val element = element ?: return null
|
||||||
|
val reference = reference ?: return null
|
||||||
|
val referencedElement = referencedElement ?: return null
|
||||||
|
if (target is KtDeclaration && target.parent is KtFile) {
|
||||||
|
element.getStrictParentOfType<KtCallableReferenceExpression>()?.receiverExpression?.delete()
|
||||||
|
return UnqualifiableMoveRenameUsageInfo(element, reference, referencedElement, element.containingFile!!, addImportToOriginalFile, isInternal)
|
||||||
|
}
|
||||||
|
return QualifiableMoveRenameUsageInfo(element, reference, referencedElement, isInternal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun createMoveUsageInfoIfPossible(
|
fun createMoveUsageInfoIfPossible(
|
||||||
reference: PsiReference,
|
reference: PsiReference,
|
||||||
referencedElement: PsiElement,
|
referencedElement: PsiElement,
|
||||||
@@ -273,6 +303,9 @@ fun createMoveUsageInfoIfPossible(
|
|||||||
ReferenceKind.UNQUALIFIABLE -> UnqualifiableMoveRenameUsageInfo(
|
ReferenceKind.UNQUALIFIABLE -> UnqualifiableMoveRenameUsageInfo(
|
||||||
element, reference, referencedElement, element.containingFile!!, addImportToOriginalFile, isInternal
|
element, reference, referencedElement, element.containingFile!!, addImportToOriginalFile, isInternal
|
||||||
)
|
)
|
||||||
|
ReferenceKind.CALLABLE_REFERENCE -> CallableReferenceMoveRenameUsageInfo(
|
||||||
|
element, reference, referencedElement, element.containingFile!!, addImportToOriginalFile, isInternal
|
||||||
|
)
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -280,6 +313,7 @@ fun createMoveUsageInfoIfPossible(
|
|||||||
private enum class ReferenceKind {
|
private enum class ReferenceKind {
|
||||||
QUALIFIABLE,
|
QUALIFIABLE,
|
||||||
UNQUALIFIABLE,
|
UNQUALIFIABLE,
|
||||||
|
CALLABLE_REFERENCE,
|
||||||
IRRELEVANT
|
IRRELEVANT
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -300,7 +334,11 @@ private fun getReferenceKind(reference: PsiReference, referencedElement: PsiElem
|
|||||||
if (element.isExtensionRef() && reference.element.getNonStrictParentOfType<KtImportDirective>() == null) return ReferenceKind.UNQUALIFIABLE
|
if (element.isExtensionRef() && reference.element.getNonStrictParentOfType<KtImportDirective>() == null) return ReferenceKind.UNQUALIFIABLE
|
||||||
|
|
||||||
element.getParentOfTypeAndBranch<KtCallableReferenceExpression> { callableReference }?.let {
|
element.getParentOfTypeAndBranch<KtCallableReferenceExpression> { callableReference }?.let {
|
||||||
if (it.receiverExpression != null) return ReferenceKind.IRRELEVANT
|
val receiverExpression = it.receiverExpression
|
||||||
|
if (receiverExpression != null) {
|
||||||
|
val lhs = it.analyze(BodyResolveMode.PARTIAL)[BindingContext.DOUBLE_COLON_LHS, receiverExpression]
|
||||||
|
return if (lhs is DoubleColonLHS.Type) ReferenceKind.CALLABLE_REFERENCE else ReferenceKind.IRRELEVANT
|
||||||
|
}
|
||||||
if (target is KtDeclaration && target.parent is KtFile) return ReferenceKind.UNQUALIFIABLE
|
if (target is KtDeclaration && target.parent is KtFile) return ReferenceKind.UNQUALIFIABLE
|
||||||
if (target is PsiMember && target.containingClass == null) return ReferenceKind.UNQUALIFIABLE
|
if (target is PsiMember && target.containingClass == null) return ReferenceKind.UNQUALIFIABLE
|
||||||
}
|
}
|
||||||
@@ -364,20 +402,28 @@ private fun postProcessMoveUsage(
|
|||||||
nonCodeUsages: ArrayList<NonCodeUsageInfo>,
|
nonCodeUsages: ArrayList<NonCodeUsageInfo>,
|
||||||
shorteningMode: ShorteningMode
|
shorteningMode: ShorteningMode
|
||||||
) {
|
) {
|
||||||
|
if (usage is NonCodeUsageInfo) {
|
||||||
|
nonCodeUsages.add(usage)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (usage !is MoveRenameUsageInfo) return
|
||||||
|
|
||||||
|
val oldElement = usage.referencedElement!!
|
||||||
|
val newElement = mapToNewOrThis(oldElement, oldToNewElementsMapping)
|
||||||
|
|
||||||
when (usage) {
|
when (usage) {
|
||||||
is NonCodeUsageInfo -> {
|
is DeferredKotlinMoveUsage -> {
|
||||||
nonCodeUsages.add(usage)
|
val newUsage = usage.resolve(newElement) ?: return
|
||||||
|
postProcessMoveUsage(newUsage, oldToNewElementsMapping, nonCodeUsages, shorteningMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
is UnqualifiableMoveRenameUsageInfo -> {
|
is UnqualifiableMoveRenameUsageInfo -> {
|
||||||
val file = with(usage) { if (addImportToOriginalFile) originalFile else mapToNewOrThis(originalFile, oldToNewElementsMapping) } as KtFile
|
val file = with(usage) { if (addImportToOriginalFile) originalFile else mapToNewOrThis(originalFile, oldToNewElementsMapping) } as KtFile
|
||||||
val declaration = mapToNewOrThis(usage.referencedElement!!, oldToNewElementsMapping)
|
addDelayedImportRequest(newElement, file)
|
||||||
addDelayedImportRequest(declaration, file)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
is MoveRenameUsageInfo -> {
|
else -> {
|
||||||
val oldElement = usage.referencedElement!!
|
|
||||||
val newElement = mapToNewOrThis(oldElement, oldToNewElementsMapping)
|
|
||||||
val reference = (usage.element as? KtSimpleNameExpression)?.mainReference ?: usage.reference
|
val reference = (usage.element as? KtSimpleNameExpression)?.mainReference ?: usage.reference
|
||||||
processReference(reference, newElement, shorteningMode, oldElement)
|
processReference(reference, newElement, shorteningMode, oldElement)
|
||||||
}
|
}
|
||||||
|
|||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
package bar
|
||||||
|
|
||||||
|
class MemberNewEnd {
|
||||||
|
class Nested
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
import bar.MemberNewEnd
|
||||||
|
|
||||||
|
val v = MemberNewEnd::Nested
|
||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
class MemberEnd {
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
package bar
|
||||||
|
|
||||||
|
class MemberNewEnd
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
val v = MemberEnd::Nested
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
class MemberEnd {
|
||||||
|
class <caret>Nested
|
||||||
|
}
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"mainFile": "foo/MemberEnd.kt",
|
||||||
|
"type": "MOVE_KOTLIN_NESTED_CLASS",
|
||||||
|
"targetClass": "bar.MemberNewEnd",
|
||||||
|
"withRuntime": "true"
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
val v = ::Nested
|
||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
class MemberEnd {
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
class Nested
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
val v = MemberEnd::Nested
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
class MemberEnd {
|
||||||
|
class <caret>Nested
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"mainFile": "foo/MemberEnd.kt",
|
||||||
|
"type": "MOVE_KOTLIN_NESTED_CLASS",
|
||||||
|
"withRuntime": "true"
|
||||||
|
}
|
||||||
+12
@@ -319,6 +319,18 @@ public class MoveTestGenerated extends AbstractMoveTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kotlin/moveNestedClass/callableReferences/nestedToAnotherClass/nestedToAnotherClass.test")
|
||||||
|
public void testKotlin_moveNestedClass_callableReferences_nestedToAnotherClass_NestedToAnotherClass() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/move/kotlin/moveNestedClass/callableReferences/nestedToAnotherClass/nestedToAnotherClass.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kotlin/moveNestedClass/callableReferences/nestedToTopLevel/nestedToTopLevel.test")
|
||||||
|
public void testKotlin_moveNestedClass_callableReferences_nestedToTopLevel_NestedToTopLevel() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/move/kotlin/moveNestedClass/callableReferences/nestedToTopLevel/nestedToTopLevel.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("kotlin/moveNestedClass/deepInnerToTopLevelWithOuterOuterThis/deepInnerToTopLevelWithOuterOuterThis.test")
|
@TestMetadata("kotlin/moveNestedClass/deepInnerToTopLevelWithOuterOuterThis/deepInnerToTopLevelWithOuterOuterThis.test")
|
||||||
public void testKotlin_moveNestedClass_deepInnerToTopLevelWithOuterOuterThis_DeepInnerToTopLevelWithOuterOuterThis() throws Exception {
|
public void testKotlin_moveNestedClass_deepInnerToTopLevelWithOuterOuterThis_DeepInnerToTopLevelWithOuterOuterThis() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/move/kotlin/moveNestedClass/deepInnerToTopLevelWithOuterOuterThis/deepInnerToTopLevelWithOuterOuterThis.test");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/move/kotlin/moveNestedClass/deepInnerToTopLevelWithOuterOuterThis/deepInnerToTopLevelWithOuterOuterThis.test");
|
||||||
|
|||||||
Reference in New Issue
Block a user