Move: Fix processing of callable references to nested Java classes and static methods

#KT-17827 Fixed
This commit is contained in:
Alexey Sedunov
2018-02-13 18:58:11 +03:00
parent 441f88c144
commit 17e64ebae0
46 changed files with 230 additions and 12 deletions
@@ -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
}
}
@@ -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)
}
@@ -0,0 +1,3 @@
package foo
val v = MemberNewEndJava::NestedJava
@@ -0,0 +1,4 @@
package foo;
public class MemberEndJava {
}
@@ -0,0 +1,5 @@
package foo;
public class MemberNewEndJava {
public static class NestedJava {}
}
@@ -0,0 +1,3 @@
package foo
val v = MemberEndJava::NestedJava
@@ -0,0 +1,5 @@
package foo;
public class MemberEndJava {
public static class <caret>NestedJava {}
}
@@ -0,0 +1,4 @@
package foo;
public class MemberNewEndJava {
}
@@ -0,0 +1,5 @@
{
"mainFile": "foo/MemberEndJava.java",
"type": "MOVE_MEMBERS",
"targetClass": "foo.MemberNewEndJava"
}
@@ -0,0 +1,5 @@
package bar;
public class MemberNewEndJava {
public static class NestedJava {}
}
@@ -0,0 +1,5 @@
package foo
import bar.MemberNewEndJava
val v = MemberNewEndJava::NestedJava
@@ -0,0 +1,4 @@
package foo;
public class MemberEndJava {
}
@@ -0,0 +1,4 @@
package bar;
public class MemberNewEndJava {
}
@@ -0,0 +1,3 @@
package foo
val v = MemberEndJava::NestedJava
@@ -0,0 +1,5 @@
package foo;
public class MemberEndJava {
public static class <caret>NestedJava {}
}
@@ -0,0 +1,5 @@
{
"mainFile": "foo/MemberEndJava.java",
"type": "MOVE_MEMBERS",
"targetClass": "bar.MemberNewEndJava"
}
@@ -0,0 +1,3 @@
package foo
val v = ::NestedJava
@@ -0,0 +1,4 @@
package foo;
public class MemberEndJava {
}
@@ -0,0 +1,3 @@
package foo;
public class NestedJava {}
@@ -0,0 +1,3 @@
package foo
val v = MemberEndJava::NestedJava
@@ -0,0 +1,5 @@
package foo;
public class MemberEndJava {
public static class <caret>NestedJava {}
}
@@ -0,0 +1,5 @@
{
"mainFile": "foo/MemberEndJava.java",
"type": "MOVE_INNER_CLASS",
"targetPackage": "foo"
}
@@ -0,0 +1,3 @@
package bar;
public class NestedJava {}
@@ -0,0 +1,5 @@
package foo
import bar.NestedJava
val v = ::NestedJava
@@ -0,0 +1,4 @@
package foo;
public class MemberEndJava {
}
@@ -0,0 +1,3 @@
package foo
val v = MemberEndJava::NestedJava
@@ -0,0 +1,5 @@
package foo;
public class MemberEndJava {
public static class <caret>NestedJava {}
}
@@ -0,0 +1,5 @@
{
"mainFile": "foo/MemberEndJava.java",
"type": "MOVE_INNER_CLASS",
"targetPackage": "bar"
}
@@ -0,0 +1,3 @@
package foo
val v2 = MemberNewEndJava::staticMethod
@@ -0,0 +1,4 @@
package foo;
public class MemberEndJava {
}
@@ -0,0 +1,5 @@
package foo;
public class MemberNewEndJava {
public static void staticMethod() {}
}
@@ -0,0 +1,3 @@
package foo
val v2 = MemberEndJava::staticMethod
@@ -0,0 +1,5 @@
package foo;
public class MemberEndJava {
public static void <caret>staticMethod() {}
}
@@ -0,0 +1,4 @@
package foo;
public class MemberNewEndJava {
}
@@ -0,0 +1,5 @@
{
"mainFile": "foo/MemberEndJava.java",
"type": "MOVE_MEMBERS",
"targetClass": "foo.MemberNewEndJava"
}
@@ -0,0 +1,5 @@
package bar;
public class MemberNewEndJava {
public static void staticMethod() {}
}
@@ -0,0 +1,5 @@
package foo
import bar.MemberNewEndJava
val v2 = MemberNewEndJava::staticMethod
@@ -0,0 +1,4 @@
package foo;
public class MemberEndJava {
}
@@ -0,0 +1,4 @@
package bar;
public class MemberNewEndJava {
}
@@ -0,0 +1,3 @@
package foo
val v2 = MemberEndJava::staticMethod
@@ -0,0 +1,5 @@
package foo;
public class MemberEndJava {
public static void <caret>staticMethod() {}
}
@@ -0,0 +1,5 @@
{
"mainFile": "foo/MemberEndJava.java",
"type": "MOVE_MEMBERS",
"targetClass": "bar.MemberNewEndJava"
}
@@ -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");