Do not implicitly propagate deprecations originated in Java
^KT-29604 Fixed
This commit is contained in:
+2
-1
@@ -40,6 +40,7 @@ import org.jetbrains.kotlin.resolve.constants.KClassValue
|
||||
import org.jetbrains.kotlin.resolve.deprecation.CoroutineCompatibilitySupport
|
||||
import org.jetbrains.kotlin.resolve.deprecation.DeprecationLevelValue
|
||||
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
|
||||
import org.jetbrains.kotlin.resolve.deprecation.DeprecationSettings
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
||||
@@ -215,7 +216,7 @@ class ExperimentalUsageChecker(project: Project) : CallChecker {
|
||||
// "-Xuse-experimental" arguments. However, it's not easy to do this. This should be solved in the future with the support of
|
||||
// module annotations. For now, we only check deprecations because this is needed to correctly retire unneeded compiler arguments.
|
||||
val deprecationResolver =
|
||||
DeprecationResolver(LockBasedStorageManager("ExperimentalUsageChecker"), languageVersionSettings, CoroutineCompatibilitySupport.ENABLED)
|
||||
DeprecationResolver(LockBasedStorageManager("ExperimentalUsageChecker"), languageVersionSettings, CoroutineCompatibilitySupport.ENABLED, DeprecationSettings.Default)
|
||||
|
||||
// Returns true if fqName refers to a valid experimental API marker.
|
||||
fun checkAnnotation(fqName: String): Boolean {
|
||||
|
||||
@@ -20,7 +20,11 @@ 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(val annotation: AnnotationDescriptor, override val target: DeclarationDescriptor) : Deprecation {
|
||||
internal data 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
|
||||
|
||||
+29
-2
@@ -36,7 +36,8 @@ import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
class DeprecationResolver(
|
||||
storageManager: StorageManager,
|
||||
private val languageVersionSettings: LanguageVersionSettings,
|
||||
private val coroutineCompatibilitySupport: CoroutineCompatibilitySupport
|
||||
private val coroutineCompatibilitySupport: CoroutineCompatibilitySupport,
|
||||
private val deprecationSettings: DeprecationSettings
|
||||
) {
|
||||
private val deprecations = storageManager.createMemoizedFunction { descriptor: DeclarationDescriptor ->
|
||||
val deprecations = descriptor.getOwnDeprecations()
|
||||
@@ -126,6 +127,28 @@ class DeprecationResolver(
|
||||
|
||||
if (hasUndeprecatedOverridden || deprecations.isEmpty()) return null
|
||||
|
||||
// We might've filtered out not-propagating deprecations already in the initializer of `deprecationsByAnnotation` in the code above.
|
||||
// But it would lead to treating Java overridden as not-deprecated at all that works controversially in case of mixed J/K override:
|
||||
// interface J {
|
||||
// @Deprecated
|
||||
// void foo();
|
||||
// }
|
||||
//
|
||||
// interface K {
|
||||
// @Deprecated("")
|
||||
// fun foo();
|
||||
// }
|
||||
//
|
||||
// class K1 : K, J {
|
||||
// // We'd probably better treating it as deprecated
|
||||
// // Basically, it's just a corner case and we may change the behavior if it's too annoying
|
||||
// override fun foo() {}
|
||||
// }
|
||||
//
|
||||
// Also, we don't ignore non-propagating deprecations in case of fake overrides
|
||||
// Because we don't want to depend on the choice of the base descriptor
|
||||
if (root.kind.isReal && deprecations.none(Deprecation::propagatesToOverrides)) return null
|
||||
|
||||
return DeprecatedByOverridden(deprecations)
|
||||
}
|
||||
|
||||
@@ -166,7 +189,11 @@ class DeprecationResolver(
|
||||
val annotation = annotations.findAnnotation(KotlinBuiltIns.FQ_NAMES.deprecated)
|
||||
?: annotations.findAnnotation(JAVA_DEPRECATED)
|
||||
if (annotation != null) {
|
||||
val deprecatedByAnnotation = DeprecatedByAnnotation(annotation, this)
|
||||
val deprecatedByAnnotation =
|
||||
DeprecatedByAnnotation(
|
||||
annotation, this,
|
||||
deprecationSettings.propagatedToOverrides(annotation)
|
||||
)
|
||||
val deprecation = when {
|
||||
this is TypeAliasConstructorDescriptor ->
|
||||
DeprecatedTypealiasByAnnotation(typeAliasDescriptor, deprecatedByAnnotation)
|
||||
|
||||
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.resolve.deprecation
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.container.DefaultImplementation
|
||||
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
|
||||
@@ -70,3 +71,12 @@ class CoroutineCompatibilitySupport private constructor(val enabled: Boolean) {
|
||||
val DISABLED = CoroutineCompatibilitySupport(false)
|
||||
}
|
||||
}
|
||||
|
||||
@DefaultImplementation(DeprecationSettings.Default::class)
|
||||
interface DeprecationSettings {
|
||||
fun propagatedToOverrides(deprecationAnnotation: AnnotationDescriptor): Boolean
|
||||
|
||||
object Default : DeprecationSettings {
|
||||
override fun propagatedToOverrides(deprecationAnnotation: AnnotationDescriptor) = true
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user