Recompile all class usages when constructor is changed

Original commit: 94d4dae1fe
This commit is contained in:
Alexey Tsvetkov
2015-12-24 14:35:18 +03:00
parent ad3e9ac65c
commit cfec0481b4
17 changed files with 85 additions and 27 deletions
@@ -753,8 +753,9 @@ private fun CompilationResult.doProcessChangesUsingLookups(
KotlinBuilder.LOG.debug("Process $change")
if (change is ChangeInfo.SignatureChanged) {
for (classFqName in withSubtypes(change.fqName, allCaches)) {
val fqNames = if (!change.areSubclassesAffected) listOf(change.fqName) else withSubtypes(change.fqName, allCaches)
for (classFqName in fqNames) {
assert(!classFqName.isRoot) { "classFqName is root when processing $change" }
val scope = classFqName.parent().asString()
@@ -353,8 +353,8 @@ open class IncrementalCacheImpl(
val fqName = if (isPackage) className.packageFqName else className.fqNameForClassNameWithoutDollars
val changeList = SmartList<ChangeInfo>()
if (difference.isClassSignatureChanged) {
changeList.add(ChangeInfo.SignatureChanged(fqName))
if (difference.isClassAffected) {
changeList.add(ChangeInfo.SignatureChanged(fqName, difference.areSubclassesAffected))
}
if (difference.changedMembersNames.isNotEmpty()) {
@@ -566,7 +566,7 @@ sealed class ChangeInfo(val fqName: FqName) {
class Removed(fqName: FqName, names: Collection<String>) : MembersChanged(fqName, names)
class SignatureChanged(fqName: FqName) : ChangeInfo(fqName)
class SignatureChanged(fqName: FqName, val areSubclassesAffected: Boolean) : ChangeInfo(fqName)
protected open fun toStringProperties(): String = "fqName = $fqName"
@@ -30,12 +30,15 @@ import org.jetbrains.kotlin.utils.HashSetUtil
import java.util.*
data class Difference(
val isClassSignatureChanged: Boolean = false,
val isClassAffected: Boolean = false,
val areSubclassesAffected: Boolean = false,
val changedMembersNames: Set<String> = emptySet()
)
fun difference(oldData: ProtoMapValue, newData: ProtoMapValue): Difference {
if (oldData.isPackageFacade != newData.isPackageFacade) return Difference(isClassSignatureChanged = true)
if (!oldData.isPackageFacade && newData.isPackageFacade) return Difference(isClassAffected = true, areSubclassesAffected = true)
if (oldData.isPackageFacade && !newData.isPackageFacade) return Difference(isClassAffected = true)
val differenceObject =
if (oldData.isPackageFacade) {
@@ -174,7 +177,8 @@ private class DifferenceCalculatorForClass(oldData: ProtoMapValue, newData: Prot
val diff = compareObject.difference(oldProto, newProto)
override fun difference(): Difference {
var isClassSignatureChanged = false
var isClassAffected = false
var areSubclassesAffected = false
val names = hashSetOf<String>()
val classIsSealed = newProto.isSealed && oldProto.isSealed
@@ -197,31 +201,37 @@ private class DifferenceCalculatorForClass(oldData: ProtoMapValue, newData: Prot
if (classIsSealed) {
// when class is sealed, adding an implementation can break exhaustive when expressions
// the workaround is to recompile all class usages
isClassSignatureChanged = true
isClassAffected = true
}
names.addAll(calcDifferenceForNames(oldProto.nestedClassNameList, newProto.nestedClassNameList))
}
ProtoBufClassKind.CONSTRUCTOR_LIST ->
names.addAll(calcDifferenceForNonPrivateMembers(ProtoBuf.Class::getConstructorList))
ProtoBufClassKind.CONSTRUCTOR_LIST -> {
val differentNonPrivateConstructors = calcDifferenceForNonPrivateMembers(ProtoBuf.Class::getConstructorList)
if (differentNonPrivateConstructors.isNotEmpty()) {
isClassAffected = true
}
}
ProtoBufClassKind.FUNCTION_LIST ->
names.addAll(calcDifferenceForNonPrivateMembers(ProtoBuf.Class::getFunctionList))
ProtoBufClassKind.PROPERTY_LIST ->
names.addAll(calcDifferenceForNonPrivateMembers(ProtoBuf.Class::getPropertyList))
ProtoBufClassKind.ENUM_ENTRY_LIST -> {
isClassSignatureChanged = true
isClassAffected = true
}
ProtoBufClassKind.TYPE_TABLE -> {
// TODO
}
in CLASS_SIGNATURE_ENUMS -> {
isClassSignatureChanged = true
isClassAffected = true
areSubclassesAffected = true
}
else -> throw IllegalArgumentException("Unsupported kind: $kind")
}
}
return Difference(isClassSignatureChanged, names)
return Difference(isClassAffected, areSubclassesAffected, names)
}
}
@@ -259,7 +269,7 @@ private class DifferenceCalculatorForPackageFacade(oldData: ProtoMapValue, newDa
}
}
return Difference(isClassSignatureChanged = false, changedMembersNames = names)
return Difference(changedMembersNames = names)
}
}
@@ -1165,6 +1165,12 @@ public class ExperimentalIncrementalJpsTestGenerated extends AbstractExperimenta
doTest(fileName);
}
@TestMetadata("secondaryConstructorAdded")
public void testSecondaryConstructorAdded() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/classHierarchyAffected/secondaryConstructorAdded/");
doTest(fileName);
}
@TestMetadata("starProjectionUpperBoundChanged")
public void testStarProjectionUpperBoundChanged() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/classHierarchyAffected/starProjectionUpperBoundChanged/");
@@ -104,7 +104,7 @@ abstract class AbstractProtoComparisonTest : UsefulTestCase() {
val changes = SmartList<String>()
if (diff.isClassSignatureChanged) {
if (diff.isClassAffected) {
changes.add("CLASS_SIGNATURE")
}
@@ -1,10 +1,5 @@
changes in test/ClassWithPrimaryConstructorChanged: MEMBERS
[<init>]
changes in test/ClassWithPrimaryConstructorVisibilityChanged: MEMBERS
[<init>]
changes in test/ClassWithSecondaryConstructorVisibilityChanged: MEMBERS
[<init>]
changes in test/ClassWithSecondaryConstructorsAdded: MEMBERS
[<init>]
changes in test/ClassWithSecondaryConstructorsRemoved: MEMBERS
[<init>]
changes in test/ClassWithPrimaryConstructorChanged: CLASS_SIGNATURE
changes in test/ClassWithPrimaryConstructorVisibilityChanged: CLASS_SIGNATURE
changes in test/ClassWithSecondaryConstructorVisibilityChanged: CLASS_SIGNATURE
changes in test/ClassWithSecondaryConstructorsAdded: CLASS_SIGNATURE
changes in test/ClassWithSecondaryConstructorsRemoved: CLASS_SIGNATURE
@@ -1,2 +1 @@
changes in test/ClassWithClassAnnotationListChanged: CLASS_SIGNATURE, MEMBERS
[<init>]
changes in test/ClassWithClassAnnotationListChanged: CLASS_SIGNATURE
@@ -0,0 +1 @@
open class A(val x: Int)
@@ -0,0 +1,3 @@
open class A(val x: Int) {
constructor(x: String) : this(x.toInt())
}
@@ -0,0 +1 @@
class AChild(x: Int) : A(x)
@@ -0,0 +1 @@
fun A(x: String) = A(x.toInt())
@@ -0,0 +1,37 @@
Cleaning output files:
out/production/module/A.class
End of files
Compiling files:
src/A.kt
End of files
Cleaning output files:
out/production/module/AChild.class
out/production/module/AConstructorFunctionKt.class
out/production/module/CreateAFromIntKt.class
out/production/module/CreateAFromStringKt.class
out/production/module/META-INF/module.kotlin_module
out/production/module/UseAKt.class
End of files
Compiling files:
src/AChild.kt
src/AConstructorFunction.kt
src/createAFromInt.kt
src/createAFromString.kt
src/useA.kt
End of files
COMPILATION FAILED
Overload resolution ambiguity:
public constructor A(x: kotlin.String) defined in A
public fun A(x: kotlin.String): A defined in root package
Cleaning output files:
out/production/module/A.class
End of files
Compiling files:
src/A.kt
src/AChild.kt
src/createAFromInt.kt
src/createAFromString.kt
src/useA.kt
End of files
@@ -0,0 +1 @@
fun createAFromInt() = A(5)
@@ -0,0 +1 @@
fun createAFromString() = A("5")
@@ -0,0 +1 @@
fun useAChild(a: AChild) {}