FIR Java: correctly handle overridden Kotlin properties in use-site scope

This commit is contained in:
Mikhail Glukhikh
2019-05-24 13:44:04 +03:00
parent 56435fa283
commit 806d2d628c
9 changed files with 121 additions and 47 deletions
@@ -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) {
@@ -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|
}
@@ -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/");