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.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getImportableDescriptor
|
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.descriptorUtil.isSubclassOf
|
||||||
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
|
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
|
||||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
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(
|
fun checkAllConflicts(
|
||||||
externalUsages: MutableSet<UsageInfo>,
|
externalUsages: MutableSet<UsageInfo>,
|
||||||
internalUsages: MutableSet<UsageInfo>,
|
internalUsages: MutableSet<UsageInfo>,
|
||||||
@@ -461,6 +494,7 @@ class MoveConflictChecker(
|
|||||||
checkVisibilityInUsages(externalUsages, conflicts)
|
checkVisibilityInUsages(externalUsages, conflicts)
|
||||||
checkVisibilityInDeclarations(conflicts)
|
checkVisibilityInDeclarations(conflicts)
|
||||||
checkInternalMemberUsages(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);
|
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")
|
@TestMetadata("kotlin/moveTopLevelDeclarations/misc/selfReferences/selfReferences.test")
|
||||||
public void testKotlin_moveTopLevelDeclarations_misc_selfReferences_SelfReferences() throws Exception {
|
public void testKotlin_moveTopLevelDeclarations_misc_selfReferences_SelfReferences() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/selfReferences/selfReferences.test");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/selfReferences/selfReferences.test");
|
||||||
|
|||||||
Reference in New Issue
Block a user