Move: Fix processing of callable references to nested Java classes and static methods
#KT-17827 Fixed
This commit is contained in:
@@ -19,10 +19,7 @@ package org.jetbrains.kotlin.psi.psiUtil
|
||||
import com.intellij.extapi.psi.StubBasedPsiElementBase
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiParameter
|
||||
import com.intellij.psi.PsiParameterList
|
||||
import com.intellij.psi.PsiWhiteSpace
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.stubs.StubElement
|
||||
import com.intellij.psi.tree.TokenSet
|
||||
import org.jetbrains.kotlin.KtNodeTypes
|
||||
@@ -596,4 +593,12 @@ fun String?.isIdentifier(): Boolean {
|
||||
return lexer.tokenType == null
|
||||
}
|
||||
|
||||
fun String.quoteIfNeeded(): String = if (this.isIdentifier()) this else "`$this`"
|
||||
fun String.quoteIfNeeded(): String = if (this.isIdentifier()) this else "`$this`"
|
||||
|
||||
fun PsiElement.isTopLevelKtOrJavaMember(): Boolean {
|
||||
return when (this) {
|
||||
is KtDeclaration -> parent is KtFile
|
||||
is PsiClass -> containingClass == null && this.qualifiedName != null
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
+15
-6
@@ -25,6 +25,7 @@ import com.intellij.util.SmartList
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference
|
||||
import org.jetbrains.kotlin.idea.codeInsight.shorten.addDelayedImportRequest
|
||||
import org.jetbrains.kotlin.idea.codeInsight.shorten.addToShorteningWaitSet
|
||||
import org.jetbrains.kotlin.idea.core.ShortenReferences
|
||||
import org.jetbrains.kotlin.idea.core.copied
|
||||
@@ -152,9 +153,9 @@ class KtSimpleNameReference(expression: KtSimpleNameExpression) : KtSimpleRefere
|
||||
bindToElement(element, ShorteningMode.DELAYED_SHORTENING)
|
||||
|
||||
fun bindToElement(element: PsiElement, shorteningMode: ShorteningMode): PsiElement =
|
||||
element.getKotlinFqName()?.let { fqName -> bindToFqName(fqName, shorteningMode) } ?: expression
|
||||
element.getKotlinFqName()?.let { fqName -> bindToFqName(fqName, shorteningMode, element) } ?: expression
|
||||
|
||||
fun bindToFqName(fqName: FqName, shorteningMode: ShorteningMode = ShorteningMode.DELAYED_SHORTENING): PsiElement {
|
||||
fun bindToFqName(fqName: FqName, shorteningMode: ShorteningMode = ShorteningMode.DELAYED_SHORTENING, targetElement: PsiElement? = null): PsiElement {
|
||||
val expression = expression
|
||||
if (fqName.isRoot) return expression
|
||||
|
||||
@@ -162,7 +163,7 @@ class KtSimpleNameReference(expression: KtSimpleNameExpression) : KtSimpleRefere
|
||||
if (expression !is KtNameReferenceExpression) return expression
|
||||
if (expression.parent is KtThisExpression || expression.parent is KtSuperExpression) return expression // TODO: it's a bad design of PSI tree, we should change it
|
||||
|
||||
val newExpression = expression.changeQualifiedName(fqName.quoteIfNeeded())
|
||||
val newExpression = expression.changeQualifiedName(fqName.quoteIfNeeded(), targetElement)
|
||||
val newQualifiedElement = newExpression.getQualifiedElementOrCallableRef()
|
||||
|
||||
if (shorteningMode == ShorteningMode.NO_SHORTENING) return newExpression
|
||||
@@ -184,7 +185,7 @@ class KtSimpleNameReference(expression: KtSimpleNameExpression) : KtSimpleRefere
|
||||
* Result is either the same as original element, or [[KtQualifiedExpression]], or [[KtUserType]]
|
||||
* Note that FqName may not be empty
|
||||
*/
|
||||
private fun KtNameReferenceExpression.changeQualifiedName(fqName: FqName): KtNameReferenceExpression {
|
||||
private fun KtNameReferenceExpression.changeQualifiedName(fqName: FqName, targetElement: PsiElement? = null): KtNameReferenceExpression {
|
||||
assert(!fqName.isRoot) { "Can't set empty FqName for element $this" }
|
||||
|
||||
val shortName = fqName.shortName().asString()
|
||||
@@ -195,7 +196,7 @@ class KtSimpleNameReference(expression: KtSimpleNameExpression) : KtSimpleRefere
|
||||
val qualifier = parent.qualifier
|
||||
val qualifierReference = qualifier?.referenceExpression as? KtNameReferenceExpression
|
||||
if (qualifierReference != null && qualifier.typeArguments.isNotEmpty()) {
|
||||
qualifierReference.changeQualifiedName(fqName.parent())
|
||||
qualifierReference.changeQualifiedName(fqName.parent(), targetElement)
|
||||
return this
|
||||
}
|
||||
}
|
||||
@@ -210,7 +211,15 @@ class KtSimpleNameReference(expression: KtSimpleNameExpression) : KtSimpleRefere
|
||||
parentDelimiter = ""
|
||||
val callableRefCopy = parent.copied()
|
||||
callableRefCopy.receiverExpression?.delete()
|
||||
callableRefCopy.callableReference.replace(psiFactory.createSimpleName(shortName)).parent!!.text
|
||||
val newCallableRef = callableRefCopy
|
||||
.callableReference
|
||||
.replace(psiFactory.createSimpleName(shortName))
|
||||
.parent as KtCallableReferenceExpression
|
||||
if (targetElement != null && targetElement.isTopLevelKtOrJavaMember()) {
|
||||
addDelayedImportRequest(targetElement, parent.containingKtFile)
|
||||
return parent.replaced(newCallableRef).callableReference as KtNameReferenceExpression
|
||||
}
|
||||
newCallableRef.text
|
||||
}
|
||||
else -> shortName
|
||||
}
|
||||
|
||||
@@ -281,7 +281,7 @@ class CallableReferenceMoveRenameUsageInfo(
|
||||
val element = element ?: return null
|
||||
val reference = reference ?: return null
|
||||
val referencedElement = referencedElement ?: return null
|
||||
if (target is KtDeclaration && target.parent is KtFile) {
|
||||
if (target != null && target.isTopLevelKtOrJavaMember()) {
|
||||
element.getStrictParentOfType<KtCallableReferenceExpression>()?.receiverExpression?.delete()
|
||||
return UnqualifiableMoveRenameUsageInfo(element, reference, referencedElement, element.containingFile!!, addImportToOriginalFile, isInternal)
|
||||
}
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
package foo
|
||||
|
||||
val v = MemberNewEndJava::NestedJava
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
package foo;
|
||||
|
||||
public class MemberEndJava {
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package foo;
|
||||
|
||||
public class MemberNewEndJava {
|
||||
public static class NestedJava {}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
package foo
|
||||
|
||||
val v = MemberEndJava::NestedJava
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package foo;
|
||||
|
||||
public class MemberEndJava {
|
||||
public static class <caret>NestedJava {}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
package foo;
|
||||
|
||||
public class MemberNewEndJava {
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"mainFile": "foo/MemberEndJava.java",
|
||||
"type": "MOVE_MEMBERS",
|
||||
"targetClass": "foo.MemberNewEndJava"
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package bar;
|
||||
|
||||
public class MemberNewEndJava {
|
||||
public static class NestedJava {}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package foo
|
||||
|
||||
import bar.MemberNewEndJava
|
||||
|
||||
val v = MemberNewEndJava::NestedJava
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
package foo;
|
||||
|
||||
public class MemberEndJava {
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
package bar;
|
||||
|
||||
public class MemberNewEndJava {
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
package foo
|
||||
|
||||
val v = MemberEndJava::NestedJava
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package foo;
|
||||
|
||||
public class MemberEndJava {
|
||||
public static class <caret>NestedJava {}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"mainFile": "foo/MemberEndJava.java",
|
||||
"type": "MOVE_MEMBERS",
|
||||
"targetClass": "bar.MemberNewEndJava"
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
package foo
|
||||
|
||||
val v = ::NestedJava
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
package foo;
|
||||
|
||||
public class MemberEndJava {
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
package foo;
|
||||
|
||||
public class NestedJava {}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
package foo
|
||||
|
||||
val v = MemberEndJava::NestedJava
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package foo;
|
||||
|
||||
public class MemberEndJava {
|
||||
public static class <caret>NestedJava {}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"mainFile": "foo/MemberEndJava.java",
|
||||
"type": "MOVE_INNER_CLASS",
|
||||
"targetPackage": "foo"
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
package bar;
|
||||
|
||||
public class NestedJava {}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package foo
|
||||
|
||||
import bar.NestedJava
|
||||
|
||||
val v = ::NestedJava
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
package foo;
|
||||
|
||||
public class MemberEndJava {
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
package foo
|
||||
|
||||
val v = MemberEndJava::NestedJava
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package foo;
|
||||
|
||||
public class MemberEndJava {
|
||||
public static class <caret>NestedJava {}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"mainFile": "foo/MemberEndJava.java",
|
||||
"type": "MOVE_INNER_CLASS",
|
||||
"targetPackage": "bar"
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
package foo
|
||||
|
||||
val v2 = MemberNewEndJava::staticMethod
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
package foo;
|
||||
|
||||
public class MemberEndJava {
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package foo;
|
||||
|
||||
public class MemberNewEndJava {
|
||||
public static void staticMethod() {}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
package foo
|
||||
|
||||
val v2 = MemberEndJava::staticMethod
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package foo;
|
||||
|
||||
public class MemberEndJava {
|
||||
public static void <caret>staticMethod() {}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
package foo;
|
||||
|
||||
public class MemberNewEndJava {
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"mainFile": "foo/MemberEndJava.java",
|
||||
"type": "MOVE_MEMBERS",
|
||||
"targetClass": "foo.MemberNewEndJava"
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package bar;
|
||||
|
||||
public class MemberNewEndJava {
|
||||
public static void staticMethod() {}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package foo
|
||||
|
||||
import bar.MemberNewEndJava
|
||||
|
||||
val v2 = MemberNewEndJava::staticMethod
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
package foo;
|
||||
|
||||
public class MemberEndJava {
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
package bar;
|
||||
|
||||
public class MemberNewEndJava {
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
package foo
|
||||
|
||||
val v2 = MemberEndJava::staticMethod
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package foo;
|
||||
|
||||
public class MemberEndJava {
|
||||
public static void <caret>staticMethod() {}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"mainFile": "foo/MemberEndJava.java",
|
||||
"type": "MOVE_MEMBERS",
|
||||
"targetClass": "bar.MemberNewEndJava"
|
||||
}
|
||||
+36
@@ -25,6 +25,30 @@ public class MoveTestGenerated extends AbstractMoveTest {
|
||||
KotlinTestUtils.assertAllTestsPresentInSingleGeneratedClass(this.getClass(), new File("idea/testData/refactoring/move"), Pattern.compile("^(.+)\\.test$"), TargetBackend.ANY);
|
||||
}
|
||||
|
||||
@TestMetadata("java/moveClass/callableReferences/nestedToAnotherClass/nestedToAnotherClass.test")
|
||||
public void testJava_moveClass_callableReferences_nestedToAnotherClass_NestedToAnotherClass() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/move/java/moveClass/callableReferences/nestedToAnotherClass/nestedToAnotherClass.test");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("java/moveClass/callableReferences/nestedToAnotherClassAndAnotherPackage/nestedToAnotherClassAndAnotherPackage.test")
|
||||
public void testJava_moveClass_callableReferences_nestedToAnotherClassAndAnotherPackage_NestedToAnotherClassAndAnotherPackage() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/move/java/moveClass/callableReferences/nestedToAnotherClassAndAnotherPackage/nestedToAnotherClassAndAnotherPackage.test");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("java/moveClass/callableReferences/nestedToTopLevel/nestedToTopLevel.test")
|
||||
public void testJava_moveClass_callableReferences_nestedToTopLevel_NestedToTopLevel() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/move/java/moveClass/callableReferences/nestedToTopLevel/nestedToTopLevel.test");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("java/moveClass/callableReferences/nestedToTopLevelAndAnotherPackage/nestedToTopLevelAndAnotherPackage.test")
|
||||
public void testJava_moveClass_callableReferences_nestedToTopLevelAndAnotherPackage_NestedToTopLevelAndAnotherPackage() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/move/java/moveClass/callableReferences/nestedToTopLevelAndAnotherPackage/nestedToTopLevelAndAnotherPackage.test");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("java/moveClass/moveAsMember/moveClassToExternalNestedClass/moveClassToExternalNestedClass.test")
|
||||
public void testJava_moveClass_moveAsMember_moveClassToExternalNestedClass_MoveClassToExternalNestedClass() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/move/java/moveClass/moveAsMember/moveClassToExternalNestedClass/moveClassToExternalNestedClass.test");
|
||||
@@ -157,6 +181,18 @@ public class MoveTestGenerated extends AbstractMoveTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("java/moveMethod/callableReferences/staticMethodToTopLevelClass/staticMethodToTopLevelClass.test")
|
||||
public void testJava_moveMethod_callableReferences_staticMethodToTopLevelClass_StaticMethodToTopLevelClass() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/move/java/moveMethod/callableReferences/staticMethodToTopLevelClass/staticMethodToTopLevelClass.test");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("java/moveMethod/callableReferences/staticMethodToTopLevelClassInAnotherPackage/staticMethodToTopLevelClassInAnotherPackage.test")
|
||||
public void testJava_moveMethod_callableReferences_staticMethodToTopLevelClassInAnotherPackage_StaticMethodToTopLevelClassInAnotherPackage() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/move/java/moveMethod/callableReferences/staticMethodToTopLevelClassInAnotherPackage/staticMethodToTopLevelClassInAnotherPackage.test");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("java/moveMethod/moveMethodToExternalNestedClass/moveMethodToExternalNestedClass.test")
|
||||
public void testJava_moveMethod_moveMethodToExternalNestedClass_MoveMethodToExternalNestedClass() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/move/java/moveMethod/moveMethodToExternalNestedClass/moveMethodToExternalNestedClass.test");
|
||||
|
||||
Reference in New Issue
Block a user