diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/KtSimpleNameReference.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/KtSimpleNameReference.kt index a7311f8a7a2..3204cbc2d5d 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/KtSimpleNameReference.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/KtSimpleNameReference.kt @@ -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 diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveUtils.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveUtils.kt index 27d80cebcea..f79455c846a 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveUtils.kt @@ -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()?.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() == null) return ReferenceKind.UNQUALIFIABLE element.getParentOfTypeAndBranch { 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, 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) } diff --git a/idea/testData/refactoring/move/kotlin/moveNestedClass/callableReferences/nestedToAnotherClass/after/bar/MemberNewEnd.kt b/idea/testData/refactoring/move/kotlin/moveNestedClass/callableReferences/nestedToAnotherClass/after/bar/MemberNewEnd.kt new file mode 100644 index 00000000000..46195295ba8 --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveNestedClass/callableReferences/nestedToAnotherClass/after/bar/MemberNewEnd.kt @@ -0,0 +1,5 @@ +package bar + +class MemberNewEnd { + class Nested +} \ No newline at end of file diff --git a/idea/testData/refactoring/move/kotlin/moveNestedClass/callableReferences/nestedToAnotherClass/after/foo/KotlinReferences.kt b/idea/testData/refactoring/move/kotlin/moveNestedClass/callableReferences/nestedToAnotherClass/after/foo/KotlinReferences.kt new file mode 100644 index 00000000000..07737daf65c --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveNestedClass/callableReferences/nestedToAnotherClass/after/foo/KotlinReferences.kt @@ -0,0 +1,5 @@ +package foo + +import bar.MemberNewEnd + +val v = MemberNewEnd::Nested \ No newline at end of file diff --git a/idea/testData/refactoring/move/kotlin/moveNestedClass/callableReferences/nestedToAnotherClass/after/foo/MemberEnd.kt b/idea/testData/refactoring/move/kotlin/moveNestedClass/callableReferences/nestedToAnotherClass/after/foo/MemberEnd.kt new file mode 100644 index 00000000000..d1f137192f7 --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveNestedClass/callableReferences/nestedToAnotherClass/after/foo/MemberEnd.kt @@ -0,0 +1,4 @@ +package foo + +class MemberEnd { +} \ No newline at end of file diff --git a/idea/testData/refactoring/move/kotlin/moveNestedClass/callableReferences/nestedToAnotherClass/before/bar/MemberNewEnd.kt b/idea/testData/refactoring/move/kotlin/moveNestedClass/callableReferences/nestedToAnotherClass/before/bar/MemberNewEnd.kt new file mode 100644 index 00000000000..d0176158cb2 --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveNestedClass/callableReferences/nestedToAnotherClass/before/bar/MemberNewEnd.kt @@ -0,0 +1,3 @@ +package bar + +class MemberNewEnd \ No newline at end of file diff --git a/idea/testData/refactoring/move/kotlin/moveNestedClass/callableReferences/nestedToAnotherClass/before/foo/KotlinReferences.kt b/idea/testData/refactoring/move/kotlin/moveNestedClass/callableReferences/nestedToAnotherClass/before/foo/KotlinReferences.kt new file mode 100644 index 00000000000..71a74dfe024 --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveNestedClass/callableReferences/nestedToAnotherClass/before/foo/KotlinReferences.kt @@ -0,0 +1,3 @@ +package foo + +val v = MemberEnd::Nested \ No newline at end of file diff --git a/idea/testData/refactoring/move/kotlin/moveNestedClass/callableReferences/nestedToAnotherClass/before/foo/MemberEnd.kt b/idea/testData/refactoring/move/kotlin/moveNestedClass/callableReferences/nestedToAnotherClass/before/foo/MemberEnd.kt new file mode 100644 index 00000000000..7569abb1fb0 --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveNestedClass/callableReferences/nestedToAnotherClass/before/foo/MemberEnd.kt @@ -0,0 +1,5 @@ +package foo + +class MemberEnd { + class Nested +} \ No newline at end of file diff --git a/idea/testData/refactoring/move/kotlin/moveNestedClass/callableReferences/nestedToAnotherClass/nestedToAnotherClass.test b/idea/testData/refactoring/move/kotlin/moveNestedClass/callableReferences/nestedToAnotherClass/nestedToAnotherClass.test new file mode 100644 index 00000000000..f1eede8a491 --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveNestedClass/callableReferences/nestedToAnotherClass/nestedToAnotherClass.test @@ -0,0 +1,6 @@ +{ + "mainFile": "foo/MemberEnd.kt", + "type": "MOVE_KOTLIN_NESTED_CLASS", + "targetClass": "bar.MemberNewEnd", + "withRuntime": "true" +} \ No newline at end of file diff --git a/idea/testData/refactoring/move/kotlin/moveNestedClass/callableReferences/nestedToTopLevel/after/foo/KotlinReferences.kt b/idea/testData/refactoring/move/kotlin/moveNestedClass/callableReferences/nestedToTopLevel/after/foo/KotlinReferences.kt new file mode 100644 index 00000000000..bf594e958a2 --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveNestedClass/callableReferences/nestedToTopLevel/after/foo/KotlinReferences.kt @@ -0,0 +1,3 @@ +package foo + +val v = ::Nested \ No newline at end of file diff --git a/idea/testData/refactoring/move/kotlin/moveNestedClass/callableReferences/nestedToTopLevel/after/foo/MemberEnd.kt b/idea/testData/refactoring/move/kotlin/moveNestedClass/callableReferences/nestedToTopLevel/after/foo/MemberEnd.kt new file mode 100644 index 00000000000..d1f137192f7 --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveNestedClass/callableReferences/nestedToTopLevel/after/foo/MemberEnd.kt @@ -0,0 +1,4 @@ +package foo + +class MemberEnd { +} \ No newline at end of file diff --git a/idea/testData/refactoring/move/kotlin/moveNestedClass/callableReferences/nestedToTopLevel/after/foo/Nested.kt b/idea/testData/refactoring/move/kotlin/moveNestedClass/callableReferences/nestedToTopLevel/after/foo/Nested.kt new file mode 100644 index 00000000000..b5064100e7d --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveNestedClass/callableReferences/nestedToTopLevel/after/foo/Nested.kt @@ -0,0 +1,3 @@ +package foo + +class Nested \ No newline at end of file diff --git a/idea/testData/refactoring/move/kotlin/moveNestedClass/callableReferences/nestedToTopLevel/before/foo/KotlinReferences.kt b/idea/testData/refactoring/move/kotlin/moveNestedClass/callableReferences/nestedToTopLevel/before/foo/KotlinReferences.kt new file mode 100644 index 00000000000..71a74dfe024 --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveNestedClass/callableReferences/nestedToTopLevel/before/foo/KotlinReferences.kt @@ -0,0 +1,3 @@ +package foo + +val v = MemberEnd::Nested \ No newline at end of file diff --git a/idea/testData/refactoring/move/kotlin/moveNestedClass/callableReferences/nestedToTopLevel/before/foo/MemberEnd.kt b/idea/testData/refactoring/move/kotlin/moveNestedClass/callableReferences/nestedToTopLevel/before/foo/MemberEnd.kt new file mode 100644 index 00000000000..7569abb1fb0 --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveNestedClass/callableReferences/nestedToTopLevel/before/foo/MemberEnd.kt @@ -0,0 +1,5 @@ +package foo + +class MemberEnd { + class Nested +} \ No newline at end of file diff --git a/idea/testData/refactoring/move/kotlin/moveNestedClass/callableReferences/nestedToTopLevel/nestedToTopLevel.test b/idea/testData/refactoring/move/kotlin/moveNestedClass/callableReferences/nestedToTopLevel/nestedToTopLevel.test new file mode 100644 index 00000000000..2ab6ad68b92 --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveNestedClass/callableReferences/nestedToTopLevel/nestedToTopLevel.test @@ -0,0 +1,5 @@ +{ + "mainFile": "foo/MemberEnd.kt", + "type": "MOVE_KOTLIN_NESTED_CLASS", + "withRuntime": "true" +} \ 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 89fdc46d4c5..55b451462c4 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/move/MoveTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/move/MoveTestGenerated.java @@ -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");