FIR: handle 'SinceKotlin' as a special kind of deprecated

#KT-51850 Fixed
This commit is contained in:
Mikhail Glukhikh
2022-04-05 12:42:11 +03:00
committed by teamcity
parent 07b5bd72ae
commit 9bd6a9c069
19 changed files with 122 additions and 82 deletions
@@ -17255,6 +17255,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/fir/CustomThrowableMessage.kt");
}
@Test
@TestMetadata("differentSinceKotlin.kt")
public void testDifferentSinceKotlin() throws Exception {
runTest("compiler/testData/codegen/box/fir/differentSinceKotlin.kt");
}
@Test
@TestMetadata("ExtensionAlias.kt")
public void testExtensionAlias() throws Exception {
@@ -120,6 +120,18 @@ fun List<FirAnnotation>.getAnnotationsByClassId(classId: ClassId): List<FirAnnot
}
}
inline fun <T> List<FirAnnotation>.mapAnnotationsWithClassIdTo(
classId: ClassId,
destination: MutableCollection<T>,
func: (FirAnnotation) -> T
) {
for (annotation in this) {
if (annotation.annotationTypeRef.coneTypeSafe<ConeClassLikeType>()?.lookupTag?.classId == classId) {
destination.add(func(annotation))
}
}
}
fun FirExpression.unwrapVarargValue(): List<FirExpression> {
return when (this) {
is FirVarargArgumentsExpression -> arguments
@@ -25,7 +25,7 @@ import org.jetbrains.kotlin.name.StandardClassIds.Annotations.ParameterNames.dep
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.safeAs
import org.jetbrains.kotlin.utils.addToStdlib.runUnless
fun FirBasedSymbol<*>.getDeprecation(callSite: FirElement?): DeprecationInfo? {
return when (this) {
@@ -111,20 +111,38 @@ private fun List<FirAnnotation>.extractDeprecationInfoPerUseSite(
currentVersion: ApiVersion,
fromJava: Boolean
): List<Pair<AnnotationUseSiteTarget?, DeprecationInfo>> {
val annotations = getAnnotationsByClassId(StandardClassIds.Annotations.Deprecated).map { it to false } +
getAnnotationsByClassId(StandardClassIds.Annotations.Java.Deprecated).map { it to true }
@Suppress("RemoveExplicitTypeArguments")
val annotations = buildList<Pair<FirAnnotation, Boolean>> {
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 levelApplied(name: Name, level: DeprecationLevelValue): DeprecationLevelValue? {
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 = (levelApplied(deprecatedSinceKotlinHiddenSince, DeprecationLevelValue.HIDDEN)
?: levelApplied(deprecatedSinceKotlinErrorSince, DeprecationLevelValue.ERROR)
?: levelApplied(deprecatedSinceKotlinWarningSince, DeprecationLevelValue.WARNING))
val appliedLevel = (deprecatedLevelApplied(deprecatedSinceKotlinHiddenSince, DeprecationLevelValue.HIDDEN)
?: deprecatedLevelApplied(deprecatedSinceKotlinErrorSince, DeprecationLevelValue.ERROR)
?: deprecatedLevelApplied(deprecatedSinceKotlinWarningSince, DeprecationLevelValue.WARNING))
appliedLevel?.let {
val inheritable = !fromJavaAnnotation && !fromJava
@@ -27,9 +27,12 @@ import org.jetbrains.kotlin.fir.resolve.transformers.*
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.FirUserTypeRef
import org.jetbrains.kotlin.fir.types.coneTypeSafe
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.StandardClassIds.Annotations.Deprecated
import org.jetbrains.kotlin.name.StandardClassIds.Annotations.DeprecatedSinceKotlin
import org.jetbrains.kotlin.name.StandardClassIds.Annotations.WasExperimental
class FirCompilerRequiredAnnotationsResolveProcessor(
session: FirSession,
@@ -120,6 +123,14 @@ private class FirAnnotationResolveTransformer(
) : FirAbstractAnnotationResolveTransformer<Multimap<AnnotationFqn, FirRegularClass>, PersistentList<FirDeclaration>>(
session, scopeSession
) {
companion object {
private val REQUIRED_ANNOTATIONS: Set<ClassId> = setOf(
Deprecated, DeprecatedSinceKotlin, WasExperimental
)
private val REQUIRED_ANNOTATION_NAMES: Set<Name> = REQUIRED_ANNOTATIONS.mapTo(mutableSetOf()) { it.shortClassName }
}
private val predicateBasedProvider = session.predicateBasedProvider
var acceptableFqNames: Set<AnnotationFqn> = emptySet()
@@ -156,9 +167,7 @@ private class FirAnnotationResolveTransformer(
val annotationTypeRef = annotation.annotationTypeRef
if (annotationTypeRef !is FirUserTypeRef) return annotation
val name = annotationTypeRef.qualifier.last().name
if (name != Deprecated.shortClassName && name != DeprecatedSinceKotlin.shortClassName &&
acceptableFqNames.none { it.shortName() == name }
) return annotation
if (name !in REQUIRED_ANNOTATION_NAMES && acceptableFqNames.none { it.shortName() == name }) return annotation
val transformedAnnotation = annotation.transformAnnotationTypeRef(
typeResolverTransformer,