From e6bb920a1d93d532bab4d879462958f60d7e3700 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Wed, 13 Mar 2019 16:06:49 +0300 Subject: [PATCH] FIR Java: support Kotlin extension overriding: at last, #KT-29937 Fixed --- .../java/scopes/JavaClassEnhancementScope.kt | 44 ++++++++++++++----- .../fir/java/scopes/JavaClassUseSiteScope.kt | 34 +++++++------- .../extraDump.java.txt | 4 ++ .../javaInheritsKotlinExtension/jvm/Base.kt | 5 +++ .../javaInheritsKotlinExtension/jvm/Base.txt | 10 +++++ .../jvm/Inheritor.java | 3 ++ .../javaInheritsKotlinExtension/jvm/Test.kt | 5 +++ .../javaInheritsKotlinExtension/jvm/Test.txt | 9 ++++ .../FirMultiModuleResolveTestGenerated.java | 5 +++ 9 files changed, 93 insertions(+), 26 deletions(-) create mode 100644 idea/testData/fir/multiModule/javaInheritsKotlinExtension/extraDump.java.txt create mode 100644 idea/testData/fir/multiModule/javaInheritsKotlinExtension/jvm/Base.kt create mode 100644 idea/testData/fir/multiModule/javaInheritsKotlinExtension/jvm/Base.txt create mode 100644 idea/testData/fir/multiModule/javaInheritsKotlinExtension/jvm/Inheritor.java create mode 100644 idea/testData/fir/multiModule/javaInheritsKotlinExtension/jvm/Test.kt create mode 100644 idea/testData/fir/multiModule/javaInheritsKotlinExtension/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 3d68b968906..5f69861799a 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 @@ -81,7 +81,7 @@ class JavaClassEnhancementScope( val firField = (original as FirBasedSymbol<*>).fir as? FirJavaField ?: error("Can't make enhancement for $original") val memberContext = context.copyWithNewDefaultTypeQualifiers(typeQualifierResolver, jsr305State, firField.annotations) - val newReturnTypeRef = enhanceReturnType(firField, memberContext, null) + val newReturnTypeRef = enhanceReturnType(firField, emptyList(), memberContext, null) val symbol = FirPropertySymbol(original.callableId) with(firField) { @@ -124,20 +124,26 @@ class JavaClassEnhancementScope( } } - val newReceiverTypeRef = if (firMethod is FirJavaMethod && firMethod.receiverTypeRef != null) { - enhanceReceiverType(firMethod, memberContext) + val overriddenMembers = firMethod.overriddenMembers() + val hasReceiver = overriddenMembers.any { it.receiverTypeRef != null } + + val newReceiverTypeRef = if (firMethod is FirJavaMethod && hasReceiver) { + enhanceReceiverType(firMethod, overriddenMembers, memberContext) } else null val newReturnTypeRef = if (firMethod is FirJavaConstructor) { firMethod.returnTypeRef } else { - enhanceReturnType(firMethod, memberContext, predefinedEnhancementInfo) + enhanceReturnType(firMethod, overriddenMembers, memberContext, predefinedEnhancementInfo) } val newValueParameterInfo = mutableListOf() for ((index, valueParameter) in firMethod.valueParameters.withIndex()) { + if (hasReceiver && index == 0) continue newValueParameterInfo += enhanceValueParameter( - firMethod, memberContext, predefinedEnhancementInfo, valueParameter as FirJavaValueParameter, index + firMethod, overriddenMembers, hasReceiver, + memberContext, predefinedEnhancementInfo, valueParameter as FirJavaValueParameter, + if (hasReceiver) index - 1 else index ) } @@ -213,10 +219,12 @@ class JavaClassEnhancementScope( private fun enhanceReceiverType( ownerFunction: FirJavaMethod, + overriddenMembers: List, memberContext: FirJavaEnhancementContext ): FirResolvedTypeRef { val signatureParts = ownerFunction.partsForValueParameter( typeQualifierResolver, + overriddenMembers, // TODO: check me parameterContainer = ownerFunction, methodContext = memberContext, @@ -229,6 +237,8 @@ class JavaClassEnhancementScope( private fun enhanceValueParameter( ownerFunction: FirCallableMember, + overriddenMembers: List, + hasReceiver: Boolean, memberContext: FirJavaEnhancementContext, predefinedEnhancementInfo: PredefinedFunctionEnhancementInfo?, ownerParameter: FirJavaValueParameter, @@ -236,9 +246,10 @@ class JavaClassEnhancementScope( ): EnhanceValueParameterResult { val signatureParts = ownerFunction.partsForValueParameter( typeQualifierResolver, + overriddenMembers, parameterContainer = ownerParameter, methodContext = memberContext, - typeInSignature = TypeInSignature.ValueParameter(index) + typeInSignature = TypeInSignature.ValueParameter(hasReceiver, index) ).enhance(session, jsr305State, predefinedEnhancementInfo?.parametersInfo?.getOrNull(index)) val firResolvedTypeRef = signatureParts.type val defaultValue = ownerParameter.getDefaultValueFromAnnotation() @@ -252,11 +263,13 @@ class JavaClassEnhancementScope( private fun enhanceReturnType( owner: FirCallableMember, + overriddenMembers: List, memberContext: FirJavaEnhancementContext, predefinedEnhancementInfo: PredefinedFunctionEnhancementInfo? ): FirResolvedTypeRef { val signatureParts = owner.parts( typeQualifierResolver, + overriddenMembers, typeContainer = owner, isCovariant = true, containerContext = memberContext, containerApplicabilityType = @@ -297,22 +310,32 @@ class JavaClassEnhancementScope( } object Receiver : TypeInSignature() { - override fun getTypeRef(member: FirCallableMember): FirTypeRef = member.receiverTypeRef!! + override fun getTypeRef(member: FirCallableMember): FirTypeRef { + if (member is FirJavaMethod) return member.valueParameters[0].returnTypeRef + return member.receiverTypeRef!! + } } - class ValueParameter(val index: Int) : TypeInSignature() { - override fun getTypeRef(member: FirCallableMember): FirTypeRef = (member as FirFunction).valueParameters[index].returnTypeRef + class ValueParameter(val hasReceiver: Boolean, val index: Int) : TypeInSignature() { + override fun getTypeRef(member: FirCallableMember): FirTypeRef { + if (hasReceiver && member is FirJavaMethod) { + return (member as FirFunction).valueParameters[index + 1].returnTypeRef + } + return (member as FirFunction).valueParameters[index].returnTypeRef + } } } private fun FirCallableMember.partsForValueParameter( typeQualifierResolver: FirAnnotationTypeQualifierResolver, + overriddenMembers: List, // TODO: investigate if it's really can be a null (check properties' with extension overrides in Java) parameterContainer: FirAnnotationContainer?, methodContext: FirJavaEnhancementContext, typeInSignature: TypeInSignature ) = parts( typeQualifierResolver, + overriddenMembers, parameterContainer, false, parameterContainer?.let { methodContext.copyWithNewDefaultTypeQualifiers(typeQualifierResolver, jsr305State, it.annotations) @@ -323,6 +346,7 @@ class JavaClassEnhancementScope( private fun FirCallableMember.parts( typeQualifierResolver: FirAnnotationTypeQualifierResolver, + overriddenMembers: List, typeContainer: FirAnnotationContainer?, isCovariant: Boolean, containerContext: FirJavaEnhancementContext, @@ -334,7 +358,7 @@ class JavaClassEnhancementScope( typeQualifierResolver, typeContainer, typeRef as FirJavaTypeRef, - this.overriddenMembers().map { + overriddenMembers.map { typeInSignature.getTypeRef(it) }, isCovariant, 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 90c110ad721..d70d81fbedb 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 @@ -10,6 +10,7 @@ import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.declarations.FirNamedFunction import org.jetbrains.kotlin.fir.declarations.FirRegularClass +import org.jetbrains.kotlin.fir.java.declarations.FirJavaMethod import org.jetbrains.kotlin.fir.java.toNotNullConeKotlinType import org.jetbrains.kotlin.fir.scopes.FirScope import org.jetbrains.kotlin.fir.scopes.ProcessorAction @@ -54,29 +55,30 @@ class JavaClassUseSiteScope( private fun isEqualTypes(a: FirTypeRef, b: FirTypeRef) = isEqualTypes(a.toNotNullConeKotlinType(session), b.toNotNullConeKotlinType(session)) - private fun isOverriddenFunCheck(member: FirNamedFunction, self: FirNamedFunction): Boolean { - return member.valueParameters.size == self.valueParameters.size && - member.valueParameters.zip(self.valueParameters).all { (memberParam, selfParam) -> - isEqualTypes(memberParam.returnTypeRef, selfParam.returnTypeRef) - } + private fun isOverriddenFunCheck(overriddenInJava: FirJavaMethod, base: FirNamedFunction): Boolean { + val receiverTypeRef = base.receiverTypeRef + if (receiverTypeRef == null) { + return overriddenInJava.valueParameters.size == base.valueParameters.size && + overriddenInJava.valueParameters.zip(base.valueParameters).all { (memberParam, selfParam) -> + isEqualTypes(memberParam.returnTypeRef, selfParam.returnTypeRef) + } + } else { + if (overriddenInJava.valueParameters.size != base.valueParameters.size + 1) return false + val baseParameterTypes = listOf(receiverTypeRef) + base.valueParameters.map { it.returnTypeRef } + val javaParameterTypes = overriddenInJava.valueParameters.map { it.returnTypeRef } + return baseParameterTypes.zip(javaParameterTypes).all { (baseType, typeFromJava) -> + isEqualTypes(baseType, typeFromJava) + } + } } internal fun ConeFunctionSymbol.getOverridden(candidates: Set): ConeCallableSymbol? { if (overriddenByBase.containsKey(this)) return overriddenByBase[this] - fun sameReceivers(memberTypeRef: FirTypeRef?, selfTypeRef: FirTypeRef?): Boolean { - return when { - memberTypeRef != null && selfTypeRef != null -> isEqualTypes(memberTypeRef, selfTypeRef) - else -> memberTypeRef == null && selfTypeRef == null - } - } - val self = (this as FirFunctionSymbol).fir as FirNamedFunction val overriding = candidates.firstOrNull { - val member = (it as FirFunctionSymbol).fir as FirNamedFunction - self.modality != Modality.FINAL - && sameReceivers(member.receiverTypeRef, self.receiverTypeRef) - && isOverriddenFunCheck(member, self) + val member = (it as FirFunctionSymbol).fir as FirJavaMethod + self.modality != Modality.FINAL && isOverriddenFunCheck(member, self) } // TODO: two or more overrides for one fun? overriddenByBase[this] = overriding return overriding diff --git a/idea/testData/fir/multiModule/javaInheritsKotlinExtension/extraDump.java.txt b/idea/testData/fir/multiModule/javaInheritsKotlinExtension/extraDump.java.txt new file mode 100644 index 00000000000..df4d706f7b4 --- /dev/null +++ b/idea/testData/fir/multiModule/javaInheritsKotlinExtension/extraDump.java.txt @@ -0,0 +1,4 @@ +public open class Inheritor : R|Second| { + public open operator function foo R|First|.(first: R|kotlin/String|, s: R|kotlin/Int|): R|kotlin/Unit| + +} diff --git a/idea/testData/fir/multiModule/javaInheritsKotlinExtension/jvm/Base.kt b/idea/testData/fir/multiModule/javaInheritsKotlinExtension/jvm/Base.kt new file mode 100644 index 00000000000..3ab84197793 --- /dev/null +++ b/idea/testData/fir/multiModule/javaInheritsKotlinExtension/jvm/Base.kt @@ -0,0 +1,5 @@ +interface First + +open class Second { + open fun First.foo(s: String, i: Int) {} +} \ No newline at end of file diff --git a/idea/testData/fir/multiModule/javaInheritsKotlinExtension/jvm/Base.txt b/idea/testData/fir/multiModule/javaInheritsKotlinExtension/jvm/Base.txt new file mode 100644 index 00000000000..bf3b9c23721 --- /dev/null +++ b/idea/testData/fir/multiModule/javaInheritsKotlinExtension/jvm/Base.txt @@ -0,0 +1,10 @@ +FILE: Base.kt + public abstract interface First { + } + public open class Second { + public constructor(): super() + + public open function foo R|First|.(s: R|kotlin/String|, i: R|kotlin/Int|): R|kotlin/Unit| { + } + + } diff --git a/idea/testData/fir/multiModule/javaInheritsKotlinExtension/jvm/Inheritor.java b/idea/testData/fir/multiModule/javaInheritsKotlinExtension/jvm/Inheritor.java new file mode 100644 index 00000000000..a9b4a3ef323 --- /dev/null +++ b/idea/testData/fir/multiModule/javaInheritsKotlinExtension/jvm/Inheritor.java @@ -0,0 +1,3 @@ +public class Inheritor extends Second { + public void foo(First first, String s, int i) {} +} \ No newline at end of file diff --git a/idea/testData/fir/multiModule/javaInheritsKotlinExtension/jvm/Test.kt b/idea/testData/fir/multiModule/javaInheritsKotlinExtension/jvm/Test.kt new file mode 100644 index 00000000000..3e1bc93c301 --- /dev/null +++ b/idea/testData/fir/multiModule/javaInheritsKotlinExtension/jvm/Test.kt @@ -0,0 +1,5 @@ +class Tester : Inheritor(), First { + fun test() { + foo("abc", 456) + } +} \ No newline at end of file diff --git a/idea/testData/fir/multiModule/javaInheritsKotlinExtension/jvm/Test.txt b/idea/testData/fir/multiModule/javaInheritsKotlinExtension/jvm/Test.txt new file mode 100644 index 00000000000..c845dac9c39 --- /dev/null +++ b/idea/testData/fir/multiModule/javaInheritsKotlinExtension/jvm/Test.txt @@ -0,0 +1,9 @@ +FILE: Test.kt + public final class Tester : R|Inheritor|, R|First| { + public constructor(): super() + + public final function test(): R|kotlin/Unit| { + R|/Inheritor.foo|(String(abc), Int(456)) + } + + } diff --git a/idea/tests/org/jetbrains/kotlin/idea/fir/FirMultiModuleResolveTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/fir/FirMultiModuleResolveTestGenerated.java index 81f1971df29..4f7c3e31a95 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/fir/FirMultiModuleResolveTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/fir/FirMultiModuleResolveTestGenerated.java @@ -64,6 +64,11 @@ public class FirMultiModuleResolveTestGenerated extends AbstractFirMultiModuleRe runTest("idea/testData/fir/multiModule/fullWithJavaPredefinedSignature/"); } + @TestMetadata("javaInheritsKotlinExtension") + public void testJavaInheritsKotlinExtension() throws Exception { + runTest("idea/testData/fir/multiModule/javaInheritsKotlinExtension/"); + } + @TestMetadata("javaInheritsRawKotlin") public void testJavaInheritsRawKotlin() throws Exception { runTest("idea/testData/fir/multiModule/javaInheritsRawKotlin/");