FIR: Rework FirNotImplementedOverrideChecker around delegated members reporting
This commit is contained in:
committed by
TeamCityServer
parent
a213ee0e01
commit
a77cbb8f63
@@ -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
|
||||
|
||||
+79
-58
@@ -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<FirCallableSymbol<*>>()
|
||||
val notImplementedIntersectionSymbols = mutableListOf<FirCallableSymbol<*>>()
|
||||
val manyImplementationsDelegationSymbols = mutableListOf<FirCallableSymbol<*>>()
|
||||
val delegationOverrideOfFinal = mutableListOf<Pair<FirCallableSymbol<*>, FirCallableSymbol<*>>>()
|
||||
val delegationOverrideOfOpen = mutableListOf<Pair<FirCallableSymbol<*>, FirCallableSymbol<*>>>()
|
||||
val invisibleSymbols = mutableListOf<FirCallableSymbol<*>>()
|
||||
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<FirNamedFunctionSymbol> {
|
||||
fun FirTypeScope.getDirectOverriddenMembers(
|
||||
member: FirCallableSymbol<*>,
|
||||
unwrapIntersectionAndSubstitutionOverride: Boolean = false,
|
||||
): List<FirCallableSymbol<out FirCallableMemberDeclaration>> =
|
||||
when (member) {
|
||||
is FirNamedFunctionSymbol -> getDirectOverriddenFunctions(member, unwrapIntersectionAndSubstitutionOverride)
|
||||
is FirPropertySymbol -> getDirectOverriddenProperties(member, unwrapIntersectionAndSubstitutionOverride)
|
||||
else -> emptyList()
|
||||
}
|
||||
|
||||
fun FirTypeScope.getDirectOverriddenFunctions(
|
||||
function: FirNamedFunctionSymbol,
|
||||
unwrapIntersectionAndSubstitutionOverride: Boolean = false,
|
||||
): List<FirNamedFunctionSymbol> {
|
||||
val overriddenFunctions = mutableSetOf<FirNamedFunctionSymbol>()
|
||||
|
||||
processDirectlyOverriddenFunctions(function) {
|
||||
overriddenFunctions.add(it)
|
||||
overriddenFunctions.addOverridden(it, unwrapIntersectionAndSubstitutionOverride)
|
||||
ProcessorAction.NEXT
|
||||
}
|
||||
|
||||
return overriddenFunctions.toList()
|
||||
}
|
||||
|
||||
fun FirTypeScope.getDirectOverriddenProperties(property: FirPropertySymbol): List<FirPropertySymbol> {
|
||||
fun FirTypeScope.getDirectOverriddenProperties(
|
||||
property: FirPropertySymbol,
|
||||
unwrapIntersectionAndSubstitutionOverride: Boolean = false,
|
||||
): List<FirPropertySymbol> {
|
||||
val overriddenProperties = mutableSetOf<FirPropertySymbol>()
|
||||
|
||||
processDirectlyOverriddenProperties(property) {
|
||||
overriddenProperties.add(it)
|
||||
overriddenProperties.addOverridden(it, unwrapIntersectionAndSubstitutionOverride)
|
||||
ProcessorAction.NEXT
|
||||
}
|
||||
|
||||
return overriddenProperties.toList()
|
||||
}
|
||||
|
||||
private inline fun <reified D : FirCallableSymbol<*>> MutableCollection<D>.addOverridden(
|
||||
symbol: D,
|
||||
unwrapIntersectionAndSubstitutionOverride: Boolean
|
||||
) {
|
||||
if (unwrapIntersectionAndSubstitutionOverride) {
|
||||
if (symbol is FirIntersectionCallableSymbol) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
addAll(symbol.intersections as Collection<D>)
|
||||
} else {
|
||||
add(symbol.originalForSubstitutionOverride ?: symbol)
|
||||
}
|
||||
} else {
|
||||
add(symbol)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -51,7 +51,7 @@ abstract class Test8 : IGeneric<String> by CGeneric<String>(), IInt
|
||||
|
||||
abstract <!MANY_IMPL_MEMBER_NOT_IMPLEMENTED!>class Test10<!> : IInt by CInt(), IStr by CStr(), IAny by CAny()
|
||||
|
||||
abstract <!DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE!>class Test11<!> : IInt, IStr by CStr(), IAny by CAny()
|
||||
abstract <!MANY_IMPL_MEMBER_NOT_IMPLEMENTED!>class Test11<!> : IInt, IStr by CStr(), IAny by CAny()
|
||||
|
||||
abstract class Test12 : IInt, IStr, IAny by CAny()
|
||||
|
||||
|
||||
+1
-1
@@ -53,7 +53,7 @@ abstract class Test8 : IGeneric<String> by CGeneric<String>(), IInt
|
||||
|
||||
abstract <!MANY_IMPL_MEMBER_NOT_IMPLEMENTED!>class Test10<!> : IInt by CInt(), IStr by CStr(), IAny by CAny()
|
||||
|
||||
abstract <!DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE!>class Test11<!> : IInt, IStr by CStr(), IAny by CAny()
|
||||
abstract <!MANY_IMPL_MEMBER_NOT_IMPLEMENTED!>class Test11<!> : IInt, IStr by CStr(), IAny by CAny()
|
||||
|
||||
abstract class Test12 : IInt, IStr, IAny by CAny()
|
||||
|
||||
|
||||
+1
-1
@@ -13,5 +13,5 @@ object AImpl : IA {
|
||||
open class C : IA by AImpl, IB
|
||||
|
||||
class D : C() {
|
||||
override fun foo(): <!RETURN_TYPE_MISMATCH_ON_OVERRIDE!>Double<!> = 3.14
|
||||
override fun foo(): Double = 3.14
|
||||
}
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ fun box(): String {
|
||||
|
||||
}
|
||||
|
||||
<!MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED!>object<!> : Base2 by Impl2(), Base3 by Impl3(), Base by Impl() {
|
||||
<!DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE, MANY_IMPL_MEMBER_NOT_IMPLEMENTED!>object<!> : Base2 by Impl2(), Base3 by Impl3(), Base by Impl() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
-16
@@ -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 {
|
||||
<!MANY_IMPL_MEMBER_NOT_IMPLEMENTED!>object<!> : MyClass(), Base by Delegate() {
|
||||
}
|
||||
return "OK"
|
||||
}
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
public interface Base {
|
||||
fun test() = "Base"
|
||||
}
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ class Delegate : Derived {
|
||||
public open class MyClass : Base by Delegate()
|
||||
|
||||
fun box(): String {
|
||||
<!MANY_IMPL_MEMBER_NOT_IMPLEMENTED!>object<!> : MyClass(), Derived by Delegate() {
|
||||
<!DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE!>object<!> : MyClass(), Derived by Delegate() {
|
||||
}
|
||||
return "OK"
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -22,6 +22,6 @@ public abstract class MyClass : Base1, Base2 {
|
||||
}
|
||||
}
|
||||
|
||||
<!DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE!>class A<!> : MyClass(), Base1 by Delegate1(), <!SUPERTYPE_APPEARS_TWICE!>Base1<!> by <!TYPE_MISMATCH!>Delegate2()<!> {
|
||||
<!DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE, MANY_IMPL_MEMBER_NOT_IMPLEMENTED!>class A<!> : MyClass(), Base1 by Delegate1(), <!SUPERTYPE_APPEARS_TWICE!>Base1<!> by <!TYPE_MISMATCH!>Delegate2()<!> {
|
||||
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
interface D {
|
||||
fun foo()
|
||||
}
|
||||
|
||||
interface E {
|
||||
fun foo() {}
|
||||
}
|
||||
|
||||
object Impl : D, E {
|
||||
override fun foo() {}
|
||||
}
|
||||
|
||||
val obj: D = <!MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED!>object<!> : D by Impl, E by Impl {}
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
interface D {
|
||||
fun foo()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user