Move: Fix processing of callable references to nested Kotlin classes

#KT-17827 In Progress
This commit is contained in:
Alexey Sedunov
2018-02-13 15:54:41 +03:00
parent bdfc814389
commit 441f88c144
16 changed files with 121 additions and 8 deletions
@@ -209,6 +209,7 @@ class KtSimpleNameReference(expression: KtSimpleNameExpression) : KtSimpleRefere
parent is KtCallableReferenceExpression && parent.callableReference == this -> {
parentDelimiter = ""
val callableRefCopy = parent.copied()
callableRefCopy.receiverExpression?.delete()
callableRefCopy.callableReference.replace(psiFactory.createSimpleName(shortName)).parent!!.text
}
else -> shortName
@@ -55,6 +55,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.*
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver
import org.jetbrains.kotlin.types.expressions.DoubleColonLHS
import org.jetbrains.kotlin.utils.addIfNotNull
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(
reference: PsiReference,
referencedElement: PsiElement,
@@ -273,6 +303,9 @@ fun createMoveUsageInfoIfPossible(
ReferenceKind.UNQUALIFIABLE -> UnqualifiableMoveRenameUsageInfo(
element, reference, referencedElement, element.containingFile!!, addImportToOriginalFile, isInternal
)
ReferenceKind.CALLABLE_REFERENCE -> CallableReferenceMoveRenameUsageInfo(
element, reference, referencedElement, element.containingFile!!, addImportToOriginalFile, isInternal
)
else -> null
}
}
@@ -280,6 +313,7 @@ fun createMoveUsageInfoIfPossible(
private enum class ReferenceKind {
QUALIFIABLE,
UNQUALIFIABLE,
CALLABLE_REFERENCE,
IRRELEVANT
}
@@ -300,7 +334,11 @@ private fun getReferenceKind(reference: PsiReference, referencedElement: PsiElem
if (element.isExtensionRef() && reference.element.getNonStrictParentOfType<KtImportDirective>() == null) return ReferenceKind.UNQUALIFIABLE
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 PsiMember && target.containingClass == null) return ReferenceKind.UNQUALIFIABLE
}
@@ -364,20 +402,28 @@ private fun postProcessMoveUsage(
nonCodeUsages: ArrayList<NonCodeUsageInfo>,
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) {
is NonCodeUsageInfo -> {
nonCodeUsages.add(usage)
is DeferredKotlinMoveUsage -> {
val newUsage = usage.resolve(newElement) ?: return
postProcessMoveUsage(newUsage, oldToNewElementsMapping, nonCodeUsages, shorteningMode)
}
is UnqualifiableMoveRenameUsageInfo -> {
val file = with(usage) { if (addImportToOriginalFile) originalFile else mapToNewOrThis(originalFile, oldToNewElementsMapping) } as KtFile
val declaration = mapToNewOrThis(usage.referencedElement!!, oldToNewElementsMapping)
addDelayedImportRequest(declaration, file)
addDelayedImportRequest(newElement, file)
}
is MoveRenameUsageInfo -> {
val oldElement = usage.referencedElement!!
val newElement = mapToNewOrThis(oldElement, oldToNewElementsMapping)
else -> {
val reference = (usage.element as? KtSimpleNameExpression)?.mainReference ?: usage.reference
processReference(reference, newElement, shorteningMode, oldElement)
}
@@ -0,0 +1,5 @@
package bar
class MemberNewEnd {
class Nested
}
@@ -0,0 +1,5 @@
package foo
import bar.MemberNewEnd
val v = MemberNewEnd::Nested
@@ -0,0 +1,4 @@
package foo
class MemberEnd {
}
@@ -0,0 +1,3 @@
package bar
class MemberNewEnd
@@ -0,0 +1,3 @@
package foo
val v = MemberEnd::Nested
@@ -0,0 +1,5 @@
package foo
class MemberEnd {
class <caret>Nested
}
@@ -0,0 +1,6 @@
{
"mainFile": "foo/MemberEnd.kt",
"type": "MOVE_KOTLIN_NESTED_CLASS",
"targetClass": "bar.MemberNewEnd",
"withRuntime": "true"
}
@@ -0,0 +1,3 @@
package foo
val v = ::Nested
@@ -0,0 +1,4 @@
package foo
class MemberEnd {
}
@@ -0,0 +1,3 @@
package foo
class Nested
@@ -0,0 +1,3 @@
package foo
val v = MemberEnd::Nested
@@ -0,0 +1,5 @@
package foo
class MemberEnd {
class <caret>Nested
}
@@ -0,0 +1,5 @@
{
"mainFile": "foo/MemberEnd.kt",
"type": "MOVE_KOTLIN_NESTED_CLASS",
"withRuntime": "true"
}
@@ -319,6 +319,18 @@ public class MoveTestGenerated extends AbstractMoveTest {
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")
public void testKotlin_moveNestedClass_deepInnerToTopLevelWithOuterOuterThis_DeepInnerToTopLevelWithOuterOuterThis() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/move/kotlin/moveNestedClass/deepInnerToTopLevelWithOuterOuterThis/deepInnerToTopLevelWithOuterOuterThis.test");