mirror of
https://github.com/jlengrand/kotlin.git
synced 2026-03-10 08:31:29 +00:00
[Commonizer] InlineTypeAliasCirNodeTransformer: add artificial supertypes
When inlining any TypeAlias pointing towards forward declarations, the inlined class will respect implicit supertypes. This logic is analog to KlibResolvedModuleDescriptorsFactoryImpl.createForwardDeclarationsModule ^KT-47430 In Progress
This commit is contained in:
committed by
Space
parent
0c35a3b699
commit
cdfe8d60a4
@@ -10,8 +10,13 @@ import org.jetbrains.kotlin.commonizer.mergedtree.*
|
||||
import org.jetbrains.kotlin.commonizer.mergedtree.CirNodeRelationship.Composite.Companion.plus
|
||||
import org.jetbrains.kotlin.commonizer.mergedtree.CirNodeRelationship.ParentNode
|
||||
import org.jetbrains.kotlin.commonizer.mergedtree.CirNodeRelationship.PreferredNode
|
||||
import org.jetbrains.kotlin.commonizer.transformer.ArtificialSupertypes.artificialSupertypes
|
||||
import org.jetbrains.kotlin.commonizer.utils.CNAMES_STRUCTS_PACKAGE
|
||||
import org.jetbrains.kotlin.commonizer.utils.OBJCNAMES_CLASSES_PACKAGE
|
||||
import org.jetbrains.kotlin.commonizer.utils.OBJCNAMES_PROTOCOLS_PACKAGE
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.storage.StorageManager
|
||||
|
||||
internal class InlineTypeAliasCirNodeTransformer(
|
||||
@@ -142,4 +147,35 @@ private fun CirTypeAlias.toArtificialCirClass(): CirClass = CirClass.create(
|
||||
annotations = emptyList(), name = name, typeParameters = typeParameters,
|
||||
visibility = this.visibility, modality = Modality.FINAL, kind = ClassKind.CLASS,
|
||||
companion = null, isCompanion = false, isData = false, isValue = false, isInner = false, isExternal = false
|
||||
).also { it.supertypes = emptyList() }
|
||||
).also { it.supertypes = artificialSupertypes() }
|
||||
|
||||
|
||||
/**
|
||||
* Analog to "KlibResolvedModuleDescriptorsFactoryImpl.createForwardDeclarationsModule" which also
|
||||
* automatically assumes relevant supertypes for forward declarations based upon the package they are in.
|
||||
*/
|
||||
private object ArtificialSupertypes {
|
||||
private fun createType(classId: String): CirClassType {
|
||||
return CirClassType.createInterned(
|
||||
classId = CirEntityId.create(classId),
|
||||
outerType = null, visibility = Visibilities.Public, arguments = emptyList(), isMarkedNullable = false
|
||||
)
|
||||
}
|
||||
|
||||
private val cOpaqueType = listOf(createType("kotlinx/cinterop/COpaque"))
|
||||
private val objcObjectBase = listOf(createType("kotlinx/cinterop/ObjCObjectBase"))
|
||||
private val objcCObject = listOf(createType("kotlinx/cinterop/ObjCObject"))
|
||||
|
||||
fun CirTypeAlias.artificialSupertypes(): List<CirType> {
|
||||
/* Not supported (yet). No real life examples known (yet), that would benefit */
|
||||
if (this.expandedType.isMarkedNullable) return emptyList()
|
||||
|
||||
return when (underlyingType.classifierId.packageName) {
|
||||
CNAMES_STRUCTS_PACKAGE -> cOpaqueType
|
||||
OBJCNAMES_CLASSES_PACKAGE -> objcObjectBase
|
||||
OBJCNAMES_PROTOCOLS_PACKAGE -> objcCObject
|
||||
else -> emptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,12 @@ private val KOTLIN_NATIVE_SYNTHETIC_PACKAGES: List<CirPackageName> = ForwardDecl
|
||||
CirPackageName.create(packageFqName)
|
||||
}
|
||||
|
||||
internal val CNAMES_STRUCTS_PACKAGE = CirPackageName.create("cnames.structs")
|
||||
|
||||
internal val OBJCNAMES_CLASSES_PACKAGE = CirPackageName.create("objcnames.classes")
|
||||
|
||||
internal val OBJCNAMES_PROTOCOLS_PACKAGE = CirPackageName.create("objcnames.protocols")
|
||||
|
||||
private val CINTEROP_PACKAGE: CirPackageName = CirPackageName.create("kotlinx.cinterop")
|
||||
|
||||
private val OBJC_INTEROP_CALLABLE_ANNOTATIONS: List<CirName> = listOf(
|
||||
|
||||
@@ -572,4 +572,58 @@ class HierarchicalClassAndTypeAliasCommonizationTest : AbstractInlineSourcesComm
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
fun `test supertypes being retained`() {
|
||||
val result = commonize {
|
||||
outputTarget("(a, b)")
|
||||
simpleSingleSourceTarget(
|
||||
"a", """
|
||||
interface SuperInterface
|
||||
class X: SuperInterface
|
||||
""".trimIndent()
|
||||
)
|
||||
simpleSingleSourceTarget(
|
||||
"b", """
|
||||
interface SuperInterface
|
||||
class B: SuperInterface
|
||||
typealias X = B
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
result.assertCommonized(
|
||||
"(a, b)", """
|
||||
expect interface SuperInterface
|
||||
expect class X(): SuperInterface
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
fun `test supertypes being retained from dependencies`() {
|
||||
val result = commonize {
|
||||
outputTarget("(a, b)")
|
||||
|
||||
registerDependency("a", "b", "(a, b)") {
|
||||
source("""interface SuperInterface""")
|
||||
}
|
||||
|
||||
simpleSingleSourceTarget(
|
||||
"a", """
|
||||
class X: SuperInterface
|
||||
""".trimIndent()
|
||||
)
|
||||
simpleSingleSourceTarget(
|
||||
"b", """
|
||||
class B: SuperInterface
|
||||
typealias X = B
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
result.assertCommonized(
|
||||
"(a, b)", """
|
||||
expect class X(): SuperInterface
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
package org.jetbrains.kotlin.commonizer.transformer
|
||||
|
||||
import org.jetbrains.kotlin.commonizer.cir.*
|
||||
import org.jetbrains.kotlin.commonizer.mergedtree.*
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.storage.LockBasedStorageManager
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
|
||||
class InlineTypeAliasCirNodeTransformerTest {
|
||||
|
||||
private val storageManager = LockBasedStorageManager("test")
|
||||
|
||||
private val classifiers = CirKnownClassifiers(
|
||||
commonizedNodes = CirCommonizedClassifierNodes.default(),
|
||||
commonDependencies = CirProvidedClassifiers.EMPTY
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `test artificial supertypes - regular package`() {
|
||||
val root = setup(CirEntityId.create("regular/package/__X"))
|
||||
InlineTypeAliasCirNodeTransformer(storageManager, classifiers).invoke(root)
|
||||
val artificialXClass = root.assertInlinedXClass
|
||||
assertEquals(
|
||||
emptyList(), artificialXClass.supertypes,
|
||||
"Expected no artificial supertype for artificial class X"
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test artificial supertypes - cnames structs`() {
|
||||
val root = setup(CirEntityId.create("cnames/structs/__X"))
|
||||
InlineTypeAliasCirNodeTransformer(storageManager, classifiers).invoke(root)
|
||||
val artificialXClass = root.assertInlinedXClass
|
||||
|
||||
assertEquals(
|
||||
setOf(CirEntityId.create("kotlinx/cinterop/COpaque")),
|
||||
artificialXClass.supertypes.map { it as CirClassType }.map { it.classifierId }.toSet(),
|
||||
"Expected 'COpaque' supertype being attached automatically"
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test artificial supertypes - objcnames classes`() {
|
||||
val root = setup(CirEntityId.create("objcnames/classes/__X"))
|
||||
InlineTypeAliasCirNodeTransformer(storageManager, classifiers).invoke(root)
|
||||
val artificialXClass = root.assertInlinedXClass
|
||||
|
||||
assertEquals(
|
||||
setOf(CirEntityId.create("kotlinx/cinterop/ObjCObjectBase")),
|
||||
artificialXClass.supertypes.map { it as CirClassType }.map { it.classifierId }.toSet(),
|
||||
"Expected 'ObjCObjectBase' supertype being attached automatically"
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test artificial supertypes - objcnames protocols`() {
|
||||
val root = setup(CirEntityId.create("objcnames/protocols/__X"))
|
||||
InlineTypeAliasCirNodeTransformer(storageManager, classifiers).invoke(root)
|
||||
val artificialXClass = root.assertInlinedXClass
|
||||
|
||||
assertEquals(
|
||||
setOf(CirEntityId.create("kotlinx/cinterop/ObjCObject")),
|
||||
artificialXClass.supertypes.map { it as CirClassType }.map { it.classifierId }.toSet(),
|
||||
"Expected 'ObjCObject' supertype being attached automatically"
|
||||
)
|
||||
}
|
||||
|
||||
private fun setup(typeAliasPointingTo: CirEntityId): CirRootNode {
|
||||
val root = buildRootNode(storageManager, 1)
|
||||
root.modules[CirName.create("test-module")] = buildModuleNode(storageManager, 1).apply {
|
||||
packages[CirPackageName.create("under.test")] = buildPackageNode(storageManager, 2).apply {
|
||||
|
||||
typeAliases[CirName.create("X")] = buildTypeAliasNode(
|
||||
storageManager, 2, classifiers, CirEntityId.create("under/test/X")
|
||||
).apply {
|
||||
val underlyingType = CirClassType.createInterned(
|
||||
classId = typeAliasPointingTo,
|
||||
outerType = null, visibility = Visibilities.Public,
|
||||
arguments = emptyList(), isMarkedNullable = false
|
||||
)
|
||||
targetDeclarations[0] = CirTypeAlias.create(
|
||||
annotations = emptyList(),
|
||||
name = CirName.create("X"),
|
||||
typeParameters = emptyList(),
|
||||
visibility = Visibilities.Public,
|
||||
underlyingType = underlyingType,
|
||||
expandedType = underlyingType
|
||||
)
|
||||
}
|
||||
|
||||
classes[CirName.create("X")] = buildClassNode(
|
||||
storageManager, 2, classifiers, null, CirEntityId.create("under/test/X")
|
||||
).apply {
|
||||
targetDeclarations[1] = CirClass.create(
|
||||
name = CirName.create("X"), typeParameters = emptyList(), visibility = Visibilities.Public,
|
||||
companion = null, isCompanion = false, isData = false, isExternal = false,
|
||||
isInner = false, isValue = false, kind = ClassKind.CLASS,
|
||||
modality = Modality.FINAL, annotations = emptyList(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return root
|
||||
}
|
||||
|
||||
private val CirRootNode.xClassNode: CirClassNode
|
||||
get() = modules.getValue(CirName.create("test-module"))
|
||||
.packages.getValue(CirPackageName.create("under.test"))
|
||||
.classes.getValue(CirName.create("X"))
|
||||
|
||||
private val CirRootNode.assertInlinedXClass: CirClass
|
||||
get() = assertNotNull(xClassNode.targetDeclarations[0], "Missing inlined class 'X' at index 0")
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user