From 806d2d628cf7675dae0b9ab494cfec6df12e398a Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Fri, 24 May 2019 13:44:04 +0300 Subject: [PATCH] FIR Java: correctly handle overridden Kotlin properties in use-site scope --- .../java/scopes/JavaClassEnhancementScope.kt | 2 +- .../fir/java/scopes/JavaClassUseSiteScope.kt | 115 +++++++++++------- .../extraDump.java.txt | 4 + .../javaInheritsKotlinDerived/jvm/Base.kt | 7 ++ .../javaInheritsKotlinDerived/jvm/Base.txt | 23 ++++ .../jvm/JavaClass.java | 3 + .../javaInheritsKotlinDerived/jvm/Test.kt | 4 + .../javaInheritsKotlinDerived/jvm/Test.txt | 5 + .../FirMultiModuleResolveTestGenerated.java | 5 + 9 files changed, 121 insertions(+), 47 deletions(-) create mode 100644 idea/testData/fir/multiModule/javaInheritsKotlinDerived/extraDump.java.txt create mode 100644 idea/testData/fir/multiModule/javaInheritsKotlinDerived/jvm/Base.kt create mode 100644 idea/testData/fir/multiModule/javaInheritsKotlinDerived/jvm/Base.txt create mode 100644 idea/testData/fir/multiModule/javaInheritsKotlinDerived/jvm/JavaClass.java create mode 100644 idea/testData/fir/multiModule/javaInheritsKotlinDerived/jvm/Test.kt create mode 100644 idea/testData/fir/multiModule/javaInheritsKotlinDerived/jvm/Test.txt diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/scopes/JavaClassEnhancementScope.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/scopes/JavaClassEnhancementScope.kt index 5a8f0348d20..0a77b4de92f 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/scopes/JavaClassEnhancementScope.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/scopes/JavaClassEnhancementScope.kt @@ -315,7 +315,7 @@ class JavaClassEnhancementScope( return signatureParts.type } - private val overrideBindCache = mutableMapOf>>() + private val overrideBindCache = mutableMapOf>>() private fun FirCallableMemberDeclaration.overriddenMembers(): List { val backMap = overrideBindCache.getOrPut(this.name) { diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/scopes/JavaClassUseSiteScope.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/scopes/JavaClassUseSiteScope.kt index 9fc059655d7..75189b1e4b8 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/scopes/JavaClassUseSiteScope.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/scopes/JavaClassUseSiteScope.kt @@ -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() + internal val overriddenByBase = mutableMapOf() 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() declaredMemberScope.processFunctionsByName(name) { overrideCandidates += it - ProcessorAction.NEXT + NEXT } superTypesScope.processFunctionsByName(name) { it.getOverridden(overrideCandidates) - ProcessorAction.NEXT + NEXT } } - private fun ConeCallableSymbol.getOverridden(candidates: Set): ConeCallableSymbol? { + private fun ConeCallableSymbol.getOverridden(candidates: Set): ConeCallableSymbol? { if (overriddenByBase.containsKey(this)) return overriddenByBase[this] val overriding = when (this) { @@ -121,23 +132,32 @@ class JavaClassUseSiteScope( val self = firUnsafe() 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() - 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() + 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 { diff --git a/idea/testData/fir/multiModule/javaInheritsKotlinDerived/extraDump.java.txt b/idea/testData/fir/multiModule/javaInheritsKotlinDerived/extraDump.java.txt new file mode 100644 index 00000000000..d49a9e738f2 --- /dev/null +++ b/idea/testData/fir/multiModule/javaInheritsKotlinDerived/extraDump.java.txt @@ -0,0 +1,4 @@ +public open class JavaClass : R|Derived| { + public constructor(): R|JavaClass| + +} diff --git a/idea/testData/fir/multiModule/javaInheritsKotlinDerived/jvm/Base.kt b/idea/testData/fir/multiModule/javaInheritsKotlinDerived/jvm/Base.kt new file mode 100644 index 00000000000..9358101bf45 --- /dev/null +++ b/idea/testData/fir/multiModule/javaInheritsKotlinDerived/jvm/Base.kt @@ -0,0 +1,7 @@ +open class Base { + open val some: String get() = "Base" +} + +open class Derived : Base() { + override val some: String get() = "Derived" +} \ No newline at end of file diff --git a/idea/testData/fir/multiModule/javaInheritsKotlinDerived/jvm/Base.txt b/idea/testData/fir/multiModule/javaInheritsKotlinDerived/jvm/Base.txt new file mode 100644 index 00000000000..21df10c1487 --- /dev/null +++ b/idea/testData/fir/multiModule/javaInheritsKotlinDerived/jvm/Base.txt @@ -0,0 +1,23 @@ +FILE: Base.kt + public open class Base : R|kotlin/Any| { + public constructor(): R|Base| { + super() + } + + 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() + } + + public open override val some: R|kotlin/String| + public get(): R|kotlin/String| { + ^ String(Derived) + } + + } diff --git a/idea/testData/fir/multiModule/javaInheritsKotlinDerived/jvm/JavaClass.java b/idea/testData/fir/multiModule/javaInheritsKotlinDerived/jvm/JavaClass.java new file mode 100644 index 00000000000..aa3ccdc5429 --- /dev/null +++ b/idea/testData/fir/multiModule/javaInheritsKotlinDerived/jvm/JavaClass.java @@ -0,0 +1,3 @@ +public class JavaClass extends Derived { + +} \ No newline at end of file diff --git a/idea/testData/fir/multiModule/javaInheritsKotlinDerived/jvm/Test.kt b/idea/testData/fir/multiModule/javaInheritsKotlinDerived/jvm/Test.kt new file mode 100644 index 00000000000..350847cd3b0 --- /dev/null +++ b/idea/testData/fir/multiModule/javaInheritsKotlinDerived/jvm/Test.kt @@ -0,0 +1,4 @@ +fun test() { + val jc = JavaClass() + val result = jc.some +} \ No newline at end of file diff --git a/idea/testData/fir/multiModule/javaInheritsKotlinDerived/jvm/Test.txt b/idea/testData/fir/multiModule/javaInheritsKotlinDerived/jvm/Test.txt new file mode 100644 index 00000000000..33d09290429 --- /dev/null +++ b/idea/testData/fir/multiModule/javaInheritsKotlinDerived/jvm/Test.txt @@ -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|/jc|.R|/Derived.some| + } diff --git a/idea/tests/org/jetbrains/kotlin/idea/fir/FirMultiModuleResolveTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/fir/FirMultiModuleResolveTestGenerated.java index a704dfc56a1..734d819ccc1 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/fir/FirMultiModuleResolveTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/fir/FirMultiModuleResolveTestGenerated.java @@ -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/");