[Commonizer] Support generic/simple annotation commonization (to support kotlinx.cinterop.* annotations)
^KT-59302 Verification Pending
This commit is contained in:
committed by
Space Team
parent
c2ad475153
commit
d510c93241
+1
@@ -1,3 +1,4 @@
|
||||
fun c(){
|
||||
@OptIn(kotlinx.cinterop.ExperimentalForeignApi::class)
|
||||
dummy.max(2,3)
|
||||
}
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
fun sn(){
|
||||
@OptIn(kotlinx.cinterop.ExperimentalForeignApi::class)
|
||||
dummy.max(1,2)
|
||||
}
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
package includedLib
|
||||
|
||||
@OptIn(kotlinx.cinterop.ExperimentalForeignApi::class)
|
||||
fun linuxArm64(
|
||||
a: cinterop.a.StructA
|
||||
) {
|
||||
|
||||
+1
@@ -4,6 +4,7 @@ import kotlinx.cinterop.ExperimentalForeignApi
|
||||
import kotlinx.cinterop.alloc
|
||||
import kotlinx.cinterop.memScoped
|
||||
|
||||
@OptIn(kotlinx.cinterop.ExperimentalForeignApi::class)
|
||||
fun linuxMain() {
|
||||
cinterop.a.a()
|
||||
@OptIn(ExperimentalForeignApi::class)
|
||||
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
package includedLib
|
||||
|
||||
@OptIn(kotlinx.cinterop.ExperimentalForeignApi::class)
|
||||
fun linuxX64(
|
||||
a: cinterop.a.StructA
|
||||
) {
|
||||
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
package lib
|
||||
|
||||
@OptIn(kotlinx.cinterop.ExperimentalForeignApi::class)
|
||||
fun linuxArm64(
|
||||
a: cinterop.lib.a.StructA
|
||||
) {
|
||||
|
||||
+1
@@ -4,6 +4,7 @@ import kotlinx.cinterop.ExperimentalForeignApi
|
||||
import kotlinx.cinterop.alloc
|
||||
import kotlinx.cinterop.memScoped
|
||||
|
||||
@OptIn(kotlinx.cinterop.ExperimentalForeignApi::class)
|
||||
fun linuxMain() {
|
||||
cinterop.lib.a.a()
|
||||
@OptIn(ExperimentalForeignApi::class)
|
||||
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
package lib
|
||||
|
||||
@OptIn(kotlinx.cinterop.ExperimentalForeignApi::class)
|
||||
fun linuxX64(
|
||||
a: cinterop.lib.a.StructA
|
||||
) {
|
||||
|
||||
+36
-239
@@ -5,69 +5,48 @@
|
||||
|
||||
package org.jetbrains.kotlin.commonizer.core
|
||||
|
||||
import org.jetbrains.kotlin.commonizer.cir.*
|
||||
import org.jetbrains.kotlin.commonizer.cir.CirConstantValue.*
|
||||
import org.jetbrains.kotlin.commonizer.core.AnnotationsCommonizer.Companion.FALLBACK_MESSAGE
|
||||
import org.jetbrains.kotlin.commonizer.utils.*
|
||||
import kotlin.DeprecationLevel.WARNING
|
||||
import org.jetbrains.kotlin.commonizer.cir.CirAnnotation
|
||||
import org.jetbrains.kotlin.commonizer.cir.CirClassType
|
||||
import org.jetbrains.kotlin.commonizer.cir.CirEntityId
|
||||
import org.jetbrains.kotlin.commonizer.utils.COMMONIZER_OBJC_INTEROP_CALLABLE_ANNOTATION_ID
|
||||
import org.jetbrains.kotlin.commonizer.utils.DEPRECATED_ANNOTATION_CLASS_ID
|
||||
import org.jetbrains.kotlin.commonizer.utils.isObjCInteropCallableAnnotation
|
||||
|
||||
/**
|
||||
* This is limited implementation of annotations commonizer. It helps to commonize only [kotlin.Deprecated] annotations.
|
||||
*/
|
||||
class AnnotationsCommonizer : AbstractStandardCommonizer<List<CirAnnotation>, List<CirAnnotation>>() {
|
||||
private var deprecatedAnnotationCommonizer: DeprecatedAnnotationCommonizer? = null
|
||||
private var deprecatedAnnotationCommonizerHasResult: Boolean = true
|
||||
private typealias Annotations = Map<CirEntityId, CirAnnotation>
|
||||
|
||||
private val objCInteropCallableAnnotationCommonizer = ObjCInteropCallableAnnotationCommonizer.asCommonizer()
|
||||
private var objCInteropCallableAnnotationCommonizerHasResult = true
|
||||
object AnnotationsCommonizer : AssociativeCommonizer<List<CirAnnotation>> {
|
||||
|
||||
override fun commonizationResult(): List<CirAnnotation> {
|
||||
val deprecatedAnnotation = if (deprecatedAnnotationCommonizerHasResult)
|
||||
deprecatedAnnotationCommonizer?.result else null
|
||||
|
||||
val objCInteropCallableAnnotations = if (objCInteropCallableAnnotationCommonizerHasResult)
|
||||
objCInteropCallableAnnotationCommonizer.result else emptyList()
|
||||
|
||||
return if (deprecatedAnnotation != null) {
|
||||
objCInteropCallableAnnotations.plus(deprecatedAnnotation)
|
||||
} else objCInteropCallableAnnotations
|
||||
}
|
||||
|
||||
override fun initialize(first: List<CirAnnotation>) = Unit
|
||||
|
||||
override fun doCommonizeWith(next: List<CirAnnotation>): Boolean {
|
||||
if (deprecatedAnnotationCommonizerHasResult) {
|
||||
deprecatedAnnotationCommonizerHasResult = doCommonizeDeprecatedAnnotation(next)
|
||||
}
|
||||
|
||||
if (objCInteropCallableAnnotationCommonizerHasResult) {
|
||||
objCInteropCallableAnnotationCommonizerHasResult = objCInteropCallableAnnotationCommonizer.commonizeWith(next)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun doCommonizeDeprecatedAnnotation(next: List<CirAnnotation>): Boolean {
|
||||
val nextDeprecatedAnnotation = next.firstOrNull { it.type.classifierId == DEPRECATED_ANNOTATION_CLASS_ID } ?: return true
|
||||
|
||||
val deprecatedAnnotationCommonizer = deprecatedAnnotationCommonizer
|
||||
?: DeprecatedAnnotationCommonizer().also { this.deprecatedAnnotationCommonizer = it }
|
||||
|
||||
return deprecatedAnnotationCommonizer.commonizeWith(nextDeprecatedAnnotation)
|
||||
}
|
||||
|
||||
companion object {
|
||||
internal const val FALLBACK_MESSAGE = "See concrete deprecation messages in actual declarations"
|
||||
}
|
||||
}
|
||||
|
||||
object ObjCInteropCallableAnnotationCommonizer : AssociativeCommonizer<List<CirAnnotation>> {
|
||||
override fun commonize(first: List<CirAnnotation>, second: List<CirAnnotation>): List<CirAnnotation> {
|
||||
val firstAnnotations = first.associateBy { it.type.classifierId }
|
||||
val secondAnnotations = second.associateBy { it.type.classifierId }
|
||||
|
||||
return setOfNotNull(
|
||||
commonizedObjcCallableAnnotation(first, second),
|
||||
commonizedDeprecatedAnnotation(firstAnnotations, secondAnnotations)
|
||||
).plus(commonizedSimpleAnnotations(firstAnnotations, secondAnnotations)).toList()
|
||||
}
|
||||
|
||||
private fun commonizedObjcCallableAnnotation(first: List<CirAnnotation>, second: List<CirAnnotation>): CirAnnotation? {
|
||||
return if (first.any { it.type.classifierId.isObjCInteropCallableAnnotation } &&
|
||||
second.any { it.type.classifierId.isObjCInteropCallableAnnotation }
|
||||
) {
|
||||
objCCallableAnnotationList
|
||||
} else emptyList()
|
||||
) objCCallableAnnotation else null
|
||||
}
|
||||
|
||||
private fun commonizedSimpleAnnotations(first: Annotations, second: Annotations): List<CirAnnotation> {
|
||||
fun CirAnnotation.isSimple() = type.arguments.isEmpty() && !type.classifierId.isObjCInteropCallableAnnotation &&
|
||||
annotationValueArguments.isEmpty() && constantValueArguments.isEmpty()
|
||||
|
||||
return first.values.filter { annotation ->
|
||||
annotation.isSimple() && second[annotation.type.classifierId].let { secondAnnotation ->
|
||||
secondAnnotation != null && secondAnnotation.isSimple()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun commonizedDeprecatedAnnotation(first: Annotations, second: Annotations): CirAnnotation? {
|
||||
val firstDeprecation = first[DEPRECATED_ANNOTATION_CLASS_ID] ?: return null
|
||||
val secondDeprecation = second[DEPRECATED_ANNOTATION_CLASS_ID] ?: return null
|
||||
return DeprecationAnnotationCommonizer.commonize(firstDeprecation, secondDeprecation)
|
||||
}
|
||||
|
||||
private val objCCallableAnnotation = CirAnnotation.createInterned(
|
||||
@@ -78,186 +57,4 @@ object ObjCInteropCallableAnnotationCommonizer : AssociativeCommonizer<List<CirA
|
||||
constantValueArguments = emptyMap(),
|
||||
annotationValueArguments = emptyMap()
|
||||
)
|
||||
|
||||
private val objCCallableAnnotationList = listOf(objCCallableAnnotation)
|
||||
}
|
||||
|
||||
private class DeprecatedAnnotationCommonizer : Commonizer<CirAnnotation, CirAnnotation> {
|
||||
private var level: DeprecationLevel? = null // null level means that state is empty
|
||||
private var message: String? = null // null -> message is not equal
|
||||
private lateinit var replaceWithExpression: String
|
||||
private lateinit var replaceWithImports: List<String>
|
||||
|
||||
override val result: CirAnnotation
|
||||
get() {
|
||||
val level: DeprecationLevel = level ?: failInEmptyState()
|
||||
val messageValue: StringValue = message.toDeprecationMessageValue()
|
||||
|
||||
val constantValueArguments: Map<CirName, CirConstantValue> =
|
||||
if (level == WARNING) {
|
||||
// don't populate with the default level value
|
||||
compactMapOf(PROPERTY_NAME_MESSAGE, messageValue)
|
||||
} else
|
||||
compactMapOf(
|
||||
PROPERTY_NAME_MESSAGE, messageValue,
|
||||
PROPERTY_NAME_LEVEL, level.toDeprecationLevelValue()
|
||||
)
|
||||
|
||||
val annotationValueArguments: Map<CirName, CirAnnotation> =
|
||||
if (replaceWithExpression.isEmpty() && replaceWithImports.isEmpty()) {
|
||||
// don't populate with empty (default) ReplaceWith
|
||||
emptyMap()
|
||||
} else
|
||||
compactMapOf(PROPERTY_NAME_REPLACE_WITH, replaceWithExpression.toReplaceWithValue(replaceWithImports))
|
||||
|
||||
return CirAnnotation.createInterned(
|
||||
type = DEPRECATED_ANNOTATION_TYPE,
|
||||
constantValueArguments = constantValueArguments,
|
||||
annotationValueArguments = annotationValueArguments
|
||||
)
|
||||
}
|
||||
|
||||
override fun commonizeWith(next: CirAnnotation): Boolean {
|
||||
val nextLevel: DeprecationLevel = next.getDeprecationLevel() ?: WARNING
|
||||
val nextMessage: String = next.getDeprecationMessage().orEmpty()
|
||||
val nextReplaceWith: CirAnnotation? = next.getReplaceWith()
|
||||
val nextReplaceWithExpression: String = nextReplaceWith?.getReplaceWithExpression().orEmpty()
|
||||
val nextReplaceWithImports: List<String> = nextReplaceWith?.getReplaceWithImports().orEmpty()
|
||||
|
||||
return if (level != null) {
|
||||
doCommonizeWith(nextLevel, nextMessage, nextReplaceWithExpression, nextReplaceWithImports)
|
||||
} else {
|
||||
// empty, just fill in
|
||||
initialize(nextLevel, nextMessage, nextReplaceWithExpression, nextReplaceWithImports)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
private fun initialize(
|
||||
nextLevel: DeprecationLevel,
|
||||
nextMessage: String?,
|
||||
nextReplaceWithExpression: String,
|
||||
nextReplaceWithImports: List<String>
|
||||
) {
|
||||
level = nextLevel
|
||||
message = nextMessage
|
||||
replaceWithExpression = nextReplaceWithExpression
|
||||
replaceWithImports = nextReplaceWithImports
|
||||
}
|
||||
|
||||
private fun doCommonizeWith(
|
||||
nextLevel: DeprecationLevel,
|
||||
nextMessage: String?,
|
||||
nextReplaceWithExpression: String,
|
||||
nextReplaceWithImports: List<String>
|
||||
): Boolean {
|
||||
if (nextLevel.ordinal > level!!.ordinal)
|
||||
level = nextLevel
|
||||
|
||||
if (nextMessage != message)
|
||||
message = null
|
||||
|
||||
if (nextReplaceWithExpression != replaceWithExpression || nextReplaceWithImports != replaceWithImports) {
|
||||
replaceWithExpression = ""
|
||||
replaceWithImports = emptyList()
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
companion object {
|
||||
private val PROPERTY_NAME_MESSAGE = CirName.create(Deprecated::message.name)
|
||||
private val PROPERTY_NAME_REPLACE_WITH = CirName.create(Deprecated::replaceWith.name)
|
||||
private val PROPERTY_NAME_LEVEL = CirName.create(Deprecated::level.name)
|
||||
|
||||
private val PROPERTY_NAME_EXPRESSION = CirName.create(ReplaceWith::expression.name)
|
||||
private val PROPERTY_NAME_IMPORTS = CirName.create(ReplaceWith::imports.name)
|
||||
|
||||
// Optimization: Keep most frequently used message constants.
|
||||
private val FREQUENTLY_USED_MESSAGE_VALUES: Map<String, StringValue> = listOf(
|
||||
"Use constructor instead",
|
||||
"Use factory method instead"
|
||||
).associateWith { StringValue(it) }
|
||||
private val FALLBACK_MESSAGE_VALUE = StringValue(FALLBACK_MESSAGE)
|
||||
|
||||
private val DEPRECATED_ANNOTATION_TYPE = buildAnnotationType(DEPRECATED_ANNOTATION_CLASS_ID)
|
||||
private val REPLACE_WITH_ANNOTATION_TYPE = buildAnnotationType(CirEntityId.create("kotlin/ReplaceWith"))
|
||||
|
||||
private val DEPRECATION_LEVEL_CLASS_ID = CirEntityId.create("kotlin/DeprecationLevel")
|
||||
|
||||
// Optimization: Keep DeprecationLevel enum constants.
|
||||
private val DEPRECATION_LEVEL_ENUM_ENTRY_VALUES: Map<String, EnumValue> = DeprecationLevel.values().associate {
|
||||
it.name to EnumValue(DEPRECATION_LEVEL_CLASS_ID, CirName.create(it.name))
|
||||
}
|
||||
|
||||
private fun buildAnnotationType(classId: CirEntityId) = CirClassType.createInterned(
|
||||
classId = classId,
|
||||
outerType = null,
|
||||
arguments = emptyList(),
|
||||
isMarkedNullable = false
|
||||
)
|
||||
|
||||
private fun CirAnnotation.getDeprecationMessage(): String? = constantValueArguments.getString(PROPERTY_NAME_MESSAGE)
|
||||
|
||||
private fun String?.toDeprecationMessageValue(): StringValue =
|
||||
if (this == null)
|
||||
FALLBACK_MESSAGE_VALUE
|
||||
else
|
||||
FREQUENTLY_USED_MESSAGE_VALUES[this] ?: StringValue(this)
|
||||
|
||||
private fun CirAnnotation.getDeprecationLevel(): DeprecationLevel? {
|
||||
val enumEntryName = constantValueArguments.getEnumEntryName(PROPERTY_NAME_LEVEL) ?: return null
|
||||
return DeprecationLevel.values().firstOrNull { it.name == enumEntryName }
|
||||
}
|
||||
|
||||
private fun DeprecationLevel.toDeprecationLevelValue(): EnumValue =
|
||||
DEPRECATION_LEVEL_ENUM_ENTRY_VALUES.getValue(name)
|
||||
|
||||
private fun CirAnnotation.getReplaceWith(): CirAnnotation? =
|
||||
annotationValueArguments.getAnnotation(PROPERTY_NAME_REPLACE_WITH)
|
||||
|
||||
private fun CirAnnotation.getReplaceWithExpression(): String? =
|
||||
constantValueArguments.getString(PROPERTY_NAME_EXPRESSION)
|
||||
|
||||
private fun CirAnnotation.getReplaceWithImports(): List<String>? =
|
||||
constantValueArguments.getStringArray(PROPERTY_NAME_IMPORTS)
|
||||
|
||||
private fun String.toReplaceWithValue(imports: List<String>): CirAnnotation =
|
||||
createReplaceWithAnnotation(this, imports)
|
||||
|
||||
private inline fun Map<CirName, CirConstantValue>.getString(name: CirName): String? =
|
||||
(this[name] as? StringValue)?.value
|
||||
|
||||
private inline fun Map<CirName, CirConstantValue>.getEnumEntryName(name: CirName): String? =
|
||||
(this[name] as? EnumValue)?.enumEntryName?.name
|
||||
|
||||
private inline fun Map<CirName, CirAnnotation>.getAnnotation(name: CirName): CirAnnotation? =
|
||||
this[name]
|
||||
|
||||
private inline fun Map<CirName, CirConstantValue>.getStringArray(name: CirName): List<String>? {
|
||||
val elements: List<CirConstantValue> = (this[name] as? ArrayValue)?.elements ?: return null
|
||||
if (elements.isEmpty()) return emptyList()
|
||||
|
||||
val result = ArrayList<String>(elements.size)
|
||||
for (element in elements) {
|
||||
if (element is StringValue) {
|
||||
result += element.value
|
||||
} else
|
||||
return null
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private inline fun createReplaceWithAnnotation(expression: String, imports: List<String>): CirAnnotation =
|
||||
CirAnnotation.createInterned(
|
||||
type = REPLACE_WITH_ANNOTATION_TYPE,
|
||||
constantValueArguments = compactMapOf(
|
||||
PROPERTY_NAME_EXPRESSION, StringValue(expression),
|
||||
PROPERTY_NAME_IMPORTS, ArrayValue(imports.compactMap(::StringValue))
|
||||
),
|
||||
annotationValueArguments = emptyMap()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ class ClassConstructorCommonizer(
|
||||
private val visibility = VisibilityCommonizer.equalizing()
|
||||
private val typeParameterListCommonizer = TypeParameterListCommonizer(typeCommonizer)
|
||||
private val valueParametersCommonizer = CallableValueParametersCommonizer(typeCommonizer)
|
||||
private val annotationsCommonizer: AnnotationsCommonizer = AnnotationsCommonizer()
|
||||
private val annotationsCommonizer = AnnotationsCommonizer.asCommonizer()
|
||||
|
||||
override fun commonizationResult(): CirClassConstructor? {
|
||||
val valueParameters = valueParametersCommonizer.result ?: return null
|
||||
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.commonizer.core
|
||||
|
||||
import org.jetbrains.kotlin.commonizer.cir.*
|
||||
import org.jetbrains.kotlin.commonizer.utils.DEPRECATED_ANNOTATION_CLASS_ID
|
||||
import org.jetbrains.kotlin.commonizer.utils.compactMap
|
||||
import org.jetbrains.kotlin.commonizer.utils.compactMapOf
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
object DeprecationAnnotationCommonizer : AssociativeCommonizer<CirAnnotation> {
|
||||
override fun commonize(first: CirAnnotation, second: CirAnnotation): CirAnnotation {
|
||||
val deprecationLevel = run {
|
||||
val firstLevel = first.getDeprecationLevel() ?: DeprecationLevel.WARNING
|
||||
val secondLevel = second.getDeprecationLevel() ?: DeprecationLevel.WARNING
|
||||
if (secondLevel.ordinal > firstLevel.ordinal) secondLevel else firstLevel
|
||||
}
|
||||
|
||||
val deprecationMessage = run {
|
||||
val firstMessage = first.getDeprecationMessage() ?: return@run null
|
||||
val secondMessage = second.getDeprecationMessage() ?: return@run null
|
||||
if (firstMessage == secondMessage) firstMessage else null
|
||||
}
|
||||
|
||||
val replaceWith = run {
|
||||
val firstReplaceWith = first.getReplaceWith() ?: return@run null
|
||||
val secondReplaceWith = second.getReplaceWith() ?: return@run null
|
||||
|
||||
val firstReplaceWithExpression = firstReplaceWith.getReplaceWithExpression().orEmpty()
|
||||
val secondReplaceWithExpression = secondReplaceWith.getReplaceWithExpression().orEmpty()
|
||||
|
||||
val firstReplaceWithImports = firstReplaceWith.getReplaceWithImports().orEmpty()
|
||||
val secondReplaceWithImports = secondReplaceWith.getReplaceWithImports().orEmpty()
|
||||
|
||||
if (
|
||||
firstReplaceWithExpression == secondReplaceWithExpression &&
|
||||
firstReplaceWithImports == secondReplaceWithImports &&
|
||||
/* Empty replace with */
|
||||
(firstReplaceWithExpression.isNotEmpty() || firstReplaceWithImports.isNotEmpty())
|
||||
) firstReplaceWithExpression.toReplaceWithValue(firstReplaceWithImports) else null
|
||||
}
|
||||
|
||||
|
||||
val constantValueArguments: Map<CirName, CirConstantValue> = if (deprecationLevel == DeprecationLevel.WARNING) {
|
||||
// don't populate with the default level value
|
||||
compactMapOf(PROPERTY_NAME_MESSAGE, deprecationMessage.toDeprecationMessageValue())
|
||||
} else compactMapOf(
|
||||
PROPERTY_NAME_MESSAGE, deprecationMessage.toDeprecationMessageValue(),
|
||||
PROPERTY_NAME_LEVEL, deprecationLevel.toDeprecationLevelValue()
|
||||
)
|
||||
|
||||
val annotationValueArguments: Map<CirName, CirAnnotation> = if (replaceWith == null) {
|
||||
// don't populate with empty (default) ReplaceWith
|
||||
emptyMap()
|
||||
} else compactMapOf(PROPERTY_NAME_REPLACE_WITH, replaceWith)
|
||||
|
||||
return CirAnnotation.createInterned(
|
||||
type = DEPRECATED_ANNOTATION_TYPE,
|
||||
constantValueArguments = constantValueArguments,
|
||||
annotationValueArguments = annotationValueArguments
|
||||
)
|
||||
}
|
||||
|
||||
private val PROPERTY_NAME_MESSAGE = CirName.create(Deprecated::message.name)
|
||||
private val PROPERTY_NAME_REPLACE_WITH = CirName.create(Deprecated::replaceWith.name)
|
||||
private val PROPERTY_NAME_LEVEL = CirName.create(Deprecated::level.name)
|
||||
|
||||
private val PROPERTY_NAME_EXPRESSION = CirName.create(ReplaceWith::expression.name)
|
||||
private val PROPERTY_NAME_IMPORTS = CirName.create(ReplaceWith::imports.name)
|
||||
|
||||
internal const val FALLBACK_MESSAGE = "See concrete deprecation messages in actual declarations"
|
||||
|
||||
|
||||
// Optimization: Keep most frequently used message constants.
|
||||
private val FREQUENTLY_USED_MESSAGE_VALUES: Map<String, CirConstantValue.StringValue> = listOf(
|
||||
"Use constructor instead",
|
||||
"Use factory method instead"
|
||||
).associateWith { CirConstantValue.StringValue(it) }
|
||||
private val FALLBACK_MESSAGE_VALUE = CirConstantValue.StringValue(FALLBACK_MESSAGE)
|
||||
|
||||
private val DEPRECATED_ANNOTATION_TYPE = buildAnnotationType(DEPRECATED_ANNOTATION_CLASS_ID)
|
||||
private val REPLACE_WITH_ANNOTATION_TYPE = buildAnnotationType(CirEntityId.create("kotlin/ReplaceWith"))
|
||||
|
||||
private val DEPRECATION_LEVEL_CLASS_ID = CirEntityId.create("kotlin/DeprecationLevel")
|
||||
|
||||
// Optimization: Keep DeprecationLevel enum constants.
|
||||
private val DEPRECATION_LEVEL_ENUM_ENTRY_VALUES: Map<String, CirConstantValue.EnumValue> = DeprecationLevel.entries.associate {
|
||||
it.name to CirConstantValue.EnumValue(DEPRECATION_LEVEL_CLASS_ID, CirName.create(it.name))
|
||||
}
|
||||
|
||||
private fun buildAnnotationType(classId: CirEntityId) = CirClassType.createInterned(
|
||||
classId = classId,
|
||||
outerType = null,
|
||||
arguments = emptyList(),
|
||||
isMarkedNullable = false
|
||||
)
|
||||
|
||||
private fun CirAnnotation.getDeprecationMessage(): String? = constantValueArguments.getString(PROPERTY_NAME_MESSAGE)
|
||||
|
||||
private fun String?.toDeprecationMessageValue(): CirConstantValue.StringValue =
|
||||
if (this == null)
|
||||
FALLBACK_MESSAGE_VALUE
|
||||
else
|
||||
FREQUENTLY_USED_MESSAGE_VALUES[this] ?: CirConstantValue.StringValue(this)
|
||||
|
||||
private fun CirAnnotation.getDeprecationLevel(): DeprecationLevel? {
|
||||
val enumEntryName = constantValueArguments.getEnumEntryName(PROPERTY_NAME_LEVEL) ?: return null
|
||||
return DeprecationLevel.entries.firstOrNull { it.name == enumEntryName }
|
||||
}
|
||||
|
||||
private fun DeprecationLevel.toDeprecationLevelValue(): CirConstantValue.EnumValue =
|
||||
DEPRECATION_LEVEL_ENUM_ENTRY_VALUES.getValue(name)
|
||||
|
||||
private fun CirAnnotation.getReplaceWith(): CirAnnotation? =
|
||||
annotationValueArguments.getAnnotation(PROPERTY_NAME_REPLACE_WITH)
|
||||
|
||||
private fun CirAnnotation.getReplaceWithExpression(): String? =
|
||||
constantValueArguments.getString(PROPERTY_NAME_EXPRESSION)
|
||||
|
||||
private fun CirAnnotation.getReplaceWithImports(): List<String>? =
|
||||
constantValueArguments.getStringArray(PROPERTY_NAME_IMPORTS)
|
||||
|
||||
private fun String.toReplaceWithValue(imports: List<String>): CirAnnotation =
|
||||
createReplaceWithAnnotation(this, imports)
|
||||
|
||||
private inline fun Map<CirName, CirConstantValue>.getString(name: CirName): String? =
|
||||
(this[name] as? CirConstantValue.StringValue)?.value
|
||||
|
||||
private inline fun Map<CirName, CirConstantValue>.getEnumEntryName(name: CirName): String? =
|
||||
(this[name] as? CirConstantValue.EnumValue)?.enumEntryName?.name
|
||||
|
||||
private inline fun Map<CirName, CirAnnotation>.getAnnotation(name: CirName): CirAnnotation? =
|
||||
this[name]
|
||||
|
||||
private inline fun Map<CirName, CirConstantValue>.getStringArray(name: CirName): List<String>? {
|
||||
val elements: List<CirConstantValue> = (this[name] as? CirConstantValue.ArrayValue)?.elements ?: return null
|
||||
if (elements.isEmpty()) return emptyList()
|
||||
|
||||
val result = ArrayList<String>(elements.size)
|
||||
for (element in elements) {
|
||||
if (element is CirConstantValue.StringValue) {
|
||||
result += element.value
|
||||
} else
|
||||
return null
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private inline fun createReplaceWithAnnotation(expression: String, imports: List<String>): CirAnnotation =
|
||||
CirAnnotation.createInterned(
|
||||
type = REPLACE_WITH_ANNOTATION_TYPE,
|
||||
constantValueArguments = compactMapOf(
|
||||
PROPERTY_NAME_EXPRESSION, CirConstantValue.StringValue(expression),
|
||||
PROPERTY_NAME_IMPORTS, CirConstantValue.ArrayValue(imports.compactMap(CirConstantValue::StringValue))
|
||||
),
|
||||
annotationValueArguments = emptyMap()
|
||||
)
|
||||
}
|
||||
@@ -16,7 +16,7 @@ class FunctionCommonizer(
|
||||
val functionOrProperty = functionOrPropertyBaseCommonizer(values) ?: return null
|
||||
val valueParametersResult = CallableValueParametersCommonizer(typeCommonizer).commonize(values) ?: return null
|
||||
return CirFunction(
|
||||
annotations = AnnotationsCommonizer().commonize(values.map { it.annotations })
|
||||
annotations = AnnotationsCommonizer.commonize(values.map { it.annotations.toList() })
|
||||
?.plus(functionOrProperty.additionalAnnotations)
|
||||
?: return null,
|
||||
name = values.first().name,
|
||||
|
||||
+4
-4
@@ -42,7 +42,7 @@ class AnnotationsCommonizerTest : AbstractCommonizerTest<List<CirAnnotation>, Li
|
||||
|
||||
@Test
|
||||
fun noRelevantAnnotations3() = doTestSuccess(
|
||||
expected = emptyList(),
|
||||
expected = listOf(mockAnnotation("org/sample/Foo")),
|
||||
listOf(mockAnnotation("org/sample/Foo")),
|
||||
listOf(mockAnnotation("org/sample/Foo")),
|
||||
listOf(mockAnnotation("org/sample/Foo"))
|
||||
@@ -58,7 +58,7 @@ class AnnotationsCommonizerTest : AbstractCommonizerTest<List<CirAnnotation>, Li
|
||||
|
||||
@Test
|
||||
fun noRelevantAnnotations5() = doTestSuccess(
|
||||
expected = emptyList(),
|
||||
expected = listOf(mockAnnotation("kotlin/PublishedApi")),
|
||||
listOf(mockAnnotation("kotlin/PublishedApi")),
|
||||
listOf(mockAnnotation("kotlin/PublishedApi")),
|
||||
listOf(mockAnnotation("kotlin/PublishedApi"))
|
||||
@@ -74,7 +74,7 @@ class AnnotationsCommonizerTest : AbstractCommonizerTest<List<CirAnnotation>, Li
|
||||
|
||||
@Test
|
||||
fun differentMessages() = doTestSuccess(
|
||||
expected = listOf(mockDeprecated(message = AnnotationsCommonizer.FALLBACK_MESSAGE)),
|
||||
expected = listOf(mockDeprecated(message = DeprecationAnnotationCommonizer.FALLBACK_MESSAGE)),
|
||||
listOf(mockDeprecated(message = "please don't use it because ...")),
|
||||
listOf(mockDeprecated(message = "it should not be used due to ...")),
|
||||
listOf(mockDeprecated(message = "please don't use it because ..."))
|
||||
@@ -276,7 +276,7 @@ class AnnotationsCommonizerTest : AbstractCommonizerTest<List<CirAnnotation>, Li
|
||||
)
|
||||
)
|
||||
|
||||
override fun createCommonizer() = AnnotationsCommonizer()
|
||||
override fun createCommonizer() = AnnotationsCommonizer.asCommonizer()
|
||||
}
|
||||
|
||||
private fun mockAnnotation(
|
||||
|
||||
+33
@@ -135,4 +135,37 @@ class HierarchicalFunctionCommonizationTest : AbstractInlineSourcesCommonization
|
||||
result.assertCommonized("(a, b)", "expect fun x(): ABCD")
|
||||
result.assertCommonized("((a,b), c)", "expect fun x(): ABCD")
|
||||
}
|
||||
|
||||
fun `test function with simple annotation`() {
|
||||
val result = commonize {
|
||||
outputTarget("(a, b)")
|
||||
registerDependency("a", "b", "(a, b)") { source("annotation class FooAnnotation") }
|
||||
simpleSingleSourceTarget("a", "@FooAnnotation fun x() = Unit")
|
||||
simpleSingleSourceTarget("b", "@FooAnnotation fun x() = Unit")
|
||||
}
|
||||
|
||||
result.assertCommonized("(a, b)", "@FooAnnotation expect fun x()")
|
||||
}
|
||||
|
||||
fun `test function with non-simple annotation - 1`() {
|
||||
val result = commonize {
|
||||
outputTarget("(a, b)")
|
||||
registerDependency("a", "b", "(a, b)") { source("annotation class FooAnnotation(val param: String)") }
|
||||
simpleSingleSourceTarget("a", """@FooAnnotation("a") fun x() = Unit""")
|
||||
simpleSingleSourceTarget("b", """@FooAnnotation("b") fun x() = Unit""")
|
||||
}
|
||||
|
||||
result.assertCommonized("(a, b)", "expect fun x()")
|
||||
}
|
||||
|
||||
fun `test function with non-simple annotation - 2`() {
|
||||
val result = commonize {
|
||||
outputTarget("(a, b)")
|
||||
registerDependency("a", "b", "(a, b)") { source("annotation class FooAnnotation<T: Any>(val param: String)") }
|
||||
simpleSingleSourceTarget("a", """@FooAnnotation<Unit>("a") fun x() = Unit""")
|
||||
simpleSingleSourceTarget("b", """@FooAnnotation<Unit>("b") fun x() = Unit""")
|
||||
}
|
||||
|
||||
result.assertCommonized("(a, b)", "expect fun x()")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user