Support instantiation of Java annotations with defaults

For annotations defined in Java, IrProperties do not contain initializers in backing fields,
as annotation properties are represented as Java methods.
Therefore, it is not possible to use initializer values as default values for constructor parameters.

However, K2 stores default values in annotation's constructor parameters,
so it is possible to fix this issue if they're properly transfered to the IR
and inspected in JvmAnnotationImplementationTransformer

#KT-47702 Fixed
#KT-47702 tag fixed-in-k2
This commit is contained in:
Leonid Startsev
2023-07-26 18:44:14 +02:00
committed by Space Team
parent cc2bc5e8b1
commit 7f795f212f
16 changed files with 239 additions and 41 deletions
@@ -17,8 +17,8 @@ import org.jetbrains.kotlin.fir.expressions.builder.buildArrayLiteral
import org.jetbrains.kotlin.fir.expressions.builder.buildConstExpression
import org.jetbrains.kotlin.fir.expressions.builder.buildErrorExpression
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.expectedConeType
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
import org.jetbrains.kotlin.fir.types.createArrayType
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
import org.jetbrains.kotlin.load.java.RXJAVA3_ANNOTATIONS
import org.jetbrains.kotlin.load.java.structure.JavaAnnotation
@@ -54,8 +54,19 @@ val JavaClass.classKind: ClassKind
fun JavaClass.hasMetadataAnnotation(): Boolean =
annotations.any { it.isResolvedTo(JvmAnnotationNames.METADATA_FQ_NAME) }
internal fun Any?.createConstantOrError(session: FirSession): FirExpression {
return createConstantIfAny(session) ?: buildErrorExpression {
internal fun Any?.createConstantOrError(session: FirSession, expectedTypeRef: FirTypeRef? = null): FirExpression {
val coneType = expectedTypeRef?.coneTypeOrNull
val value = if (this is Int && coneType != null) {
// special case for Java literals in annotation default values:
// literal value is always integer, but an expected parameter type can be any other number type
when {
coneType.isByte -> this.toByte()
coneType.isShort -> this.toShort()
coneType.isLong -> this.toLong()
else -> this
}
} else this
return value.createConstantIfAny(session) ?: buildErrorExpression {
diagnostic = ConeSimpleDiagnostic("Unknown value in JavaLiteralAnnotationArgument: $this", DiagnosticKind.Java)
}
}
@@ -84,7 +84,10 @@ internal fun JavaAnnotationArgument.toFirExpression(
session: FirSession, javaTypeParameterStack: JavaTypeParameterStack, expectedTypeRef: FirTypeRef?
): FirExpression {
return when (this) {
is JavaLiteralAnnotationArgument -> value.createConstantOrError(session)
is JavaLiteralAnnotationArgument -> value.createConstantOrError(
session,
expectedTypeRef?.resolveIfJavaType(session, javaTypeParameterStack)
)
is JavaArrayAnnotationArgument -> buildArrayLiteral {
val argumentTypeRef = expectedTypeRef?.let {
typeRef = if (it is FirJavaTypeRef) buildResolvedTypeRef {
@@ -275,7 +278,9 @@ private fun JavaAnnotation.toFirAnnotationCall(session: FirSession): FirAnnotati
classId == StandardClassIds.Annotations.Java.Deprecated -> {
mapOf(
StandardClassIds.Annotations.ParameterNames.deprecatedMessage to "Deprecated in Java".createConstantOrError(session)
StandardClassIds.Annotations.ParameterNames.deprecatedMessage to "Deprecated in Java".createConstantOrError(
session,
)
)
}