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 08f8a5c0232..62f93f29628 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 @@ -6657,6 +6657,11 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirOldFronte KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); } + @TestMetadata("deprecatedSinceKotlinDeclaration.kt") + public void testDeprecatedSinceKotlinDeclaration() throws Exception { + runTest("compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/deprecatedSinceKotlinDeclaration.kt"); + } + @TestMetadata("error.kt") public void testError() throws Exception { runTest("compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/error.kt"); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 68a2bc39667..8c74e663a87 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -100,6 +100,10 @@ public interface Errors { DiagnosticFactory3> VERSION_REQUIREMENT_DEPRECATION_ERROR = DiagnosticFactory3.create(ERROR); + DiagnosticFactory0 DEPRECATED_SINCE_KOTLIN_WITHOUT_DEPRECATED = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 DEPRECATED_SINCE_KOTLIN_WITH_DEPRECATED_LEVEL = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 DEPRECATED_SINCE_KOTLIN_WITH_UNORDERED_VERSIONS = DiagnosticFactory0.create(ERROR); + DiagnosticFactory2 API_NOT_AVAILABLE = DiagnosticFactory2.create(ERROR); DiagnosticFactory1 MISSING_DEPENDENCY_CLASS = DiagnosticFactory1.create(ERROR); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index 044dff02279..46e87809b6b 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -377,6 +377,10 @@ public class DefaultErrorMessages { (obj, renderingContext) -> obj.equals(VersionRequirement.Version.INFINITY) ? "" : " is only available since Kotlin " + obj.asString() + " and", versionRequirementMessage); + MAP.put(DEPRECATED_SINCE_KOTLIN_WITHOUT_DEPRECATED, "DeprecatedSinceKotlin annotation can be used only together with Deprecated annotation"); + MAP.put(DEPRECATED_SINCE_KOTLIN_WITH_DEPRECATED_LEVEL, "DeprecatedSinceKotlin annotation can be used only with unspecified deprecation level of Deprecated annotation"); + MAP.put(DEPRECATED_SINCE_KOTLIN_WITH_UNORDERED_VERSIONS, "Values of DeprecatedSinceKotlin annotation should be ordered so 'warningSince' <= 'errorSince' <= 'hiddenSince' if specified"); + MAP.put(API_NOT_AVAILABLE, "This declaration is only available since Kotlin {0} and cannot be used with the specified API version {1}", STRING, STRING); MAP.put(MISSING_DEPENDENCY_CLASS, "Cannot access class ''{0}''. Check your module classpath for missing or conflicting dependencies", TO_STRING); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt index 6618dc1d24d..6fdc43476fc 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt @@ -36,7 +36,8 @@ private val DEFAULT_DECLARATION_CHECKERS = listOf( TailrecFunctionChecker, TrailingCommaDeclarationChecker, MissingDependencySupertypeChecker.ForDeclarations, - FunInterfaceDeclarationChecker() + FunInterfaceDeclarationChecker(), + DeprecatedSinceKotlinAnnotationChecker ) private val DEFAULT_CALL_CHECKERS = listOf( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/DeprecatedSinceKotlinAnnotationChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/DeprecatedSinceKotlinAnnotationChecker.kt new file mode 100644 index 00000000000..769393bd91e --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/DeprecatedSinceKotlinAnnotationChecker.kt @@ -0,0 +1,60 @@ +/* + * Copyright 2010-2020 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.resolve.checkers + +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.config.ApiVersion +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.psi.KtAnnotationEntry +import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.resolve.annotations.argumentValue +import org.jetbrains.kotlin.resolve.deprecation.getSinceVersion +import org.jetbrains.kotlin.resolve.source.getPsi + +object DeprecatedSinceKotlinAnnotationChecker : DeclarationChecker { + override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) { + val deprecatedSinceAnnotation = descriptor.annotations.findAnnotation(KotlinBuiltIns.FQ_NAMES.deprecatedSinceKotlin) ?: return + val deprecatedSinceAnnotationPsi = deprecatedSinceAnnotation.source.getPsi() as? KtAnnotationEntry ?: return + + val deprecatedAnnotation = descriptor.annotations.findAnnotation(KotlinBuiltIns.FQ_NAMES.deprecated) + + val deprecatedSinceAnnotationName = deprecatedSinceAnnotationPsi.typeReference ?: return + + if (deprecatedAnnotation == null) { + context.trace.report( + Errors.DEPRECATED_SINCE_KOTLIN_WITHOUT_DEPRECATED.on( + deprecatedSinceAnnotationName + ) + ) + return + } + + if (deprecatedAnnotation.argumentValue(Deprecated::level.name) != null) { + context.trace.report( + Errors.DEPRECATED_SINCE_KOTLIN_WITH_DEPRECATED_LEVEL.on( + deprecatedSinceAnnotationName + ) + ) + return + } + + val warningSince = deprecatedSinceAnnotation.getSinceVersion("warningSince") + val errorSince = deprecatedSinceAnnotation.getSinceVersion("errorSince") + val hiddenSince = deprecatedSinceAnnotation.getSinceVersion("hiddenSince") + if (!lessOrNull(warningSince, errorSince) || !lessOrNull(errorSince, hiddenSince) || !lessOrNull(warningSince, hiddenSince)) { + context.trace.report( + Errors.DEPRECATED_SINCE_KOTLIN_WITH_UNORDERED_VERSIONS.on( + deprecatedSinceAnnotationName + ) + ) + return + } + } + + private fun lessOrNull(a: ApiVersion?, b: ApiVersion?): Boolean = + if (a == null || b == null) true else a <= b +} 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 ced9e9aefe2..34b197ae087 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecation/Deprecation.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecation/Deprecation.kt @@ -72,16 +72,13 @@ internal sealed class DeprecatedByAnnotation( } 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") + val hiddenSince = annotation.getSinceVersion("hiddenSince") if (hiddenSince != null && apiVersion >= hiddenSince) return HIDDEN - val errorSince = getSinceVersion("errorSince") + val errorSince = annotation.getSinceVersion("errorSince") if (errorSince != null && apiVersion >= errorSince) return ERROR - val warningSince = getSinceVersion("warningSince") + val warningSince = annotation.getSinceVersion("warningSince") if (warningSince != null && apiVersion >= warningSince) return WARNING return null 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 77305d7d841..5ca10484e4d 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecation/deprecationUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecation/deprecationUtil.kt @@ -6,19 +6,26 @@ package org.jetbrains.kotlin.resolve.deprecation import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.config.ApiVersion import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.container.DefaultImplementation -import org.jetbrains.kotlin.container.PlatformExtensionsClashResolver 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.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? = (this as? DeprecatedByAnnotation)?.replaceWithValue +// The function extracts value of warningSince/errorSince/hiddenSince from DeprecatedSinceKotlin annotation +fun AnnotationDescriptor.getSinceVersion(name: String): ApiVersion? = + argumentValue(name)?.safeAs()?.value?.takeUnless(String::isEmpty)?.let(ApiVersion.Companion::parse) + internal fun createDeprecationDiagnostic( element: PsiElement, deprecation: Deprecation, languageVersionSettings: LanguageVersionSettings ): Diagnostic { diff --git a/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/deprecatedSinceKotlinDeclaration.fir.kt b/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/deprecatedSinceKotlinDeclaration.fir.kt new file mode 100644 index 00000000000..69b7c21d0c6 --- /dev/null +++ b/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/deprecatedSinceKotlinDeclaration.fir.kt @@ -0,0 +1,42 @@ +@Deprecated("", ReplaceWith("")) +@DeprecatedSinceKotlin("", warningSince = "1.0", errorSince = "1.1", hiddenSince = "1.2") +fun good() {} + +@DeprecatedSinceKotlin("") +class Clazz + +@Deprecated("", level = DeprecationLevel.WARNING) +@DeprecatedSinceKotlin("") +fun fooWarning() {} + +@Deprecated("", ReplaceWith(""), DeprecationLevel.WARNING) +@DeprecatedSinceKotlin("") +fun fooDefaultWarning() {} + +@Deprecated("", level = DeprecationLevel.ERROR) +@DeprecatedSinceKotlin("") +fun fooError() {} + +@Deprecated("", level = DeprecationLevel.HIDDEN) +@DeprecatedSinceKotlin("") +fun fooHidden() {} + +@Deprecated("") +@DeprecatedSinceKotlin("", warningSince = "1.1", errorSince = "1.0") +fun fooWarningIsGreater1() {} + +@Deprecated("") +@DeprecatedSinceKotlin("", warningSince = "1.1", hiddenSince = "1.0") +fun fooWarningIsGreater2() {} + +@Deprecated("") +@DeprecatedSinceKotlin("", warningSince = "1.1", errorSince = "1.3", hiddenSince = "1.2") +fun fooErrorIsGreater() {} + +@Deprecated("") +@DeprecatedSinceKotlin("", ReplaceWith(""), "1.2", "1.1", "1.1") +fun fooDefault() {} + +@Deprecated("") +@DeprecatedSinceKotlin("", ReplaceWith(""), "1.1", "1.1", "1.1") +fun fooEqual() {} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/deprecatedSinceKotlinDeclaration.kt b/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/deprecatedSinceKotlinDeclaration.kt new file mode 100644 index 00000000000..3bba894274d --- /dev/null +++ b/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/deprecatedSinceKotlinDeclaration.kt @@ -0,0 +1,42 @@ +@Deprecated("", ReplaceWith("")) +@DeprecatedSinceKotlin("", warningSince = "1.0", errorSince = "1.1", hiddenSince = "1.2") +fun good() {} + +@DeprecatedSinceKotlin("") +class Clazz + +@Deprecated("", level = DeprecationLevel.WARNING) +@DeprecatedSinceKotlin("") +fun fooWarning() {} + +@Deprecated("", ReplaceWith(""), DeprecationLevel.WARNING) +@DeprecatedSinceKotlin("") +fun fooDefaultWarning() {} + +@Deprecated("", level = DeprecationLevel.ERROR) +@DeprecatedSinceKotlin("") +fun fooError() {} + +@Deprecated("", level = DeprecationLevel.HIDDEN) +@DeprecatedSinceKotlin("") +fun fooHidden() {} + +@Deprecated("") +@DeprecatedSinceKotlin("", warningSince = "1.1", errorSince = "1.0") +fun fooWarningIsGreater1() {} + +@Deprecated("") +@DeprecatedSinceKotlin("", warningSince = "1.1", hiddenSince = "1.0") +fun fooWarningIsGreater2() {} + +@Deprecated("") +@DeprecatedSinceKotlin("", warningSince = "1.1", errorSince = "1.3", hiddenSince = "1.2") +fun fooErrorIsGreater() {} + +@Deprecated("") +@DeprecatedSinceKotlin("", ReplaceWith(""), "1.2", "1.1", "1.1") +fun fooDefault() {} + +@Deprecated("") +@DeprecatedSinceKotlin("", ReplaceWith(""), "1.1", "1.1", "1.1") +fun fooEqual() {} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/deprecatedSinceKotlinDeclaration.txt b/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/deprecatedSinceKotlinDeclaration.txt new file mode 100644 index 00000000000..2a36c37d296 --- /dev/null +++ b/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/deprecatedSinceKotlinDeclaration.txt @@ -0,0 +1,19 @@ +package + +@kotlin.Deprecated(message = "") @kotlin.DeprecatedSinceKotlin(errorSince = "1.1", hiddenSince = "1.1", message = "", replaceWith = kotlin.ReplaceWith(expression = "", imports = {}), warningSince = "1.2") public fun fooDefault(): kotlin.Unit +@kotlin.Deprecated(level = DeprecationLevel.WARNING, message = "", replaceWith = kotlin.ReplaceWith(expression = "", imports = {})) @kotlin.DeprecatedSinceKotlin(message = "") public fun fooDefaultWarning(): kotlin.Unit +@kotlin.Deprecated(message = "") @kotlin.DeprecatedSinceKotlin(errorSince = "1.1", hiddenSince = "1.1", message = "", replaceWith = kotlin.ReplaceWith(expression = "", imports = {}), warningSince = "1.1") public fun fooEqual(): kotlin.Unit +@kotlin.Deprecated(level = DeprecationLevel.ERROR, message = "") @kotlin.DeprecatedSinceKotlin(message = "") public fun fooError(): kotlin.Unit +@kotlin.Deprecated(message = "") @kotlin.DeprecatedSinceKotlin(errorSince = "1.3", hiddenSince = "1.2", message = "", warningSince = "1.1") public fun fooErrorIsGreater(): kotlin.Unit +@kotlin.Deprecated(level = DeprecationLevel.HIDDEN, message = "") @kotlin.DeprecatedSinceKotlin(message = "") public fun fooHidden(): kotlin.Unit +@kotlin.Deprecated(level = DeprecationLevel.WARNING, message = "") @kotlin.DeprecatedSinceKotlin(message = "") public fun fooWarning(): kotlin.Unit +@kotlin.Deprecated(message = "") @kotlin.DeprecatedSinceKotlin(errorSince = "1.0", message = "", warningSince = "1.1") public fun fooWarningIsGreater1(): kotlin.Unit +@kotlin.Deprecated(message = "") @kotlin.DeprecatedSinceKotlin(hiddenSince = "1.0", message = "", warningSince = "1.1") public fun fooWarningIsGreater2(): kotlin.Unit +@kotlin.Deprecated(message = "", replaceWith = kotlin.ReplaceWith(expression = "", imports = {})) @kotlin.DeprecatedSinceKotlin(errorSince = "1.1", hiddenSince = "1.2", message = "", warningSince = "1.0") public fun good(): kotlin.Unit + +@kotlin.DeprecatedSinceKotlin(message = "") public final class Clazz { + public constructor Clazz() + 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 78f7dd73aea..449146b0820 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -6664,6 +6664,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTestWithFirVali KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); } + @TestMetadata("deprecatedSinceKotlinDeclaration.kt") + public void testDeprecatedSinceKotlinDeclaration() throws Exception { + runTest("compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/deprecatedSinceKotlinDeclaration.kt"); + } + @TestMetadata("error.kt") public void testError() throws Exception { runTest("compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/error.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java index 880c4f5e8f3..c2631f5fe48 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java @@ -6659,6 +6659,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); } + @TestMetadata("deprecatedSinceKotlinDeclaration.kt") + public void testDeprecatedSinceKotlinDeclaration() throws Exception { + runTest("compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/deprecatedSinceKotlinDeclaration.kt"); + } + @TestMetadata("error.kt") public void testError() throws Exception { runTest("compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/error.kt");