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:
committed by
Space Team
parent
1e86a82ee1
commit
eae97e4f76
@@ -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
|
||||
|
||||
+7
-2
@@ -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:
|
||||
|
||||
+1
-1
@@ -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<!>("")
|
||||
}
|
||||
|
||||
+4
-4
@@ -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))
|
||||
}
|
||||
|
||||
+2
-1
@@ -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
|
||||
|
||||
+15
-2
@@ -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")
|
||||
|
||||
+14
-1
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user