K1: Force propagating deprecation on NOT_CONSIDERED JDK members to overrides

^KT-60858 Related
KT-60659 In Progress
^KT-60769 In Progress
This commit is contained in:
Denis.Zharkov
2023-07-28 19:59:25 +02:00
committed by Space Team
parent 1e86a82ee1
commit eae97e4f76
9 changed files with 74 additions and 22 deletions
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.TypeAliasDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.BuiltInAnnotationDescriptor
import org.jetbrains.kotlin.metadata.deserialization.VersionRequirement
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.annotations.argumentValue
@@ -23,8 +24,16 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
internal sealed class DeprecatedByAnnotation(
val annotation: AnnotationDescriptor,
override val target: DeclarationDescriptor,
override val propagatesToOverrides: Boolean
final override val propagatesToOverrides: Boolean,
final override val forcePropagationToOverrides: Boolean = false,
) : DescriptorBasedDeprecationInfo() {
init {
require(!forcePropagationToOverrides || propagatesToOverrides) {
"if something is `forcePropagationToOverrides`, it's expected that `propagatesToOverrides` == true, too"
}
}
override val message: String?
get() = (annotation.argumentValue("message") as? StringValue)?.value
@@ -37,8 +46,9 @@ internal sealed class DeprecatedByAnnotation(
class StandardDeprecated(
annotation: AnnotationDescriptor,
target: DeclarationDescriptor,
propagatesToOverrides: Boolean
) : DeprecatedByAnnotation(annotation, target, propagatesToOverrides) {
propagatesToOverrides: Boolean,
forcePropagationToOverrides: Boolean = false,
) : DeprecatedByAnnotation(annotation, target, propagatesToOverrides, forcePropagationToOverrides) {
override val deprecationLevel: DeprecationLevelValue
get() = when ((annotation.argumentValue("level") as? EnumValue)?.enumEntryName?.asString()) {
"WARNING" -> WARNING
@@ -105,7 +115,14 @@ internal sealed class DeprecatedByAnnotation(
val level = computeLevelForDeprecatedSinceKotlin(deprecatedSinceKotlinAnnotation, apiVersion) ?: return null
return DeprecatedSince(deprecatedAnnotation, target, propagatesToOverrides, level)
}
return StandardDeprecated(deprecatedAnnotation, target, propagatesToOverrides)
val forcePropagationToOverrides =
(deprecatedAnnotation as? BuiltInAnnotationDescriptor)?.forcePropagationDeprecationToOverrides == true
return StandardDeprecated(
deprecatedAnnotation,
target,
propagatesToOverrides || forcePropagationToOverrides,
forcePropagationToOverrides = forcePropagationToOverrides
)
}
}
}
@@ -116,6 +133,8 @@ internal data class DeprecatedByOverridden(private val deprecations: Collection<
assert(deprecations.none { it is DeprecatedByOverridden })
}
override val forcePropagationToOverrides: Boolean = deprecations.any { it.forcePropagationToOverrides }
override val deprecationLevel: DeprecationLevelValue = deprecations.map(DescriptorBasedDeprecationInfo::deprecationLevel).minOrNull()!!
override val target: DeclarationDescriptor
@@ -46,7 +46,11 @@ class DeprecationResolver(
val inheritedDeprecations = listOfNotNull(deprecationByOverridden(descriptor))
when (inheritedDeprecations.isNotEmpty()) {
true -> when (languageVersionSettings.supportsFeature(LanguageFeature.StopPropagatingDeprecationThroughOverrides)) {
true -> DeprecationInfo(emptyList(), hasInheritedDeprecations = true, inheritedDeprecations)
true -> DeprecationInfo(
inheritedDeprecations.filter { it.forcePropagationToOverrides },
hasInheritedDeprecations = true,
inheritedDeprecations
)
false -> DeprecationInfo(inheritedDeprecations, hasInheritedDeprecations = true)
}
false -> DeprecationInfo.EMPTY
@@ -167,7 +171,8 @@ class DeprecationResolver(
traverse(root)
if (hasUndeprecatedOverridden || deprecations.isEmpty()) return null
if (deprecations.isEmpty()) return null
if (hasUndeprecatedOverridden && deprecations.none { it.forcePropagationToOverrides }) 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:
@@ -9,5 +9,5 @@ interface A : MutableCollection<String> {
fun foo(x: MutableCollection<Int>, y: Collection<String>, z: A) {
x.<!DEPRECATION!>nonExistingMethod<!>(1).checkType { _<String>() }
y.<!DEPRECATION!>nonExistingMethod<!>("")
z.nonExistingMethod("")
z.<!DEPRECATION!>nonExistingMethod<!>("")
}
@@ -3,10 +3,10 @@
fun foo(ll: java.util.LinkedList<String>, al: ArrayList<String>, ad: ArrayDeque<String>, jad: java.util.ArrayDeque<String>) {
ll.addFirst("")
ll.addLast("")
ll.getFirst()
ll.<!DEPRECATION!>getFirst<!>()
ll.first // synthetic property for getFirst()
ll.first() // stdlib extension on List
ll.getLast()
ll.<!DEPRECATION!>getLast<!>()
ll.last
ll.last()
ll.<!DEBUG_INFO_CALL("fqName: java.util.LinkedList.removeFirst; typeCall: function")!>removeFirst()<!>
@@ -15,10 +15,10 @@ fun foo(ll: java.util.LinkedList<String>, al: ArrayList<String>, ad: ArrayDeque<
al.addFirst("")
al.addLast("")
al.getFirst()
al.<!DEPRECATION!>getFirst<!>()
al.first
al.first()
al.getLast()
al.<!DEPRECATION!>getLast<!>()
al.last
al.last()
al.<!DEBUG_INFO_CALL("fqName: java.util.ArrayList.removeFirst; typeCall: function")!>removeFirst()<!>
@@ -10,8 +10,8 @@ class A<T> : ArrayList<T>() {
super.addLast(t)
}
override fun <!OVERRIDE_DEPRECATION!>getFirst<!>(): T = super.getFirst()
override fun <!OVERRIDE_DEPRECATION!>getLast<!>(): T = super.getLast()
override fun <!OVERRIDE_DEPRECATION!>getFirst<!>(): T = super.<!DEPRECATION!>getFirst<!>()
override fun <!OVERRIDE_DEPRECATION!>getLast<!>(): T = super.<!DEPRECATION!>getLast<!>()
override fun removeFirst(): T = super.removeFirst()
override fun removeLast(): T = super.removeLast()
@@ -34,10 +34,10 @@ fun foo(x: MutableList<String>, y: ArrayList<String>, z: A<String>) {
y.addFirst("")
y.addLast("")
y.getFirst()
y.<!DEPRECATION!>getFirst<!>()
y.first
y.first()
y.getLast()
y.<!DEPRECATION!>getLast<!>()
y.last
y.last()
y.<!DEBUG_INFO_CALL("fqName: java.util.ArrayList.removeFirst; typeCall: function")!>removeFirst()<!>
@@ -46,10 +46,10 @@ fun foo(x: MutableList<String>, y: ArrayList<String>, z: A<String>) {
z.addFirst("")
z.addLast("")
z.getFirst()
z.<!DEPRECATION!>getFirst<!>()
z.first
z.first()
z.getLast()
z.<!DEPRECATION!>getLast<!>()
z.last
z.last()
z.<!DEBUG_INFO_CALL("fqName: A.removeFirst; typeCall: function")!>removeFirst()<!>
@@ -68,7 +68,8 @@ class JvmBuiltInsCustomizer(
// Most this properties are lazy because they depends on KotlinBuiltIns initialization that depends on JvmBuiltInsSettings object
private val notConsideredDeprecation by storageManager.createLazyValue {
val annotation = moduleDescriptor.builtIns.createDeprecatedAnnotation(
"This member is not fully supported by Kotlin compiler, so it may be absent or have different signature in next major version"
"This member is not fully supported by Kotlin compiler, so it may be absent or have different signature in next major version",
forcePropagationDeprecationToOverrides = true,
)
Annotations.create(listOf(annotation))
}
@@ -27,7 +27,8 @@ import kotlin.LazyThreadSafetyMode.PUBLICATION
class BuiltInAnnotationDescriptor(
private val builtIns: KotlinBuiltIns,
override val fqName: FqName,
override val allValueArguments: Map<Name, ConstantValue<*>>
override val allValueArguments: Map<Name, ConstantValue<*>>,
val forcePropagationDeprecationToOverrides: Boolean = false,
) : AnnotationDescriptor {
override val type: KotlinType by lazy(PUBLICATION) {
builtIns.getBuiltInClassByFqName(fqName).defaultType
@@ -29,7 +29,8 @@ import org.jetbrains.kotlin.types.Variance
fun KotlinBuiltIns.createDeprecatedAnnotation(
message: String,
replaceWith: String = "",
level: String = "WARNING"
level: String = "WARNING",
forcePropagationDeprecationToOverrides: Boolean = false,
): AnnotationDescriptor {
val replaceWithAnnotation = BuiltInAnnotationDescriptor(
this,
@@ -52,10 +53,22 @@ fun KotlinBuiltIns.createDeprecatedAnnotation(
ClassId.topLevel(StandardNames.FqNames.deprecationLevel),
Name.identifier(level)
)
)
),
forcePropagationDeprecationToOverrides,
)
}
// Temporary workaround for kotlinx-serialization-cbor compilation.
// We need some kotlinx-serialization components to be resolved to that function instead of one with default argument
// because for some time, they have a new version at compile time, but the old core in the classpath.
// Might be removed after K/N version is advanced (KT-60858 to track)
@Suppress("unused")
fun KotlinBuiltIns.createDeprecatedAnnotation(
message: String,
replaceWith: String = "",
level: String = "WARNING",
): AnnotationDescriptor = createDeprecatedAnnotation(message, replaceWith, level, forcePropagationDeprecationToOverrides = false)
private val DEPRECATED_MESSAGE_NAME = Name.identifier("message")
private val DEPRECATED_REPLACE_WITH_NAME = Name.identifier("replaceWith")
private val DEPRECATED_LEVEL_NAME = Name.identifier("level")
@@ -10,7 +10,20 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
abstract class DescriptorBasedDeprecationInfo : DeprecationInfo() {
override val propagatesToOverrides: Boolean
get() = true
get() = forcePropagationToOverrides
/**
* Marks deprecation as necessary to propagate to overrides
* even if LanguageFeature.StopPropagatingDeprecationThroughOverrides is disabled or one of the overrides "undeprecated"
* See DeprecationResolver.deprecationByOverridden for details.
*
* Currently, it's only expected to be true for deprecation from unsupported JDK members that might be removed in future versions:
* we'd like to mark their overrides as unsafe as well.
*
* Also, there's an implicit contract that if `forcePropagationToOverrides`, then `propagatesToOverrides` should also be true
*/
open val forcePropagationToOverrides: Boolean
get() = false
abstract val target: DeclarationDescriptor
}