Move: Check if sealed class is moved with its subclasses
#KT-22769 Fixed
This commit is contained in:
+34
@@ -59,6 +59,7 @@ import org.jetbrains.kotlin.renderer.ParameterNameRenderingPolicy
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getImportableDescriptor
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isSubclassOf
|
||||
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
@@ -451,6 +452,38 @@ class MoveConflictChecker(
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkSealedClassMove(conflicts: MultiMap<PsiElement, String>) {
|
||||
val visited = HashSet<PsiElement>()
|
||||
for (elementToMove in elementsToMove) {
|
||||
if (!visited.add(elementToMove)) continue
|
||||
if (elementToMove !is KtClassOrObject) continue
|
||||
|
||||
val rootClass: KtClass
|
||||
val rootClassDescriptor: ClassDescriptor
|
||||
if (elementToMove is KtClass && elementToMove.isSealed()) {
|
||||
rootClass = elementToMove
|
||||
rootClassDescriptor = rootClass.resolveToDescriptorIfAny() as? ClassDescriptor ?: return
|
||||
}
|
||||
else {
|
||||
val classDescriptor = elementToMove.resolveToDescriptorIfAny() as? ClassDescriptor ?: return
|
||||
val superClassDescriptor = classDescriptor.getSuperClassNotAny() ?: return
|
||||
if (superClassDescriptor.modality != Modality.SEALED) return
|
||||
rootClassDescriptor = superClassDescriptor
|
||||
rootClass = rootClassDescriptor.source.getPsi() as? KtClass ?: return
|
||||
}
|
||||
|
||||
val subclasses = rootClassDescriptor.sealedSubclasses.mapNotNull { it.source.getPsi() }
|
||||
if (subclasses.isEmpty()) continue
|
||||
|
||||
visited.add(rootClass)
|
||||
visited.addAll(subclasses)
|
||||
|
||||
if (isToBeMoved(rootClass) && subclasses.all { isToBeMoved(it) }) continue
|
||||
|
||||
conflicts.putValue(rootClass, "Sealed class '${rootClass.name}' must be moved with all its subclasses")
|
||||
}
|
||||
}
|
||||
|
||||
fun checkAllConflicts(
|
||||
externalUsages: MutableSet<UsageInfo>,
|
||||
internalUsages: MutableSet<UsageInfo>,
|
||||
@@ -461,6 +494,7 @@ class MoveConflictChecker(
|
||||
checkVisibilityInUsages(externalUsages, conflicts)
|
||||
checkVisibilityInDeclarations(conflicts)
|
||||
checkInternalMemberUsages(conflicts)
|
||||
checkSealedClassMove(conflicts)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
package source
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
package target
|
||||
|
||||
sealed class Expr
|
||||
data class Const(val number: Double) : Expr()
|
||||
data class Sum(val e1: Expr, val e2: Expr) : Expr()
|
||||
object NotANumber : Expr()
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
package source
|
||||
|
||||
sealed class <caret>Expr
|
||||
data class <caret>Const(val number: Double) : Expr()
|
||||
data class <caret>Sum(val e1: Expr, val e2: Expr) : Expr()
|
||||
object <caret>NotANumber : Expr()
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"mainFile": "source/Foo.kt",
|
||||
"type": "MOVE_KOTLIN_TOP_LEVEL_DECLARATIONS",
|
||||
"targetPackage": "target"
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package source
|
||||
|
||||
import target.Expr
|
||||
|
||||
data class Const(val number: Double) : Expr()
|
||||
data class Sum(val e1: Expr, val e2: Expr) : Expr()
|
||||
object NotANumber : Expr()
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
package target
|
||||
|
||||
sealed class Expr
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
package source
|
||||
|
||||
sealed class <caret>Expr
|
||||
data class Const(val number: Double) : Expr()
|
||||
data class Sum(val e1: Expr, val e2: Expr) : Expr()
|
||||
object NotANumber : Expr()
|
||||
+1
@@ -0,0 +1 @@
|
||||
Sealed class 'Expr' must be moved with all its subclasses
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"mainFile": "source/Foo.kt",
|
||||
"type": "MOVE_KOTLIN_TOP_LEVEL_DECLARATIONS",
|
||||
"targetPackage": "target"
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package source
|
||||
|
||||
sealed class Expr
|
||||
data class Sum(val e1: Expr, val e2: Expr) : Expr()
|
||||
object NotANumber : Expr()
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package target
|
||||
|
||||
import source.Expr
|
||||
|
||||
data class Const(val number: Double) : Expr()
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
package source
|
||||
|
||||
sealed class Expr
|
||||
data class <caret>Const(val number: Double) : Expr()
|
||||
data class Sum(val e1: Expr, val e2: Expr) : Expr()
|
||||
object NotANumber : Expr()
|
||||
+1
@@ -0,0 +1 @@
|
||||
Sealed class 'Expr' must be moved with all its subclasses
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"mainFile": "source/Foo.kt",
|
||||
"type": "MOVE_KOTLIN_TOP_LEVEL_DECLARATIONS",
|
||||
"targetPackage": "target"
|
||||
}
|
||||
+18
@@ -613,6 +613,24 @@ public class MoveTestGenerated extends AbstractMoveTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("kotlin/moveTopLevelDeclarations/misc/sealedClassWithAllSubclasses/sealedClassWithAllSubclasses.test")
|
||||
public void testKotlin_moveTopLevelDeclarations_misc_sealedClassWithAllSubclasses_SealedClassWithAllSubclasses() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/sealedClassWithAllSubclasses/sealedClassWithAllSubclasses.test");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("kotlin/moveTopLevelDeclarations/misc/sealedClassWithSkippedSubclasses/sealedClassWithSkippedSubclasses.test")
|
||||
public void testKotlin_moveTopLevelDeclarations_misc_sealedClassWithSkippedSubclasses_SealedClassWithSkippedSubclasses() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/sealedClassWithSkippedSubclasses/sealedClassWithSkippedSubclasses.test");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("kotlin/moveTopLevelDeclarations/misc/sealedSubclassWithSkippedRoot/sealedSubclassWithSkippedRoot.test")
|
||||
public void testKotlin_moveTopLevelDeclarations_misc_sealedSubclassWithSkippedRoot_SealedSubclassWithSkippedRoot() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/sealedSubclassWithSkippedRoot/sealedSubclassWithSkippedRoot.test");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("kotlin/moveTopLevelDeclarations/misc/selfReferences/selfReferences.test")
|
||||
public void testKotlin_moveTopLevelDeclarations_misc_selfReferences_SelfReferences() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/selfReferences/selfReferences.test");
|
||||
|
||||
Reference in New Issue
Block a user