diff --git a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java index 72e10ea193f..08f8a5c0232 100644 --- a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java @@ -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") diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecation/Deprecation.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecation/Deprecation.kt index 99f445f7478..ced9e9aefe2 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecation/Deprecation.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecation/Deprecation.kt @@ -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()?.enumEntryName?.asString()) { - "WARNING" -> WARNING - "ERROR" -> ERROR - "HIDDEN" -> HIDDEN - else -> WARNING - } - override val message: String? get() = annotation.argumentValue("message")?.safeAs()?.value + + internal val replaceWithValue: String? + get() { + val replaceWithAnnotation = annotation.argumentValue(Deprecated::replaceWith.name)?.safeAs()?.value + return replaceWithAnnotation?.argumentValue(ReplaceWith::expression.name)?.safeAs()?.value + } + + class StandardDeprecated( + annotation: AnnotationDescriptor, + target: DeclarationDescriptor, + propagatesToOverrides: Boolean + ) : DeprecatedByAnnotation(annotation, target, propagatesToOverrides) { + override val deprecationLevel: DeprecationLevelValue + get() = when (annotation.argumentValue("level")?.safeAs()?.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()?.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 { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecation/DeprecationResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecation/DeprecationResolver.kt index aabc42767f1..f25634d90e9 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecation/DeprecationResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecation/DeprecationResolver.kt @@ -187,23 +187,27 @@ class DeprecationResolver( private fun DeclarationDescriptor.addDeprecationIfPresent(result: MutableList) { 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)) { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecation/deprecationUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecation/deprecationUtil.kt index bb2f9712fad..77305d7d841 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecation/deprecationUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecation/deprecationUtil.kt @@ -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()?.value ?: return null - return replaceWithAnnotation.argumentValue(kotlin.ReplaceWith::expression.name)?.safeAs()?.value -} +fun Deprecation.deprecatedByAnnotationReplaceWithExpression(): String? = (this as? DeprecatedByAnnotation)?.replaceWithValue internal fun createDeprecationDiagnostic( element: PsiElement, deprecation: Deprecation, languageVersionSettings: LanguageVersionSettings diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightUtils.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightUtils.kt index dd40600e8ab..ff585a554a7 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightUtils.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightUtils.kt @@ -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 diff --git a/compiler/psi/src/org/jetbrains/kotlin/psi/KtPsiUtil.java b/compiler/psi/src/org/jetbrains/kotlin/psi/KtPsiUtil.java index 67b7a99d193..e6b60f7315f 100644 --- a/compiler/psi/src/org/jetbrains/kotlin/psi/KtPsiUtil.java +++ b/compiler/psi/src/org/jetbrains/kotlin/psi/KtPsiUtil.java @@ -178,7 +178,8 @@ public class KtPsiUtil { List 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; } } diff --git a/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/error.fir.kt b/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/error.fir.kt new file mode 100644 index 00000000000..7fbeabf6a5e --- /dev/null +++ b/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/error.fir.kt @@ -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() { + ClassCur() + funCur() + valCur + + ClassNext() + funNext() + valNext +} diff --git a/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/error.kt b/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/error.kt new file mode 100644 index 00000000000..7fbeabf6a5e --- /dev/null +++ b/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/error.kt @@ -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() { + ClassCur() + funCur() + valCur + + ClassNext() + funNext() + valNext +} diff --git a/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/error.txt b/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/error.txt new file mode 100644 index 00000000000..1e458887acf --- /dev/null +++ b/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/error.txt @@ -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 +} diff --git a/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/hidden.fir.kt b/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/hidden.fir.kt new file mode 100644 index 00000000000..27c21f92b44 --- /dev/null +++ b/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/hidden.fir.kt @@ -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() { + ClassCur() + funCur() + valCur + + ClassNext() + funNext() + valNext +} diff --git a/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/hidden.kt b/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/hidden.kt new file mode 100644 index 00000000000..27c21f92b44 --- /dev/null +++ b/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/hidden.kt @@ -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() { + ClassCur() + funCur() + valCur + + ClassNext() + funNext() + valNext +} diff --git a/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/hidden.txt b/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/hidden.txt new file mode 100644 index 00000000000..4d90a846206 --- /dev/null +++ b/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/hidden.txt @@ -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 +} diff --git a/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/warning.fir.kt b/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/warning.fir.kt new file mode 100644 index 00000000000..1fd556775bf --- /dev/null +++ b/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/warning.fir.kt @@ -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() { + ClassCur() + funCur() + valCur + + ClassNext() + funNext() + valNext +} diff --git a/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/warning.kt b/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/warning.kt new file mode 100644 index 00000000000..1fd556775bf --- /dev/null +++ b/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/warning.kt @@ -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() { + ClassCur() + funCur() + valCur + + ClassNext() + funNext() + valNext +} diff --git a/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/warning.txt b/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/warning.txt new file mode 100644 index 00000000000..0837a112476 --- /dev/null +++ b/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/warning.txt @@ -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 +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index dc6ea1a20cc..78f7dd73aea 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -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") diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java index edbeaba0daf..880c4f5e8f3 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java @@ -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") diff --git a/core/builtins/src/kotlin/Annotations.kt b/core/builtins/src/kotlin/Annotations.kt index 79d3ccbfe55..4890646e580 100644 --- a/core/builtins/src/kotlin/Annotations.kt +++ b/core/builtins/src/kotlin/Annotations.kt @@ -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. diff --git a/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java b/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java index 870f412722e..50eb9e1d2ef 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java +++ b/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java @@ -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; diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/DeprecatedCallableAddReplaceWithInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/DeprecatedCallableAddReplaceWithInspection.kt index 517923b474c..27e11d14893 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/DeprecatedCallableAddReplaceWithInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/DeprecatedCallableAddReplaceWithInspection.kt @@ -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 diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/DeprecatedSymbolUsageFixBase.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/DeprecatedSymbolUsageFixBase.kt index e79b5f4e113..6b227db8d05 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/DeprecatedSymbolUsageFixBase.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/DeprecatedSymbolUsageFixBase.kt @@ -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()?.value ?: return null