All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.kotlin.fir.declarations.deprecationUtils.kt Maven / Gradle / Ivy

There is a newer version: 2.1.0-RC
Show newest version
/*
 * 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.fir.declarations

import org.jetbrains.kotlin.config.ApiVersion
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
import org.jetbrains.kotlin.fir.FirAnnotationContainer
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.caches.FirCachesFactory
import org.jetbrains.kotlin.fir.caches.firCachesFactory
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.languageVersionSettings
import org.jetbrains.kotlin.fir.references.FirNamedReference
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.forEachType
import org.jetbrains.kotlin.fir.types.toSymbol
import org.jetbrains.kotlin.metadata.deserialization.VersionRequirement
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.name.StandardClassIds.Annotations.ParameterNames
import org.jetbrains.kotlin.name.StandardClassIds.Annotations.ParameterNames.deprecatedSinceKotlinErrorSince
import org.jetbrains.kotlin.name.StandardClassIds.Annotations.ParameterNames.deprecatedSinceKotlinHiddenSince
import org.jetbrains.kotlin.name.StandardClassIds.Annotations.ParameterNames.deprecatedSinceKotlinWarningSince
import org.jetbrains.kotlin.resolve.deprecation.DeprecationInfo
import org.jetbrains.kotlin.resolve.deprecation.DeprecationLevelValue

class DeprecationAnnotationInfoPerUseSiteStorage(val storage: Map>) {
    fun toDeprecationsProvider(firCachesFactory: FirCachesFactory): DeprecationsProvider {
        if (storage.isEmpty()) {
            return EmptyDeprecationsProvider
        }
        @Suppress("UNCHECKED_CAST")
        val specificCallSite = storage.filterKeys { it != null } as Map>
        return DeprecationsProviderImpl(
            firCachesFactory,
            storage[null],
            specificCallSite.takeIf { it.isNotEmpty() }
        )
    }

}

class DeprecationAnnotationInfoPerUseSiteStorageBuilder {
    private val storage = mutableMapOf>()

    fun add(useSite: AnnotationUseSiteTarget?, info: DeprecationAnnotationInfo) {
        storage.getOrPut(useSite) { mutableListOf() }.add(info)
    }

    fun add(useSite: AnnotationUseSiteTarget?, infos: Iterable) {
        storage.getOrPut(useSite) { mutableListOf() }.addAll(infos)
    }

    fun add(other: DeprecationAnnotationInfoPerUseSiteStorage) {
        other.storage.forEach { (useSite, info) ->
            add(useSite, info)
        }
    }

    fun build(): DeprecationAnnotationInfoPerUseSiteStorage {
        return DeprecationAnnotationInfoPerUseSiteStorage(storage)
    }
}

inline fun buildDeprecationAnnotationInfoPerUseSiteStorage(builder: DeprecationAnnotationInfoPerUseSiteStorageBuilder.() -> Unit)
        : DeprecationAnnotationInfoPerUseSiteStorage {
    return DeprecationAnnotationInfoPerUseSiteStorageBuilder().apply(builder).build()
}

private fun FirBasedSymbol<*>.getUseSitesForCallSite(callSite: FirElement?): Array {
    return when (this) {
        is FirPropertySymbol -> when (callSite) {
            is FirVariableAssignment -> arrayOf(AnnotationUseSiteTarget.PROPERTY_SETTER, AnnotationUseSiteTarget.PROPERTY)
            is FirPropertyAccessExpression -> arrayOf(AnnotationUseSiteTarget.PROPERTY_GETTER, AnnotationUseSiteTarget.PROPERTY)
            else -> arrayOf(AnnotationUseSiteTarget.PROPERTY)
        }
        else -> arrayOf()
    }
}

/**
 * Returns deprecation that is declared on the
 * corresponding declaration.
 */
fun FirBasedSymbol<*>.getOwnDeprecation(session: FirSession, callSite: FirElement?): DeprecationInfo? {
    return getOwnDeprecationForCallSite(session.languageVersionSettings, *getUseSitesForCallSite(callSite))
}

/**
 * Returns deprecation that is declared on
 * the corresponding declaration directly
 * or, in case of a typealias, on any of
 * its expansions.
 */
fun FirBasedSymbol<*>.getDeprecation(session: FirSession, callSite: FirElement?): DeprecationInfo? {
    return getDeprecationForCallSite(session, *getUseSitesForCallSite(callSite))
}

fun FirAnnotationContainer.getDeprecationsProvider(session: FirSession): DeprecationsProvider {
    return extractDeprecationInfoPerUseSite(session).toDeprecationsProvider(session.firCachesFactory)
}

