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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user