[Commonizer] TypeCommonizer: Support commonizing typeAlias and their underlying types

This commit is contained in:
sebastian.sellmair
2021-06-02 17:35:13 +02:00
committed by Space
parent 92093bbabd
commit ffcd57cb31
8 changed files with 177 additions and 23 deletions

View File

@@ -29,6 +29,12 @@ abstract class AbstractStandardCommonizer<T, R> : Commonizer<T, R> {
State.IN_PROGRESS -> commonizationResult()
}
val resultOrNull: R?
get() = when (state) {
State.EMPTY, State.ERROR -> null
State.IN_PROGRESS -> commonizationResult()
}
final override fun commonizeWith(next: T): Boolean {
val result = when (state) {
State.ERROR -> return false

View File

@@ -52,9 +52,6 @@ private class TypeAliasShortCircuitingCommonizer(
expandedType = expandedType.result as CirClassType
)
val resultOrNull: CirTypeAlias?
get() = if (hasResult) commonizationResult() else null
override fun initialize(first: CirTypeAlias) {
name = first.name
}

View File

@@ -14,25 +14,57 @@ import org.jetbrains.kotlin.commonizer.utils.isUnderKotlinNativeSyntheticPackage
import org.jetbrains.kotlin.descriptors.Visibility
class TypeCommonizer(private val classifiers: CirKnownClassifiers) : AbstractStandardCommonizer<CirType, CirType>() {
private lateinit var wrapped: Commonizer<*, CirType>
private var typeCommonizer: AbstractStandardCommonizer<*, CirType>? = null
private var underlyingTypeCommonizer: TypeCommonizer? = null
override fun commonizationResult() = wrapped.result
override fun commonizationResult() = typeCommonizer?.resultOrNull
?: underlyingTypeCommonizer?.result
?: typeCommonizer?.result
?: failInErrorState()
override fun initialize(first: CirType) {
@Suppress("UNCHECKED_CAST")
wrapped = when (first) {
typeCommonizer = when (first) {
is CirClassType -> ClassTypeCommonizer(classifiers)
is CirTypeAliasType -> TypeAliasTypeCommonizer(classifiers)
is CirTypeParameterType -> TypeParameterTypeCommonizer()
is CirFlexibleType -> FlexibleTypeCommonizer(classifiers)
} as Commonizer<*, CirType>
} as AbstractStandardCommonizer<*, CirType>
if (first is CirTypeAliasType) {
underlyingTypeCommonizer = TypeCommonizer(classifiers)
}
}
override fun doCommonizeWith(next: CirType) = when (next) {
is CirClassType -> (wrapped as? ClassTypeCommonizer)?.commonizeWith(next) == true
is CirTypeAliasType -> (wrapped as? TypeAliasTypeCommonizer)?.commonizeWith(next) == true
is CirTypeParameterType -> (wrapped as? TypeParameterTypeCommonizer)?.commonizeWith(next) == true
is CirFlexibleType -> (wrapped as? FlexibleTypeCommonizer)?.commonizeWith(next) == true
override fun doCommonizeWith(next: CirType): Boolean {
return when (next) {
is CirClassType -> doCommonizeWithClassType(next)
is CirTypeAliasType -> doCommonizeWithTypeAliasType(next)
is CirTypeParameterType -> (typeCommonizer as? TypeParameterTypeCommonizer)?.commonizeWith(next) == true
is CirFlexibleType -> (typeCommonizer as? FlexibleTypeCommonizer)?.commonizeWith(next) == true
}
}
private fun doCommonizeWithClassType(next: CirClassType): Boolean {
if ((typeCommonizer as? ClassTypeCommonizer) == null) {
typeCommonizer = null
}
val firstAnswer = (typeCommonizer as? ClassTypeCommonizer)?.commonizeWith(next) == true
val secondAnswer = underlyingTypeCommonizer?.commonizeWith(next) == true
return firstAnswer || secondAnswer
}
private fun doCommonizeWithTypeAliasType(next: CirTypeAliasType): Boolean {
if (typeCommonizer != null && underlyingTypeCommonizer == null) {
underlyingTypeCommonizer = TypeCommonizer(classifiers)
underlyingTypeCommonizer?.commonizeWith(typeCommonizer?.resultOrNull ?: return false)
typeCommonizer = null
}
val firstAnswer = (typeCommonizer as? TypeAliasTypeCommonizer)?.commonizeWith(next) == true
val secondAnswer = underlyingTypeCommonizer?.commonizeWith(next.underlyingType) == true
return firstAnswer || secondAnswer
}
}

View File

@@ -14,12 +14,16 @@ typealias C = Planet
expect val property1: Int
expect val property2: String
expect val property3: Planet
expect val property4: Planet
expect val property5: Planet
expect val property6: Planet
expect val property7: C
expect fun function1(): Int
expect fun function2(): String
expect fun function3(): Planet
expect fun function4(): Planet
expect fun function5(): Planet
expect fun function6(): Planet
expect fun function7(): C

View File

@@ -48,7 +48,8 @@ abstract class AbstractCommonizerTest<T, R> {
val commonized = createCommonizer().apply {
variants.forEachIndexed { index, variant ->
val result = commonizeWith(variant)
if (index >= failureIndex) assertFalse(result) else assertTrue(result)
if (index >= failureIndex) assertFalse(result, "Expected to fail at index $index")
else assertTrue(result, "Expected to not fail at index $index")
}
}

View File

@@ -189,8 +189,9 @@ class TypeCommonizerTest : AbstractCommonizerTest<CirType, CirType>() {
mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope") }
)
@Test(expected = IllegalCommonizerStateException::class)
fun taTypesInKotlinPackageWithDifferentNames() = doTestFailure(
@Test
fun taTypesInKotlinPackageWithDifferentNames() = doTestSuccess(
expected = 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") }
@@ -214,8 +215,9 @@ class TypeCommonizerTest : AbstractCommonizerTest<CirType, CirType>() {
mockTAType("kotlinx/cinterop/CArrayPointer") { mockClassType("kotlinx/cinterop/CPointer") }
)
@Test(expected = IllegalCommonizerStateException::class)
fun taTypesInKotlinxPackageWithDifferentNames() = doTestFailure(
@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") }
@@ -265,11 +267,11 @@ class TypeCommonizerTest : AbstractCommonizerTest<CirType, CirType>() {
mockTAType("org/sample/FooAlias") { mockClassType("org/sample/Foo") }
)
@Test(expected = IllegalCommonizerStateException::class)
fun taTypesInUserPackageWithDifferentNames() = doTestFailure(
@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") },
shouldFailOnFirstVariant = true
)
@Test
@@ -402,8 +404,7 @@ class TypeCommonizerTest : AbstractCommonizerTest<CirType, CirType>() {
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") },
shouldFailOnFirstVariant = true
mockTAType("org/sample/FooAlias", nullable = true) { mockClassType("org/sample/Foo") }
)
@Test(expected = IllegalCommonizerStateException::class)
@@ -411,7 +412,6 @@ class TypeCommonizerTest : AbstractCommonizerTest<CirType, CirType>() {
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") },
shouldFailOnFirstVariant = true
)
private fun prepareCache(variants: Array<out CirClassOrTypeAliasType>) {

View File

@@ -241,4 +241,33 @@ class HierarchicalClassAndTypeAliasCommonizationTest : AbstractInlineSourcesComm
""".trimIndent()
)
}
fun `test typeAlias chain with nullability 2`() {
val result = commonize {
outputTarget("(a, b)")
simpleSingleSourceTarget(
"a", """
class AB
typealias V = AB
typealias Y = V?
""".trimIndent()
)
simpleSingleSourceTarget(
"b", """
class AB
typealias V = AB
typealias Y = V
""".trimIndent()
)
}
result.assertCommonized(
"(a, b)", """
expect class AB expect constructor()
expect typealias V = AB
expect class Y
""".trimIndent()
)
}
}

View File

@@ -106,4 +106,89 @@ class HierarchicalPropertyCommonizationTest : AbstractInlineSourcesCommonization
""".trimIndent()
)
}
fun `test typeAliased property and class typed property`() {
val result = commonize {
outputTarget("(a, b)")
simpleSingleSourceTarget(
"a", """
class AB
typealias TA = AB
val x: TA = TA()
""".trimIndent()
)
simpleSingleSourceTarget(
"b", """
class AB
val x: AB = AB()
""".trimIndent()
)
}
result.assertCommonized(
"(a, b)", """
expect class AB expect constructor()
expect val x: AB
""".trimIndent()
)
}
fun `test class typed property and typeAliased property`() {
val result = commonize {
outputTarget("(a, b)")
simpleSingleSourceTarget(
"a", """
class AB
val x: AB = AB()
""".trimIndent()
)
simpleSingleSourceTarget(
"b", """
class AB
typealias TA = AB
val x: TA = TA
""".trimIndent()
)
}
result.assertCommonized(
"(a, b)", """
expect class AB expect constructor()
expect val x: AB
""".trimIndent()
)
}
fun `test single typeAliased property and double typeAliased property`() {
val result = commonize {
outputTarget("(a, b)")
simpleSingleSourceTarget(
"a", """
class AB
typealias TA_AB = AB
val x: TA_AB = TA_AB()
""".trimIndent()
)
simpleSingleSourceTarget(
"b", """
class AB
typealias TA_AB = AB
typealias TA_B = TA_AB
val x: TA_B = TA_B()
""".trimIndent()
)
}
result.assertCommonized(
"(a, b)", """
expect class AB expect constructor()
expect typealias TA_AB = AB
expect val x: AB
""".trimIndent()
)
}
}