Introduce declaration checker for DeprecatedSinceKotlin annotation
- DeprecatedSinceKotlin annotation should only be applicable when there's `@Deprecated` annotation on the same declaration - Deprecation level shouldn't be specified in the relevant `@Deprecated` annotation - Check that warningSince <= errorSince <= hiddenSince
This commit is contained in:
+5
@@ -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");
|
||||
|
||||
@@ -100,6 +100,10 @@ public interface Errors {
|
||||
DiagnosticFactory3<PsiElement, DeclarationDescriptor, VersionRequirement.Version, Pair<LanguageVersion, String>>
|
||||
VERSION_REQUIREMENT_DEPRECATION_ERROR = DiagnosticFactory3.create(ERROR);
|
||||
|
||||
DiagnosticFactory0<PsiElement> DEPRECATED_SINCE_KOTLIN_WITHOUT_DEPRECATED = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> DEPRECATED_SINCE_KOTLIN_WITH_DEPRECATED_LEVEL = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> DEPRECATED_SINCE_KOTLIN_WITH_UNORDERED_VERSIONS = DiagnosticFactory0.create(ERROR);
|
||||
|
||||
DiagnosticFactory2<PsiElement, String, String> API_NOT_AVAILABLE = DiagnosticFactory2.create(ERROR);
|
||||
|
||||
DiagnosticFactory1<PsiElement, FqName> MISSING_DEPENDENCY_CLASS = DiagnosticFactory1.create(ERROR);
|
||||
|
||||
+4
@@ -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);
|
||||
|
||||
@@ -36,7 +36,8 @@ private val DEFAULT_DECLARATION_CHECKERS = listOf(
|
||||
TailrecFunctionChecker,
|
||||
TrailingCommaDeclarationChecker,
|
||||
MissingDependencySupertypeChecker.ForDeclarations,
|
||||
FunInterfaceDeclarationChecker()
|
||||
FunInterfaceDeclarationChecker(),
|
||||
DeprecatedSinceKotlinAnnotationChecker
|
||||
)
|
||||
|
||||
private val DEFAULT_CALL_CHECKERS = listOf(
|
||||
|
||||
+60
@@ -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
|
||||
}
|
||||
@@ -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<StringValue>()?.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
|
||||
|
||||
@@ -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<StringValue>()?.value?.takeUnless(String::isEmpty)?.let(ApiVersion.Companion::parse)
|
||||
|
||||
internal fun createDeprecationDiagnostic(
|
||||
element: PsiElement, deprecation: Deprecation, languageVersionSettings: LanguageVersionSettings
|
||||
): Diagnostic {
|
||||
|
||||
+42
@@ -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() {}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
@Deprecated("", ReplaceWith(""))
|
||||
@DeprecatedSinceKotlin("", warningSince = "1.0", errorSince = "1.1", hiddenSince = "1.2")
|
||||
fun good() {}
|
||||
|
||||
@<!DEPRECATED_SINCE_KOTLIN_WITHOUT_DEPRECATED!>DeprecatedSinceKotlin<!>("")
|
||||
class Clazz
|
||||
|
||||
@Deprecated("", level = DeprecationLevel.WARNING)
|
||||
@<!DEPRECATED_SINCE_KOTLIN_WITH_DEPRECATED_LEVEL!>DeprecatedSinceKotlin<!>("")
|
||||
fun fooWarning() {}
|
||||
|
||||
@Deprecated("", ReplaceWith(""), DeprecationLevel.WARNING)
|
||||
@<!DEPRECATED_SINCE_KOTLIN_WITH_DEPRECATED_LEVEL!>DeprecatedSinceKotlin<!>("")
|
||||
fun fooDefaultWarning() {}
|
||||
|
||||
@Deprecated("", level = DeprecationLevel.ERROR)
|
||||
@<!DEPRECATED_SINCE_KOTLIN_WITH_DEPRECATED_LEVEL!>DeprecatedSinceKotlin<!>("")
|
||||
fun fooError() {}
|
||||
|
||||
@Deprecated("", level = DeprecationLevel.HIDDEN)
|
||||
@<!DEPRECATED_SINCE_KOTLIN_WITH_DEPRECATED_LEVEL!>DeprecatedSinceKotlin<!>("")
|
||||
fun fooHidden() {}
|
||||
|
||||
@Deprecated("")
|
||||
@<!DEPRECATED_SINCE_KOTLIN_WITH_UNORDERED_VERSIONS!>DeprecatedSinceKotlin<!>("", warningSince = "1.1", errorSince = "1.0")
|
||||
fun fooWarningIsGreater1() {}
|
||||
|
||||
@Deprecated("")
|
||||
@<!DEPRECATED_SINCE_KOTLIN_WITH_UNORDERED_VERSIONS!>DeprecatedSinceKotlin<!>("", warningSince = "1.1", hiddenSince = "1.0")
|
||||
fun fooWarningIsGreater2() {}
|
||||
|
||||
@Deprecated("")
|
||||
@<!DEPRECATED_SINCE_KOTLIN_WITH_UNORDERED_VERSIONS!>DeprecatedSinceKotlin<!>("", warningSince = "1.1", errorSince = "1.3", hiddenSince = "1.2")
|
||||
fun fooErrorIsGreater() {}
|
||||
|
||||
@Deprecated("")
|
||||
@<!DEPRECATED_SINCE_KOTLIN_WITH_UNORDERED_VERSIONS!>DeprecatedSinceKotlin<!>("", ReplaceWith(""), "1.2", "1.1", "1.1")
|
||||
fun fooDefault() {}
|
||||
|
||||
@Deprecated("")
|
||||
@DeprecatedSinceKotlin("", ReplaceWith(""), "1.1", "1.1", "1.1")
|
||||
fun fooEqual() {}
|
||||
+19
@@ -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
|
||||
}
|
||||
@@ -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");
|
||||
|
||||
Generated
+5
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user