Introduce DeprecatedSinceKotlin annotation

This annotation will be used in the standard library to prevent the new
compiler from reporting deprecation diagnostics in case an older API
version is used (where the declaration was not deprecated yet).

 #KT-23575 Fixed
This commit is contained in:
Alexander Udalov
2018-09-07 16:24:06 +03:00
committed by Mikhail Zarechenskiy
parent b2022144e6
commit 0aaf29c045
21 changed files with 435 additions and 38 deletions
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.resolve.deprecation
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.config.ApiVersion
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
@@ -14,27 +15,78 @@ import org.jetbrains.kotlin.metadata.deserialization.VersionRequirement
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.annotations.argumentValue
import org.jetbrains.kotlin.resolve.calls.checkers.shouldWarnAboutDeprecatedModFromBuiltIns
import org.jetbrains.kotlin.resolve.constants.AnnotationValue
import org.jetbrains.kotlin.resolve.constants.EnumValue
import org.jetbrains.kotlin.resolve.constants.StringValue
import org.jetbrains.kotlin.resolve.deprecation.DeprecationLevelValue.*
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
internal data class DeprecatedByAnnotation(
internal sealed class DeprecatedByAnnotation(
val annotation: AnnotationDescriptor,
override val target: DeclarationDescriptor,
override val propagatesToOverrides: Boolean
) : Deprecation {
override val deprecationLevel: DeprecationLevelValue
get() = when (annotation.argumentValue("level")?.safeAs<EnumValue>()?.enumEntryName?.asString()) {
"WARNING" -> WARNING
"ERROR" -> ERROR
"HIDDEN" -> HIDDEN
else -> WARNING
}
override val message: String?
get() = annotation.argumentValue("message")?.safeAs<StringValue>()?.value
internal val replaceWithValue: String?
get() {
val replaceWithAnnotation = annotation.argumentValue(Deprecated::replaceWith.name)?.safeAs<AnnotationValue>()?.value
return replaceWithAnnotation?.argumentValue(ReplaceWith::expression.name)?.safeAs<StringValue>()?.value
}
class StandardDeprecated(
annotation: AnnotationDescriptor,
target: DeclarationDescriptor,
propagatesToOverrides: Boolean
) : DeprecatedByAnnotation(annotation, target, propagatesToOverrides) {
override val deprecationLevel: DeprecationLevelValue
get() = when (annotation.argumentValue("level")?.safeAs<EnumValue>()?.enumEntryName?.asString()) {
"WARNING" -> WARNING
"ERROR" -> ERROR
"HIDDEN" -> HIDDEN
else -> WARNING
}
}
class DeprecatedSince(
annotation: AnnotationDescriptor,
target: DeclarationDescriptor,
propagatesToOverrides: Boolean,
override val deprecationLevel: DeprecationLevelValue
) : DeprecatedByAnnotation(annotation, target, propagatesToOverrides)
companion object {
fun create(
annotation: AnnotationDescriptor,
target: DeclarationDescriptor,
propagatesToOverrides: Boolean,
apiVersion: ApiVersion
): DeprecatedByAnnotation? {
if (annotation.fqName == KotlinBuiltIns.FQ_NAMES.deprecatedSinceKotlin) {
val level = computeLevelForDeprecatedSinceKotlin(annotation, apiVersion) ?: return null
return DeprecatedSince(annotation, target, propagatesToOverrides, level)
}
return StandardDeprecated(annotation, target, propagatesToOverrides)
}
private fun computeLevelForDeprecatedSinceKotlin(annotation: AnnotationDescriptor, apiVersion: ApiVersion): DeprecationLevelValue? {
fun getSinceVersion(name: String): ApiVersion? =
annotation.argumentValue(name)?.safeAs<StringValue>()?.value?.takeUnless(String::isEmpty)?.let(ApiVersion.Companion::parse)
val hiddenSince = getSinceVersion("hiddenSince")
if (hiddenSince != null && apiVersion >= hiddenSince) return HIDDEN
val errorSince = getSinceVersion("errorSince")
if (errorSince != null && apiVersion >= errorSince) return ERROR
val warningSince = getSinceVersion("warningSince")
if (warningSince != null && apiVersion >= warningSince) return WARNING
return null
}
}
}
internal data class DeprecatedByOverridden(private val deprecations: Collection<Deprecation>) : Deprecation {
@@ -187,23 +187,27 @@ class DeprecationResolver(
private fun DeclarationDescriptor.addDeprecationIfPresent(result: MutableList<Deprecation>) {
val annotation = annotations.findAnnotation(KotlinBuiltIns.FQ_NAMES.deprecated)
?: annotations.findAnnotation(KotlinBuiltIns.FQ_NAMES.deprecatedSinceKotlin)
?: annotations.findAnnotation(JAVA_DEPRECATED)
if (annotation != null) {
val deprecatedByAnnotation =
DeprecatedByAnnotation(
DeprecatedByAnnotation.create(
annotation, this,
deprecationSettings.propagatedToOverrides(annotation)
deprecationSettings.propagatedToOverrides(annotation),
languageVersionSettings.apiVersion
)
val deprecation = when {
this is TypeAliasConstructorDescriptor ->
DeprecatedTypealiasByAnnotation(typeAliasDescriptor, deprecatedByAnnotation)
if (deprecatedByAnnotation != null) {
val deprecation = when {
this is TypeAliasConstructorDescriptor ->
DeprecatedTypealiasByAnnotation(typeAliasDescriptor, deprecatedByAnnotation)
isBuiltInOperatorMod ->
DeprecatedOperatorMod(languageVersionSettings, deprecatedByAnnotation)
isBuiltInOperatorMod ->
DeprecatedOperatorMod(languageVersionSettings, deprecatedByAnnotation)
else -> deprecatedByAnnotation
else -> deprecatedByAnnotation
}
result.add(deprecation)
}
result.add(deprecation)
}
for (deprecation in getDeprecationByVersionRequirement(this)) {
@@ -13,20 +13,11 @@ import org.jetbrains.kotlin.container.PlatformSpecificExtension
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.resolve.annotations.argumentValue
import org.jetbrains.kotlin.resolve.constants.AnnotationValue
import org.jetbrains.kotlin.resolve.constants.StringValue
import org.jetbrains.kotlin.resolve.deprecation.DeprecationLevelValue.*
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
fun Deprecation.deprecatedByOverriddenMessage(): String? = (this as? DeprecatedByOverridden)?.additionalMessage()
fun Deprecation.deprecatedByAnnotationReplaceWithExpression(): String? {
val annotation = (this as? DeprecatedByAnnotation)?.annotation ?: return null
val replaceWithAnnotation =
annotation.argumentValue(kotlin.Deprecated::replaceWith.name)?.safeAs<AnnotationValue>()?.value ?: return null
return replaceWithAnnotation.argumentValue(kotlin.ReplaceWith::expression.name)?.safeAs<StringValue>()?.value
}
fun Deprecation.deprecatedByAnnotationReplaceWithExpression(): String? = (this as? DeprecatedByAnnotation)?.replaceWithValue
internal fun createDeprecationDiagnostic(
element: PsiElement, deprecation: Deprecation, languageVersionSettings: LanguageVersionSettings