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.backend.Fir2IrComponents
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirProperty
|
import org.jetbrains.kotlin.fir.declarations.FirProperty
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirValueParameter
|
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.fir.expressions.FirAnnotationCall
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
|
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
|
||||||
@@ -40,17 +40,24 @@ internal class AnnotationGenerator(private val components: Fir2IrComponents) : F
|
|||||||
irValueParameter.annotations +=
|
irValueParameter.annotations +=
|
||||||
firValueParameter.annotations
|
firValueParameter.annotations
|
||||||
.filter {
|
.filter {
|
||||||
|
// TODO: for `null` use-site, refer to targets on the meta annotation
|
||||||
it.useSiteTarget == null || !isInConstructor || it.useSiteTarget == AnnotationUseSiteTarget.CONSTRUCTOR_PARAMETER
|
it.useSiteTarget == null || !isInConstructor || it.useSiteTarget == AnnotationUseSiteTarget.CONSTRUCTOR_PARAMETER
|
||||||
}
|
}
|
||||||
.toIrAnnotations()
|
.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) {
|
fun generate(irProperty: IrProperty, property: FirProperty) {
|
||||||
irProperty.annotations +=
|
irProperty.annotations +=
|
||||||
property.annotations
|
property.annotations
|
||||||
.filter {
|
.filter {
|
||||||
!it.isJvmFieldAnnotation &&
|
it.useSiteTarget == AnnotationUseSiteTarget.PROPERTY ||
|
||||||
(it.useSiteTarget == null || 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()
|
.toIrAnnotations()
|
||||||
}
|
}
|
||||||
@@ -62,9 +69,9 @@ internal class AnnotationGenerator(private val components: Fir2IrComponents) : F
|
|||||||
irField.annotations +=
|
irField.annotations +=
|
||||||
property.annotations
|
property.annotations
|
||||||
.filter {
|
.filter {
|
||||||
it.isJvmFieldAnnotation ||
|
it.useSiteTarget == AnnotationUseSiteTarget.FIELD ||
|
||||||
it.useSiteTarget == AnnotationUseSiteTarget.FIELD ||
|
it.useSiteTarget == AnnotationUseSiteTarget.PROPERTY_DELEGATE_FIELD ||
|
||||||
it.useSiteTarget == AnnotationUseSiteTarget.PROPERTY_DELEGATE_FIELD
|
(it.useSiteTarget == null && it.targetsField())
|
||||||
}
|
}
|
||||||
.toIrAnnotations()
|
.toIrAnnotations()
|
||||||
}
|
}
|
||||||
|
|||||||
+48
-13
@@ -5,10 +5,10 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.fir.declarations
|
package org.jetbrains.kotlin.fir.declarations
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
|
||||||
import org.jetbrains.kotlin.fir.FirAnnotationContainer
|
import org.jetbrains.kotlin.fir.FirAnnotationContainer
|
||||||
import org.jetbrains.kotlin.fir.FirSession
|
import org.jetbrains.kotlin.fir.FirSession
|
||||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
|
import org.jetbrains.kotlin.fir.expressions.*
|
||||||
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
|
|
||||||
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
||||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
|
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.ClassId
|
||||||
import org.jetbrains.kotlin.name.Name
|
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 =
|
private fun FirAnnotationCall.toAnnotationLookupTag(): ConeClassLikeLookupTag =
|
||||||
(annotationTypeRef.coneType as ConeClassLikeType).lookupTag
|
(annotationTypeRef.coneType as ConeClassLikeType).lookupTag
|
||||||
|
|
||||||
@@ -27,29 +31,60 @@ fun FirAnnotationCall.toAnnotationClassId(): ClassId =
|
|||||||
private fun FirAnnotationCall.toAnnotationClass(session: FirSession): FirRegularClass? =
|
private fun FirAnnotationCall.toAnnotationClass(session: FirSession): FirRegularClass? =
|
||||||
toAnnotationLookupTag().toSymbol(session)?.fir as? 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> =
|
fun FirAnnotationContainer.nonSourceAnnotations(session: FirSession): List<FirAnnotationCall> =
|
||||||
annotations.filter { annotation ->
|
annotations.filter { annotation ->
|
||||||
val firAnnotationClass = annotation.toAnnotationClass(session)
|
val firAnnotationClass = annotation.toAnnotationClass(session)
|
||||||
firAnnotationClass != null && firAnnotationClass.annotations.none { meta ->
|
firAnnotationClass != null && firAnnotationClass.annotations.none { meta ->
|
||||||
val firMetaAnnotationClassId = meta.toAnnotationClassId()
|
meta.toAnnotationClassId() == RETENTION_CLASS_ID &&
|
||||||
firMetaAnnotationClassId.asString() == "kotlin/annotation/Retention" &&
|
meta.argumentList.arguments.singleOrNull()?.callableNameOfMetaAnnotationArgument == Name.identifier("SOURCE")
|
||||||
// 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")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline val FirProperty.hasJvmFieldAnnotation: Boolean
|
inline val FirProperty.hasJvmFieldAnnotation: Boolean
|
||||||
get() = annotations.any { it.isJvmFieldAnnotation }
|
get() = annotations.any { it.isJvmFieldAnnotation }
|
||||||
|
|
||||||
inline val FirAnnotationCall.isJvmFieldAnnotation: Boolean
|
val FirAnnotationCall.isJvmFieldAnnotation: Boolean
|
||||||
get() {
|
get() = toAnnotationClassId() == JVM_FIELD_CLASS_ID
|
||||||
val classId = toAnnotationClassId()
|
|
||||||
return classId.packageFqName.asString() == "kotlin.jvm" && classId.relativeClassName.asString() == "JvmField"
|
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 {
|
fun FirAnnotatedDeclaration.hasAnnotation(classId: ClassId): Boolean {
|
||||||
return annotations.any { it.toAnnotationClassId() == classId }
|
return annotations.any { it.toAnnotationClassId() == classId }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND_FIR: JVM_IR
|
|
||||||
// TARGET_BACKEND: JVM
|
// TARGET_BACKEND: JVM
|
||||||
|
|
||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
|||||||
Reference in New Issue
Block a user