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.0.0
Show newest version
/*
 * 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.declarations

import org.jetbrains.kotlin.config.ApiVersion
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
import org.jetbrains.kotlin.fir.FirAnnotationContainer
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.declarations.utils.isJavaOrEnhancement
import org.jetbrains.kotlin.fir.expressions.*
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.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
import org.jetbrains.kotlin.resolve.deprecation.SimpleDeprecationInfo
import org.jetbrains.kotlin.utils.addToStdlib.runUnless

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

fun FirAnnotationContainer.getDeprecationInfos(currentVersion: ApiVersion): DeprecationsPerUseSite {
    val deprecationByUseSite = mutableMapOf()
    val fromJava = this is FirDeclaration && this.isJavaOrEnhancement
    annotations.extractDeprecationInfoPerUseSite(currentVersion, fromJava).toMap(deprecationByUseSite)

    if (this is FirProperty) {
        getDeprecationsFromAccessors(getter, setter, currentVersion).bySpecificSite?.forEach { (k, v) -> deprecationByUseSite[k] = v }
    }

    return DeprecationsPerUseSite.fromMap(deprecationByUseSite)
}

@OptIn(ExperimentalStdlibApi::class)
fun getDeprecationsFromAccessors(
    getter: FirFunction?,
    setter: FirFunction?,
    currentVersion: ApiVersion
): DeprecationsPerUseSite {
    val perUseSite = buildMap {
        val setterDeprecations = setter?.getDeprecationInfos(currentVersion)
        setterDeprecations?.all?.let { put(AnnotationUseSiteTarget.PROPERTY_SETTER, it) }
        setterDeprecations?.bySpecificSite?.let { putAll(it) }
        val getterDeprecations = getter?.getDeprecationInfos(currentVersion)
        getterDeprecations?.all?.let { put(AnnotationUseSiteTarget.PROPERTY_GETTER, it) }
        getterDeprecations?.bySpecificSite?.let { putAll(it) }
    }
    return if (perUseSite.isEmpty()) EmptyDeprecationsPerUseSite else DeprecationsPerUseSite(null, perUseSite)
}

fun List.getDeprecationInfosFromAnnotations(currentVersion: ApiVersion, fromJava: Boolean): DeprecationsPerUseSite {
    val deprecationByUseSite = extractDeprecationInfoPerUseSite(currentVersion, fromJava).toMap()
    return DeprecationsPerUseSite.fromMap(deprecationByUseSite)
}

fun FirBasedSymbol<*>.getDeprecationForCallSite(
    vararg sites: AnnotationUseSiteTarget
): DeprecationInfo? {
    val deprecations = when (this) {
        is FirCallableSymbol<*> -> deprecation
        is FirClassLikeSymbol<*> -> deprecation
        else -> null
    }
    return (deprecations ?: EmptyDeprecationsPerUseSite).forUseSite(*sites)
}

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 }
}

private fun List.extractDeprecationInfoPerUseSite(
    currentVersion: ApiVersion,
    fromJava: Boolean
): List> {
    @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 annotations.mapNotNull { (deprecated, fromJavaAnnotation) ->
        if (deprecated.classId == StandardClassIds.Annotations.SinceKotlin) {
            val sinceKotlinSingleArgument = deprecated.findArgumentByName(ParameterNames.sinceKotlinVersion)
            val apiVersion = ((sinceKotlinSingleArgument as? FirConstExpression<*>)?.value as? String)
                ?.let(ApiVersion.Companion::parse) ?: return@mapNotNull null
            if (apiVersion <= currentVersion) return@mapNotNull null
            val wasExperimental = this.any { it.classId == StandardClassIds.Annotations.WasExperimental }
            return@mapNotNull runUnless(wasExperimental) {
                deprecated.useSiteTarget to SimpleDeprecationInfo(
                    deprecationLevel = DeprecationLevelValue.HIDDEN,
                    propagatesToOverrides = true,
                    message = null
                )
            }
        }
        val deprecationLevel = deprecated.getDeprecationLevel() ?: DeprecationLevelValue.WARNING
        val deprecatedSinceKotlin = getAnnotationsByClassId(StandardClassIds.Annotations.DeprecatedSinceKotlin).firstOrNull()

        fun deprecatedLevelApplied(name: Name, level: DeprecationLevelValue): DeprecationLevelValue? {
            deprecatedSinceKotlin?.getVersionFromArgument(name)?.takeIf { it <= currentVersion }?.let { return level }
            return level.takeIf { deprecatedSinceKotlin == null && level == deprecationLevel }
        }

        val appliedLevel = (deprecatedLevelApplied(deprecatedSinceKotlinHiddenSince, DeprecationLevelValue.HIDDEN)
            ?: deprecatedLevelApplied(deprecatedSinceKotlinErrorSince, DeprecationLevelValue.ERROR)
            ?: deprecatedLevelApplied(deprecatedSinceKotlinWarningSince, DeprecationLevelValue.WARNING))

        appliedLevel?.let {
            val inheritable = !fromJavaAnnotation && !fromJava
            deprecated.useSiteTarget to SimpleDeprecationInfo(
                it,
                inheritable,
                deprecated.getStringArgument(ParameterNames.deprecatedMessage)
            )
        }
    }
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy