[FIR] Implement extension for providing additional type attributes

This commit is contained in:
Dmitriy Novozhilov
2021-09-30 14:34:51 +03:00
committed by TeamCityServer
parent 32709151c3
commit 38877ba842
19 changed files with 374 additions and 25 deletions
@@ -89,6 +89,11 @@ object CompilerConeAttributes {
UnsafeVariance.ANNOTATION_CLASS_ID to UnsafeVariance
)
val classIdByCompilerAttribute: Map<ConeAttribute<*>, ClassId> = compilerAttributeByClassId.entries.associateBy(
keySelector = { it.value },
valueTransform = { it.key }
)
val compilerAttributeByFqName: Map<FqName, ConeAttribute<*>> = compilerAttributeByClassId.mapKeys { it.key.asSingleFqName() }
}
@@ -25,7 +25,7 @@ abstract class ConeAttribute<T : ConeAttribute<T>> : AnnotationMarker {
*
* This function must be symmetrical: a.add(b) == b.add(a)
*/
abstract fun add(other: @UnsafeVariance T?): T
abstract fun add(other: @UnsafeVariance T?): T?
abstract fun isSubtypeOf(other: @UnsafeVariance T?): Boolean
abstract override fun toString(): String
@@ -609,7 +609,7 @@ class FirMemberDeserializer(private val c: FirDeserializationContext) {
private fun ProtoBuf.Type.toTypeRef(context: FirDeserializationContext): FirTypeRef {
return buildResolvedTypeRef {
annotations += context.annotationDeserializer.loadTypeAnnotations(this@toTypeRef, context.nameResolver)
val attributes = annotations.computeTypeAttributes()
val attributes = annotations.computeTypeAttributes(context.session)
type = context.typeDeserializer.type(this@toTypeRef, attributes)
}
}
@@ -109,7 +109,7 @@ class FirTypeDeserializer(
fun type(proto: ProtoBuf.Type): ConeKotlinType {
val annotations = annotationDeserializer.loadTypeAnnotations(proto, nameResolver)
val attributes = annotations.computeTypeAttributes()
val attributes = annotations.computeTypeAttributes(moduleData.session)
return type(proto, attributes)
}
@@ -23,6 +23,8 @@ import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.FirNamedArgumentExpression
import org.jetbrains.kotlin.fir.expressions.builder.buildAnnotation
import org.jetbrains.kotlin.fir.expressions.impl.FirEmptyAnnotationArgumentMapping
import org.jetbrains.kotlin.fir.extensions.extensionService
import org.jetbrains.kotlin.fir.extensions.typeAttributeExtensions
import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.resolve.calls.varargElementType
import org.jetbrains.kotlin.fir.resolve.inference.isSuspendFunctionType
@@ -34,9 +36,11 @@ import org.jetbrains.kotlin.fir.serialization.constant.EnumValue
import org.jetbrains.kotlin.fir.serialization.constant.IntValue
import org.jetbrains.kotlin.fir.serialization.constant.StringValue
import org.jetbrains.kotlin.fir.serialization.constant.toConstantValue
import org.jetbrains.kotlin.fir.symbols.impl.ConeClassLikeLookupTagImpl
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
import org.jetbrains.kotlin.fir.types.impl.FirImplicitNullableAnyTypeRef
import org.jetbrains.kotlin.metadata.ProtoBuf
import org.jetbrains.kotlin.metadata.deserialization.Flags
@@ -606,9 +610,41 @@ class FirElementSerializer private constructor(
fun typeId(type: ConeKotlinType): Int = typeTable[typeProto(type)]
private fun typeProto(typeRef: FirTypeRef, toSuper: Boolean = false): ProtoBuf.Type.Builder {
return typeProto(typeRef.coneType, toSuper, correspondingTypeRef = typeRef).also {
for (annotation in typeRef.annotations) {
extension.serializeTypeAnnotation(annotation, it)
val coneType = typeRef.coneType
return typeProto(coneType, toSuper, correspondingTypeRef = typeRef).also { typeProto ->
for (annotation in coneType.attributes.customAnnotations) {
extension.serializeTypeAnnotation(annotation, typeProto)
}
val compilerAttributes = mutableListOf<ConeAttribute<*>>()
val extensionAttributes = mutableListOf<ConeAttribute<*>>()
for (attribute in coneType.attributes) {
when (attribute) {
is CustomAnnotationTypeAttribute -> continue
in CompilerConeAttributes.classIdByCompilerAttribute -> compilerAttributes += attribute
else -> extensionAttributes += attribute
}
}
for (attribute in compilerAttributes) {
val annotation = buildAnnotation {
annotationTypeRef = buildResolvedTypeRef {
type = ConeClassLikeTypeImpl(
ConeClassLikeLookupTagImpl(CompilerConeAttributes.classIdByCompilerAttribute.getValue(attribute)),
emptyArray(),
isNullable = false
)
}
argumentMapping = FirEmptyAnnotationArgumentMapping
}
extension.serializeTypeAnnotation(annotation, typeProto)
}
for (attributeExtension in session.extensionService.typeAttributeExtensions) {
for (attribute in extensionAttributes) {
val annotation = attributeExtension.convertAttributeToAnnotation(attribute) ?: continue
extension.serializeTypeAnnotation(annotation, typeProto)
}
}
}
}
@@ -15,7 +15,8 @@ abstract class FirExtensionRegistrar {
FirStatusTransformerExtension::class,
FirDeclarationGenerationExtension::class,
AbstractFirAdditionalCheckersExtension::class,
FirSupertypeGenerationExtension::class
FirSupertypeGenerationExtension::class,
FirTypeAttributeExtension::class,
)
}
@@ -44,6 +45,11 @@ abstract class FirExtensionRegistrar {
operator fun ((FirSession) -> FirSupertypeGenerationExtension).unaryPlus() {
registerExtension(FirSupertypeGenerationExtension::class, FirSupertypeGenerationExtension.Factory { this.invoke(it) })
}
@JvmName("plusTypeAttributeExtension")
operator fun ((FirSession) -> FirTypeAttributeExtension).unaryPlus() {
registerExtension(FirTypeAttributeExtension::class, FirTypeAttributeExtension.Factory { this.invoke(it) })
}
}
@OptIn(PluginServicesInitialization::class)
@@ -243,13 +243,16 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() {
}
}
return symbol.constructType(allTypeArguments.toTypedArray(), typeRef.isMarkedNullable, typeRef.annotations.computeTypeAttributes())
.also {
val lookupTag = it.lookupTag
if (lookupTag is ConeClassLikeLookupTagImpl && symbol is FirClassLikeSymbol<*>) {
lookupTag.bindSymbolToLookupTag(session, symbol)
}
return symbol.constructType(
allTypeArguments.toTypedArray(),
typeRef.isMarkedNullable,
typeRef.annotations.computeTypeAttributes(session)
).also {
val lookupTag = it.lookupTag
if (lookupTag is ConeClassLikeLookupTagImpl && symbol is FirClassLikeSymbol<*>) {
lookupTag.bindSymbolToLookupTag(session, symbol)
}
}
}
@OptIn(SymbolInternals::class)
@@ -352,7 +355,7 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() {
} else {
StandardClassIds.FunctionN(typeRef.parametersCount)
}
val attributes = typeRef.annotations.computeTypeAttributes()
val attributes = typeRef.annotations.computeTypeAttributes(session)
val symbol = resolveBuiltInQualified(classId, session)
return ConeClassLikeTypeImpl(
symbol.toLookupTag().also {
@@ -0,0 +1,42 @@
/*
* Copyright 2010-2021 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.fir.extensions
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
import org.jetbrains.kotlin.fir.types.ConeAttribute
import kotlin.reflect.KClass
/**
* Note that when you declare your own attribute you HAVE TO declare accessor to it using following syntax:
*
* class MyAttribute : ConeAttribute<MyAttribute>() {...}
*
* val ConeAttributes.myAttribute: MyAttribute? by ConeAttributes.attributeAccessor<MyAttribute>()
*/
abstract class FirTypeAttributeExtension(session: FirSession) : FirExtension(session) {
companion object {
val NAME = FirExtensionPointName("AdditionalTypeAttributeExtension")
}
final override val name: FirExtensionPointName
get() = NAME
final override val extensionType: KClass<out FirExtension>
get() = FirTypeAttributeExtension::class
abstract fun extractAttributeFromAnnotation(annotation: FirAnnotation): ConeAttribute<*>?
/**
* Please don't convert attributes which you didn't create
* If [attribute] came from compiler or another plugin just return null
*/
abstract fun convertAttributeToAnnotation(attribute: ConeAttribute<*>): FirAnnotation?
fun interface Factory : FirExtension.Factory<FirTypeAttributeExtension>
}
val FirExtensionService.typeAttributeExtensions: List<FirTypeAttributeExtension> by FirExtensionService.registeredExtensions()
@@ -6,10 +6,13 @@
package org.jetbrains.kotlin.fir.types
import org.jetbrains.kotlin.builtins.functions.FunctionClassKind
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
import org.jetbrains.kotlin.fir.expressions.FirConstExpression
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.FirExpressionWithSmartcast
import org.jetbrains.kotlin.fir.extensions.extensionService
import org.jetbrains.kotlin.fir.extensions.typeAttributeExtensions
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.types.impl.FirImplicitBuiltinTypeRef
import org.jetbrains.kotlin.name.ClassId
@@ -109,26 +112,25 @@ fun ConeClassLikeType.toConstKind(): ConstantValueKind<*>? = when (lookupTag.cla
else -> null
}
fun List<FirAnnotation>.computeTypeAttributes(
additionalProcessor: MutableList<ConeAttribute<*>>.(ClassId) -> Unit = {}
): ConeAttributes {
fun List<FirAnnotation>.computeTypeAttributes(session: FirSession): ConeAttributes {
if (this.isEmpty()) return ConeAttributes.Empty
val attributes = mutableListOf<ConeAttribute<*>>()
val customAnnotations = mutableListOf<FirAnnotation>()
for (annotation in this) {
val type = annotation.annotationTypeRef.coneTypeSafe<ConeClassLikeType>() ?: continue
when (val classId = type.lookupTag.classId) {
when (type.lookupTag.classId) {
CompilerConeAttributes.Exact.ANNOTATION_CLASS_ID -> attributes += CompilerConeAttributes.Exact
CompilerConeAttributes.NoInfer.ANNOTATION_CLASS_ID -> attributes += CompilerConeAttributes.NoInfer
CompilerConeAttributes.ExtensionFunctionType.ANNOTATION_CLASS_ID -> attributes += CompilerConeAttributes.ExtensionFunctionType
CompilerConeAttributes.UnsafeVariance.ANNOTATION_CLASS_ID -> attributes += CompilerConeAttributes.UnsafeVariance
else -> {
val annotationAttributes = mutableListOf<ConeAttribute<*>>()
additionalProcessor.invoke(annotationAttributes, classId)
if (annotationAttributes.isEmpty()) {
customAnnotations += annotation
val attributeFromPlugin = session.extensionService.typeAttributeExtensions.firstNotNullOfOrNull {
it.extractAttributeFromAnnotation(annotation)
}
if (attributeFromPlugin != null) {
attributes += attributeFromPlugin
} else {
attributes += annotationAttributes
customAnnotations += annotation
}
}
}