fun FirAnnotationContainer.extractDeprecationInfoPerUseSite(
    session: FirSession,
    customAnnotations: List? = annotations,
    getterAnnotations: List? = null,
    setterAnnotations: List? = null,
): DeprecationAnnotationInfoPerUseSiteStorage {
    var fromJava = false
    var versionRequirements: List? = null
    if (this is FirDeclaration) {
        fromJava = this.isJavaOrEnhancement
        versionRequirements = this.versionRequirements
    }
    return buildDeprecationAnnotationInfoPerUseSiteStorage {
        add((customAnnotations ?: annotations).extractDeprecationAnnotationInfoPerUseSite(fromJava, versionRequirements))
        if (this@extractDeprecationInfoPerUseSite is FirProperty) {
            add(
                getDeprecationsAnnotationInfoByUseSiteFromAccessors(
                    session = session,
                    getter = getter,
                    getterAnnotations = getterAnnotations,
                    setter = setter,
                    setterAnnotations = setterAnnotations,
                )
            )
        }
    }
}

fun getDeprecationsProviderFromAccessors(
    session: FirSession,
    getter: FirFunction?,
    setter: FirFunction?
): DeprecationsProvider = getDeprecationsAnnotationInfoByUseSiteFromAccessors(
    session = session,
    getter = getter,
    setter = setter,
).toDeprecationsProvider(session.firCachesFactory)

fun getDeprecationsAnnotationInfoByUseSiteFromAccessors(
    session: FirSession,
    getter: FirFunction?,
    getterAnnotations: List? = getter?.annotations,
    setter: FirFunction?,
    setterAnnotations: List? = setter?.annotations,
): DeprecationAnnotationInfoPerUseSiteStorage = buildDeprecationAnnotationInfoPerUseSiteStorage {
    val setterDeprecations = setter?.extractDeprecationInfoPerUseSite(session, customAnnotations = setterAnnotations)
    setterDeprecations?.storage?.forEach { (useSite, infos) ->
        if (useSite == null) {
            add(AnnotationUseSiteTarget.PROPERTY_SETTER, infos)
        } else {
            add(useSite, infos)
        }
    }

    val getterDeprecations = getter?.extractDeprecationInfoPerUseSite(session, customAnnotations = getterAnnotations)
    getterDeprecations?.storage?.forEach { (useSite, infos) ->
        if (useSite == null) {
            add(AnnotationUseSiteTarget.PROPERTY_GETTER, infos)
        } else {
            add(useSite, infos)
        }
    }
}

fun List.getDeprecationsProviderFromAnnotations(
    session: FirSession,
    fromJava: Boolean,
    versionRequirements: List? = null,
): DeprecationsProvider {
    val deprecationAnnotationByUseSite = extractDeprecationAnnotationInfoPerUseSite(fromJava, versionRequirements)
    return deprecationAnnotationByUseSite.toDeprecationsProvider(session.firCachesFactory)
}

/**
 * Returns deprecation that is declared on the
 * corresponding declaration.
 */
private fun FirBasedSymbol<*>.getOwnDeprecationForCallSite(
    languageVersionSettings: LanguageVersionSettings,
    vararg sites: AnnotationUseSiteTarget
): DeprecationInfo? {
    val deprecations = when (this) {
        is FirCallableSymbol<*> -> getDeprecation(languageVersionSettings)
        is FirClassLikeSymbol<*> -> getOwnDeprecation(languageVersionSettings)
        else -> null
    }
    return (deprecations ?: EmptyDeprecationsPerUseSite).forUseSite(*sites)
}

/**
 * Returns deprecation that is declared on
 * the corresponding declaration directly
 * or, in case of a typealias, on any of
 * its expansions.
 */
fun FirBasedSymbol<*>.getDeprecationForCallSite(
    session: FirSession,
    vararg sites: AnnotationUseSiteTarget,
): DeprecationInfo? {
    return when (this) {
        !is FirTypeAliasSymbol -> getOwnDeprecationForCallSite(session.languageVersionSettings, *sites)
        else -> {
            var worstDeprecationInfo = getOwnDeprecationForCallSite(session.languageVersionSettings, *sites)
            val visited = mutableMapOf()

            resolvedExpandedTypeRef.type.forEachType {
                val deprecationInfo = visited.getOrPut(it) {
                    val symbol = it.toSymbol(session) ?: return@forEachType
                    symbol.getDeprecationForCallSite(session, *sites)
                } ?: return@forEachType

                val currentWorstDeprecation = worstDeprecationInfo

                if (currentWorstDeprecation == null || deprecationInfo > currentWorstDeprecation) {
                    worstDeprecationInfo = deprecationInfo
                }
            }

            worstDeprecationInfo
        }
    }
}

