diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt index 3cd13138b71..f4073da075d 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt @@ -27,6 +27,7 @@ import org.jetbrains.kotlin.fir.resolve.symbolProvider import org.jetbrains.kotlin.fir.resolve.toSymbol import org.jetbrains.kotlin.fir.resolve.transformers.firClassLike import org.jetbrains.kotlin.fir.scopes.ProcessorAction +import org.jetbrains.kotlin.fir.scopes.impl.multipleDelegatesWithTheSameSignature import org.jetbrains.kotlin.fir.scopes.processOverriddenFunctions import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope import org.jetbrains.kotlin.fir.symbols.impl.* @@ -438,6 +439,11 @@ fun FirCallableMemberDeclaration.isVisibleInClass(parentClass: FirClass): Boolea fun FirCallableMemberDeclaration.getImplementationStatus(sessionHolder: SessionHolder, parentClass: FirClass): ImplementationStatus { val containingClass = getContainingClass(sessionHolder) val symbol = this.symbol + + if (this.multipleDelegatesWithTheSameSignature == true && containingClass == parentClass) { + return ImplementationStatus.AMBIGUOUSLY_INHERITED + } + if (symbol is FirIntersectionCallableSymbol) { if (containingClass === parentClass && symbol.subjectToManyNotImplemented(sessionHolder)) { return ImplementationStatus.AMBIGUOUSLY_INHERITED diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirNotImplementedOverrideChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirNotImplementedOverrideChecker.kt index fceae0b20f4..ced99ab0129 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirNotImplementedOverrideChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirNotImplementedOverrideChecker.kt @@ -12,23 +12,26 @@ import org.jetbrains.kotlin.fir.FirFakeSourceElementKind import org.jetbrains.kotlin.fir.analysis.checkers.* import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ABSTRACT_CLASS_MEMBER_NOT_IMPLEMENTED import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ABSTRACT_MEMBER_NOT_IMPLEMENTED import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INVISIBLE_ABSTRACT_MEMBER_FROM_SUPER import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INVISIBLE_ABSTRACT_MEMBER_FROM_SUPER_WARNING import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MANY_IMPL_MEMBER_NOT_IMPLEMENTED -import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.OVERRIDING_FINAL_MEMBER_BY_DELEGATION import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn import org.jetbrains.kotlin.fir.containingClass -import org.jetbrains.kotlin.fir.declarations.* -import org.jetbrains.kotlin.fir.declarations.utils.isEnumClass -import org.jetbrains.kotlin.fir.declarations.utils.isExpect -import org.jetbrains.kotlin.fir.declarations.utils.isInterface -import org.jetbrains.kotlin.fir.declarations.utils.modality +import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration +import org.jetbrains.kotlin.fir.declarations.FirCallableMemberDeclaration +import org.jetbrains.kotlin.fir.declarations.FirClass +import org.jetbrains.kotlin.fir.declarations.FirRegularClass +import org.jetbrains.kotlin.fir.declarations.utils.* import org.jetbrains.kotlin.fir.languageVersionSettings import org.jetbrains.kotlin.fir.resolve.toSymbol +import org.jetbrains.kotlin.fir.scopes.getDirectOverriddenMembers +import org.jetbrains.kotlin.fir.scopes.impl.delegatedWrapperData +import org.jetbrains.kotlin.fir.scopes.impl.multipleDelegatesWithTheSameSignature import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirIntersectionCallableSymbol import org.jetbrains.kotlin.fir.unwrapFakeOverrides @@ -50,10 +53,39 @@ object FirNotImplementedOverrideChecker : FirClassChecker() { val notImplementedSymbols = mutableListOf>() val notImplementedIntersectionSymbols = mutableListOf>() + val manyImplementationsDelegationSymbols = mutableListOf>() + val delegationOverrideOfFinal = mutableListOf, FirCallableSymbol<*>>>() + val delegationOverrideOfOpen = mutableListOf, FirCallableSymbol<*>>>() val invisibleSymbols = mutableListOf>() fun collectSymbol(symbol: FirCallableSymbol<*>) { val fir = symbol.fir as? FirCallableMemberDeclaration ?: return + + if (fir.delegatedWrapperData != null) { + val directOverriddenMembers = classScope.getDirectOverriddenMembers( + symbol, + unwrapIntersectionAndSubstitutionOverride = true + ) + + val delegatedTo = fir.delegatedWrapperData!!.wrapped.unwrapFakeOverrides() + + if (symbol.fir.multipleDelegatesWithTheSameSignature == true) { + manyImplementationsDelegationSymbols.add(symbol) + } + + val firstFinal = directOverriddenMembers.firstOrNull { it.fir.isFinal } + val firstOpen = directOverriddenMembers.firstOrNull { it.fir.isOpen && delegatedTo != it.unwrapFakeOverrides().fir } + + when { + firstFinal != null -> + delegationOverrideOfFinal.add(symbol to firstFinal) + + firstOpen != null -> + delegationOverrideOfOpen.add(symbol to firstOpen) + } + + return + } when (fir.getImplementationStatus(context.sessionHolder, declaration)) { ImplementationStatus.AMBIGUOUSLY_INHERITED -> notImplementedIntersectionSymbols.add(symbol) ImplementationStatus.NOT_IMPLEMENTED -> when { @@ -87,59 +119,48 @@ object FirNotImplementedOverrideChecker : FirClassChecker() { reporter.reportOn(source, INVISIBLE_ABSTRACT_MEMBER_FROM_SUPER_WARNING, declaration, invisible, context) } } - if (notImplementedIntersectionSymbols.isNotEmpty()) { - var overridingFinalByDelegationReported = false - var manyMemberNotImplementedReported = false - var delegatedHidesSupertypeReported = false - for (notImplementedIntersectionSymbol in notImplementedIntersectionSymbols) { - val notImplementedIntersection = notImplementedIntersectionSymbol.fir - val intersections = (notImplementedIntersectionSymbol as FirIntersectionCallableSymbol).intersections - val delegatedIntersected = intersections.find { - val fir = it.fir as FirCallableMemberDeclaration - fir.origin == FirDeclarationOrigin.Delegated + + manyImplementationsDelegationSymbols.firstOrNull()?.let { + reporter.reportOn(source, MANY_IMPL_MEMBER_NOT_IMPLEMENTED, declaration, it.fir, context) + } + + delegationOverrideOfFinal.firstOrNull()?.let { (delegated, final) -> + reporter.reportOn( + source, + OVERRIDING_FINAL_MEMBER_BY_DELEGATION, + delegated.fir, + final.fir, + context + ) + } + + delegationOverrideOfOpen.firstOrNull()?.let { (delegated, open) -> + reporter.reportOn( + source, + DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE, + delegated.fir, + open.fir, + context + ) + } + + if (manyImplementationsDelegationSymbols.isEmpty() && notImplementedIntersectionSymbols.isNotEmpty()) { + val notImplementedIntersectionSymbol = notImplementedIntersectionSymbols.first() + val notImplementedIntersection = notImplementedIntersectionSymbol.fir + val intersections = (notImplementedIntersectionSymbol as FirIntersectionCallableSymbol).intersections + if (intersections.any { + (it.containingClass()?.toSymbol(context.session)?.fir as? FirRegularClass)?.classKind == ClassKind.CLASS } - if (delegatedIntersected != null) { - val finalIntersected = intersections.find { (it.fir as FirCallableMemberDeclaration).modality == Modality.FINAL } - if (finalIntersected != null) { - if (!overridingFinalByDelegationReported) { - reporter.reportOn( - source, - OVERRIDING_FINAL_MEMBER_BY_DELEGATION, - delegatedIntersected.fir, - finalIntersected.fir, - context - ) - overridingFinalByDelegationReported = true - } - continue - } - val notDelegatedIntersected = intersections.firstOrNull { - (it.fir as FirCallableMemberDeclaration).origin != FirDeclarationOrigin.Delegated - } - if (notDelegatedIntersected != null) { - if (!delegatedHidesSupertypeReported) { - reporter.reportOn( - source, - DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE, - delegatedIntersected.fir, - notDelegatedIntersected.fir, - context - ) - delegatedHidesSupertypeReported = true - } - continue - } - } - if (manyMemberNotImplementedReported) continue - if (intersections.any { - (it.containingClass()?.toSymbol(context.session)?.fir as? FirRegularClass)?.classKind == ClassKind.CLASS - } - ) { - reporter.reportOn(source, MANY_IMPL_MEMBER_NOT_IMPLEMENTED, declaration, notImplementedIntersection, context) - } else { - reporter.reportOn(source, MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED, declaration, notImplementedIntersection, context) - } - manyMemberNotImplementedReported = true + ) { + reporter.reportOn(source, MANY_IMPL_MEMBER_NOT_IMPLEMENTED, declaration, notImplementedIntersection, context) + } else { + reporter.reportOn( + source, + FirErrors.MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED, + declaration, + notImplementedIntersection, + context + ) } } } diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/scopes/FirTypeScope.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/scopes/FirTypeScope.kt index a1b542042fc..1931c353996 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/scopes/FirTypeScope.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/scopes/FirTypeScope.kt @@ -5,8 +5,11 @@ package org.jetbrains.kotlin.fir.scopes +import org.jetbrains.kotlin.fir.declarations.FirCallableMemberDeclaration import org.jetbrains.kotlin.fir.isIntersectionOverride +import org.jetbrains.kotlin.fir.originalForSubstitutionOverride import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirIntersectionCallableSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol import org.jetbrains.kotlin.name.Name @@ -140,24 +143,56 @@ inline fun FirTypeScope.processDirectlyOverriddenProperties( processor(overridden) } -fun FirTypeScope.getDirectOverriddenFunctions(function: FirNamedFunctionSymbol): List { +fun FirTypeScope.getDirectOverriddenMembers( + member: FirCallableSymbol<*>, + unwrapIntersectionAndSubstitutionOverride: Boolean = false, +): List> = + when (member) { + is FirNamedFunctionSymbol -> getDirectOverriddenFunctions(member, unwrapIntersectionAndSubstitutionOverride) + is FirPropertySymbol -> getDirectOverriddenProperties(member, unwrapIntersectionAndSubstitutionOverride) + else -> emptyList() + } + +fun FirTypeScope.getDirectOverriddenFunctions( + function: FirNamedFunctionSymbol, + unwrapIntersectionAndSubstitutionOverride: Boolean = false, +): List { val overriddenFunctions = mutableSetOf() processDirectlyOverriddenFunctions(function) { - overriddenFunctions.add(it) + overriddenFunctions.addOverridden(it, unwrapIntersectionAndSubstitutionOverride) ProcessorAction.NEXT } return overriddenFunctions.toList() } -fun FirTypeScope.getDirectOverriddenProperties(property: FirPropertySymbol): List { +fun FirTypeScope.getDirectOverriddenProperties( + property: FirPropertySymbol, + unwrapIntersectionAndSubstitutionOverride: Boolean = false, +): List { val overriddenProperties = mutableSetOf() processDirectlyOverriddenProperties(property) { - overriddenProperties.add(it) + overriddenProperties.addOverridden(it, unwrapIntersectionAndSubstitutionOverride) ProcessorAction.NEXT } return overriddenProperties.toList() } + +private inline fun > MutableCollection.addOverridden( + symbol: D, + unwrapIntersectionAndSubstitutionOverride: Boolean +) { + if (unwrapIntersectionAndSubstitutionOverride) { + if (symbol is FirIntersectionCallableSymbol) { + @Suppress("UNCHECKED_CAST") + addAll(symbol.intersections as Collection) + } else { + add(symbol.originalForSubstitutionOverride ?: symbol) + } + } else { + add(symbol) + } +} diff --git a/compiler/testData/diagnostics/tests/delegation/clashes/propertyTypeMismatch.fir.kt b/compiler/testData/diagnostics/tests/delegation/clashes/propertyTypeMismatch.fir.kt index 27c3b9c6d03..e3f5a382dc4 100644 --- a/compiler/testData/diagnostics/tests/delegation/clashes/propertyTypeMismatch.fir.kt +++ b/compiler/testData/diagnostics/tests/delegation/clashes/propertyTypeMismatch.fir.kt @@ -51,7 +51,7 @@ abstract class Test8 : IGeneric by CGeneric(), IInt abstract class Test10 : IInt by CInt(), IStr by CStr(), IAny by CAny() -abstract class Test11 : IInt, IStr by CStr(), IAny by CAny() +abstract class Test11 : IInt, IStr by CStr(), IAny by CAny() abstract class Test12 : IInt, IStr, IAny by CAny() diff --git a/compiler/testData/diagnostics/tests/delegation/clashes/returnTypeMismatch.fir.kt b/compiler/testData/diagnostics/tests/delegation/clashes/returnTypeMismatch.fir.kt index d5a08e52976..7d43fa00374 100644 --- a/compiler/testData/diagnostics/tests/delegation/clashes/returnTypeMismatch.fir.kt +++ b/compiler/testData/diagnostics/tests/delegation/clashes/returnTypeMismatch.fir.kt @@ -53,7 +53,7 @@ abstract class Test8 : IGeneric by CGeneric(), IInt abstract class Test10 : IInt by CInt(), IStr by CStr(), IAny by CAny() -abstract class Test11 : IInt, IStr by CStr(), IAny by CAny() +abstract class Test11 : IInt, IStr by CStr(), IAny by CAny() abstract class Test12 : IInt, IStr, IAny by CAny() diff --git a/compiler/testData/diagnostics/tests/delegation/covariantOverrides/kt13952.fir.kt b/compiler/testData/diagnostics/tests/delegation/covariantOverrides/kt13952.fir.kt index 353194cf6cd..127b8af6c69 100644 --- a/compiler/testData/diagnostics/tests/delegation/covariantOverrides/kt13952.fir.kt +++ b/compiler/testData/diagnostics/tests/delegation/covariantOverrides/kt13952.fir.kt @@ -13,5 +13,5 @@ object AImpl : IA { open class C : IA by AImpl, IB class D : C() { - override fun foo(): Double = 3.14 + override fun foo(): Double = 3.14 } diff --git a/compiler/testData/diagnostics/tests/delegation/memberHidesSupertypeOverride/diamond.fir.kt b/compiler/testData/diagnostics/tests/delegation/memberHidesSupertypeOverride/diamond.fir.kt index b44cfad8f5b..03754d04f82 100644 --- a/compiler/testData/diagnostics/tests/delegation/memberHidesSupertypeOverride/diamond.fir.kt +++ b/compiler/testData/diagnostics/tests/delegation/memberHidesSupertypeOverride/diamond.fir.kt @@ -35,7 +35,7 @@ fun box(): String { } - object : Base2 by Impl2(), Base3 by Impl3(), Base by Impl() { + object : Base2 by Impl2(), Base3 by Impl3(), Base by Impl() { } diff --git a/compiler/testData/diagnostics/tests/delegation/memberHidesSupertypeOverride/sameDelegationInHierarchy.fir.kt b/compiler/testData/diagnostics/tests/delegation/memberHidesSupertypeOverride/sameDelegationInHierarchy.fir.kt deleted file mode 100644 index 9df5bada552..00000000000 --- a/compiler/testData/diagnostics/tests/delegation/memberHidesSupertypeOverride/sameDelegationInHierarchy.fir.kt +++ /dev/null @@ -1,16 +0,0 @@ -public interface Base { - fun test() = "Base" -} - -class Delegate : Base { - override fun test() = "Base" -} - -public open class MyClass : Base by Delegate() - -fun box(): String { - object : MyClass(), Base by Delegate() { - } - return "OK" -} - diff --git a/compiler/testData/diagnostics/tests/delegation/memberHidesSupertypeOverride/sameDelegationInHierarchy.kt b/compiler/testData/diagnostics/tests/delegation/memberHidesSupertypeOverride/sameDelegationInHierarchy.kt index 41e3f89b543..0d3627ecd88 100644 --- a/compiler/testData/diagnostics/tests/delegation/memberHidesSupertypeOverride/sameDelegationInHierarchy.kt +++ b/compiler/testData/diagnostics/tests/delegation/memberHidesSupertypeOverride/sameDelegationInHierarchy.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL public interface Base { fun test() = "Base" } diff --git a/compiler/testData/diagnostics/tests/delegation/memberHidesSupertypeOverride/sameDelegationInHierarchy2.fir.kt b/compiler/testData/diagnostics/tests/delegation/memberHidesSupertypeOverride/sameDelegationInHierarchy2.fir.kt index 2ceed7aa62a..b4ab82e67ab 100644 --- a/compiler/testData/diagnostics/tests/delegation/memberHidesSupertypeOverride/sameDelegationInHierarchy2.fir.kt +++ b/compiler/testData/diagnostics/tests/delegation/memberHidesSupertypeOverride/sameDelegationInHierarchy2.fir.kt @@ -14,7 +14,7 @@ class Delegate : Derived { public open class MyClass : Base by Delegate() fun box(): String { - object : MyClass(), Derived by Delegate() { + object : MyClass(), Derived by Delegate() { } return "OK" } diff --git a/compiler/testData/diagnostics/tests/delegation/memberHidesSupertypeOverride/severalDelegates.fir.kt b/compiler/testData/diagnostics/tests/delegation/memberHidesSupertypeOverride/severalDelegates.fir.kt index 0875a883077..b38444844f5 100644 --- a/compiler/testData/diagnostics/tests/delegation/memberHidesSupertypeOverride/severalDelegates.fir.kt +++ b/compiler/testData/diagnostics/tests/delegation/memberHidesSupertypeOverride/severalDelegates.fir.kt @@ -22,6 +22,6 @@ public abstract class MyClass : Base1, Base2 { } } -class A : MyClass(), Base1 by Delegate1(), Base1 by Delegate2() { +class A : MyClass(), Base1 by Delegate1(), Base1 by Delegate2() { } diff --git a/compiler/testData/diagnostics/tests/override/ObjectDelegationManyImpl.fir.kt b/compiler/testData/diagnostics/tests/override/ObjectDelegationManyImpl.fir.kt deleted file mode 100644 index 95dbae75529..00000000000 --- a/compiler/testData/diagnostics/tests/override/ObjectDelegationManyImpl.fir.kt +++ /dev/null @@ -1,13 +0,0 @@ -interface D { - fun foo() -} - -interface E { - fun foo() {} -} - -object Impl : D, E { - override fun foo() {} -} - -val obj: D = object : D by Impl, E by Impl {} diff --git a/compiler/testData/diagnostics/tests/override/ObjectDelegationManyImpl.kt b/compiler/testData/diagnostics/tests/override/ObjectDelegationManyImpl.kt index e18871edaae..ab39412d97d 100644 --- a/compiler/testData/diagnostics/tests/override/ObjectDelegationManyImpl.kt +++ b/compiler/testData/diagnostics/tests/override/ObjectDelegationManyImpl.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL interface D { fun foo() }