[FIR] By default, only hide members from mapped scope but not overrides

This affects methods like List.getFirst/getLast where it was decided
to keep them hidden but to make them visible on overrides like
ArrayList or LinkedList.
The goal is to prevent a breaking change since some implementations
like LinkedList had this method before JDK 21.

#KT-65438
This commit is contained in:
Kirill Rakhman
2024-02-02 18:20:47 +01:00
committed by Space Team
parent fbe8db4b45
commit fc6d25e50f
8 changed files with 45 additions and 50 deletions
@@ -134,7 +134,7 @@ class JvmMappedScope(
if (jdkMemberStatus == JDKMemberStatus.DROP) return@processor
// hidden methods in final class can't be overridden or called with 'super'
if (jdkMemberStatus == JDKMemberStatus.HIDDEN && firKotlinClass.isFinal) return@processor
if ((jdkMemberStatus == JDKMemberStatus.HIDDEN || jdkMemberStatus == JDKMemberStatus.HIDDEN_IN_DECLARING_CLASS_ONLY) && firKotlinClass.isFinal) return@processor
val newSymbol = mappedSymbolCache.mappedFunctions.getValue(symbol, this to jdkMemberStatus)
processor(newSymbol)
@@ -201,12 +201,13 @@ class JvmMappedScope(
}
}
// For unknown methods, we use HIDDEN policy by default
return JDKMemberStatus.HIDDEN
// For unknown methods, we use HIDDEN_IN_DECLARING_CLASS_ONLY policy by default,
// meaning they are hidden in the declaring class but visible with deprecation in overrides.
return JDKMemberStatus.HIDDEN_IN_DECLARING_CLASS_ONLY
}
internal enum class JDKMemberStatus {
HIDDEN, VISIBLE, DROP
HIDDEN, VISIBLE, DROP, HIDDEN_IN_DECLARING_CLASS_ONLY,
}
override fun processPropertiesByName(name: Name, processor: (FirVariableSymbol<*>) -> Unit) {
@@ -228,7 +229,9 @@ class JvmMappedScope(
newSource = oldFunction.source,
).apply {
if (jdkMemberStatus == JDKMemberStatus.HIDDEN) {
isHiddenEverywhereBesideSuperCalls = true
isHiddenEverywhereBesideSuperCalls = HiddenEverywhereBesideSuperCallsStatus.HIDDEN
} else if (jdkMemberStatus == JDKMemberStatus.HIDDEN_IN_DECLARING_CLASS_ONLY) {
isHiddenEverywhereBesideSuperCalls = HiddenEverywhereBesideSuperCallsStatus.HIDDEN_IN_DECLARING_CLASS_ONLY
}
}
return newSymbol
@@ -15,7 +15,6 @@ import org.jetbrains.kotlin.fir.caches.FirCachesFactory
import org.jetbrains.kotlin.fir.caches.firCachesFactory
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.languageVersionSettings
import org.jetbrains.kotlin.fir.references.FirNamedReference
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
@@ -327,12 +326,25 @@ fun FirBasedSymbol<*>.isDeprecationLevelHidden(languageVersionSettings: Language
private object IsHiddenEverywhereBesideSuperCalls : FirDeclarationDataKey()
var FirCallableDeclaration.isHiddenEverywhereBesideSuperCalls: Boolean? by FirDeclarationDataRegistry.data(
var FirCallableDeclaration.isHiddenEverywhereBesideSuperCalls: HiddenEverywhereBesideSuperCallsStatus? by FirDeclarationDataRegistry.data(
IsHiddenEverywhereBesideSuperCalls
)
enum class HiddenEverywhereBesideSuperCallsStatus(val affectsOverrides: Boolean) {
HIDDEN(true), HIDDEN_IN_DECLARING_CLASS_ONLY(false)
}
private object IsHiddenToOvercomeSignatureClash : FirDeclarationDataKey()
var FirCallableDeclaration.isHiddenToOvercomeSignatureClash: Boolean? by FirDeclarationDataRegistry.data(
IsHiddenToOvercomeSignatureClash
)
fun FirCallableSymbol<*>.isHidden(isSuperCall: Boolean, isOverridden: Boolean): Boolean {
val fir = fir
return when {
fir.isHiddenToOvercomeSignatureClash == true -> true
!isSuperCall && fir.isHiddenEverywhereBesideSuperCalls?.let { it.affectsOverrides || !isOverridden } == true -> true
else -> false
}
}
@@ -264,8 +264,8 @@ class FirSyntheticPropertiesScope private constructor(
var result = Incompatible
val visited = mutableSetOf<MemberWithBaseScope<FirNamedFunctionSymbol>>()
fun checkJavaOrigin(symbol: FirNamedFunctionSymbol, scope: FirTypeScope) {
if (symbol.fir.isHiddenEverywhereBesideSuperCalls == true) {
fun checkJavaOrigin(symbol: FirNamedFunctionSymbol, scope: FirTypeScope, isOverridden: Boolean) {
if (symbol.isHidden(isSuperCall = false, isOverridden = isOverridden)) {
isHiddenEverywhereBesideSuperCalls = true
}
@@ -286,11 +286,11 @@ class FirSyntheticPropertiesScope private constructor(
overriddenWithScope.forEach {
if (!visited.add(it)) return@forEach
checkJavaOrigin(it.member, it.baseScope)
checkJavaOrigin(it.member, it.baseScope, isOverridden = true)
}
}
checkJavaOrigin(this, baseScope)
checkJavaOrigin(this, baseScope, isOverridden = false)
return when {
isHiddenEverywhereBesideSuperCalls -> Incompatible
@@ -768,13 +768,13 @@ internal object CheckHiddenDeclaration : ResolutionStage() {
): Boolean {
val isSuperCall = callInfo.callSite.isSuperCall(session)
if (symbol.fir.dispatchReceiverType == null || symbol !is FirNamedFunctionSymbol) return false
if (symbol.isHidden(isSuperCall)) return true
if (symbol.isHidden(isSuperCall, isOverridden = false)) return true
val scope = candidate.originScope as? FirTypeScope ?: return false
var result = false
scope.processOverriddenFunctions(symbol) {
if (it.isHidden(isSuperCall)) {
if (it.isHidden(isSuperCall, isOverridden = true)) {
result = true
ProcessorAction.STOP
} else {
@@ -788,10 +788,6 @@ internal object CheckHiddenDeclaration : ResolutionStage() {
private fun FirElement.isSuperCall(session: FirSession): Boolean =
this is FirQualifiedAccessExpression && explicitReceiver?.toReference(session) is FirSuperReference
private fun FirCallableSymbol<*>.isHidden(isSuperCall: Boolean): Boolean {
val fir = fir
return !isSuperCall && fir.isHiddenEverywhereBesideSuperCalls == true || fir.isHiddenToOvercomeSignatureClash == true
}
}
private val DYNAMIC_EXTENSION_ANNOTATION_CLASS_ID: ClassId = ClassId.topLevel(DYNAMIC_EXTENSION_FQ_NAME)
@@ -1,11 +1,11 @@
fun foo(ll: java.util.LinkedList<String>, al: ArrayList<String>, ad: ArrayDeque<String>, jad: java.util.ArrayDeque<String>) {
ll.addFirst("")
ll.addLast("")
ll.<!UNRESOLVED_REFERENCE!>getFirst<!>()
ll.<!INVISIBLE_REFERENCE!>first<!> // synthetic property for getFirst()
ll.getFirst()
ll.first // synthetic property for getFirst()
ll.first() // stdlib extension on List
ll.<!UNRESOLVED_REFERENCE!>getLast<!>()
ll.<!INVISIBLE_REFERENCE!>last<!>
ll.getLast()
ll.last
ll.last()
ll.removeFirst()
ll.removeLast()
@@ -13,11 +13,11 @@ fun foo(ll: java.util.LinkedList<String>, al: ArrayList<String>, ad: ArrayDeque<
al.addFirst("")
al.addLast("")
al.<!UNRESOLVED_REFERENCE!>getFirst<!>()
al.<!FUNCTION_CALL_EXPECTED!>first<!>
al.getFirst()
al.first
al.first()
al.<!UNRESOLVED_REFERENCE!>getLast<!>()
al.<!FUNCTION_CALL_EXPECTED!>last<!>
al.getLast()
al.last
al.last()
al.removeFirst()
al.removeLast()
@@ -5,19 +5,3 @@
/newListMethods.fir.kt:(704,711): error: Unresolved reference 'getLast'.
/newListMethods.fir.kt:(720,724): error: Function invocation 'last()' expected.
/newListMethods.fir.kt:(838,846): error: Unresolved reference 'getFirst'.
/newListMethods.fir.kt:(855,860): error: Function invocation 'first()' expected.
/newListMethods.fir.kt:(881,888): error: Unresolved reference 'getLast'.
/newListMethods.fir.kt:(897,901): error: Function invocation 'last()' expected.
/newListMethods.fir.kt:(1015,1023): error: Unresolved reference 'getFirst'.
/newListMethods.fir.kt:(1032,1037): error: Function invocation 'first()' expected.
/newListMethods.fir.kt:(1058,1065): error: Unresolved reference 'getLast'.
/newListMethods.fir.kt:(1074,1078): error: Function invocation 'last()' expected.
@@ -34,11 +34,11 @@ fun foo(x: MutableList<String>, y: ArrayList<String>, z: A<String>) {
y.addFirst("")
y.addLast("")
y.<!UNRESOLVED_REFERENCE!>getFirst<!>()
y.<!FUNCTION_CALL_EXPECTED!>first<!>
y.getFirst()
y.first
y.first()
y.<!UNRESOLVED_REFERENCE!>getLast<!>()
y.<!FUNCTION_CALL_EXPECTED!>last<!>
y.getLast()
y.last
y.last()
y.<!DEBUG_INFO_CALL("fqName: java.util.ArrayList.removeFirst; typeCall: function")!>removeFirst()<!>
y.<!DEBUG_INFO_CALL("fqName: java.util.ArrayList.removeLast; typeCall: function")!>removeLast()<!>
@@ -46,11 +46,11 @@ fun foo(x: MutableList<String>, y: ArrayList<String>, z: A<String>) {
z.addFirst("")
z.addLast("")
z.<!UNRESOLVED_REFERENCE!>getFirst<!>()
z.<!FUNCTION_CALL_EXPECTED!>first<!>
z.getFirst()
z.first
z.first()
z.<!UNRESOLVED_REFERENCE!>getLast<!>()
z.<!FUNCTION_CALL_EXPECTED!>last<!>
z.getLast()
z.last
z.last()
z.<!DEBUG_INFO_CALL("fqName: A.removeFirst; typeCall: function")!>removeFirst()<!>
z.<!DEBUG_INFO_CALL("fqName: A.removeLast; typeCall: function")!>removeLast()<!>
@@ -146,7 +146,7 @@ class FirScopeDumpHandler(testServices: TestServices) : FirAnalysisHandler(testS
private fun SmartPrinter.printInfo(declaration: FirCallableDeclaration, scope: FirTypeScope, counter: SymbolCounter) {
val origin = declaration.origin.takeUnless { it == FirDeclarationOrigin.BuiltIns } ?: FirDeclarationOrigin.Library
print("[$origin]: ")
if (declaration.isHiddenEverywhereBesideSuperCalls == true) {
if (declaration.isHiddenEverywhereBesideSuperCalls != null) {
print("/* hidden beside supers */ ")
} else if (declaration.isHiddenToOvercomeSignatureClash == true) {
print("/* hidden due to clash */ ")