private fun FirAnnotation.getVersionFromArgument(name: Name): ApiVersion? =
    getStringArgument(name)?.let { ApiVersion.parse(it) }

private fun FirAnnotation.getDeprecationLevel(): DeprecationLevelValue? {
    //take last because Annotation might be not resolved yet and arguments passed without explicit names
    val argument = if (resolved) {
        argumentMapping.mapping[ParameterNames.deprecatedLevel]
    } else {
        val call = this as? FirAnnotationCall ?: return null
        call.arguments
            .firstOrNull { it is FirNamedArgumentExpression && it.name == ParameterNames.deprecatedLevel }
            ?.unwrapArgument()
            ?: arguments.lastOrNull()
    } ?: return null
    val targetExpression = argument as? FirQualifiedAccessExpression ?: return null
    val targetName = (targetExpression.calleeReference as? FirNamedReference)?.name?.asString() ?: return null
    return DeprecationLevelValue.values().find { it.name == targetName }
}

val deprecationAnnotationSimpleNames: Set = setOf(
    StandardClassIds.Annotations.Deprecated.shortClassName.asString(),
    StandardClassIds.Annotations.Java.Deprecated.shortClassName.asString(),
    StandardClassIds.Annotations.SinceKotlin.shortClassName.asString(),
)

private fun List.extractDeprecationAnnotationInfoPerUseSite(
    fromJava: Boolean,
    versionRequirements: List?,
): DeprecationAnnotationInfoPerUseSiteStorage {
    // NB: We can't expand typealiases (`toAnnotationClassId`), because it
    // requires `lookupTag.tySymbol()`, but we can have cycles in annotations.
    // See the commit message for an example.

    @Suppress("RemoveExplicitTypeArguments")
    val annotations = buildList> {
        mapAnnotationsWithClassIdTo(StandardClassIds.Annotations.Deprecated, this) { it to false }
        mapAnnotationsWithClassIdTo(StandardClassIds.Annotations.Java.Deprecated, this) { it to true }
        mapAnnotationsWithClassIdTo(StandardClassIds.Annotations.SinceKotlin, this) { it to false }
    }

    return buildDeprecationAnnotationInfoPerUseSiteStorage {
        for ((deprecated, fromJavaAnnotation) in annotations) {
            if (deprecated.unexpandedClassId == StandardClassIds.Annotations.SinceKotlin) {
                val sinceKotlinSingleArgument = deprecated.findArgumentByName(ParameterNames.sinceKotlinVersion)
                val apiVersion = ((sinceKotlinSingleArgument as? FirConstExpression<*>)?.value as? String)
                    ?.let(ApiVersion.Companion::parse) ?: continue
                val wasExperimental = [email protected] {
                    it.unexpandedClassId == StandardClassIds.Annotations.WasExperimental
                }
                if (!wasExperimental) {
                    add(deprecated.useSiteTarget, SinceKotlinInfo(apiVersion))
                }
            } else {
                val deprecationLevel = deprecated.getDeprecationLevel() ?: DeprecationLevelValue.WARNING
                val propagatesToOverride = !fromJavaAnnotation && !fromJava
                val deprecatedSinceKotlin = [email protected] {
                    it.unexpandedClassId == StandardClassIds.Annotations.DeprecatedSinceKotlin
                }
                val message = deprecated.getStringArgument(ParameterNames.deprecatedMessage)

                val deprecatedInfo =
                    if (deprecatedSinceKotlin == null) {
                        DeprecatedInfo(deprecationLevel, propagatesToOverride, message)
                    } else {
                        DeprecatedSinceKotlinInfo(
                            deprecatedSinceKotlin.getVersionFromArgument(deprecatedSinceKotlinWarningSince),
                            deprecatedSinceKotlin.getVersionFromArgument(deprecatedSinceKotlinErrorSince),
                            deprecatedSinceKotlin.getVersionFromArgument(deprecatedSinceKotlinHiddenSince),
                            message,
                            propagatesToOverride
                        )
                    }
                add(deprecated.useSiteTarget, deprecatedInfo)
            }
        }

        versionRequirements?.forEach {
            add(null, RequireKotlinInfo(it))
        }
    }
}

private object IsHiddenEverywhereBesideSuperCalls : FirDeclarationDataKey()

var FirCallableDeclaration.isHiddenEverywhereBesideSuperCalls: Boolean? by FirDeclarationDataRegistry.data(
    IsHiddenEverywhereBesideSuperCalls
)




© 2015 - 2024 Weber Informatics LLC | Privacy Policy