diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecation/Deprecation.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecation/Deprecation.kt index 97ab48a86a7..3d19352de8d 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecation/Deprecation.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecation/Deprecation.kt @@ -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 diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecation/DeprecationResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecation/DeprecationResolver.kt index 452e4407860..430c70bb5a5 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecation/DeprecationResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecation/DeprecationResolver.kt @@ -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: diff --git a/compiler/testData/diagnostics/tests/testWithModifiedMockJdk/notConsideredMethod.kt b/compiler/testData/diagnostics/tests/testWithModifiedMockJdk/notConsideredMethod.kt index b93a1e3d2f1..d1d87cd1e57 100644 --- a/compiler/testData/diagnostics/tests/testWithModifiedMockJdk/notConsideredMethod.kt +++ b/compiler/testData/diagnostics/tests/testWithModifiedMockJdk/notConsideredMethod.kt @@ -9,5 +9,5 @@ interface A : MutableCollection { fun foo(x: MutableCollection, y: Collection, z: A) { x.nonExistingMethod(1).checkType { _() } y.nonExistingMethod("") - z.nonExistingMethod("") + z.nonExistingMethod("") } diff --git a/compiler/testData/diagnostics/testsWithJdk21/implementationsForSequencedCollection.kt b/compiler/testData/diagnostics/testsWithJdk21/implementationsForSequencedCollection.kt index dbfcea98e9c..c31bb0cce3e 100644 --- a/compiler/testData/diagnostics/testsWithJdk21/implementationsForSequencedCollection.kt +++ b/compiler/testData/diagnostics/testsWithJdk21/implementationsForSequencedCollection.kt @@ -3,10 +3,10 @@ fun foo(ll: java.util.LinkedList, al: ArrayList, ad: ArrayDeque, jad: java.util.ArrayDeque) { ll.addFirst("") ll.addLast("") - ll.getFirst() + ll.getFirst() ll.first // synthetic property for getFirst() ll.first() // stdlib extension on List - ll.getLast() + ll.getLast() ll.last ll.last() ll.removeFirst() @@ -15,10 +15,10 @@ fun foo(ll: java.util.LinkedList, al: ArrayList, ad: ArrayDeque< al.addFirst("") al.addLast("") - al.getFirst() + al.getFirst() al.first al.first() - al.getLast() + al.getLast() al.last al.last() al.removeFirst() diff --git a/compiler/testData/diagnostics/testsWithJdk21/newListMethods.kt b/compiler/testData/diagnostics/testsWithJdk21/newListMethods.kt index 79420fd70ab..9913816cda5 100644 --- a/compiler/testData/diagnostics/testsWithJdk21/newListMethods.kt +++ b/compiler/testData/diagnostics/testsWithJdk21/newListMethods.kt @@ -10,8 +10,8 @@ class A : ArrayList() { super.addLast(t) } - override fun getFirst(): T = super.getFirst() - override fun getLast(): T = super.getLast() + override fun getFirst(): T = super.getFirst() + override fun getLast(): T = super.getLast() override fun removeFirst(): T = super.removeFirst() override fun removeLast(): T = super.removeLast() @@ -34,10 +34,10 @@ fun foo(x: MutableList, y: ArrayList, z: A) { y.addFirst("") y.addLast("") - y.getFirst() + y.getFirst() y.first y.first() - y.getLast() + y.getLast() y.last y.last() y.removeFirst() @@ -46,10 +46,10 @@ fun foo(x: MutableList, y: ArrayList, z: A) { z.addFirst("") z.addLast("") - z.getFirst() + z.getFirst() z.first z.first() - z.getLast() + z.getLast() z.last z.last() z.removeFirst() diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/builtins/jvm/JvmBuiltInsCustomizer.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/builtins/jvm/JvmBuiltInsCustomizer.kt index b6a3728f092..62ac0b0ca85 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/builtins/jvm/JvmBuiltInsCustomizer.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/builtins/jvm/JvmBuiltInsCustomizer.kt @@ -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)) } diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/BuiltInAnnotationDescriptor.kt b/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/BuiltInAnnotationDescriptor.kt index e328c7fc9ee..ac10778492b 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/BuiltInAnnotationDescriptor.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/BuiltInAnnotationDescriptor.kt @@ -27,7 +27,8 @@ import kotlin.LazyThreadSafetyMode.PUBLICATION class BuiltInAnnotationDescriptor( private val builtIns: KotlinBuiltIns, override val fqName: FqName, - override val allValueArguments: Map> + override val allValueArguments: Map>, + val forcePropagationDeprecationToOverrides: Boolean = false, ) : AnnotationDescriptor { override val type: KotlinType by lazy(PUBLICATION) { builtIns.getBuiltInClassByFqName(fqName).defaultType diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/annotationUtil.kt b/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/annotationUtil.kt index 5de5f592c0d..a4c5dd4d7f8 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/annotationUtil.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/annotationUtil.kt @@ -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") diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/deprecation/DescriptorBasedDeprecationInfo.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/deprecation/DescriptorBasedDeprecationInfo.kt index 12c9e44191b..b267ef5f85e 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/deprecation/DescriptorBasedDeprecationInfo.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/deprecation/DescriptorBasedDeprecationInfo.kt @@ -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 }