[Commonizer] Adapt TypeCommonizerTest

^KT-48288
This commit is contained in:
sebastian.sellmair
2021-09-13 11:48:46 +02:00
committed by Space
parent 36a5b53398
commit 9d2553bd18
6 changed files with 610 additions and 460 deletions
@@ -28,7 +28,7 @@ internal class ClassOrTypeAliasTypeCommonizer(
val arguments = TypeArgumentListCommonizer(typeCommonizer).commonize(values.map { it.arguments }) ?: return null val arguments = TypeArgumentListCommonizer(typeCommonizer).commonize(values.map { it.arguments }) ?: return null
val classifierId = selectClassifierId(values) val classifierId = selectClassifierId(values)
?: typeCommonizer.options.enableOptimisticNumberTypeCommonization.ifTrue { ?: typeCommonizer.options.enableOptimisticNumberTypeCommonization.ifTrue {
return OptimisticNumbersTypeCommonizer.commonize(values.map { it.expandedType() }) return OptimisticNumbersTypeCommonizer.commonize(expansions)
} ?: return null } ?: return null
val outerTypes = values.safeCastValues<CirClassOrTypeAliasType, CirClassType>()?.map { it.outerType } val outerTypes = values.safeCastValues<CirClassOrTypeAliasType, CirClassType>()?.map { it.outerType }
@@ -15,9 +15,10 @@ import org.jetbrains.kotlin.commonizer.cir.CirTypeAlias
internal fun CirCommonClassifierIdResolver( internal fun CirCommonClassifierIdResolver(
classifierIndices: TargetDependent<CirClassifierIndex>, classifierIndices: TargetDependent<CirClassifierIndex>,
dependencies: CirProvidedClassifiers = CirProvidedClassifiers.EMPTY targetDependencies: TargetDependent<CirProvidedClassifiers>,
commonDependencies: CirProvidedClassifiers = CirProvidedClassifiers.EMPTY
): CirCommonClassifierIdResolver { ): CirCommonClassifierIdResolver {
return CirCommonClassifierIdResolverImpl(classifierIndices, dependencies) return CirCommonClassifierIdResolverImpl(classifierIndices, targetDependencies, commonDependencies)
} }
interface CirCommonClassifierIdResolver { interface CirCommonClassifierIdResolver {
@@ -26,7 +27,8 @@ interface CirCommonClassifierIdResolver {
private class CirCommonClassifierIdResolverImpl( private class CirCommonClassifierIdResolverImpl(
private val classifierIndices: TargetDependent<CirClassifierIndex>, private val classifierIndices: TargetDependent<CirClassifierIndex>,
private val dependencies: CirProvidedClassifiers private val targetDependencies: TargetDependent<CirProvidedClassifiers>,
private val commonDependencies: CirProvidedClassifiers
) : CirCommonClassifierIdResolver { ) : CirCommonClassifierIdResolver {
private val cachedResults = THashMap<CirEntityId, CirCommonClassifierId>() private val cachedResults = THashMap<CirEntityId, CirCommonClassifierId>()
@@ -54,8 +56,10 @@ private class CirCommonClassifierIdResolverImpl(
val nextClassifierId = queue.removeFirst() val nextClassifierId = queue.removeFirst()
/* Either CirClassifier or CirProvided.Classifier or null */ /* Either CirClassifier or CirProvided.Classifier or null */
val foundClassifiers = classifierIndices.associateWith { index -> val foundClassifiers = classifierIndices.targets.associateWith { index ->
index.findClassifier(nextClassifierId) ?: dependencies.classifier(nextClassifierId) classifierIndices[index].findClassifier(nextClassifierId)
?: targetDependencies[index].classifier(nextClassifierId)
?: commonDependencies.classifier(nextClassifierId)
} }
/* Classifier is available for all targets */ /* Classifier is available for all targets */
@@ -63,17 +67,23 @@ private class CirCommonClassifierIdResolverImpl(
results.add(nextClassifierId) results.add(nextClassifierId)
} }
foundClassifiers.forEach { (index, classifier) -> foundClassifiers.forEach { (target, classifier) ->
if (classifier == null) return@forEach if (classifier == null) return@forEach
// Propagate to the left (towards typealias) // Propagate to the left (towards typealias)
index.findTypeAliasesWithUnderlyingType(nextClassifierId).forEach { alias -> classifierIndices[target].findTypeAliasesWithUnderlyingType(nextClassifierId).forEach { alias ->
if (visited.add(alias.id)) { if (visited.add(alias.id)) {
queue.add(alias.id) queue.add(alias.id)
} }
} }
dependencies.findTypeAliasesWithUnderlyingType(nextClassifierId).forEach { aliasId -> targetDependencies[target].findTypeAliasesWithUnderlyingType(nextClassifierId).forEach { aliasId ->
if (visited.add(aliasId)) {
queue.add(aliasId)
}
}
commonDependencies.findTypeAliasesWithUnderlyingType(nextClassifierId).forEach { aliasId ->
if (visited.add(aliasId)) { if (visited.add(aliasId)) {
queue.add(aliasId) queue.add(aliasId)
} }
@@ -14,7 +14,8 @@ class CirKnownClassifiers(
val targetDependencies: TargetDependent<CirProvidedClassifiers>, val targetDependencies: TargetDependent<CirProvidedClassifiers>,
val commonizedNodes: CirCommonizedClassifierNodes, val commonizedNodes: CirCommonizedClassifierNodes,
val commonDependencies: CirProvidedClassifiers, val commonDependencies: CirProvidedClassifiers,
val commonClassifierIdResolver: CirCommonClassifierIdResolver = CirCommonClassifierIdResolver(classifierIndices, commonDependencies), val commonClassifierIdResolver: CirCommonClassifierIdResolver =
CirCommonClassifierIdResolver(classifierIndices, targetDependencies, commonDependencies),
) )
/** A set of all CIR nodes built for commonized classes and type aliases. */ /** A set of all CIR nodes built for commonized classes and type aliases. */
@@ -277,7 +277,9 @@ private fun createCommonClassifierIdResolver(
return CirCommonClassifierIdResolver( return CirCommonClassifierIdResolver(
TargetDependent(root.withIndex().associate { (index, root) -> LeafCommonizerTarget(index.toString()) to root }) TargetDependent(root.withIndex().associate { (index, root) -> LeafCommonizerTarget(index.toString()) to root })
.mapValue(::CirClassifierIndex), .mapValue(::CirClassifierIndex),
dependencies = dependencies targetDependencies = root.withIndex()
.associate { (index, _) -> LeafCommonizerTarget(index.toString()) to CirProvidedClassifiers.EMPTY }.toTargetDependent(),
commonDependencies = dependencies
) )
} }
@@ -5,243 +5,454 @@
package org.jetbrains.kotlin.commonizer.core package org.jetbrains.kotlin.commonizer.core
import org.jetbrains.kotlin.commonizer.AbstractInlineSourcesCommonizationTest
import org.jetbrains.kotlin.commonizer.LeafCommonizerTarget
import org.jetbrains.kotlin.commonizer.TargetDependent import org.jetbrains.kotlin.commonizer.TargetDependent
import org.jetbrains.kotlin.commonizer.cir.* import org.jetbrains.kotlin.commonizer.cir.CirType
import org.jetbrains.kotlin.commonizer.mergedtree.* import org.jetbrains.kotlin.commonizer.mapValue
import org.jetbrains.kotlin.commonizer.utils.isUnderStandardKotlinPackages import org.jetbrains.kotlin.commonizer.mergedtree.CirClassifierIndex
import org.jetbrains.kotlin.commonizer.utils.mockClassType import org.jetbrains.kotlin.commonizer.mergedtree.CirCommonizedClassifierNodes
import org.jetbrains.kotlin.commonizer.utils.mockTAType import org.jetbrains.kotlin.commonizer.mergedtree.CirKnownClassifiers
import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.commonizer.mergedtree.CirProvidedClassifiers
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.commonizer.tree.mergeCirTree
import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.commonizer.utils.*
import org.jetbrains.kotlin.storage.LockBasedStorageManager import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.junit.Before
import org.junit.Test
class TypeCommonizerTest : AbstractCommonizerTest<CirType, CirType?>() { class TypeCommonizerTest : AbstractInlineSourcesCommonizationTest() {
private lateinit var classifiers: CirKnownClassifiers fun createCommonizer(
commonTargetSources: InlineSourceBuilder.ModuleBuilder.() -> Unit = {
@Before source(
fun initialize() { """
// reset cache package org.sample
classifiers = CirKnownClassifiers( class Foo
classifierIndices = TargetDependent.empty(), class Bar
targetDependencies = TargetDependent.empty(), """.trimIndent(), "commonTargetSource.kt"
commonizedNodes = CirCommonizedClassifierNodes.default(),
commonDependencies = object : CirProvidedClassifiers {
override fun hasClassifier(classifierId: CirEntityId) = classifierId.packageName.isUnderStandardKotlinPackages
override fun classifier(classifierId: CirEntityId) = error("This method should not be called")
override fun findTypeAliasesWithUnderlyingType(underlyingClassifier: CirEntityId): List<CirEntityId> = emptyList()
}
) )
source(
"""
package org.fictitiousPackageName
class Foo
""".trimIndent()
)
},
commonDependencySources: InlineSourceBuilder.ModuleBuilder.() -> Unit = {
source(
"""
package kotlin.collections
class List
class Set
""".trimIndent(), "collections.kt"
)
source(
"""
package kotlinx.cinterop
class CPointer
""".trimIndent(), "cinterop.kt"
)
source(
"""
package kotlin.fictitiousPackageName
class List
class CPointer
""".trimIndent(), "fictitiousPackageName.kt"
)
},
targetASpecificSources: (InlineSourceBuilder.ModuleBuilder.() -> Unit)? = null,
targetBSpecificSources: (InlineSourceBuilder.ModuleBuilder.() -> Unit)? = null,
targetCSpecificSources: (InlineSourceBuilder.ModuleBuilder.() -> Unit)? = null,
targetADependencySources: (InlineSourceBuilder.ModuleBuilder.() -> Unit)? = null,
targetBDependencySources: (InlineSourceBuilder.ModuleBuilder.() -> Unit)? = null,
targetCDependencySources: (InlineSourceBuilder.ModuleBuilder.() -> Unit)? = null
): TypeCommonizer {
val targetARoot = createCirTreeRoot {
commonTargetSources()
if (targetASpecificSources != null) targetASpecificSources()
} }
@Test val targetBRoot = createCirTreeRoot {
fun classTypesInKotlinPackageWithSameName() = doTestSuccess( commonTargetSources()
expected = mockClassType("kotlin/collections/List"), if (targetBSpecificSources != null) targetBSpecificSources()
}
val targetCRoot = createCirTreeRoot {
commonTargetSources()
if (targetCSpecificSources != null) targetCSpecificSources()
}
val commonDependencies = createCirProvidedClassifiers { commonDependencySources() }
val targetADependencies = targetADependencySources?.let { createCirProvidedClassifiers { it() } } ?: CirProvidedClassifiers.EMPTY
val targetBDependencies = targetBDependencySources?.let { createCirProvidedClassifiers { it() } } ?: CirProvidedClassifiers.EMPTY
val targetCDependencies = targetCDependencySources?.let { createCirProvidedClassifiers { it() } } ?: CirProvidedClassifiers.EMPTY
val targetDependencies = TargetDependent(
LeafCommonizerTarget("a") to targetADependencies,
LeafCommonizerTarget("b") to targetBDependencies,
LeafCommonizerTarget("c") to targetCDependencies
)
val roots = TargetDependent(
LeafCommonizerTarget("a") to targetARoot,
LeafCommonizerTarget("b") to targetBRoot,
LeafCommonizerTarget("c") to targetCRoot
)
val classifiers = CirKnownClassifiers(
classifierIndices = roots.mapValue(::CirClassifierIndex),
targetDependencies = targetDependencies,
commonizedNodes = CirCommonizedClassifierNodes.default(),
commonDependencies = commonDependencies
).also { classifiers ->
mergeCirTree(LockBasedStorageManager.NO_LOCKS, classifiers, roots)
}
return TypeCommonizer(classifiers)
}
fun `test class types in Kotlin package with same name`() {
assertEquals(
mockClassType("kotlin/collections/List"), createCommonizer().invoke(
listOf(
mockClassType("kotlin/collections/List"), mockClassType("kotlin/collections/List"),
mockClassType("kotlin/collections/List"), mockClassType("kotlin/collections/List"),
mockClassType("kotlin/collections/List") mockClassType("kotlin/collections/List")
) )
)
)
}
@Test(expected = IllegalCommonizerStateException::class) fun `test class types in Kotlin package with different names - 1`() {
fun classTypesInKotlinPackageWithDifferentNames1() = doTestFailure( assertEquals(
null, createCommonizer().invoke(
listOf(
mockClassType("kotlin/collections/List"), mockClassType("kotlin/collections/List"),
mockClassType("kotlin/collections/List"), mockClassType("kotlin/collections/List"),
mockClassType("kotlin/fictitiousPackageName/List") mockClassType("kotlin/fictitiousPackageName/List")
) )
)
)
}
@Test(expected = IllegalCommonizerStateException::class) fun `test class types in Kotlin package with different names - 2`() {
fun classTypesInKotlinPackageWithDifferentNames2() = doTestFailure( assertEquals(
null, createCommonizer().invoke(
listOf(
mockClassType("kotlin/collections/List"), mockClassType("kotlin/collections/List"),
mockClassType("kotlin/collections/List"), mockClassType("kotlin/collections/List"),
mockClassType("kotlin/collections/Set") mockClassType("kotlin/collections/Set")
) )
)
)
}
@Test(expected = IllegalCommonizerStateException::class) fun `test class types in Kotlin package with different names - 3`() {
fun classTypesInKotlinPackageWithDifferentNames3() = doTestFailure( assertEquals(
null, createCommonizer().invoke(
listOf(
mockClassType("kotlin/collections/List"), mockClassType("kotlin/collections/List"),
mockClassType("kotlin/collections/List"), mockClassType("kotlin/collections/List"),
mockClassType("org/sample/Foo") mockClassType("org/sample/Foo")
) )
)
)
}
@Test fun `test class types in Kotlinx package with same name`() {
fun classTypesInKotlinxPackageWithSameName() = doTestSuccess( assertEquals(
expected = mockClassType("kotlinx/cinterop/CPointer"), mockClassType("kotlinx/cinterop/CPointer"), createCommonizer().invoke(
listOf(
mockClassType("kotlinx/cinterop/CPointer"), mockClassType("kotlinx/cinterop/CPointer"),
mockClassType("kotlinx/cinterop/CPointer"), mockClassType("kotlinx/cinterop/CPointer"),
mockClassType("kotlinx/cinterop/CPointer") mockClassType("kotlinx/cinterop/CPointer")
) )
@Test(expected = IllegalCommonizerStateException::class)
fun classTypesInKotlinxPackageWithDifferentNames1() = doTestFailure(
mockClassType("kotlinx/cinterop/CPointer"),
mockClassType("kotlinx/cinterop/CPointer"),
mockClassType("kotlinx/fictitiousPackageName/CPointer")
) )
@Test(expected = IllegalCommonizerStateException::class)
fun classTypesInKotlinxPackageWithDifferentNames2() = doTestFailure(
mockClassType("kotlinx/cinterop/CPointer"),
mockClassType("kotlinx/cinterop/CPointer"),
mockClassType("kotlinx/cinterop/ObjCObject")
) )
}
@Test(expected = IllegalCommonizerStateException::class) fun `test class types in Kotlinx package with different names - 1`() {
fun classTypesInKotlinxPackageWithDifferentNames3() = doTestFailure( assertEquals(
null, createCommonizer().invoke(
listOf(
mockClassType("kotlinx/cinterop/CPointer"), mockClassType("kotlinx/cinterop/CPointer"),
mockClassType("kotlinx/cinterop/CPointer"), mockClassType("kotlinx/cinterop/CPointer"),
mockClassType("org/sample/Foo") mockClassType("kotlin/fictitiousPackageName/CPointer")
) )
)
)
}
@Test fun `test class types in user package with same name`() {
fun classTypesInUserPackageWithSameName() = doTestSuccess( assertEquals(
expected = mockClassType("org/sample/Foo"), mockClassType("org/sample/Foo"), createCommonizer().invoke(
listOf(
mockClassType("org/sample/Foo"), mockClassType("org/sample/Foo"),
mockClassType("org/sample/Foo"), mockClassType("org/sample/Foo"),
mockClassType("org/sample/Foo") mockClassType("org/sample/Foo")
) )
@Test(expected = IllegalCommonizerStateException::class)
fun classTypesInUserPackageWithDifferentNames1() = doTestFailure(
mockClassType("org/sample/Foo"),
mockClassType("org/fictitiousPackageName/Foo"),
shouldFailOnFirstVariant = false
) )
@Test(expected = IllegalCommonizerStateException::class)
fun classTypesInUserPackageWithDifferentNames2() = doTestFailure(
mockClassType("org/sample/Foo"),
mockClassType("org/sample/Bar"),
shouldFailOnFirstVariant = false
) )
}
@Test(expected = IllegalCommonizerStateException::class) fun `test class types in user package with different names - 1`() {
fun classTypesInUserPackageWithDifferentNames3() = doTestFailure( assertEquals(
null, createCommonizer().invoke(
listOf(
mockClassType("org/sample/Foo"), mockClassType("org/sample/Foo"),
mockClassType("kotlin/String"), mockClassType("org/fictitiousPackageName/Foo")
shouldFailOnFirstVariant = false
) )
)
)
}
@Test fun `test class types in user package with different names - 2`() {
fun classTypesInKotlinPackageWithSameNullability1() = doTestSuccess( assertEquals(
expected = mockClassType("kotlin/collections/List", nullable = false), null, createCommonizer().invoke(
listOf(
mockClassType("org/sample/Foo"),
mockClassType("org/sample/Bar")
)
)
)
}
fun `test class types in user package with different names - 3`() {
assertEquals(
null, createCommonizer().invoke(
listOf(
mockClassType("org/sample/Foo"),
mockClassType("kotlin/String")
)
)
)
}
fun `test class types in Kotlin package with same nullability - 1`() {
assertEquals(
mockClassType("kotlin/collections/List", nullable = false), createCommonizer().invoke(
listOf(
mockClassType("kotlin/collections/List", nullable = false), mockClassType("kotlin/collections/List", nullable = false),
mockClassType("kotlin/collections/List", nullable = false), mockClassType("kotlin/collections/List", nullable = false),
mockClassType("kotlin/collections/List", nullable = false) mockClassType("kotlin/collections/List", nullable = false)
) )
)
)
}
@Test fun `test class types in Kotlin package with same nullability - 2`() {
fun classTypesInKotlinPackageWithSameNullability2() = doTestSuccess( assertEquals(
expected = mockClassType("kotlin/collections/List", nullable = true), mockClassType("kotlin/collections/List", nullable = true), createCommonizer().invoke(
listOf(
mockClassType("kotlin/collections/List", nullable = true), mockClassType("kotlin/collections/List", nullable = true),
mockClassType("kotlin/collections/List", nullable = true), mockClassType("kotlin/collections/List", nullable = true),
mockClassType("kotlin/collections/List", nullable = true) mockClassType("kotlin/collections/List", nullable = true)
) )
)
)
}
@Test(expected = IllegalCommonizerStateException::class) fun `test class types in Kotlin package with different nullability - 1`() {
fun classTypesInKotlinPackageWithDifferentNullability1() = doTestFailure( assertEquals(
null, createCommonizer().invoke(
listOf(
mockClassType("kotlin/collections/List", nullable = false), mockClassType("kotlin/collections/List", nullable = false),
mockClassType("kotlin/collections/List", nullable = false), mockClassType("kotlin/collections/List", nullable = false),
mockClassType("kotlin/collections/List", nullable = true) mockClassType("kotlin/collections/List", nullable = true)
) )
)
)
}
@Test(expected = IllegalCommonizerStateException::class) fun `test class types in Kotlin package with different nullability - 2`() {
fun classTypesInKotlinPackageWithDifferentNullability2() = doTestFailure( assertEquals(
null, createCommonizer().invoke(
listOf(
mockClassType("kotlin/collections/List", nullable = true), mockClassType("kotlin/collections/List", nullable = true),
mockClassType("kotlin/collections/List", nullable = true), mockClassType("kotlin/collections/List", nullable = true),
mockClassType("kotlin/collections/List", nullable = false) mockClassType("kotlin/collections/List", nullable = false)
) )
)
)
}
@Test fun `test class types in user package with same nullability - 1`() {
fun classTypesInUserPackageWithSameNullability1() = doTestSuccess( assertEquals(
expected = mockClassType("org/sample/Foo", nullable = false), mockClassType("org/sample/Foo", nullable = false), createCommonizer().invoke(
listOf(
mockClassType("org/sample/Foo", nullable = false), mockClassType("org/sample/Foo", nullable = false),
mockClassType("org/sample/Foo", nullable = false), mockClassType("org/sample/Foo", nullable = false),
mockClassType("org/sample/Foo", nullable = false) mockClassType("org/sample/Foo", nullable = false)
) )
)
)
}
@Test fun `test class types in user package with same nullability - 2`() {
fun classTypesInUserPackageWithSameNullability2() = doTestSuccess( assertEquals(
expected = mockClassType("org/sample/Foo", nullable = true), mockClassType("org/sample/Foo", nullable = true), createCommonizer().invoke(
listOf(
mockClassType("org/sample/Foo", nullable = true), mockClassType("org/sample/Foo", nullable = true),
mockClassType("org/sample/Foo", nullable = true), mockClassType("org/sample/Foo", nullable = true),
mockClassType("org/sample/Foo", nullable = true) mockClassType("org/sample/Foo", nullable = true)
) )
)
)
}
@Test(expected = IllegalCommonizerStateException::class) fun `test class types in user package with different nullability - 1`() {
fun classTypesInUserPackageWithDifferentNullability1() = doTestFailure( assertEquals(
null, createCommonizer().invoke(
listOf(
mockClassType("org/sample/Foo", nullable = false), mockClassType("org/sample/Foo", nullable = false),
mockClassType("org/sample/Foo", nullable = false), mockClassType("org/sample/Foo", nullable = false),
mockClassType("org/sample/Foo", nullable = true) mockClassType("org/sample/Foo", nullable = true)
) )
)
)
}
@Test(expected = IllegalCommonizerStateException::class) fun `test class types in user package with different nullability - 2`() {
fun classTypesInUserPackageWithDifferentNullability2() = doTestFailure( assertEquals(
null, createCommonizer().invoke(
listOf(
mockClassType("org/sample/Foo", nullable = true), mockClassType("org/sample/Foo", nullable = true),
mockClassType("org/sample/Foo", nullable = true), mockClassType("org/sample/Foo", nullable = true),
mockClassType("org/sample/Foo", nullable = false) mockClassType("org/sample/Foo", nullable = false)
) )
)
)
}
@Test fun `test ta types in Kotlin package with same name and class`() {
// why success: matching FQNs from the standard Kotlin packages val commonizer = createCommonizer(
fun taTypesInKotlinPackageWithSameNameAndClass() = doTestSuccess( commonDependencySources = {
expected = mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope") }, source(
"""
package kotlin.sequences
class SequenceScope
typealias SequenceBuilder = SequenceScope
""".trimIndent(), "sequences.kt"
)
}
)
assertEquals(mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope") }, commonizer(
listOf(
mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope") }, mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope") },
mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope") }, mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope") },
mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope") } mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope") }
) )))
}
@Test fun `test ta types in Kotlin package with different names`() {
fun taTypesInKotlinPackageWithDifferentNames() = doTestSuccess( val commonizer = createCommonizer(
expected = mockClassType("kotlin/sequences/SequenceScope"), commonDependencySources = {
source(
"""
package kotlin.sequences
class SequenceScope
typealias SequenceBuilder = SequenceScope
""".trimIndent()
)
},
targetCDependencySources = {
source(
"""
package kotlin.sequences
class SequenceScope
typealias FictitiousTypeAlias = SequenceScope
""".trimIndent(), "fictitious.kt"
)
}
)
assertEquals(
mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope") }, commonizer(
listOf(
mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope") }, mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope") },
mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope") }, mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope") },
mockTAType("kotlin/sequences/FictitiousTypeAlias") { mockClassType("kotlin/sequences/SequenceScope") } mockTAType("kotlin/sequences/FictitiousTypeAlias") { mockClassType("kotlin/sequences/SequenceScope") }
) )))
}
@Test fun `test ta types in Kotlin package with different classes`() {
// why success: matching FQNs from the standard Kotlin packages val commonizer = createCommonizer(
fun taTypesInKotlinPackageWithDifferentClasses() = doTestSuccess( targetADependencySources = {
expected = mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope") }, source(
"""
package kotlin.sequences
class SequenceScope
typealias SequenceBuilder = SequenceScope
""".trimIndent()
)
},
targetBDependencySources = {
source(
"""
package kotlin.sequences
class SequenceScope
typealias SequenceBuilder = SequenceScope
""".trimIndent()
)
},
targetCDependencySources = {
source(
"""
package kotlin.sequences
class FictitiousClass
typealias SequenceBuilder = FictitiousClass
""".trimIndent()
)
}
)
assertEquals(null, commonizer(
listOf(
mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope") }, mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope") },
mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope") }, mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope") },
mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/FictitiousClass") } mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/FictitiousClass") }
) )))
}
@Test fun `test multilevel ta types in Kotlin package with same name and right hand side class`() {
// why success: matching FQNs from the standard Kotlin packages val commonizer = createCommonizer(
fun taTypesInKotlinxPackageWithSameNameAndClass() = doTestSuccess( targetASpecificSources = {
expected = mockTAType("kotlinx/cinterop/CArrayPointer") { mockClassType("kotlinx/cinterop/CPointer") }, source(
mockTAType("kotlinx/cinterop/CArrayPointer") { mockClassType("kotlinx/cinterop/CPointer") }, """
mockTAType("kotlinx/cinterop/CArrayPointer") { mockClassType("kotlinx/cinterop/CPointer") }, package kotlin
mockTAType("kotlinx/cinterop/CArrayPointer") { mockClassType("kotlinx/cinterop/CPointer") } class FictitiousClass
typealias FictitiousTypeAlias = FictitiousClass
""".trimIndent()
) )
@Test()
fun taTypesInKotlinxPackageWithDifferentNames() = doTestSuccess(
expected = mockClassType("kotlinx/cinterop/CPointer"),
mockTAType("kotlinx/cinterop/CArrayPointer") { mockClassType("kotlinx/cinterop/CPointer") },
mockTAType("kotlinx/cinterop/CArrayPointer") { mockClassType("kotlinx/cinterop/CPointer") },
mockTAType("kotlinx/cinterop/FictitiousTypeAlias") { mockClassType("kotlinx/cinterop/CPointer") }
)
@Test
// why success: matching FQNs from the standard Kotlin packages
fun taTypesInKotlinxPackageWithDifferentClasses() = doTestSuccess(
expected = mockTAType("kotlinx/cinterop/CArrayPointer") { mockClassType("kotlinx/cinterop/CPointer") },
mockTAType("kotlinx/cinterop/CArrayPointer") { mockClassType("kotlinx/cinterop/CPointer") },
mockTAType("kotlinx/cinterop/CArrayPointer") { mockClassType("kotlinx/cinterop/CPointer") },
mockTAType("kotlinx/cinterop/CArrayPointer") { mockClassType("kotlinx/cinterop/FictitiousClass") }
)
@Test
// why success: matching FQNs from the standard Kotlin packages
fun multilevelTATypesInKotlinPackageWithSameNameAndRightHandSideClass() = doTestSuccess(
expected = mockTAType("kotlin/FictitiousTypeAlias") {
mockClassType("kotlin/FictitiousClass")
}, },
targetBSpecificSources = {
source(
"""
package kotlin
class FictitiousClass
typealias FictitiousTypeAliasL2 = FictitiousClass
typealias FictitiousTypeAlias = FictitiousTypeAliasL2
""".trimIndent()
)
},
targetCSpecificSources = {
source(
"""
package kotlin
class FictitiousClass
typealias FictitiousTypeAliasL2 = FictitiousClass
typealias FictitiousTypeAliasL3 = FictitiousTypeAliasL2
typealias FictitiousTypeAlias = FictitiousTypeAliasL3
""".trimIndent()
)
}
)
assertEquals(mockTAType("kotlin/FictitiousTypeAlias") {
mockClassType("kotlin/FictitiousClass")
}, commonizer(listOf(
mockTAType("kotlin/FictitiousTypeAlias") { mockTAType("kotlin/FictitiousTypeAlias") {
mockClassType("kotlin/FictitiousClass") mockClassType("kotlin/FictitiousClass")
@@ -260,64 +471,86 @@ class TypeCommonizerTest : AbstractCommonizerTest<CirType, CirType?>() {
} }
} }
} }
) )))
}
@Test fun `test multilevel ta types in user package with same name and right hand side class - 1`() {
// why success: lifting up val commonizer = createCommonizer(
fun taTypesInUserPackageWithSameNameAndClass() = doTestSuccess( targetASpecificSources = {
expected = mockTAType("org/sample/FooAlias") { mockClassType("org/sample/Foo") }, source(
mockTAType("org/sample/FooAlias") { mockClassType("org/sample/Foo") }, """
mockTAType("org/sample/FooAlias") { mockClassType("org/sample/Foo") }, package org.sample
mockTAType("org/sample/FooAlias") { mockClassType("org/sample/Foo") } class F
typealias FAlias = F
""".trimIndent()
) )
@Test
fun taTypesInUserPackageWithDifferentNames() = doTestSuccess(
expected = mockClassType("org/sample/Foo"),
mockTAType("org/sample/FooAlias") { mockClassType("org/sample/Foo") },
mockTAType("org/sample/BarAlias") { mockClassType("org/sample/Foo") },
)
@Test
// why success: short-circuiting & lifting up
fun multilevelTATypesInUserPackageWithSameNameAndRightHandSideClass1() = doTestSuccess(
expected = mockTAType("org/sample/FooAlias") {
mockClassType("org/sample/Foo")
}, },
targetBSpecificSources = {
mockTAType("org/sample/FooAlias") { source(
mockClassType("org/sample/Foo") """
package org.sample
class F
typealias FAlias = F
""".trimIndent()
)
}, },
targetCSpecificSources = {
source(
"""
package org.sample
class F
typealias FAliasL2 = F
typealias FAlias = FAliasL2
""".trimIndent()
)
}
)
assertEquals(
mockTAType("org/sample/FAlias") {
mockClassType("org/sample/F")
}, commonizer(
listOf(
mockTAType("org/sample/FAlias") {
mockClassType("org/sample/F")
},
mockTAType("org/sample/FAlias") {
mockClassType("org/sample/F")
},
mockTAType("org/sample/FAlias") {
mockTAType("org/sample/FAliasL2") {
mockClassType("org/sample/F")
}
})
)
)
}
fun `test multilevel ta types in user package with same name and right hand side class - 3`() {
val commonizer = createCommonizer(
commonTargetSources = {
source(
"""
package org.sample
class Foo
typealias FooAliasL2 = Foo
typealias FooAlias = FooAliasL2
""".trimIndent()
)
}
)
assertEquals(
mockTAType("org/sample/FooAlias") { mockTAType("org/sample/FooAlias") {
mockTAType("org/sample/FooAliasL2") { mockTAType("org/sample/FooAliasL2") {
mockClassType("org/sample/Foo") mockClassType("org/sample/Foo")
} }
} }, commonizer(
) listOf(
@Test
// why success: short-circuiting & lifting up
fun multilevelTATypesInUserPackageWithSameNameAndRightHandSideClass2() = doTestSuccess(
expected = mockTAType("org/sample/FooAlias") {
mockClassType("org/sample/Foo")
},
mockTAType("org/sample/FooAlias") { mockTAType("org/sample/FooAlias") {
mockTAType("org/sample/FooAliasL2") { mockTAType("org/sample/FooAliasL2") {
mockClassType("org/sample/Foo") mockClassType("org/sample/Foo")
} }
}, },
mockTAType("org/sample/FooAlias") { mockTAType("org/sample/FooAlias") {
mockClassType("org/sample/Foo")
}
)
@Test
// why success: lifting up (inner and outer TAs)
fun multilevelTATypesInUserPackageWithSameNameAndRightHandSideClass3() = doTestSuccess(
expected = mockTAType("org/sample/FooAlias") {
mockTAType("org/sample/FooAliasL2") { mockTAType("org/sample/FooAliasL2") {
mockClassType("org/sample/Foo") mockClassType("org/sample/Foo")
} }
@@ -327,176 +560,71 @@ class TypeCommonizerTest : AbstractCommonizerTest<CirType, CirType?>() {
mockTAType("org/sample/FooAliasL2") { mockTAType("org/sample/FooAliasL2") {
mockClassType("org/sample/Foo") mockClassType("org/sample/Foo")
} }
})
)
)
}
fun `test ta types in Kotlin package with different nullability`() {
val commonizer = createCommonizer(
targetADependencySources = {
source(
"""
package kotlin.sequences
class SequenceScope
typealias SequenceBuilder = SequenceScope
""".trimIndent()
)
}, },
targetBDependencySources = {
mockTAType("org/sample/FooAlias") { source(
mockTAType("org/sample/FooAliasL2") { """
mockClassType("org/sample/Foo") package kotlin.sequences
} class SequenceScope
typealias SequenceBuilder = SequenceScope
""".trimIndent()
)
},
targetCDependencySources = {
source(
"""
package kotlin.sequences
class SequenceScope
typealias SequenceBuilder = SequenceScope?
""".trimIndent()
)
} }
) )
assertEquals(null, commonizer(listOf(
@Test
// why success: types with the same nullability are treated as equal
fun taTypesInKotlinPackageWithSameNullability1() = doTestSuccess(
expected = mockTAType("kotlin/sequences/SequenceBuilder", nullable = false) { mockClassType("kotlin/sequences/SequenceScope") },
mockTAType("kotlin/sequences/SequenceBuilder", nullable = false) { mockClassType("kotlin/sequences/SequenceScope") },
mockTAType("kotlin/sequences/SequenceBuilder", nullable = false) { mockClassType("kotlin/sequences/SequenceScope") },
mockTAType("kotlin/sequences/SequenceBuilder", nullable = false) { mockClassType("kotlin/sequences/SequenceScope") }
)
@Test
// why success: types with the same nullability are treated as equal
fun taTypesInKotlinPackageWithSameNullability2() = doTestSuccess(
expected = mockTAType("kotlin/sequences/SequenceBuilder", nullable = true) { mockClassType("kotlin/sequences/SequenceScope") },
mockTAType("kotlin/sequences/SequenceBuilder", nullable = true) { mockClassType("kotlin/sequences/SequenceScope") },
mockTAType("kotlin/sequences/SequenceBuilder", nullable = true) { mockClassType("kotlin/sequences/SequenceScope") },
mockTAType("kotlin/sequences/SequenceBuilder", nullable = true) { mockClassType("kotlin/sequences/SequenceScope") }
)
@Test(expected = IllegalCommonizerStateException::class)
fun taTypesInKotlinPackageWithDifferentNullability1() = doTestFailure(
mockTAType("kotlin/sequences/SequenceBuilder", nullable = false) { mockClassType("kotlin/sequences/SequenceScope") },
mockTAType("kotlin/sequences/SequenceBuilder", nullable = false) { mockClassType("kotlin/sequences/SequenceScope") },
mockTAType("kotlin/sequences/SequenceBuilder", nullable = true) { mockClassType("kotlin/sequences/SequenceScope") }
)
@Test(expected = IllegalCommonizerStateException::class)
fun taTypesInKotlinPackageWithDifferentNullability2() = doTestFailure(
mockTAType("kotlin/sequences/SequenceBuilder", nullable = true) { mockClassType("kotlin/sequences/SequenceScope") },
mockTAType("kotlin/sequences/SequenceBuilder", nullable = true) { mockClassType("kotlin/sequences/SequenceScope") },
mockTAType("kotlin/sequences/SequenceBuilder", nullable = false) { mockClassType("kotlin/sequences/SequenceScope") }
)
@Test(expected = IllegalCommonizerStateException::class)
// why failure: Different nullability in type-expansion can't be commonized by now
fun taTypesInKotlinPackageWithDifferentNullability3() = doTestFailure(
mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope", nullable = false) }, mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope", nullable = false) },
mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope", nullable = false) }, mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope", nullable = false) },
mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope", nullable = true) } mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope", nullable = true) }
) )))
}
@Test(expected = IllegalCommonizerStateException::class) fun `test ta types in user package with same nullability`() {
// why failure: Different nullability in type-expansion can't be commonized by now val commonizer = createCommonizer(
fun taTypesInKotlinPackageWithDifferentNullability4() = doTestFailure( commonTargetSources = {
mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope", nullable = true) }, source(
mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope", nullable = true) }, """
mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope", nullable = false) } package org.sample
class Foo
typealias FooAlias = Foo?
""".trimIndent()
) )
}
@Test
// why success: types with the same nullability are treated as equal
fun taTypesInUserPackageWithSameNullability1() = doTestSuccess(
expected = mockTAType("org/sample/FooAlias", nullable = false) { mockClassType("org/sample/Foo") },
mockTAType("org/sample/FooAlias", nullable = false) { mockClassType("org/sample/Foo") },
mockTAType("org/sample/FooAlias", nullable = false) { mockClassType("org/sample/Foo") },
mockTAType("org/sample/FooAlias", nullable = false) { mockClassType("org/sample/Foo") }
) )
assertEquals(mockTAType("org/sample/FooAlias", nullable = true) { mockClassType("org/sample/Foo") }, commonizer(listOf(
@Test
// why success: types with the same nullability are treated as equal
fun taTypesInUserPackageWithSameNullability2() = doTestSuccess(
expected = mockTAType("org/sample/FooAlias", nullable = true) { mockClassType("org/sample/Foo") },
mockTAType("org/sample/FooAlias", nullable = true) { mockClassType("org/sample/Foo") }, mockTAType("org/sample/FooAlias", nullable = true) { mockClassType("org/sample/Foo") },
mockTAType("org/sample/FooAlias", nullable = true) { mockClassType("org/sample/Foo") }, mockTAType("org/sample/FooAlias", nullable = true) { mockClassType("org/sample/Foo") },
mockTAType("org/sample/FooAlias", nullable = true) { mockClassType("org/sample/Foo") } mockTAType("org/sample/FooAlias", nullable = true) { mockClassType("org/sample/Foo") }
) )))
@Test(expected = IllegalCommonizerStateException::class)
fun taTypesInUserPackageWithDifferentNullability1() = doTestFailure(
mockTAType("org/sample/FooAlias", nullable = false) { mockClassType("org/sample/Foo") },
mockTAType("org/sample/FooAlias", nullable = false) { mockClassType("org/sample/Foo") },
mockTAType("org/sample/FooAlias", nullable = true) { mockClassType("org/sample/Foo") }
)
@Test(expected = IllegalCommonizerStateException::class)
fun taTypesInUserPackageWithDifferentNullability2() = doTestFailure(
mockTAType("org/sample/FooAlias", nullable = true) { mockClassType("org/sample/Foo") },
mockTAType("org/sample/FooAlias", nullable = true) { mockClassType("org/sample/Foo") },
mockTAType("org/sample/FooAlias", nullable = false) { mockClassType("org/sample/Foo") },
)
private fun prepareCache(variants: Array<out CirClassOrTypeAliasType>) {
check(variants.isNotEmpty())
fun recurse(type: CirClassOrTypeAliasType, index: Int) {
when (type) {
is CirClassType -> {
val node = classifiers.classNode(type.classifierId) {
buildClassNode(
storageManager = LockBasedStorageManager.NO_LOCKS,
size = variants.size,
classifiers = classifiers,
nodeRelationship = null,
classId = type.classifierId
)
}
node.targetDeclarations[index] = CirClass.create(
annotations = emptyList(),
name = type.classifierId.relativeNameSegments.last(),
typeParameters = emptyList(),
supertypes = emptyList(),
visibility = Visibilities.Public,
modality = Modality.FINAL,
kind = ClassKind.CLASS,
companion = null,
isCompanion = false,
isData = false,
isValue = false,
isInner = false,
isExternal = false
)
}
is CirTypeAliasType -> {
val node = classifiers.typeAliasNode(type.classifierId) {
buildTypeAliasNode(
storageManager = LockBasedStorageManager.NO_LOCKS,
size = variants.size,
classifiers = classifiers,
typeAliasId = type.classifierId
)
}
node.targetDeclarations[index] = CirTypeAlias.create(
annotations = emptyList(),
name = type.classifierId.relativeNameSegments.last(),
typeParameters = emptyList(),
visibility = Visibilities.Public,
underlyingType = type.underlyingType,
expandedType = computeExpandedType(type.underlyingType)
)
recurse(type.underlyingType, index) // expand underlying types recursively
}
}
} }
variants.forEachIndexed { index, type ->
recurse(type, index)
}
}
fun doTestSuccess(expected: CirClassOrTypeAliasType, vararg variants: CirClassOrTypeAliasType) {
prepareCache(variants)
super.doTestSuccess(expected, *variants)
}
fun doTestFailure(vararg variants: CirClassOrTypeAliasType, shouldFailOnFirstVariant: Boolean = false) {
prepareCache(variants)
super.doTestFailure(*variants, shouldFailOnFirstVariant = shouldFailOnFirstVariant)
}
override fun createCommonizer() = TypeCommonizer(classifiers).asCommonizer()
override fun areEqual(a: CirType?, b: CirType?) = (a === b) || (a != null && b != null && areEqual(classifiers, a, b))
companion object { companion object {
fun areEqual(classifiers: CirKnownClassifiers, a: CirType, b: CirType): Boolean = fun areEqual(classifiers: CirKnownClassifiers, a: CirType, b: CirType): Boolean =
TypeCommonizer(classifiers).asCommonizer().run { commonizeWith(a) && commonizeWith(b) } TypeCommonizer(classifiers).invoke(listOf(a, b)) != null
private fun CirKnownClassifiers.classNode(classId: CirEntityId, computation: () -> CirClassNode) =
commonizedNodes.classNode(classId) ?: computation()
private fun CirKnownClassifiers.typeAliasNode(typeAliasId: CirEntityId, computation: () -> CirTypeAliasNode) =
commonizedNodes.typeAliasNode(typeAliasId) ?: computation()
} }
} }
@@ -114,3 +114,12 @@ fun InlineSourceBuilder.createCirProvidedClassifiers(module: InlineSourceBuilder
return CirProvidedClassifiersByModules.load(modulesProvider) return CirProvidedClassifiersByModules.load(modulesProvider)
} }
@InlineSourceBuilder.ModuleBuilderDsl
fun InlineSourceBuilder.createCirProvidedClassifiers(builder: InlineSourceBuilder.ModuleBuilder.() -> Unit): CirProvidedClassifiers {
return createCirProvidedClassifiers(createModule { builder() })
}
@InlineSourceBuilder.ModuleBuilderDsl
fun InlineSourceBuilder.createCirProvidedClassifiersFromSourceCode(@Language("kotlin") sourceCode: String): CirProvidedClassifiers {
return createCirProvidedClassifiers(createModule { source(sourceCode) })
}