From 94cea25e2e5c2b0b8d0b141abb19611d66304d2c Mon Sep 17 00:00:00 2001 From: Alexey Tsvetkov Date: Mon, 28 Dec 2015 14:02:07 +0300 Subject: [PATCH] Recompile all sealed class usages when new derived class is added --- .../jps/incremental/protoDifferenceUtils.kt | 13 ++++++++- ...perimentalIncrementalJpsTestGenerated.java | 6 ++++ .../ProtoComparisonTestGenerated.java | 6 ++++ .../sealedClassImplAdded/new.kt | 7 +++++ .../sealedClassImplAdded/old.kt | 6 ++++ .../sealedClassImplAdded/result.out | 5 ++++ .../sealedClassImplAdded/Base.kt | 4 +++ .../sealedClassImplAdded/Base.kt.new.1 | 5 ++++ .../sealedClassImplAdded/build.log | 29 +++++++++++++++++++ .../sealedClassImplAdded/use.kt | 7 +++++ .../sealedClassImplAdded/use.kt.new.2 | 8 +++++ 11 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 jps-plugin/testData/comparison/classMembersOnlyChanged/sealedClassImplAdded/new.kt create mode 100644 jps-plugin/testData/comparison/classMembersOnlyChanged/sealedClassImplAdded/old.kt create mode 100644 jps-plugin/testData/comparison/classMembersOnlyChanged/sealedClassImplAdded/result.out create mode 100644 jps-plugin/testData/incremental/classHierarchyAffected/sealedClassImplAdded/Base.kt create mode 100644 jps-plugin/testData/incremental/classHierarchyAffected/sealedClassImplAdded/Base.kt.new.1 create mode 100644 jps-plugin/testData/incremental/classHierarchyAffected/sealedClassImplAdded/build.log create mode 100644 jps-plugin/testData/incremental/classHierarchyAffected/sealedClassImplAdded/use.kt create mode 100644 jps-plugin/testData/incremental/classHierarchyAffected/sealedClassImplAdded/use.kt.new.2 diff --git a/jps-plugin/src/org/jetbrains/kotlin/jps/incremental/protoDifferenceUtils.kt b/jps-plugin/src/org/jetbrains/kotlin/jps/incremental/protoDifferenceUtils.kt index 97183178c97..386acf3ad3b 100644 --- a/jps-plugin/src/org/jetbrains/kotlin/jps/incremental/protoDifferenceUtils.kt +++ b/jps-plugin/src/org/jetbrains/kotlin/jps/incremental/protoDifferenceUtils.kt @@ -177,6 +177,7 @@ private class DifferenceCalculatorForClass(oldData: ProtoMapValue, newData: Prot override fun difference(): Difference { var isClassSignatureChanged = false val names = hashSetOf() + 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) diff --git a/jps-plugin/test/org/jetbrains/kotlin/jps/build/ExperimentalIncrementalJpsTestGenerated.java b/jps-plugin/test/org/jetbrains/kotlin/jps/build/ExperimentalIncrementalJpsTestGenerated.java index 4ba99fa6da2..7981276a283 100644 --- a/jps-plugin/test/org/jetbrains/kotlin/jps/build/ExperimentalIncrementalJpsTestGenerated.java +++ b/jps-plugin/test/org/jetbrains/kotlin/jps/build/ExperimentalIncrementalJpsTestGenerated.java @@ -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/"); diff --git a/jps-plugin/test/org/jetbrains/kotlin/jps/incremental/ProtoComparisonTestGenerated.java b/jps-plugin/test/org/jetbrains/kotlin/jps/incremental/ProtoComparisonTestGenerated.java index 9b74cd9d194..896815ab04f 100644 --- a/jps-plugin/test/org/jetbrains/kotlin/jps/incremental/ProtoComparisonTestGenerated.java +++ b/jps-plugin/test/org/jetbrains/kotlin/jps/incremental/ProtoComparisonTestGenerated.java @@ -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") diff --git a/jps-plugin/testData/comparison/classMembersOnlyChanged/sealedClassImplAdded/new.kt b/jps-plugin/testData/comparison/classMembersOnlyChanged/sealedClassImplAdded/new.kt new file mode 100644 index 00000000000..7971271f92c --- /dev/null +++ b/jps-plugin/testData/comparison/classMembersOnlyChanged/sealedClassImplAdded/new.kt @@ -0,0 +1,7 @@ +package test + +sealed class Base { + class A : Base() + class B : Base() + class C : Base() +} \ No newline at end of file diff --git a/jps-plugin/testData/comparison/classMembersOnlyChanged/sealedClassImplAdded/old.kt b/jps-plugin/testData/comparison/classMembersOnlyChanged/sealedClassImplAdded/old.kt new file mode 100644 index 00000000000..c5cd9afb81d --- /dev/null +++ b/jps-plugin/testData/comparison/classMembersOnlyChanged/sealedClassImplAdded/old.kt @@ -0,0 +1,6 @@ +package test + +sealed class Base { + class A : Base() + class B : Base() +} \ No newline at end of file diff --git a/jps-plugin/testData/comparison/classMembersOnlyChanged/sealedClassImplAdded/result.out b/jps-plugin/testData/comparison/classMembersOnlyChanged/sealedClassImplAdded/result.out new file mode 100644 index 00000000000..016a54cb9a8 --- /dev/null +++ b/jps-plugin/testData/comparison/classMembersOnlyChanged/sealedClassImplAdded/result.out @@ -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] diff --git a/jps-plugin/testData/incremental/classHierarchyAffected/sealedClassImplAdded/Base.kt b/jps-plugin/testData/incremental/classHierarchyAffected/sealedClassImplAdded/Base.kt new file mode 100644 index 00000000000..c09db0c3c10 --- /dev/null +++ b/jps-plugin/testData/incremental/classHierarchyAffected/sealedClassImplAdded/Base.kt @@ -0,0 +1,4 @@ +sealed class Base { + class A : Base() + class B : Base() +} \ No newline at end of file diff --git a/jps-plugin/testData/incremental/classHierarchyAffected/sealedClassImplAdded/Base.kt.new.1 b/jps-plugin/testData/incremental/classHierarchyAffected/sealedClassImplAdded/Base.kt.new.1 new file mode 100644 index 00000000000..bda48c9d044 --- /dev/null +++ b/jps-plugin/testData/incremental/classHierarchyAffected/sealedClassImplAdded/Base.kt.new.1 @@ -0,0 +1,5 @@ +sealed class Base { + class A : Base() + class B : Base() + class C : Base() +} \ No newline at end of file diff --git a/jps-plugin/testData/incremental/classHierarchyAffected/sealedClassImplAdded/build.log b/jps-plugin/testData/incremental/classHierarchyAffected/sealedClassImplAdded/build.log new file mode 100644 index 00000000000..e55c691c0e4 --- /dev/null +++ b/jps-plugin/testData/incremental/classHierarchyAffected/sealedClassImplAdded/build.log @@ -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 \ No newline at end of file diff --git a/jps-plugin/testData/incremental/classHierarchyAffected/sealedClassImplAdded/use.kt b/jps-plugin/testData/incremental/classHierarchyAffected/sealedClassImplAdded/use.kt new file mode 100644 index 00000000000..dbf02b5d2a4 --- /dev/null +++ b/jps-plugin/testData/incremental/classHierarchyAffected/sealedClassImplAdded/use.kt @@ -0,0 +1,7 @@ +import Base.* + +fun use(x: Base): String = + when (x) { + is A -> "A" + is B -> "B" + } \ No newline at end of file diff --git a/jps-plugin/testData/incremental/classHierarchyAffected/sealedClassImplAdded/use.kt.new.2 b/jps-plugin/testData/incremental/classHierarchyAffected/sealedClassImplAdded/use.kt.new.2 new file mode 100644 index 00000000000..3034c5921b3 --- /dev/null +++ b/jps-plugin/testData/incremental/classHierarchyAffected/sealedClassImplAdded/use.kt.new.2 @@ -0,0 +1,8 @@ +import Base.* + +fun use(x: Base): String = + when (x) { + is A -> "A" + is B -> "B" + is C -> "C" + } \ No newline at end of file