Recompile all sealed class usages when new derived class is added

This commit is contained in:
Alexey Tsvetkov
2015-12-28 14:02:07 +03:00
parent 0cf2928761
commit 94cea25e2e
11 changed files with 95 additions and 1 deletions
@@ -177,6 +177,7 @@ private class DifferenceCalculatorForClass(oldData: ProtoMapValue, newData: Prot
override fun difference(): Difference {
var isClassSignatureChanged = false
val names = hashSetOf<String>()
val classIsSealed = newProto.isSealed && oldProto.isSealed
fun Int.oldToNames() = names.add(oldNameResolver.getString(this))
fun Int.newToNames() = names.add(newNameResolver.getString(this))
@@ -193,8 +194,15 @@ private class DifferenceCalculatorForClass(oldData: ProtoMapValue, newData: Prot
if (oldProto.hasCompanionObjectName()) oldProto.companionObjectName.oldToNames()
if (newProto.hasCompanionObjectName()) newProto.companionObjectName.newToNames()
}
ProtoBufClassKind.NESTED_CLASS_NAME_LIST ->
ProtoBufClassKind.NESTED_CLASS_NAME_LIST -> {
if (classIsSealed) {
// when class is sealed, adding an implementation can break exhaustive when expressions
// the workaround is to recompile all class usages
isClassSignatureChanged = true
}
names.addAll(calcDifferenceForNames(oldProto.nestedClassNameList, newProto.nestedClassNameList))
}
ProtoBufClassKind.CONSTRUCTOR_LIST ->
names.addAll(calcDifferenceForNonPrivateMembers(ProtoBuf.Class::getConstructorList))
ProtoBufClassKind.FUNCTION_LIST ->
@@ -255,3 +263,6 @@ private class DifferenceCalculatorForPackageFacade(oldData: ProtoMapValue, newDa
return Difference(isClassSignatureChanged = false, changedMembersNames = names)
}
}
private val ProtoBuf.Class.isSealed: Boolean
get() = ProtoBuf.Modality.SEALED == Flags.MODALITY.get(flags)
@@ -1159,6 +1159,12 @@ public class ExperimentalIncrementalJpsTestGenerated extends AbstractExperimenta
doTest(fileName);
}
@TestMetadata("sealedClassImplAdded")
public void testSealedClassImplAdded() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/classHierarchyAffected/sealedClassImplAdded/");
doTest(fileName);
}
@TestMetadata("starProjectionUpperBoundChanged")
public void testStarProjectionUpperBoundChanged() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/classHierarchyAffected/starProjectionUpperBoundChanged/");
@@ -159,6 +159,12 @@ public class ProtoComparisonTestGenerated extends AbstractProtoComparisonTest {
doTest(fileName);
}
@TestMetadata("sealedClassImplAdded")
public void testSealedClassImplAdded() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/comparison/classMembersOnlyChanged/sealedClassImplAdded/");
doTest(fileName);
}
}
@TestMetadata("jps-plugin/testData/comparison/packageMembers")
@@ -0,0 +1,7 @@
package test
sealed class Base {
class A : Base()
class B : Base()
class C : Base()
}
@@ -0,0 +1,6 @@
package test
sealed class Base {
class A : Base()
class B : Base()
}
@@ -0,0 +1,5 @@
ADDED: class Base$C.class
changes in test/Base.A: NONE
changes in test/Base.B: NONE
changes in test/Base: CLASS_SIGNATURE, MEMBERS
[C]
@@ -0,0 +1,4 @@
sealed class Base {
class A : Base()
class B : Base()
}
@@ -0,0 +1,5 @@
sealed class Base {
class A : Base()
class B : Base()
class C : Base()
}
@@ -0,0 +1,29 @@
Cleaning output files:
out/production/module/Base$A.class
out/production/module/Base$B.class
out/production/module/Base.class
End of files
Compiling files:
src/Base.kt
End of files
Cleaning output files:
out/production/module/META-INF/module.kotlin_module
out/production/module/UseKt.class
End of files
Compiling files:
src/use.kt
End of files
COMPILATION FAILED
'when' expression must be exhaustive or contain an 'else' branch
Cleaning output files:
out/production/module/Base$A.class
out/production/module/Base$B.class
out/production/module/Base$C.class
out/production/module/Base.class
End of files
Compiling files:
src/Base.kt
src/use.kt
End of files
@@ -0,0 +1,7 @@
import Base.*
fun use(x: Base): String =
when (x) {
is A -> "A"
is B -> "B"
}
@@ -0,0 +1,8 @@
import Base.*
fun use(x: Base): String =
when (x) {
is A -> "A"
is B -> "B"
is C -> "C"
}