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") KotlinBuilder.LOG.debug("Process $change")
if (change is ChangeInfo.SignatureChanged) { 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" } assert(!classFqName.isRoot) { "classFqName is root when processing $change" }
val scope = classFqName.parent().asString() val scope = classFqName.parent().asString()
@@ -353,8 +353,8 @@ open class IncrementalCacheImpl(
val fqName = if (isPackage) className.packageFqName else className.fqNameForClassNameWithoutDollars val fqName = if (isPackage) className.packageFqName else className.fqNameForClassNameWithoutDollars
val changeList = SmartList<ChangeInfo>() val changeList = SmartList<ChangeInfo>()
if (difference.isClassSignatureChanged) { if (difference.isClassAffected) {
changeList.add(ChangeInfo.SignatureChanged(fqName)) changeList.add(ChangeInfo.SignatureChanged(fqName, difference.areSubclassesAffected))
} }
if (difference.changedMembersNames.isNotEmpty()) { 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 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" protected open fun toStringProperties(): String = "fqName = $fqName"
@@ -30,12 +30,15 @@ import org.jetbrains.kotlin.utils.HashSetUtil
import java.util.* import java.util.*
data class Difference( data class Difference(
val isClassSignatureChanged: Boolean = false, val isClassAffected: Boolean = false,
val areSubclassesAffected: Boolean = false,
val changedMembersNames: Set<String> = emptySet() val changedMembersNames: Set<String> = emptySet()
) )
fun difference(oldData: ProtoMapValue, newData: ProtoMapValue): Difference { 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 = val differenceObject =
if (oldData.isPackageFacade) { if (oldData.isPackageFacade) {
@@ -174,7 +177,8 @@ private class DifferenceCalculatorForClass(oldData: ProtoMapValue, newData: Prot
val diff = compareObject.difference(oldProto, newProto) val diff = compareObject.difference(oldProto, newProto)
override fun difference(): Difference { override fun difference(): Difference {
var isClassSignatureChanged = false var isClassAffected = false
var areSubclassesAffected = false
val names = hashSetOf<String>() val names = hashSetOf<String>()
val classIsSealed = newProto.isSealed && oldProto.isSealed val classIsSealed = newProto.isSealed && oldProto.isSealed
@@ -197,31 +201,37 @@ private class DifferenceCalculatorForClass(oldData: ProtoMapValue, newData: Prot
if (classIsSealed) { if (classIsSealed) {
// when class is sealed, adding an implementation can break exhaustive when expressions // when class is sealed, adding an implementation can break exhaustive when expressions
// the workaround is to recompile all class usages // the workaround is to recompile all class usages
isClassSignatureChanged = true isClassAffected = true
} }
names.addAll(calcDifferenceForNames(oldProto.nestedClassNameList, newProto.nestedClassNameList)) names.addAll(calcDifferenceForNames(oldProto.nestedClassNameList, newProto.nestedClassNameList))
} }
ProtoBufClassKind.CONSTRUCTOR_LIST -> ProtoBufClassKind.CONSTRUCTOR_LIST -> {
names.addAll(calcDifferenceForNonPrivateMembers(ProtoBuf.Class::getConstructorList)) val differentNonPrivateConstructors = calcDifferenceForNonPrivateMembers(ProtoBuf.Class::getConstructorList)
if (differentNonPrivateConstructors.isNotEmpty()) {
isClassAffected = true
}
}
ProtoBufClassKind.FUNCTION_LIST -> ProtoBufClassKind.FUNCTION_LIST ->
names.addAll(calcDifferenceForNonPrivateMembers(ProtoBuf.Class::getFunctionList)) names.addAll(calcDifferenceForNonPrivateMembers(ProtoBuf.Class::getFunctionList))
ProtoBufClassKind.PROPERTY_LIST -> ProtoBufClassKind.PROPERTY_LIST ->
names.addAll(calcDifferenceForNonPrivateMembers(ProtoBuf.Class::getPropertyList)) names.addAll(calcDifferenceForNonPrivateMembers(ProtoBuf.Class::getPropertyList))
ProtoBufClassKind.ENUM_ENTRY_LIST -> { ProtoBufClassKind.ENUM_ENTRY_LIST -> {
isClassSignatureChanged = true isClassAffected = true
} }
ProtoBufClassKind.TYPE_TABLE -> { ProtoBufClassKind.TYPE_TABLE -> {
// TODO // TODO
} }
in CLASS_SIGNATURE_ENUMS -> { in CLASS_SIGNATURE_ENUMS -> {
isClassSignatureChanged = true isClassAffected = true
areSubclassesAffected = true
} }
else -> throw IllegalArgumentException("Unsupported kind: $kind") 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); doTest(fileName);
} }
@TestMetadata("secondaryConstructorAdded")
public void testSecondaryConstructorAdded() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/classHierarchyAffected/secondaryConstructorAdded/");
doTest(fileName);
}
@TestMetadata("starProjectionUpperBoundChanged") @TestMetadata("starProjectionUpperBoundChanged")
public void testStarProjectionUpperBoundChanged() throws Exception { public void testStarProjectionUpperBoundChanged() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/classHierarchyAffected/starProjectionUpperBoundChanged/"); String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/classHierarchyAffected/starProjectionUpperBoundChanged/");
@@ -104,7 +104,7 @@ abstract class AbstractProtoComparisonTest : UsefulTestCase() {
val changes = SmartList<String>() val changes = SmartList<String>()
if (diff.isClassSignatureChanged) { if (diff.isClassAffected) {
changes.add("CLASS_SIGNATURE") changes.add("CLASS_SIGNATURE")
} }
@@ -1,10 +1,5 @@
changes in test/ClassWithPrimaryConstructorChanged: MEMBERS changes in test/ClassWithPrimaryConstructorChanged: CLASS_SIGNATURE
[<init>] changes in test/ClassWithPrimaryConstructorVisibilityChanged: CLASS_SIGNATURE
changes in test/ClassWithPrimaryConstructorVisibilityChanged: MEMBERS changes in test/ClassWithSecondaryConstructorVisibilityChanged: CLASS_SIGNATURE
[<init>] changes in test/ClassWithSecondaryConstructorsAdded: CLASS_SIGNATURE
changes in test/ClassWithSecondaryConstructorVisibilityChanged: MEMBERS changes in test/ClassWithSecondaryConstructorsRemoved: CLASS_SIGNATURE
[<init>]
changes in test/ClassWithSecondaryConstructorsAdded: MEMBERS
[<init>]
changes in test/ClassWithSecondaryConstructorsRemoved: MEMBERS
[<init>]
@@ -1,2 +1 @@
changes in test/ClassWithClassAnnotationListChanged: CLASS_SIGNATURE, MEMBERS changes in test/ClassWithClassAnnotationListChanged: CLASS_SIGNATURE
[<init>]
@@ -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) {}