FIR Java: correctly handle overridden Kotlin properties in use-site scope
This commit is contained in:
+1
-1
@@ -315,7 +315,7 @@ class JavaClassEnhancementScope(
|
||||
return signatureParts.type
|
||||
}
|
||||
|
||||
private val overrideBindCache = mutableMapOf<Name, Map<ConeFunctionSymbol?, List<ConeCallableSymbol>>>()
|
||||
private val overrideBindCache = mutableMapOf<Name, Map<ConeCallableSymbol?, List<ConeCallableSymbol>>>()
|
||||
|
||||
private fun FirCallableMemberDeclaration.overriddenMembers(): List<FirCallableMemberDeclaration> {
|
||||
val backMap = overrideBindCache.getOrPut(this.name) {
|
||||
|
||||
+69
-46
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutorByMap
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.firUnsafe
|
||||
import org.jetbrains.kotlin.fir.scopes.FirScope
|
||||
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
|
||||
import org.jetbrains.kotlin.fir.scopes.ProcessorAction.*
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.FirAbstractProviderBasedScope
|
||||
import org.jetbrains.kotlin.fir.symbols.ConeCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.ConeFunctionSymbol
|
||||
@@ -41,7 +42,7 @@ class JavaClassUseSiteScope(
|
||||
if (klass is FirJavaClass) klass.javaTypeParameterStack else JavaTypeParameterStack.EMPTY
|
||||
|
||||
//base symbol as key, overridden as value
|
||||
internal val overriddenByBase = mutableMapOf<ConeCallableSymbol, ConeFunctionSymbol?>()
|
||||
internal val overriddenByBase = mutableMapOf<ConeCallableSymbol, ConeCallableSymbol?>()
|
||||
|
||||
private val context: ConeTypeContext = session.typeContext
|
||||
|
||||
@@ -99,21 +100,31 @@ class JavaClassUseSiteScope(
|
||||
}
|
||||
}
|
||||
|
||||
private fun isOverriddenPropertyCheck(overriddenInKotlin: FirProperty, base: FirProperty): Boolean {
|
||||
val receiverTypeRef = base.receiverTypeRef
|
||||
val overriddenReceiverTypeRef = overriddenInKotlin.receiverTypeRef
|
||||
return when {
|
||||
receiverTypeRef == null -> overriddenReceiverTypeRef == null
|
||||
overriddenReceiverTypeRef == null -> false
|
||||
else -> isEqualTypes(receiverTypeRef, overriddenReceiverTypeRef, ConeSubstitutor.Empty)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun bindOverrides(name: Name) {
|
||||
val overrideCandidates = mutableSetOf<ConeFunctionSymbol>()
|
||||
declaredMemberScope.processFunctionsByName(name) {
|
||||
overrideCandidates += it
|
||||
ProcessorAction.NEXT
|
||||
NEXT
|
||||
}
|
||||
|
||||
|
||||
superTypesScope.processFunctionsByName(name) {
|
||||
it.getOverridden(overrideCandidates)
|
||||
ProcessorAction.NEXT
|
||||
NEXT
|
||||
}
|
||||
}
|
||||
|
||||
private fun ConeCallableSymbol.getOverridden(candidates: Set<ConeFunctionSymbol>): ConeCallableSymbol? {
|
||||
private fun ConeCallableSymbol.getOverridden(candidates: Set<ConeCallableSymbol>): ConeCallableSymbol? {
|
||||
if (overriddenByBase.containsKey(this)) return overriddenByBase[this]
|
||||
|
||||
val overriding = when (this) {
|
||||
@@ -121,23 +132,32 @@ class JavaClassUseSiteScope(
|
||||
val self = firUnsafe<FirFunction>()
|
||||
self as FirCallableMemberDeclaration
|
||||
candidates.firstOrNull {
|
||||
val member = (it as FirFunctionSymbol).fir as FirFunction
|
||||
self.modality != Modality.FINAL && isOverriddenFunCheck(member, self)
|
||||
val overridden = (it as? FirFunctionSymbol)?.fir as? FirFunction
|
||||
overridden != null && self.modality != Modality.FINAL && isOverriddenFunCheck(overridden, self)
|
||||
}
|
||||
}
|
||||
is FirPropertySymbol -> {
|
||||
val self = fir as? FirProperty ?: return null
|
||||
candidates.firstOrNull {
|
||||
val member = (it as FirFunctionSymbol).fir as FirNamedFunction
|
||||
self.modality != Modality.FINAL && isOverriddenPropertyCheck(member, self)
|
||||
when (it) {
|
||||
is FirFunctionSymbol -> {
|
||||
val overridden = it.fir as FirNamedFunction
|
||||
self.modality != Modality.FINAL && isOverriddenPropertyCheck(overridden, self)
|
||||
}
|
||||
is FirPropertySymbol -> {
|
||||
val overridden = it.fir
|
||||
overridden is FirProperty && self.modality != Modality.FINAL && isOverriddenPropertyCheck(overridden, self)
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
is FirAccessorSymbol -> {
|
||||
val self = fir as FirNamedFunction
|
||||
candidates.firstOrNull {
|
||||
val member = (it as FirFunctionSymbol).fir as FirNamedFunction
|
||||
self.modality != Modality.FINAL && isOverriddenFunCheck(member, self)
|
||||
val overridden = (it as? FirFunctionSymbol)?.fir as? FirNamedFunction
|
||||
overridden != null && self.modality != Modality.FINAL && isOverriddenFunCheck(overridden, self)
|
||||
}
|
||||
}
|
||||
else -> error("Unexpected callable symbol: $this")
|
||||
@@ -153,7 +173,7 @@ class JavaClassUseSiteScope(
|
||||
overrideCandidates += it
|
||||
processor(it)
|
||||
}
|
||||
) return ProcessorAction.STOP
|
||||
) return STOP
|
||||
|
||||
return superTypesScope.processFunctionsByName(name) {
|
||||
|
||||
@@ -161,70 +181,73 @@ class JavaClassUseSiteScope(
|
||||
if (overriddenBy == null) {
|
||||
processor(it)
|
||||
} else {
|
||||
ProcessorAction.NEXT
|
||||
NEXT
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun processAccessorFunctionsByName(
|
||||
private fun processAccessorFunctionsAndPropertiesByName(
|
||||
propertyName: Name,
|
||||
accessorName: Name,
|
||||
isGetter: Boolean,
|
||||
processor: (ConePropertySymbol) -> ProcessorAction
|
||||
processor: (ConeVariableSymbol) -> ProcessorAction
|
||||
): ProcessorAction {
|
||||
val overrideCandidates = mutableSetOf<ConeFunctionSymbol>()
|
||||
if (!declaredMemberScope.processFunctionsByName(accessorName) { functionSymbol ->
|
||||
if (functionSymbol is FirFunctionSymbol) {
|
||||
val fir = functionSymbol.fir
|
||||
if (fir is FirNamedFunction) {
|
||||
if (fir.isStatic) {
|
||||
return@processFunctionsByName ProcessorAction.NEXT
|
||||
}
|
||||
when (isGetter) {
|
||||
true -> if (fir.valueParameters.isNotEmpty()) {
|
||||
return@processFunctionsByName ProcessorAction.NEXT
|
||||
val overrideCandidates = mutableSetOf<ConeCallableSymbol>()
|
||||
val klass = symbol.fir
|
||||
if (!declaredMemberScope.processPropertiesByName(propertyName) { variableSymbol ->
|
||||
overrideCandidates += variableSymbol
|
||||
processor(variableSymbol)
|
||||
}
|
||||
) return STOP
|
||||
if (klass is FirJavaClass) {
|
||||
if (!declaredMemberScope.processFunctionsByName(accessorName) { functionSymbol ->
|
||||
if (functionSymbol is FirFunctionSymbol) {
|
||||
val fir = functionSymbol.fir
|
||||
if (fir is FirNamedFunction) {
|
||||
if (fir.isStatic) {
|
||||
return@processFunctionsByName NEXT
|
||||
}
|
||||
false -> if (fir.valueParameters.size != 1) {
|
||||
return@processFunctionsByName ProcessorAction.NEXT
|
||||
when (isGetter) {
|
||||
true -> if (fir.valueParameters.isNotEmpty()) {
|
||||
return@processFunctionsByName NEXT
|
||||
}
|
||||
false -> if (fir.valueParameters.size != 1) {
|
||||
return@processFunctionsByName NEXT
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
overrideCandidates += functionSymbol
|
||||
val accessorSymbol = FirAccessorSymbol(
|
||||
accessorId = functionSymbol.callableId,
|
||||
callableId = CallableId(functionSymbol.callableId.packageName, functionSymbol.callableId.className, propertyName)
|
||||
)
|
||||
if (functionSymbol is FirBasedSymbol<*>) {
|
||||
(functionSymbol.fir as? FirCallableMemberDeclaration)?.let { callableMember -> accessorSymbol.bind(callableMember) }
|
||||
}
|
||||
processor(accessorSymbol)
|
||||
}
|
||||
overrideCandidates += functionSymbol
|
||||
val accessorSymbol = FirAccessorSymbol(
|
||||
accessorId = functionSymbol.callableId,
|
||||
callableId = CallableId(functionSymbol.callableId.packageName, functionSymbol.callableId.className, propertyName)
|
||||
)
|
||||
if (functionSymbol is FirBasedSymbol<*>) {
|
||||
(functionSymbol.fir as? FirCallableMemberDeclaration)?.let { callableMember -> accessorSymbol.bind(callableMember) }
|
||||
}
|
||||
processor(accessorSymbol)
|
||||
}
|
||||
) return ProcessorAction.STOP
|
||||
) return STOP
|
||||
}
|
||||
|
||||
return superTypesScope.processPropertiesByName(propertyName) {
|
||||
val firCallableMember = (it as FirBasedSymbol<*>).fir as? FirCallableMemberDeclaration
|
||||
if (firCallableMember?.isStatic == true) {
|
||||
ProcessorAction.NEXT
|
||||
NEXT
|
||||
} else {
|
||||
val overriddenBy = it.getOverridden(overrideCandidates)
|
||||
if (overriddenBy == null && it is ConePropertySymbol) {
|
||||
processor(it)
|
||||
} else {
|
||||
ProcessorAction.NEXT
|
||||
NEXT
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun processPropertiesByName(name: Name, processor: (ConeVariableSymbol) -> ProcessorAction): ProcessorAction {
|
||||
if (!declaredMemberScope.processPropertiesByName(name) {
|
||||
processor(it)
|
||||
}
|
||||
) return ProcessorAction.STOP
|
||||
|
||||
val getterName = Name.identifier(getterPrefix + name.asString().capitalize())
|
||||
return processAccessorFunctionsByName(name, getterName, isGetter = true, processor = processor)
|
||||
return processAccessorFunctionsAndPropertiesByName(name, getterName, isGetter = true, processor = processor)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
public open class JavaClass : R|Derived| {
|
||||
public constructor(): R|JavaClass|
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
open class Base {
|
||||
open val some: String get() = "Base"
|
||||
}
|
||||
|
||||
open class Derived : Base() {
|
||||
override val some: String get() = "Derived"
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
FILE: Base.kt
|
||||
public open class Base : R|kotlin/Any| {
|
||||
public constructor(): R|Base| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
public open val some: R|kotlin/String|
|
||||
public get(): R|kotlin/String| {
|
||||
^ String(Base)
|
||||
}
|
||||
|
||||
}
|
||||
public open class Derived : R|Base| {
|
||||
public constructor(): R|Derived| {
|
||||
super<R|Base|>()
|
||||
}
|
||||
|
||||
public open override val some: R|kotlin/String|
|
||||
public get(): R|kotlin/String| {
|
||||
^ String(Derived)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
public class JavaClass extends Derived {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
fun test() {
|
||||
val jc = JavaClass()
|
||||
val result = jc.some
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
FILE: Test.kt
|
||||
public final fun test(): R|kotlin/Unit| {
|
||||
lval jc: R|JavaClass| = R|/JavaClass.JavaClass|()
|
||||
lval result: R|kotlin/String| = R|<local>/jc|.R|/Derived.some|
|
||||
}
|
||||
+5
@@ -69,6 +69,11 @@ public class FirMultiModuleResolveTestGenerated extends AbstractFirMultiModuleRe
|
||||
runTest("idea/testData/fir/multiModule/javaGetPrefixConflict/");
|
||||
}
|
||||
|
||||
@TestMetadata("javaInheritsKotlinDerived")
|
||||
public void testJavaInheritsKotlinDerived() throws Exception {
|
||||
runTest("idea/testData/fir/multiModule/javaInheritsKotlinDerived/");
|
||||
}
|
||||
|
||||
@TestMetadata("javaInheritsKotlinExtension")
|
||||
public void testJavaInheritsKotlinExtension() throws Exception {
|
||||
runTest("idea/testData/fir/multiModule/javaInheritsKotlinExtension/");
|
||||
|
||||
Reference in New Issue
Block a user