FIR2IR: use @Target to put annotations on either property or backing field
This commit is contained in:
committed by
Mikhail Glukhikh
parent
b59832c5fd
commit
f3db113a82
+13
-6
@@ -10,7 +10,7 @@ import org.jetbrains.kotlin.fir.FirAnnotationContainer
|
||||
import org.jetbrains.kotlin.fir.backend.Fir2IrComponents
|
||||
import org.jetbrains.kotlin.fir.declarations.FirProperty
|
||||
import org.jetbrains.kotlin.fir.declarations.FirValueParameter
|
||||
import org.jetbrains.kotlin.fir.declarations.isJvmFieldAnnotation
|
||||
import org.jetbrains.kotlin.fir.declarations.hasMetaAnnotationUseSiteTargets
|
||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
|
||||
@@ -40,17 +40,24 @@ internal class AnnotationGenerator(private val components: Fir2IrComponents) : F
|
||||
irValueParameter.annotations +=
|
||||
firValueParameter.annotations
|
||||
.filter {
|
||||
// TODO: for `null` use-site, refer to targets on the meta annotation
|
||||
it.useSiteTarget == null || !isInConstructor || it.useSiteTarget == AnnotationUseSiteTarget.CONSTRUCTOR_PARAMETER
|
||||
}
|
||||
.toIrAnnotations()
|
||||
}
|
||||
|
||||
private fun FirAnnotationCall.targetsField(): Boolean =
|
||||
// Check if the annotation has a field-targeting meta annotation, e.g., @Target(FIELD)
|
||||
hasMetaAnnotationUseSiteTargets(session, AnnotationUseSiteTarget.FIELD, AnnotationUseSiteTarget.PROPERTY_DELEGATE_FIELD)
|
||||
|
||||
fun generate(irProperty: IrProperty, property: FirProperty) {
|
||||
irProperty.annotations +=
|
||||
property.annotations
|
||||
.filter {
|
||||
!it.isJvmFieldAnnotation &&
|
||||
(it.useSiteTarget == null || it.useSiteTarget == AnnotationUseSiteTarget.PROPERTY)
|
||||
it.useSiteTarget == AnnotationUseSiteTarget.PROPERTY ||
|
||||
// NB: annotation with null use-site should be landed on the property (ahead of field),
|
||||
// unless it has FIELD target on the meta annotation, like @Target(FIELD)
|
||||
(it.useSiteTarget == null && !it.targetsField())
|
||||
}
|
||||
.toIrAnnotations()
|
||||
}
|
||||
@@ -62,9 +69,9 @@ internal class AnnotationGenerator(private val components: Fir2IrComponents) : F
|
||||
irField.annotations +=
|
||||
property.annotations
|
||||
.filter {
|
||||
it.isJvmFieldAnnotation ||
|
||||
it.useSiteTarget == AnnotationUseSiteTarget.FIELD ||
|
||||
it.useSiteTarget == AnnotationUseSiteTarget.PROPERTY_DELEGATE_FIELD
|
||||
it.useSiteTarget == AnnotationUseSiteTarget.FIELD ||
|
||||
it.useSiteTarget == AnnotationUseSiteTarget.PROPERTY_DELEGATE_FIELD ||
|
||||
(it.useSiteTarget == null && it.targetsField())
|
||||
}
|
||||
.toIrAnnotations()
|
||||
}
|
||||
|
||||
+48
-13
@@ -5,10 +5,10 @@
|
||||
|
||||
package org.jetbrains.kotlin.fir.declarations
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
|
||||
import org.jetbrains.kotlin.fir.FirAnnotationContainer
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
|
||||
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
|
||||
@@ -18,6 +18,10 @@ import org.jetbrains.kotlin.fir.types.coneType
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
private val RETENTION_CLASS_ID = ClassId.fromString("kotlin/annotation/Retention")
|
||||
private val TARGET_CLASS_ID = ClassId.fromString("kotlin/annotation/Target")
|
||||
private val JVM_FIELD_CLASS_ID = ClassId.fromString("kotlin/jvm/JvmField")
|
||||
|
||||
private fun FirAnnotationCall.toAnnotationLookupTag(): ConeClassLikeLookupTag =
|
||||
(annotationTypeRef.coneType as ConeClassLikeType).lookupTag
|
||||
|
||||
@@ -27,29 +31,60 @@ fun FirAnnotationCall.toAnnotationClassId(): ClassId =
|
||||
private fun FirAnnotationCall.toAnnotationClass(session: FirSession): FirRegularClass? =
|
||||
toAnnotationLookupTag().toSymbol(session)?.fir as? FirRegularClass
|
||||
|
||||
// TODO: this is temporary solution, we need something better
|
||||
private val FirExpression.callableNameOfMetaAnnotationArgument: Name?
|
||||
get() =
|
||||
(this as? FirQualifiedAccessExpression)?.let {
|
||||
val callableSymbol = (it.calleeReference as? FirResolvedNamedReference)?.resolvedSymbol as? FirCallableSymbol<*>
|
||||
callableSymbol?.callableId?.callableName
|
||||
}
|
||||
|
||||
fun FirAnnotationContainer.nonSourceAnnotations(session: FirSession): List<FirAnnotationCall> =
|
||||
annotations.filter { annotation ->
|
||||
val firAnnotationClass = annotation.toAnnotationClass(session)
|
||||
firAnnotationClass != null && firAnnotationClass.annotations.none { meta ->
|
||||
val firMetaAnnotationClassId = meta.toAnnotationClassId()
|
||||
firMetaAnnotationClassId.asString() == "kotlin/annotation/Retention" &&
|
||||
// TODO: this is temporary solution, we need something better
|
||||
(meta.argumentList.arguments.singleOrNull() as? FirQualifiedAccessExpression)?.let {
|
||||
val callableSymbol = (it.calleeReference as? FirResolvedNamedReference)?.resolvedSymbol as? FirCallableSymbol<*>
|
||||
callableSymbol?.callableId?.callableName
|
||||
} == Name.identifier("SOURCE")
|
||||
meta.toAnnotationClassId() == RETENTION_CLASS_ID &&
|
||||
meta.argumentList.arguments.singleOrNull()?.callableNameOfMetaAnnotationArgument == Name.identifier("SOURCE")
|
||||
}
|
||||
}
|
||||
|
||||
inline val FirProperty.hasJvmFieldAnnotation: Boolean
|
||||
get() = annotations.any { it.isJvmFieldAnnotation }
|
||||
|
||||
inline val FirAnnotationCall.isJvmFieldAnnotation: Boolean
|
||||
get() {
|
||||
val classId = toAnnotationClassId()
|
||||
return classId.packageFqName.asString() == "kotlin.jvm" && classId.relativeClassName.asString() == "JvmField"
|
||||
val FirAnnotationCall.isJvmFieldAnnotation: Boolean
|
||||
get() = toAnnotationClassId() == JVM_FIELD_CLASS_ID
|
||||
|
||||
private fun FirAnnotationCall.useSiteTargetsFromMetaAnnotation(session: FirSession): List<AnnotationUseSiteTarget> {
|
||||
val metaAnnotationAboutTarget =
|
||||
toAnnotationClass(session)?.annotations?.find { it.toAnnotationClassId() == TARGET_CLASS_ID }
|
||||
?: return emptyList()
|
||||
return metaAnnotationAboutTarget.argumentList.arguments.toAnnotationUseSiteTargets()
|
||||
}
|
||||
|
||||
private fun List<FirExpression>.toAnnotationUseSiteTargets(): List<AnnotationUseSiteTarget> =
|
||||
flatMap { arg ->
|
||||
val unwrappedArg = if (arg is FirNamedArgumentExpression) arg.expression else arg
|
||||
if (unwrappedArg is FirArrayOfCall) {
|
||||
unwrappedArg.argumentList.arguments.toAnnotationUseSiteTargets()
|
||||
} else {
|
||||
unwrappedArg.toAnnotationUseSiteTarget()?.let { listOf(it) } ?: emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
private val USE_SITE_TARGET_NAME_MAP =
|
||||
AnnotationUseSiteTarget.values().map { it.name to it }.toMap()
|
||||
|
||||
private fun FirExpression.toAnnotationUseSiteTarget(): AnnotationUseSiteTarget? =
|
||||
// TODO: depending on the context, "PARAMETER" can be mapped to either CONSTRUCTOR_PARAMETER or SETTER_PARAMETER ?
|
||||
callableNameOfMetaAnnotationArgument?.identifier?.let {
|
||||
USE_SITE_TARGET_NAME_MAP[it]
|
||||
}
|
||||
|
||||
fun FirAnnotationCall.hasMetaAnnotationUseSiteTargets(session: FirSession, vararg useSiteTargets: AnnotationUseSiteTarget): Boolean {
|
||||
val meta = useSiteTargetsFromMetaAnnotation(session)
|
||||
return useSiteTargets.any { meta.contains(it) }
|
||||
}
|
||||
|
||||
fun FirAnnotatedDeclaration.hasAnnotation(classId: ClassId): Boolean {
|
||||
return annotations.any { it.toAnnotationClassId() == classId }
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
// TARGET_BACKEND: JVM
|
||||
|
||||
// WITH_RUNTIME
|
||||
|
||||
Reference in New Issue
Block a user