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.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
|
||||||
|
|||||||
+7
-2
@@ -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:
|
||||||
|
|||||||
+1
-1
@@ -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<!>("")
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -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))
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -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
|
||||||
|
|||||||
+15
-2
@@ -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")
|
||||||
|
|||||||
+14
-1
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user