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.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.TypeAliasDescriptor import org.jetbrains.kotlin.descriptors.TypeAliasDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor 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.metadata.deserialization.VersionRequirement
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.annotations.argumentValue import org.jetbrains.kotlin.resolve.annotations.argumentValue
@@ -23,8 +24,16 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
internal sealed class DeprecatedByAnnotation( internal sealed class DeprecatedByAnnotation(
val annotation: AnnotationDescriptor, val annotation: AnnotationDescriptor,
override val target: DeclarationDescriptor, override val target: DeclarationDescriptor,
override val propagatesToOverrides: Boolean final override val propagatesToOverrides: Boolean,
final override val forcePropagationToOverrides: Boolean = false,
) : DescriptorBasedDeprecationInfo() { ) : DescriptorBasedDeprecationInfo() {
init {
require(!forcePropagationToOverrides || propagatesToOverrides) {
"if something is `forcePropagationToOverrides`, it's expected that `propagatesToOverrides` == true, too"
}
}
override val message: String? override val message: String?
get() = (annotation.argumentValue("message") as? StringValue)?.value get() = (annotation.argumentValue("message") as? StringValue)?.value
@@ -37,8 +46,9 @@ internal sealed class DeprecatedByAnnotation(
class StandardDeprecated( class StandardDeprecated(
annotation: AnnotationDescriptor, annotation: AnnotationDescriptor,
target: DeclarationDescriptor, target: DeclarationDescriptor,
propagatesToOverrides: Boolean propagatesToOverrides: Boolean,
) : DeprecatedByAnnotation(annotation, target, propagatesToOverrides) { forcePropagationToOverrides: Boolean = false,
) : DeprecatedByAnnotation(annotation, target, propagatesToOverrides, forcePropagationToOverrides) {
override val deprecationLevel: DeprecationLevelValue override val deprecationLevel: DeprecationLevelValue
get() = when ((annotation.argumentValue("level") as? EnumValue)?.enumEntryName?.asString()) { get() = when ((annotation.argumentValue("level") as? EnumValue)?.enumEntryName?.asString()) {
"WARNING" -> WARNING "WARNING" -> WARNING
@@ -105,7 +115,14 @@ internal sealed class DeprecatedByAnnotation(
val level = computeLevelForDeprecatedSinceKotlin(deprecatedSinceKotlinAnnotation, apiVersion) ?: return null val level = computeLevelForDeprecatedSinceKotlin(deprecatedSinceKotlinAnnotation, apiVersion) ?: return null
return DeprecatedSince(deprecatedAnnotation, target, propagatesToOverrides, level) 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 }) 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 deprecationLevel: DeprecationLevelValue = deprecations.map(DescriptorBasedDeprecationInfo::deprecationLevel).minOrNull()!!
override val target: DeclarationDescriptor override val target: DeclarationDescriptor
@@ -46,7 +46,11 @@ class DeprecationResolver(
val inheritedDeprecations = listOfNotNull(deprecationByOverridden(descriptor)) val inheritedDeprecations = listOfNotNull(deprecationByOverridden(descriptor))
when (inheritedDeprecations.isNotEmpty()) { when (inheritedDeprecations.isNotEmpty()) {
true -> when (languageVersionSettings.supportsFeature(LanguageFeature.StopPropagatingDeprecationThroughOverrides)) { 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(inheritedDeprecations, hasInheritedDeprecations = true)
} }
false -> DeprecationInfo.EMPTY false -> DeprecationInfo.EMPTY
@@ -167,7 +171,8 @@ class DeprecationResolver(
traverse(root) 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. // 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: // 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) { fun foo(x: MutableCollection<Int>, y: Collection<String>, z: A) {
x.<!DEPRECATION!>nonExistingMethod<!>(1).checkType { _<String>() } x.<!DEPRECATION!>nonExistingMethod<!>(1).checkType { _<String>() }
y.<!DEPRECATION!>nonExistingMethod<!>("") 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>) { fun foo(ll: java.util.LinkedList<String>, al: ArrayList<String>, ad: ArrayDeque<String>, jad: java.util.ArrayDeque<String>) {
ll.addFirst("") ll.addFirst("")
ll.addLast("") ll.addLast("")
ll.getFirst() ll.<!DEPRECATION!>getFirst<!>()
ll.first // synthetic property for getFirst() ll.first // synthetic property for getFirst()
ll.first() // stdlib extension on List ll.first() // stdlib extension on List
ll.getLast() ll.<!DEPRECATION!>getLast<!>()
ll.last ll.last
ll.last() ll.last()
ll.<!DEBUG_INFO_CALL("fqName: java.util.LinkedList.removeFirst; typeCall: function")!>removeFirst()<!> 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.addFirst("")
al.addLast("") al.addLast("")
al.getFirst() al.<!DEPRECATION!>getFirst<!>()
al.first al.first
al.first() al.first()
al.getLast() al.<!DEPRECATION!>getLast<!>()
al.last al.last
al.last() al.last()
al.<!DEBUG_INFO_CALL("fqName: java.util.ArrayList.removeFirst; typeCall: function")!>removeFirst()<!> al.<!DEBUG_INFO_CALL("fqName: java.util.ArrayList.removeFirst; typeCall: function")!>removeFirst()<!>
@@ -10,8 +10,8 @@ class A<T> : ArrayList<T>() {
super.addLast(t) super.addLast(t)
} }
override fun <!OVERRIDE_DEPRECATION!>getFirst<!>(): T = super.getFirst() override fun <!OVERRIDE_DEPRECATION!>getFirst<!>(): T = super.<!DEPRECATION!>getFirst<!>()
override fun <!OVERRIDE_DEPRECATION!>getLast<!>(): T = super.getLast() override fun <!OVERRIDE_DEPRECATION!>getLast<!>(): T = super.<!DEPRECATION!>getLast<!>()
override fun removeFirst(): T = super.removeFirst() override fun removeFirst(): T = super.removeFirst()
override fun removeLast(): T = super.removeLast() override fun removeLast(): T = super.removeLast()
@@ -34,10 +34,10 @@ fun foo(x: MutableList<String>, y: ArrayList<String>, z: A<String>) {
y.addFirst("") y.addFirst("")
y.addLast("") y.addLast("")
y.getFirst() y.<!DEPRECATION!>getFirst<!>()
y.first y.first
y.first() y.first()
y.getLast() y.<!DEPRECATION!>getLast<!>()
y.last y.last
y.last() y.last()
y.<!DEBUG_INFO_CALL("fqName: java.util.ArrayList.removeFirst; typeCall: function")!>removeFirst()<!> 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.addFirst("")
z.addLast("") z.addLast("")
z.getFirst() z.<!DEPRECATION!>getFirst<!>()
z.first z.first
z.first() z.first()
z.getLast() z.<!DEPRECATION!>getLast<!>()
z.last z.last
z.last() z.last()
z.<!DEBUG_INFO_CALL("fqName: A.removeFirst; typeCall: function")!>removeFirst()<!> 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 // Most this properties are lazy because they depends on KotlinBuiltIns initialization that depends on JvmBuiltInsSettings object
private val notConsideredDeprecation by storageManager.createLazyValue { private val notConsideredDeprecation by storageManager.createLazyValue {
val annotation = moduleDescriptor.builtIns.createDeprecatedAnnotation( 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)) Annotations.create(listOf(annotation))
} }
@@ -27,7 +27,8 @@ import kotlin.LazyThreadSafetyMode.PUBLICATION
class BuiltInAnnotationDescriptor( class BuiltInAnnotationDescriptor(
private val builtIns: KotlinBuiltIns, private val builtIns: KotlinBuiltIns,
override val fqName: FqName, override val fqName: FqName,
override val allValueArguments: Map<Name, ConstantValue<*>> override val allValueArguments: Map<Name, ConstantValue<*>>,
val forcePropagationDeprecationToOverrides: Boolean = false,
) : AnnotationDescriptor { ) : AnnotationDescriptor {
override val type: KotlinType by lazy(PUBLICATION) { override val type: KotlinType by lazy(PUBLICATION) {
builtIns.getBuiltInClassByFqName(fqName).defaultType builtIns.getBuiltInClassByFqName(fqName).defaultType
@@ -29,7 +29,8 @@ import org.jetbrains.kotlin.types.Variance
fun KotlinBuiltIns.createDeprecatedAnnotation( fun KotlinBuiltIns.createDeprecatedAnnotation(
message: String, message: String,
replaceWith: String = "", replaceWith: String = "",
level: String = "WARNING" level: String = "WARNING",
forcePropagationDeprecationToOverrides: Boolean = false,
): AnnotationDescriptor { ): AnnotationDescriptor {
val replaceWithAnnotation = BuiltInAnnotationDescriptor( val replaceWithAnnotation = BuiltInAnnotationDescriptor(
this, this,
@@ -52,10 +53,22 @@ fun KotlinBuiltIns.createDeprecatedAnnotation(
ClassId.topLevel(StandardNames.FqNames.deprecationLevel), ClassId.topLevel(StandardNames.FqNames.deprecationLevel),
Name.identifier(level) 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_MESSAGE_NAME = Name.identifier("message")
private val DEPRECATED_REPLACE_WITH_NAME = Name.identifier("replaceWith") private val DEPRECATED_REPLACE_WITH_NAME = Name.identifier("replaceWith")
private val DEPRECATED_LEVEL_NAME = Name.identifier("level") private val DEPRECATED_LEVEL_NAME = Name.identifier("level")
@@ -10,7 +10,20 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
abstract class DescriptorBasedDeprecationInfo : DeprecationInfo() { abstract class DescriptorBasedDeprecationInfo : DeprecationInfo() {
override val propagatesToOverrides: Boolean 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 abstract val target: DeclarationDescriptor
} }