Fixed references move refactoring for NonCode usages

We need to try correctly refactor NonCode usages of Java-like facade FQ names
i.e. if we have file A.kt with method foo we need to check AKt.foo and foo entries for non-code text search

Fixed #KT-36382
This commit is contained in:
Igor Yakovlev
2020-02-19 23:01:06 +03:00
parent aef8028a9c
commit b205b017fe
8 changed files with 84 additions and 8 deletions
@@ -31,6 +31,7 @@ import com.intellij.util.IncorrectOperationException
import com.intellij.util.containers.MultiMap
import gnu.trove.THashMap
import gnu.trove.TObjectHashingStrategy
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
import org.jetbrains.kotlin.asJava.elements.KtLightDeclaration
import org.jetbrains.kotlin.asJava.findFacadeClass
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
@@ -55,6 +56,9 @@ import org.jetbrains.kotlin.psi.psiUtil.isAncestor
import org.jetbrains.kotlin.utils.ifEmpty
import org.jetbrains.kotlin.utils.keysToMap
import java.util.*
import kotlin.collections.ArrayList
import kotlin.math.max
import kotlin.math.min
interface Mover : (KtNamedDeclaration, KtElement) -> KtNamedDeclaration {
object Default : Mover {
@@ -195,6 +199,13 @@ class MoveKotlinDeclarationsProcessor(
return null
}
fun UsageInfo.intersectsWith(usage: UsageInfo): Boolean {
if (element?.containingFile != usage.element?.containingFile) return false
val firstSegment = segment ?: return false
val secondSegment = usage.segment ?: return false
return max(firstSegment.startOffset, secondSegment.startOffset) <= min(firstSegment.endOffset, secondSegment.endOffset)
}
fun collectUsages(kotlinToLightElements: Map<KtNamedDeclaration, List<PsiNamedElement>>, result: MutableCollection<UsageInfo>) {
kotlinToLightElements.values.flatten().flatMapTo(result) { lightElement ->
val searchScope = getSearchScope(lightElement) ?: return@flatMapTo emptyList()
@@ -212,14 +223,42 @@ class MoveKotlinDeclarationsProcessor(
val name = lightElement.getKotlinFqName()?.quoteIfNeeded()?.asString()
if (name != null) {
TextOccurrencesUtil.findNonCodeUsages(
lightElement,
name,
descriptor.searchInCommentsAndStrings,
descriptor.searchInNonCode,
FqName(newFqName).quoteIfNeeded().asString(),
results
)
fun searchForKotlinNameUsages(results: ArrayList<UsageInfo>) {
TextOccurrencesUtil.findNonCodeUsages(
lightElement,
name,
descriptor.searchInCommentsAndStrings,
descriptor.searchInNonCode,
FqName(newFqName).quoteIfNeeded().asString(),
results
)
}
val facadeContainer = lightElement.parent as? KtLightClassForFacade
if (facadeContainer != null) {
val oldFqNameWithFacade = StringUtil.getQualifiedName(facadeContainer.qualifiedName, elementName)
val newFqNameWithFacade = StringUtil.getQualifiedName(
StringUtil.getQualifiedName(newContainerName, facadeContainer.name),
elementName
)
TextOccurrencesUtil.findNonCodeUsages(
lightElement,
oldFqNameWithFacade,
descriptor.searchInCommentsAndStrings,
descriptor.searchInNonCode,
FqName(newFqNameWithFacade).quoteIfNeeded().asString(),
results
)
ArrayList<UsageInfo>().also { searchForKotlinNameUsages(it) }.forEach { kotlinNonCodeUsage ->
if (results.none { it.intersectsWith(kotlinNonCodeUsage) }) {
results.add(kotlinNonCodeUsage)
}
}
} else {
searchForKotlinNameUsages(results)
}
}
MoveClassHandler.EP_NAME.extensions.filter { it !is MoveKotlinClassHandler }.forEach { handler ->
@@ -0,0 +1,3 @@
package code.anotherPackage
fun MyTest() {}
@@ -0,0 +1,7 @@
package code
fun main(args: Array<String>) {
// MyTest
// code.anotherPackage.MyTest
// code.anotherPackage.TestKt.MyTest
}
@@ -0,0 +1,9 @@
package code
fun <caret>MyTest() {}
fun main(args: Array<String>) {
// MyTest
// code.MyTest
// code.TestKt.MyTest
}
@@ -0,0 +1,5 @@
{
"mainFile": "code/test.kt",
"type": "MOVE_KOTLIN_TOP_LEVEL_DECLARATIONS",
"targetPackage": "code.anotherPackage"
}
@@ -0,0 +1,4 @@
//Here we are using newPackage.foo name for test pure kotlin naming of top level kotlin function
//And we are using newPackage.FunKt.foo name for test java naming of top level kotlin function
@@ -0,0 +1,4 @@
//Here we are using oldPackage.foo name for test pure kotlin naming of top level kotlin function
//And we are using oldPackage.FunKt.foo name for test java naming of top level kotlin function
@@ -588,6 +588,11 @@ public class MoveTestGenerated extends AbstractMoveTest {
runTest("idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/mutualDependency/MutualDependency.test");
}
@TestMetadata("kotlin/moveTopLevelDeclarations/misc/nonCodeUsagesWithJavaFacadeMethod/nonCodeUsagesWithJavaFacadeMethod.test")
public void testKotlin_moveTopLevelDeclarations_misc_nonCodeUsagesWithJavaFacadeMethod_NonCodeUsagesWithJavaFacadeMethod() throws Exception {
runTest("idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/nonCodeUsagesWithJavaFacadeMethod/nonCodeUsagesWithJavaFacadeMethod.test");
}
@TestMetadata("kotlin/moveTopLevelDeclarations/misc/nonCodeUsagesWithQuotedName/nonCodeUsageWithQuotedName.test")
public void testKotlin_moveTopLevelDeclarations_misc_nonCodeUsagesWithQuotedName_NonCodeUsageWithQuotedName() throws Exception {
runTest("idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/nonCodeUsagesWithQuotedName/nonCodeUsageWithQuotedName.test");