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
@@ -6644,6 +6644,34 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirOldFronte
public void testWarningOnConstructorErrorOnClass() throws Exception {
runTest("compiler/testData/diagnostics/tests/deprecated/warningOnConstructorErrorOnClass.kt");
}
@TestMetadata("compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class DeprecatedSinceKotlin extends AbstractFirOldFrontendDiagnosticsTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInDeprecatedSinceKotlin() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
}
@TestMetadata("error.kt")
public void testError() throws Exception {
runTest("compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/error.kt");
}
@TestMetadata("hidden.kt")
public void testHidden() throws Exception {
runTest("compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/hidden.kt");
}
@TestMetadata("warning.kt")
public void testWarning() throws Exception {
runTest("compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/warning.kt");
}
}
}
@TestMetadata("compiler/testData/diagnostics/tests/duplicateJvmSignature")
@@ -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
@@ -358,22 +358,20 @@ internal fun KtDeclaration.simpleVisibility(): String = when {
}
internal fun KtModifierListOwner.isDeprecated(support: KtUltraLightSupport? = null): Boolean {
val jetModifierList = this.modifierList ?: return false
if (jetModifierList.annotationEntries.isEmpty()) return false
val modifierList = this.modifierList ?: return false
if (modifierList.annotationEntries.isEmpty()) return false
val deprecatedFqName = KotlinBuiltIns.FQ_NAMES.deprecated
val deprecatedName = deprecatedFqName.shortName().asString()
for (annotationEntry in jetModifierList.annotationEntries) {
val typeReference = annotationEntry.typeReference ?: continue
val typeElement = typeReference.typeElement as? KtUserType ?: continue
// If it's not a user type, it's definitely not a ref to deprecated
for (annotationEntry in modifierList.annotationEntries) {
// If it's not a user type, it's definitely not a reference to deprecated
val typeElement = annotationEntry.typeReference?.typeElement as? KtUserType ?: continue
val fqName = toQualifiedName(typeElement) ?: continue
if (deprecatedFqName == fqName) return true
if (deprecatedName == fqName.asString()) return true
if (fqName == deprecatedFqName || fqName == KotlinBuiltIns.FQ_NAMES.deprecatedSinceKotlin) return true
if (fqName.asString() == deprecatedName) return true
}
return support?.findAnnotation(this, KotlinBuiltIns.FQ_NAMES.deprecated) !== null
@@ -178,7 +178,8 @@ public class KtPsiUtil {
List<KtAnnotationEntry> annotationEntries = modifierList.getAnnotationEntries();
for (KtAnnotationEntry annotation : annotationEntries) {
Name shortName = annotation.getShortName();
if (KotlinBuiltIns.FQ_NAMES.deprecated.shortName().equals(shortName)) {
if (KotlinBuiltIns.FQ_NAMES.deprecated.shortName().equals(shortName) ||
KotlinBuiltIns.FQ_NAMES.deprecatedSinceKotlin.shortName().equals(shortName)) {
return true;
}
}
@@ -0,0 +1,29 @@
// !API_VERSION: 1.3
@DeprecatedSinceKotlin("", errorSince = "1.3")
class ClassCur
@DeprecatedSinceKotlin("", errorSince = "1.3")
fun funCur() {}
@DeprecatedSinceKotlin("", errorSince = "1.3")
val valCur = Unit
@DeprecatedSinceKotlin("", errorSince = "1.4")
class ClassNext
@DeprecatedSinceKotlin("", errorSince = "1.4")
fun funNext() {}
@DeprecatedSinceKotlin("", errorSince = "1.4")
val valNext = Unit
fun usage() {
<!DEPRECATION_ERROR!>ClassCur<!>()
<!DEPRECATION_ERROR!>funCur<!>()
<!DEPRECATION_ERROR!>valCur<!>
ClassNext()
funNext()
valNext
}
@@ -0,0 +1,29 @@
// !API_VERSION: 1.3
@DeprecatedSinceKotlin("", errorSince = "1.3")
class ClassCur
@DeprecatedSinceKotlin("", errorSince = "1.3")
fun funCur() {}
@DeprecatedSinceKotlin("", errorSince = "1.3")
val valCur = Unit
@DeprecatedSinceKotlin("", errorSince = "1.4")
class ClassNext
@DeprecatedSinceKotlin("", errorSince = "1.4")
fun funNext() {}
@DeprecatedSinceKotlin("", errorSince = "1.4")
val valNext = Unit
fun usage() {
<!DEPRECATION_ERROR!>ClassCur<!>()
<!DEPRECATION_ERROR!>funCur<!>()
<!DEPRECATION_ERROR!>valCur<!>
ClassNext()
funNext()
valNext
}
@@ -0,0 +1,21 @@
package
@kotlin.DeprecatedSinceKotlin(errorSince = "1.3", message = "") public val valCur: kotlin.Unit
@kotlin.DeprecatedSinceKotlin(errorSince = "1.4", message = "") public val valNext: kotlin.Unit
@kotlin.DeprecatedSinceKotlin(errorSince = "1.3", message = "") public fun funCur(): kotlin.Unit
@kotlin.DeprecatedSinceKotlin(errorSince = "1.4", message = "") public fun funNext(): kotlin.Unit
public fun usage(): kotlin.Unit
@kotlin.DeprecatedSinceKotlin(errorSince = "1.3", message = "") public final class ClassCur {
public constructor ClassCur()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@kotlin.DeprecatedSinceKotlin(errorSince = "1.4", message = "") public final class ClassNext {
public constructor ClassNext()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,29 @@
// !API_VERSION: 1.3
@DeprecatedSinceKotlin("", hiddenSince = "1.3")
class ClassCur
@DeprecatedSinceKotlin("", hiddenSince = "1.3")
fun funCur() {}
@DeprecatedSinceKotlin("", hiddenSince = "1.3")
val valCur = Unit
@DeprecatedSinceKotlin("", hiddenSince = "1.4")
class ClassNext
@DeprecatedSinceKotlin("", hiddenSince = "1.4")
fun funNext() {}
@DeprecatedSinceKotlin("", hiddenSince = "1.4")
val valNext = Unit
fun usage() {
<!DEPRECATION_ERROR!>ClassCur<!>()
<!UNRESOLVED_REFERENCE!>funCur<!>()
<!UNRESOLVED_REFERENCE!>valCur<!>
ClassNext()
funNext()
valNext
}
@@ -0,0 +1,29 @@
// !API_VERSION: 1.3
@DeprecatedSinceKotlin("", hiddenSince = "1.3")
class ClassCur
@DeprecatedSinceKotlin("", hiddenSince = "1.3")
fun funCur() {}
@DeprecatedSinceKotlin("", hiddenSince = "1.3")
val valCur = Unit
@DeprecatedSinceKotlin("", hiddenSince = "1.4")
class ClassNext
@DeprecatedSinceKotlin("", hiddenSince = "1.4")
fun funNext() {}
@DeprecatedSinceKotlin("", hiddenSince = "1.4")
val valNext = Unit
fun usage() {
<!DEPRECATION_ERROR!>ClassCur<!>()
<!UNRESOLVED_REFERENCE!>funCur<!>()
<!UNRESOLVED_REFERENCE!>valCur<!>
ClassNext()
funNext()
valNext
}
@@ -0,0 +1,21 @@
package
@kotlin.DeprecatedSinceKotlin(hiddenSince = "1.3", message = "") public val valCur: kotlin.Unit
@kotlin.DeprecatedSinceKotlin(hiddenSince = "1.4", message = "") public val valNext: kotlin.Unit
@kotlin.DeprecatedSinceKotlin(hiddenSince = "1.3", message = "") public fun funCur(): kotlin.Unit
@kotlin.DeprecatedSinceKotlin(hiddenSince = "1.4", message = "") public fun funNext(): kotlin.Unit
public fun usage(): kotlin.Unit
@kotlin.DeprecatedSinceKotlin(hiddenSince = "1.3", message = "") public final class ClassCur {
public constructor ClassCur()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@kotlin.DeprecatedSinceKotlin(hiddenSince = "1.4", message = "") public final class ClassNext {
public constructor ClassNext()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,29 @@
// !API_VERSION: 1.3
@DeprecatedSinceKotlin("", warningSince = "1.3")
class ClassCur
@DeprecatedSinceKotlin("", warningSince = "1.3")
fun funCur() {}
@DeprecatedSinceKotlin("", warningSince = "1.3")
val valCur = Unit
@DeprecatedSinceKotlin("", warningSince = "1.4")
class ClassNext
@DeprecatedSinceKotlin("", warningSince = "1.4")
fun funNext() {}
@DeprecatedSinceKotlin("", warningSince = "1.4")
val valNext = Unit
fun usage() {
<!DEPRECATION!>ClassCur<!>()
<!DEPRECATION!>funCur<!>()
<!DEPRECATION!>valCur<!>
ClassNext()
funNext()
valNext
}
@@ -0,0 +1,29 @@
// !API_VERSION: 1.3
@DeprecatedSinceKotlin("", warningSince = "1.3")
class ClassCur
@DeprecatedSinceKotlin("", warningSince = "1.3")
fun funCur() {}
@DeprecatedSinceKotlin("", warningSince = "1.3")
val valCur = Unit
@DeprecatedSinceKotlin("", warningSince = "1.4")
class ClassNext
@DeprecatedSinceKotlin("", warningSince = "1.4")
fun funNext() {}
@DeprecatedSinceKotlin("", warningSince = "1.4")
val valNext = Unit
fun usage() {
<!DEPRECATION!>ClassCur<!>()
<!DEPRECATION!>funCur<!>()
<!DEPRECATION!>valCur<!>
ClassNext()
funNext()
valNext
}
@@ -0,0 +1,21 @@
package
@kotlin.DeprecatedSinceKotlin(message = "", warningSince = "1.3") public val valCur: kotlin.Unit
@kotlin.DeprecatedSinceKotlin(message = "", warningSince = "1.4") public val valNext: kotlin.Unit
@kotlin.DeprecatedSinceKotlin(message = "", warningSince = "1.3") public fun funCur(): kotlin.Unit
@kotlin.DeprecatedSinceKotlin(message = "", warningSince = "1.4") public fun funNext(): kotlin.Unit
public fun usage(): kotlin.Unit
@kotlin.DeprecatedSinceKotlin(message = "", warningSince = "1.3") public final class ClassCur {
public constructor ClassCur()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@kotlin.DeprecatedSinceKotlin(message = "", warningSince = "1.4") public final class ClassNext {
public constructor ClassNext()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -6651,6 +6651,34 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTestWithFirVali
public void testWarningOnConstructorErrorOnClass() throws Exception {
runTest("compiler/testData/diagnostics/tests/deprecated/warningOnConstructorErrorOnClass.kt");
}
@TestMetadata("compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class DeprecatedSinceKotlin extends AbstractDiagnosticsTestWithFirValidation {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInDeprecatedSinceKotlin() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
}
@TestMetadata("error.kt")
public void testError() throws Exception {
runTest("compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/error.kt");
}
@TestMetadata("hidden.kt")
public void testHidden() throws Exception {
runTest("compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/hidden.kt");
}
@TestMetadata("warning.kt")
public void testWarning() throws Exception {
runTest("compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/warning.kt");
}
}
}
@TestMetadata("compiler/testData/diagnostics/tests/duplicateJvmSignature")
@@ -6646,6 +6646,34 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing
public void testWarningOnConstructorErrorOnClass() throws Exception {
runTest("compiler/testData/diagnostics/tests/deprecated/warningOnConstructorErrorOnClass.kt");
}
@TestMetadata("compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class DeprecatedSinceKotlin extends AbstractDiagnosticsUsingJavacTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInDeprecatedSinceKotlin() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
}
@TestMetadata("error.kt")
public void testError() throws Exception {
runTest("compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/error.kt");
}
@TestMetadata("hidden.kt")
public void testHidden() throws Exception {
runTest("compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/hidden.kt");
}
@TestMetadata("warning.kt")
public void testWarning() throws Exception {
runTest("compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/warning.kt");
}
}
}
@TestMetadata("compiler/testData/diagnostics/tests/duplicateJvmSignature")
+26
View File
@@ -37,6 +37,32 @@ public annotation class Deprecated(
val level: DeprecationLevel = DeprecationLevel.WARNING
)
/**
* Marks the annotated declaration as deprecated. In contrast to [Deprecated], severity of the reported diagnostic is not a constant value,
* but differs depending on the API version of the usage (the value of the `-api-version` argument when compiling the module where
* the usage is located). If the API version is greater or equal than [hiddenSince], the declaration will not be accessible from the code
* (as if it was deprecated with level [DeprecationLevel.HIDDEN]), otherwise if the API version is greater or equal than [errorSince],
* the usage will be marked as an error (as with [DeprecationLevel.ERROR]), otherwise if the API version is greater or equal
* than [warningSince], the usage will be marked as a warning (as with [DeprecationLevel.WARNING]), otherwise the annotation is ignored.
*
* @property message the message explaining the deprecation and recommending an alternative API to use.
* @property replaceWith if present, specifies a code fragment which should be used as a replacement for
* the deprecated API usage.
* @property warningSince the version, since which this deprecation should be reported as a warning.
* @property errorSince the version, since which this deprecation should be reported as a error.
* @property hiddenSince the version, since which the annotated declaration should not be available in code.
*/
@Target(CLASS, FUNCTION, PROPERTY, ANNOTATION_CLASS, CONSTRUCTOR, PROPERTY_SETTER, PROPERTY_GETTER, TYPEALIAS)
@MustBeDocumented
@SinceKotlin("1.3")
public annotation class DeprecatedSinceKotlin(
val message: String,
val replaceWith: ReplaceWith = ReplaceWith(""),
val warningSince: String = "",
val errorSince: String = "",
val hiddenSince: String = ""
)
/**
* Specifies a code fragment that can be used to replace a deprecated function, property or class. Tools such
* as IDEs can automatically apply the replacements specified through this annotation.
@@ -211,6 +211,7 @@ public abstract class KotlinBuiltIns {
public final FqNameUnsafe longRange = rangesFqName("LongRange");
public final FqName deprecated = fqName("Deprecated");
public final FqName deprecatedSinceKotlin = fqName("DeprecatedSinceKotlin");
public final FqName deprecationLevel = fqName("DeprecationLevel");
public final FqName replaceWith = fqName("ReplaceWith");
public final FqName extensionFunctionType = fqName("ExtensionFunctionType");
@@ -1116,6 +1117,7 @@ public abstract class KotlinBuiltIns {
return classFqNameEquals(descriptor, FQ_NAMES.cloneable);
}
// TODO: support DeprecatedSinceKotlin
public static boolean isDeprecated(@NotNull DeclarationDescriptor declarationDescriptor) {
if (declarationDescriptor.getOriginal().getAnnotations().hasAnnotation(FQ_NAMES.deprecated)) return true;
@@ -103,6 +103,7 @@ class DeprecatedCallableAddReplaceWithInspection : AbstractApplicabilityBasedIns
private fun KtCallableDeclaration.deprecatedAnnotationWithNoReplaceWith(): KtAnnotationEntry? {
for (entry in annotationEntries) {
// TODO: support DeprecatedSinceKotlin here
if (entry.shortName != DEPRECATED_NAME) continue
val bindingContext = entry.analyze()
val resolvedCall = entry.calleeExpression.getResolvedCall(bindingContext) ?: continue
@@ -79,6 +79,7 @@ abstract class DeprecatedSymbolUsageFixBase(
contextElement: KtSimpleNameExpression?,
replaceInWholeProject: Boolean
): ReplaceWith? {
// TODO: support DeprecatedSinceKotlin
val annotation = descriptor.annotations.findAnnotation(KotlinBuiltIns.FQ_NAMES.deprecated) ?: return null
val replaceWithValue =
annotation.argumentValue(Deprecated::replaceWith.name)?.safeAs<AnnotationValue>()?.value ?: return null