[FIR] Part 5. Introduce paired common checkers for expect classes

There are some cases when we want to run some platform checker not from
  platform session but from common session. All such cases appear when
  we check some `expect` class

```kotlin
// MODULE: common
expect interface A
expect class B : A

class C : A

// MODULE: platform()()(common)
actual interface A {
    fun foo()
}

actual class B : A {
    override fun foo() {}
}
```

In this example we want to report "abstract foo not implemented" on
  class `C`, but we don't want to report it on `expect class B` (as
  its supertype is always `expect A`, never `actual A`)

So to cover such cases some platform checkers were split into two parts:
- `Regular`, which is platform checkers and runs for everything except
  expect declaration
- `ForExpectClass`, which is common checkers and runs only for expect
  declarations

^KT-58881 Fixed
^KT-58881 Fixed
^KT-64187 Fixed
This commit is contained in:
Dmitriy Novozhilov
2024-01-09 15:12:05 +02:00
committed by Nikolay Lunyak
parent f5d8113de3
commit 990da9fa1a
27 changed files with 495 additions and 156 deletions
@@ -12,7 +12,8 @@ import org.jetbrains.kotlin.fir.analysis.web.common.checkers.declaration.FirJsEx
object JsDeclarationCheckers : DeclarationCheckers() {
override val functionCheckers: Set<FirFunctionChecker>
get() = setOf(
FirJsInheritanceFunctionChecker,
FirJsInheritanceFunctionChecker.Regular,
FirJsInheritanceFunctionChecker.ForExpectClass,
)
override val basicDeclarationCheckers: Set<FirBasicDeclarationChecker>
@@ -30,11 +31,15 @@ object JsDeclarationCheckers : DeclarationCheckers() {
override val classCheckers: Set<FirClassChecker>
get() = setOf(
FirJsMultipleInheritanceChecker,
FirJsMultipleInheritanceChecker.Regular,
FirJsMultipleInheritanceChecker.ForExpectClass,
FirJsDynamicDeclarationChecker,
FirJsInheritanceClassChecker,
FirJsExternalInheritorOnlyChecker,
FirJsNameClashClassMembersChecker
FirJsInheritanceClassChecker.Regular,
FirJsInheritanceClassChecker.ForExpectClass,
FirJsExternalInheritorOnlyChecker.Regular,
FirJsExternalInheritorOnlyChecker.ForExpectClass,
FirJsNameClashClassMembersChecker.Regular,
FirJsNameClashClassMembersChecker.ForExpectClass,
)
override val simpleFunctionCheckers: Set<FirSimpleFunctionChecker>
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors
import org.jetbrains.kotlin.fir.analysis.js.checkers.isEffectivelyExternal
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.hasAnnotation
import org.jetbrains.kotlin.fir.declarations.utils.isExpect
import org.jetbrains.kotlin.fir.declarations.utils.superConeTypes
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
@@ -21,8 +22,21 @@ import org.jetbrains.kotlin.fir.types.toRegularClassSymbol
import org.jetbrains.kotlin.name.JsStandardClassIds.Annotations.JsExternalInheritorsOnly
import org.jetbrains.kotlin.utils.addToStdlib.popLast
// TODO: extract common checker for expect interfaces
object FirJsExternalInheritorOnlyChecker : FirClassChecker(MppCheckerKind.Platform) {
sealed class FirJsExternalInheritorOnlyChecker(mppKind: MppCheckerKind) : FirClassChecker(mppKind) {
object Regular : FirJsExternalInheritorOnlyChecker(MppCheckerKind.Platform) {
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (declaration.isExpect) return
super.check(declaration, context, reporter)
}
}
object ForExpectClass : FirJsExternalInheritorOnlyChecker(MppCheckerKind.Common) {
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (!declaration.isExpect) return
super.check(declaration, context, reporter)
}
}
private fun FirClass.forEachParents(context: CheckerContext, f: (FirRegularClassSymbol) -> Unit) {
val todo = superConeTypes.toMutableList()
val done = hashSetOf<FirRegularClassSymbol>()
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors
import org.jetbrains.kotlin.fir.analysis.js.checkers.isEffectivelyExternal
import org.jetbrains.kotlin.fir.analysis.js.checkers.isOverridingExternalWithOptionalParams
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.utils.isExpect
import org.jetbrains.kotlin.fir.declarations.utils.superConeTypes
import org.jetbrains.kotlin.fir.scopes.collectAllFunctions
import org.jetbrains.kotlin.fir.symbols.impl.FirIntersectionOverrideFunctionSymbol
@@ -26,8 +27,21 @@ import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.isSuspendOrKSuspendFunctionType
import org.jetbrains.kotlin.fir.types.typeContext
// TODO: extract common checker for expect interfaces
object FirJsInheritanceClassChecker : FirClassChecker(MppCheckerKind.Platform) {
sealed class FirJsInheritanceClassChecker(mppKind: MppCheckerKind) : FirClassChecker(mppKind) {
object Regular : FirJsInheritanceClassChecker(MppCheckerKind.Platform) {
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (declaration.isExpect) return
super.check(declaration, context, reporter)
}
}
object ForExpectClass : FirJsInheritanceClassChecker(MppCheckerKind.Common) {
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (!declaration.isExpect) return
super.check(declaration, context, reporter)
}
}
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (!declaration.symbol.isEffectivelyExternal(context)) {
val fakeOverriddenMethod = declaration.findFakeMethodOverridingExternalWithOptionalParams(context)
@@ -13,11 +13,26 @@ import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirFunctionChecker
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors
import org.jetbrains.kotlin.fir.analysis.js.checkers.isEffectivelyExternal
import org.jetbrains.kotlin.fir.analysis.js.checkers.isOverridingExternalWithOptionalParams
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirFunction
import org.jetbrains.kotlin.fir.declarations.utils.isExpect
sealed class FirJsInheritanceFunctionChecker(mppKind: MppCheckerKind) : FirFunctionChecker(mppKind) {
object Regular : FirJsInheritanceFunctionChecker(MppCheckerKind.Platform) {
override fun check(declaration: FirFunction, context: CheckerContext, reporter: DiagnosticReporter) {
if ((context.containingDeclarations.last() as? FirClass)?.isExpect == true) return
super.check(declaration, context, reporter)
}
}
object ForExpectClass : FirJsInheritanceFunctionChecker(MppCheckerKind.Common) {
override fun check(declaration: FirFunction, context: CheckerContext, reporter: DiagnosticReporter) {
if ((context.containingDeclarations.last() as? FirClass)?.isExpect != true) return
super.check(declaration, context, reporter)
}
}
// TODO: extract common checker for expect interfaces
object FirJsInheritanceFunctionChecker : FirFunctionChecker(MppCheckerKind.Platform) {
override fun check(declaration: FirFunction, context: CheckerContext, reporter: DiagnosticReporter) {
if (declaration.isNotEffectivelyExternalFunctionButOverridesExternal(context)) {
reporter.reportOn(declaration.source, FirJsErrors.OVERRIDING_EXTERNAL_FUN_WITH_OPTIONAL_PARAMS, context)
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.fir.analysis.checkers.overriddenFunctions
import org.jetbrains.kotlin.fir.analysis.checkers.unsubstitutedScope
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.utils.isExpect
import org.jetbrains.kotlin.fir.resolve.defaultType
import org.jetbrains.kotlin.fir.scopes.getFunctions
import org.jetbrains.kotlin.fir.types.ConeKotlinType
@@ -23,8 +24,21 @@ import org.jetbrains.kotlin.fir.types.isSubtypeOf
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.util.OperatorNameConventions
// TODO: extract common checker for expect interfaces
object FirJsMultipleInheritanceChecker : FirClassChecker(MppCheckerKind.Platform) {
sealed class FirJsMultipleInheritanceChecker(mppKind: MppCheckerKind) : FirClassChecker(mppKind) {
object Regular : FirJsMultipleInheritanceChecker(MppCheckerKind.Platform) {
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (declaration.isExpect) return
super.check(declaration, context, reporter)
}
}
object ForExpectClass : FirJsMultipleInheritanceChecker(MppCheckerKind.Common) {
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (!declaration.isExpect) return
super.check(declaration, context, reporter)
}
}
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
declaration.checkFunctionIfSubtypeOf(
functionToCheck = OperatorNameConventions.GET,
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirClassLikeDeclaration
import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin
import org.jetbrains.kotlin.fir.declarations.constructors
import org.jetbrains.kotlin.fir.declarations.utils.isExpect
import org.jetbrains.kotlin.fir.declarations.utils.isFinal
import org.jetbrains.kotlin.fir.scopes.*
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
@@ -31,8 +32,21 @@ import org.jetbrains.kotlin.fir.unwrapFakeOverridesOrDelegated
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.addToStdlib.popLast
// TODO: extract common checker for expect interfaces
object FirJsNameClashClassMembersChecker : FirClassChecker(MppCheckerKind.Platform) {
sealed class FirJsNameClashClassMembersChecker(mppKind: MppCheckerKind) : FirClassChecker(mppKind) {
object Regular : FirJsNameClashClassMembersChecker(MppCheckerKind.Platform) {
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (declaration.isExpect) return
super.check(declaration, context, reporter)
}
}
object ForExpectClass : FirJsNameClashClassMembersChecker(MppCheckerKind.Common) {
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (!declaration.isExpect) return
super.check(declaration, context, reporter)
}
}
private class StableNamesCollector {
val jsStableNames = mutableSetOf<FirJsStableName>()
val overrideIntersections = hashMapOf<FirCallableSymbol<*>, HashSet<FirCallableSymbol<*>>>()
@@ -24,7 +24,8 @@ object JvmDeclarationCheckers : DeclarationCheckers() {
override val classCheckers: Set<FirClassChecker>
get() = setOf(
FirStrictfpApplicabilityChecker,
FirOverrideJavaNullabilityWarningChecker,
FirOverrideJavaNullabilityWarningChecker.Regular,
FirOverrideJavaNullabilityWarningChecker.ForExpectClass,
)
override val regularClassCheckers: Set<FirRegularClassChecker>
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirAbstractOverrid
import org.jetbrains.kotlin.fir.analysis.checkers.unsubstitutedScope
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.isExpect
import org.jetbrains.kotlin.fir.java.enhancement.EnhancedForWarningConeSubstitutor
import org.jetbrains.kotlin.fir.scopes.*
import org.jetbrains.kotlin.fir.scopes.impl.FirFakeOverrideGenerator
@@ -24,8 +25,21 @@ import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.typeContext
import org.jetbrains.kotlin.utils.addToStdlib.runIf
// TODO: extract common checker for expect interfaces
object FirOverrideJavaNullabilityWarningChecker : FirAbstractOverrideChecker(MppCheckerKind.Platform) {
sealed class FirOverrideJavaNullabilityWarningChecker(mppKind: MppCheckerKind) : FirAbstractOverrideChecker(mppKind) {
object Regular : FirOverrideJavaNullabilityWarningChecker(MppCheckerKind.Platform) {
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (declaration.isExpect) return
super.check(declaration, context, reporter)
}
}
object ForExpectClass : FirOverrideJavaNullabilityWarningChecker(MppCheckerKind.Common) {
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (!declaration.isExpect) return
super.check(declaration, context, reporter)
}
}
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
val substitutor = EnhancedForWarningConeSubstitutor(context.session.typeContext)
val scope = declaration.unsubstitutedScope(context)
@@ -14,14 +14,33 @@ import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.declarations.utils.isExpect
object FirNativeObjCNameCallableChecker : FirCallableDeclarationChecker(MppCheckerKind.Common) {
override fun check(declaration: FirCallableDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
if (declaration !is FirSimpleFunction && declaration !is FirProperty) return
val containingClass = context.containingDeclarations.lastOrNull() as? FirClass
if (containingClass != null) {
val firTypeScope = containingClass.unsubstitutedScope(context)
FirNativeObjCNameUtilities.checkCallableMember(firTypeScope, declaration.symbol, declaration, context, reporter)
sealed class FirNativeObjCNameCallableChecker(mppKind: MppCheckerKind) : FirCallableDeclarationChecker(mppKind) {
object Regular : FirNativeObjCNameCallableChecker(MppCheckerKind.Platform) {
override fun check(declaration: FirCallableDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
val containingClass = context.containingDeclarations.lastOrNull() as? FirClass ?: return
if (containingClass.isExpect) return
check(declaration, containingClass, context, reporter)
}
}
object ForExpectClass : FirNativeObjCNameCallableChecker(MppCheckerKind.Common) {
override fun check(declaration: FirCallableDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
val containingClass = context.containingDeclarations.lastOrNull() as? FirClass ?: return
if (!containingClass.isExpect) return
check(declaration, containingClass, context, reporter)
}
}
protected fun check(
declaration: FirCallableDeclaration,
containingClass: FirClass,
context: CheckerContext,
reporter: DiagnosticReporter,
) {
if (declaration !is FirSimpleFunction && declaration !is FirProperty) return
val firTypeScope = containingClass.unsubstitutedScope(context)
FirNativeObjCNameUtilities.checkCallableMember(firTypeScope, declaration.symbol, declaration, context, reporter)
}
}
@@ -12,11 +12,25 @@ import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirClassChecker
import org.jetbrains.kotlin.fir.analysis.checkers.unsubstitutedScope
import org.jetbrains.kotlin.fir.analysis.native.checkers.FirNativeObjCNameUtilities.checkCallableMember
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.utils.isExpect
import org.jetbrains.kotlin.fir.isIntersectionOverride
import org.jetbrains.kotlin.fir.scopes.processAllFunctions
import org.jetbrains.kotlin.fir.scopes.processAllProperties
object FirNativeObjCNameOverridesChecker : FirClassChecker(MppCheckerKind.Platform) {
sealed class FirNativeObjCNameOverridesChecker(mppKind: MppCheckerKind) : FirClassChecker(mppKind) {
object Regular : FirNativeObjCNameOverridesChecker(MppCheckerKind.Platform) {
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (declaration.isExpect) return
super.check(declaration, context, reporter)
}
}
object ForExpectClass : FirNativeObjCNameOverridesChecker(MppCheckerKind.Common) {
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (!declaration.isExpect) return
super.check(declaration, context, reporter)
}
}
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
// We just need to check intersection overrides, all other declarations are checked by FirNativeObjCNameChecker
@@ -13,7 +13,6 @@ import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirCallableDeclarationChecker
import org.jetbrains.kotlin.fir.analysis.checkers.unsubstitutedScope
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.REDUNDANT_SWIFT_REFINEMENT
import org.jetbrains.kotlin.fir.analysis.native.checkers.FirNativeObjCRefinementOverridesChecker.check
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
import org.jetbrains.kotlin.name.ClassId
@@ -35,7 +34,15 @@ object FirNativeObjCRefinementChecker : FirCallableDeclarationChecker(MppChecker
val containingClass = context.containingDeclarations.lastOrNull() as? FirClass
if (containingClass != null) {
val firTypeScope = containingClass.unsubstitutedScope(context)
check(firTypeScope, declaration.symbol, declaration, context, reporter, objCAnnotations, swiftAnnotations)
FirNativeObjCRefinementOverridesChecker.check(
firTypeScope,
declaration.symbol,
declaration,
context,
reporter,
objCAnnotations,
swiftAnnotations
)
}
}
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.toAnnotationClassId
import org.jetbrains.kotlin.fir.declarations.toAnnotationClassLikeSymbol
import org.jetbrains.kotlin.fir.declarations.utils.isExpect
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
import org.jetbrains.kotlin.fir.isIntersectionOverride
import org.jetbrains.kotlin.fir.resolve.toFirRegularClassSymbol
@@ -29,8 +30,20 @@ import org.jetbrains.kotlin.fir.scopes.processAllFunctions
import org.jetbrains.kotlin.fir.scopes.processAllProperties
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
// TODO: extract common checker for expect interfaces
object FirNativeObjCRefinementOverridesChecker : FirClassChecker(MppCheckerKind.Platform) {
sealed class FirNativeObjCRefinementOverridesChecker(mppKind: MppCheckerKind) : FirClassChecker(mppKind) {
object Regular : FirNativeObjCRefinementOverridesChecker(MppCheckerKind.Platform) {
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (declaration.isExpect) return
super.check(declaration, context, reporter)
}
}
object ForExpectClass : FirNativeObjCRefinementOverridesChecker(MppCheckerKind.Common) {
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (!declaration.isExpect) return
super.check(declaration, context, reporter)
}
}
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
// We just need to check intersection overrides, all other declarations are checked by FirNativeObjCRefinementChecker
@@ -45,80 +58,83 @@ object FirNativeObjCRefinementOverridesChecker : FirClassChecker(MppCheckerKind.
}
}
fun check(
baseScope: FirTypeScope,
memberSymbol: FirCallableSymbol<*>,
declarationToReport: FirDeclaration,
context: CheckerContext,
reporter: DiagnosticReporter,
objCAnnotations: List<FirAnnotation>,
swiftAnnotations: List<FirAnnotation>
) {
val overriddenMemberSymbols = baseScope.getDirectOverriddenMembersWithBaseScope(memberSymbol)
if (overriddenMemberSymbols.isEmpty()) return
var isHiddenFromObjC = objCAnnotations.isNotEmpty()
var isRefinedInSwift = swiftAnnotations.isNotEmpty()
val supersNotHiddenFromObjC = mutableListOf<FirCallableSymbol<*>>()
val supersNotRefinedInSwift = mutableListOf<FirCallableSymbol<*>>()
for ((symbol, scope) in overriddenMemberSymbols) {
val (superIsHiddenFromObjC, superIsRefinedInSwift) = symbol.inheritsRefinedAnnotations(context.session, scope)
if (superIsHiddenFromObjC) isHiddenFromObjC = true else supersNotHiddenFromObjC.add(symbol)
if (superIsRefinedInSwift) isRefinedInSwift = true else supersNotRefinedInSwift.add(symbol)
companion object {
fun check(
baseScope: FirTypeScope,
memberSymbol: FirCallableSymbol<*>,
declarationToReport: FirDeclaration,
context: CheckerContext,
reporter: DiagnosticReporter,
objCAnnotations: List<FirAnnotation>,
swiftAnnotations: List<FirAnnotation>
) {
val overriddenMemberSymbols = baseScope.getDirectOverriddenMembersWithBaseScope(memberSymbol)
if (overriddenMemberSymbols.isEmpty()) return
var isHiddenFromObjC = objCAnnotations.isNotEmpty()
var isRefinedInSwift = swiftAnnotations.isNotEmpty()
val supersNotHiddenFromObjC = mutableListOf<FirCallableSymbol<*>>()
val supersNotRefinedInSwift = mutableListOf<FirCallableSymbol<*>>()
for ((symbol, scope) in overriddenMemberSymbols) {
val (superIsHiddenFromObjC, superIsRefinedInSwift) = symbol.inheritsRefinedAnnotations(context.session, scope)
if (superIsHiddenFromObjC) isHiddenFromObjC = true else supersNotHiddenFromObjC.add(symbol)
if (superIsRefinedInSwift) isRefinedInSwift = true else supersNotRefinedInSwift.add(symbol)
}
if (isHiddenFromObjC && supersNotHiddenFromObjC.isNotEmpty()) {
reporter.reportIncompatibleOverride(declarationToReport, objCAnnotations, supersNotHiddenFromObjC, context)
}
if (isRefinedInSwift && supersNotRefinedInSwift.isNotEmpty()) {
reporter.reportIncompatibleOverride(declarationToReport, swiftAnnotations, supersNotRefinedInSwift, context)
}
}
if (isHiddenFromObjC && supersNotHiddenFromObjC.isNotEmpty()) {
reporter.reportIncompatibleOverride(declarationToReport, objCAnnotations, supersNotHiddenFromObjC, context)
private fun FirCallableSymbol<*>.inheritsRefinedAnnotations(session: FirSession, baseScope: FirTypeScope): Pair<Boolean, Boolean> {
val (hasObjC, hasSwift) = hasRefinedAnnotations(session)
if (hasObjC && hasSwift) return true to true
// Note: `checkMember` requires all overridden symbols to be either refined or not refined.
val (overriddenMemberSymbol, scope) = baseScope.getDirectOverriddenMembersWithBaseScope(this).firstOrNull()
?: return hasObjC to hasSwift
val (inheritsObjC, inheritsSwift) = overriddenMemberSymbol.inheritsRefinedAnnotations(session, scope)
return (hasObjC || inheritsObjC) to (hasSwift || inheritsSwift)
}
if (isRefinedInSwift && supersNotRefinedInSwift.isNotEmpty()) {
reporter.reportIncompatibleOverride(declarationToReport, swiftAnnotations, supersNotRefinedInSwift, context)
}
}
private fun FirCallableSymbol<*>.inheritsRefinedAnnotations(session: FirSession, baseScope: FirTypeScope): Pair<Boolean, Boolean> {
val (hasObjC, hasSwift) = hasRefinedAnnotations(session)
if (hasObjC && hasSwift) return true to true
// Note: `checkMember` requires all overridden symbols to be either refined or not refined.
val (overriddenMemberSymbol, scope) = baseScope.getDirectOverriddenMembersWithBaseScope(this).firstOrNull()
?: return hasObjC to hasSwift
val (inheritsObjC, inheritsSwift) = overriddenMemberSymbol.inheritsRefinedAnnotations(session, scope)
return (hasObjC || inheritsObjC) to (hasSwift || inheritsSwift)
}
private fun FirCallableSymbol<*>.hasRefinedAnnotations(session: FirSession): Pair<Boolean, Boolean> {
var hasObjC = false
var hasSwift = false
for (annotation in resolvedAnnotationsWithClassIds) {
val metaAnnotations = annotation.toAnnotationClassLikeSymbol(session)?.resolvedAnnotationsWithClassIds.orEmpty()
for (metaAnnotation in metaAnnotations) {
when (metaAnnotation.toAnnotationClassId(session)) {
hidesFromObjCClassId -> {
hasObjC = true
break
}
private fun FirCallableSymbol<*>.hasRefinedAnnotations(session: FirSession): Pair<Boolean, Boolean> {
var hasObjC = false
var hasSwift = false
for (annotation in resolvedAnnotationsWithClassIds) {
val metaAnnotations = annotation.toAnnotationClassLikeSymbol(session)?.resolvedAnnotationsWithClassIds.orEmpty()
for (metaAnnotation in metaAnnotations) {
when (metaAnnotation.toAnnotationClassId(session)) {
hidesFromObjCClassId -> {
hasObjC = true
break
}
refinesInSwiftClassId -> {
hasSwift = true
break
refinesInSwiftClassId -> {
hasSwift = true
break
}
}
}
if (hasObjC && hasSwift) return true to true
}
if (hasObjC && hasSwift) return true to true
return hasObjC to hasSwift
}
return hasObjC to hasSwift
}
private fun DiagnosticReporter.reportIncompatibleOverride(
declaration: FirDeclaration,
annotations: List<FirAnnotation>,
notRefinedSupers: List<FirCallableSymbol<*>>,
context: CheckerContext
) {
val containingDeclarations = notRefinedSupers.mapNotNull { it.containingClassLookupTag()?.toFirRegularClassSymbol(context.session) }
if (annotations.isEmpty()) {
reportOn(declaration.source, INCOMPATIBLE_OBJC_REFINEMENT_OVERRIDE, declaration.symbol, containingDeclarations, context)
} else {
for (annotation in annotations) {
reportOn(annotation.source, INCOMPATIBLE_OBJC_REFINEMENT_OVERRIDE, declaration.symbol, containingDeclarations, context)
private fun DiagnosticReporter.reportIncompatibleOverride(
declaration: FirDeclaration,
annotations: List<FirAnnotation>,
notRefinedSupers: List<FirCallableSymbol<*>>,
context: CheckerContext
) {
val containingDeclarations = notRefinedSupers.mapNotNull { it.containingClassLookupTag()?.toFirRegularClassSymbol(context.session) }
if (annotations.isEmpty()) {
reportOn(declaration.source, INCOMPATIBLE_OBJC_REFINEMENT_OVERRIDE, declaration.symbol, containingDeclarations, context)
} else {
for (annotation in annotations) {
reportOn(annotation.source, INCOMPATIBLE_OBJC_REFINEMENT_OVERRIDE, declaration.symbol, containingDeclarations, context)
}
}
}
}
}
@@ -18,8 +18,10 @@ import org.jetbrains.kotlin.fir.analysis.checkers.unsubstitutedScope
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors
import org.jetbrains.kotlin.fir.containingClassLookupTag
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.declarations.getAnnotationByClassId
import org.jetbrains.kotlin.fir.declarations.utils.isExpect
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.isSubstitutionOrIntersectionOverride
import org.jetbrains.kotlin.fir.references.isError
@@ -34,19 +36,34 @@ import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.annotations.KOTLIN_THROWS_ANNOTATION_FQ_NAME
// TODO: extract common checker for expect interfaces
object FirNativeThrowsChecker : FirBasicDeclarationChecker(MppCheckerKind.Platform) {
private val throwsClassId = ClassId.topLevel(KOTLIN_THROWS_ANNOTATION_FQ_NAME)
sealed class FirNativeThrowsChecker(mppKind: MppCheckerKind) : FirBasicDeclarationChecker(mppKind) {
object Regular : FirNativeThrowsChecker(MppCheckerKind.Platform) {
override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
if ((declaration as? FirMemberDeclaration)?.isExpect == true) return
super.check(declaration, context, reporter)
}
}
private val cancellationExceptionFqName = FqName("kotlin.coroutines.cancellation.CancellationException")
object ForExpectClass : FirNativeThrowsChecker(MppCheckerKind.Common) {
override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
if ((declaration as? FirMemberDeclaration)?.isExpect != true) return
super.check(declaration, context, reporter)
}
}
private val cancellationExceptionAndSupersClassIds = setOf(
ClassId.topLevel(StandardNames.FqNames.throwable),
ClassId.topLevel(FqName("kotlin.Exception")),
ClassId.topLevel(FqName("kotlin.RuntimeException")),
ClassId.topLevel(FqName("kotlin.IllegalStateException")),
ClassId.topLevel(cancellationExceptionFqName)
)
companion object {
private val throwsClassId = ClassId.topLevel(KOTLIN_THROWS_ANNOTATION_FQ_NAME)
private val cancellationExceptionFqName = FqName("kotlin.coroutines.cancellation.CancellationException")
private val cancellationExceptionAndSupersClassIds = setOf(
ClassId.topLevel(StandardNames.FqNames.throwable),
ClassId.topLevel(FqName("kotlin.Exception")),
ClassId.topLevel(FqName("kotlin.RuntimeException")),
ClassId.topLevel(FqName("kotlin.IllegalStateException")),
ClassId.topLevel(cancellationExceptionFqName)
)
}
override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
val throwsAnnotation = declaration.getAnnotationByClassId(throwsClassId, context.session) as? FirAnnotationCall
@@ -10,7 +10,8 @@ import org.jetbrains.kotlin.fir.analysis.checkers.declaration.*
object NativeDeclarationCheckers : DeclarationCheckers() {
override val basicDeclarationCheckers: Set<FirBasicDeclarationChecker>
get() = setOf(
FirNativeThrowsChecker,
FirNativeThrowsChecker.Regular,
FirNativeThrowsChecker.ForExpectClass,
FirNativeSharedImmutableChecker,
FirNativeThreadLocalChecker,
FirNativeIdentifierChecker,
@@ -20,13 +21,16 @@ object NativeDeclarationCheckers : DeclarationCheckers() {
override val callableDeclarationCheckers: Set<FirCallableDeclarationChecker>
get() = setOf(
FirNativeObjCRefinementChecker,
FirNativeObjCNameCallableChecker,
FirNativeObjCNameCallableChecker.Regular,
FirNativeObjCNameCallableChecker.ForExpectClass,
)
override val classCheckers: Set<FirClassChecker>
get() = setOf(
FirNativeObjCRefinementOverridesChecker,
FirNativeObjCNameOverridesChecker,
FirNativeObjCRefinementOverridesChecker.Regular,
FirNativeObjCRefinementOverridesChecker.ForExpectClass,
FirNativeObjCNameOverridesChecker.Regular,
FirNativeObjCNameOverridesChecker.ForExpectClass,
FirNativeObjCOutletChecker,
FirNativeObjCActionChecker,
FirNativeObjCOverrideInitChecker,
@@ -5,14 +5,17 @@
package org.jetbrains.kotlin.fir.analysis.wasm.checkers
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.*
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.DeclarationCheckers
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirClassChecker
import org.jetbrains.kotlin.fir.analysis.wasm.checkers.declaration.*
import org.jetbrains.kotlin.fir.analysis.web.common.checkers.declaration.FirJsExportAnnotationChecker
object WasmBaseDeclarationCheckers : DeclarationCheckers() {
override val classCheckers: Set<FirClassChecker>
get() = setOf(
FirWasmExternalInheritanceChecker,
FirWasmExternalInheritanceChecker.Regular,
FirWasmExternalInheritanceChecker.ForExpectClass,
)
override val basicDeclarationCheckers: Set<FirBasicDeclarationChecker>
@@ -39,4 +42,4 @@ object WasmWasiDeclarationCheckers : DeclarationCheckers() {
get() = setOf(
FirWasmWasiExternalDeclarationChecker,
)
}
}
@@ -15,11 +15,25 @@ import org.jetbrains.kotlin.fir.analysis.checkers.toClassLikeSymbol
import org.jetbrains.kotlin.fir.analysis.diagnostics.wasm.FirWasmErrors
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.utils.isEffectivelyExternal
import org.jetbrains.kotlin.fir.declarations.utils.isExpect
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.name.StandardClassIds
// TODO: extract common checker for expect interfaces
object FirWasmExternalInheritanceChecker : FirClassChecker(MppCheckerKind.Platform) {
sealed class FirWasmExternalInheritanceChecker(mppKind: MppCheckerKind) : FirClassChecker(mppKind) {
object Regular : FirWasmExternalInheritanceChecker(MppCheckerKind.Platform) {
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (declaration.isExpect) return
super.check(declaration, context, reporter)
}
}
object ForExpectClass : FirWasmExternalInheritanceChecker(MppCheckerKind.Common) {
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (!declaration.isExpect) return
super.check(declaration, context, reporter)
}
}
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
val session = context.session
val isCurrentClassExternal = declaration.symbol.isEffectivelyExternal(session)
+27
View File
@@ -143,3 +143,30 @@ So the author of each new checker should decide in which session this checker sh
it most likely should be `Common`
- if the checker is interested in the scope of some type, it should be carefully considered how the actualization of the scope may affect
the checker
#### Checkers for expect classes
```kotlin
// MODULE: common
expect interface A
expect class B : A
class C : A
// MODULE: platform()()(common)
actual interface A {
fun foo()
}
actual class B : A {
override fun foo() {}
}
```
In this example we want to report "abstract foo not implemented" on class `C`, but we don't want to report it on `expect class B` (as its supertype is always `expect A`, never `actual A`)
So to cover such cases, it's worth splitting the platform checker into two parts:
- `Regular`, which is platform checkers and runs for everything except `expect` declaration
- `ForExpectClass`, which is common checkers and runs only for `expect` declarations
As an example, check the implementation of [FirImplementationMismatchChecker](compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirImplementationMismatchChecker.kt) checker
@@ -11,7 +11,6 @@ import org.jetbrains.kotlin.fir.analysis.cfa.FirPropertyInitializationAnalyzer
import org.jetbrains.kotlin.fir.analysis.checkers.cfa.FirControlFlowChecker
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.*
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirAnonymousFunctionParametersChecker
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirFiniteBoundRestrictionChecker
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirInlinedLambdaNonSourceAnnotationsChecker
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirMissingDependencySupertypeChecker
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirSuspendAnonymousFunctionChecker
@@ -103,15 +102,19 @@ object CommonDeclarationCheckers : DeclarationCheckers() {
override val classCheckers: Set<FirClassChecker>
get() = setOf(
FirOverrideChecker,
FirNotImplementedOverrideChecker,
FirNotImplementedOverrideSimpleEnumEntryChecker,
FirOverrideChecker.Regular,
FirOverrideChecker.ForExpectClass,
FirNotImplementedOverrideChecker.Regular,
FirNotImplementedOverrideChecker.ForExpectClass,
FirNotImplementedOverrideSimpleEnumEntryChecker.Regular,
FirNotImplementedOverrideSimpleEnumEntryChecker.ForExpectClass,
FirThrowableSubclassChecker,
FirOpenMemberChecker,
FirClassVarianceChecker,
FirSealedSupertypeChecker,
FirMemberPropertiesChecker,
FirImplementationMismatchChecker,
FirImplementationMismatchChecker.Regular,
FirImplementationMismatchChecker.ForExpectClass,
FirTypeParametersInObjectChecker,
FirSupertypesChecker,
FirPrimaryConstructorSuperTypeChecker,
@@ -119,7 +122,8 @@ object CommonDeclarationCheckers : DeclarationCheckers() {
FirEnumCompanionInEnumConstructorCallChecker,
FirBadInheritedJavaSignaturesChecker,
FirSealedInterfaceAllowedChecker,
FirMixedFunctionalTypesInSupertypesChecker,
FirMixedFunctionalTypesInSupertypesChecker.Regular,
FirMixedFunctionalTypesInSupertypesChecker.ForExpectClass,
)
override val regularClassCheckers: Set<FirRegularClassChecker>
@@ -135,13 +139,16 @@ object CommonDeclarationCheckers : DeclarationCheckers() {
FirManyCompanionObjectsChecker,
FirMethodOfAnyImplementedInInterfaceChecker,
FirDataClassPrimaryConstructorChecker,
FirFunInterfaceDeclarationChecker,
FirFunInterfaceDeclarationChecker.Regular,
FirFunInterfaceDeclarationChecker.ForExpectClass,
FirNestedClassChecker,
FirValueClassDeclarationChecker,
FirValueClassDeclarationChecker.Regular,
FirValueClassDeclarationChecker.ForExpectClass,
FirOuterClassArgumentsRequiredChecker,
FirPropertyInitializationChecker,
FirDelegateFieldTypeMismatchChecker,
FirMultipleDefaultsInheritedFromSupertypesChecker,
FirMultipleDefaultsInheritedFromSupertypesChecker.Regular,
FirMultipleDefaultsInheritedFromSupertypesChecker.ForExpectClass,
FirFiniteBoundRestrictionChecker,
FirNonExpansiveInheritanceRestrictionChecker,
)
@@ -176,7 +183,8 @@ object CommonDeclarationCheckers : DeclarationCheckers() {
override val typeParameterCheckers: Set<FirTypeParameterChecker>
get() = setOf(
FirTypeParameterBoundsChecker,
FirTypeParameterBoundsChecker.Regular,
FirTypeParameterBoundsChecker.ForExpectClass,
FirTypeParameterVarianceChecker,
FirReifiedTypeParameterChecker,
FirTypeParameterSyntaxChecker,
@@ -18,18 +18,28 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FUN_INTERFACE_CAN
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FUN_INTERFACE_WITH_SUSPEND_FUNCTION
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FUN_INTERFACE_WRONG_COUNT_OF_ABSTRACT_MEMBERS
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.utils.isAbstract
import org.jetbrains.kotlin.fir.declarations.utils.isFun
import org.jetbrains.kotlin.fir.declarations.utils.isInterface
import org.jetbrains.kotlin.fir.declarations.utils.isSuspend
import org.jetbrains.kotlin.fir.declarations.utils.*
import org.jetbrains.kotlin.fir.languageVersionSettings
import org.jetbrains.kotlin.fir.scopes.getFunctions
import org.jetbrains.kotlin.fir.scopes.getProperties
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
// TODO: extract common checker for expect interfaces
object FirFunInterfaceDeclarationChecker : FirRegularClassChecker(MppCheckerKind.Platform) {
sealed class FirFunInterfaceDeclarationChecker(mppKind: MppCheckerKind) : FirRegularClassChecker(mppKind) {
object Regular : FirFunInterfaceDeclarationChecker(MppCheckerKind.Platform) {
override fun check(declaration: FirRegularClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (declaration.isExpect) return
super.check(declaration, context, reporter)
}
}
object ForExpectClass : FirFunInterfaceDeclarationChecker(MppCheckerKind.Common) {
override fun check(declaration: FirRegularClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (!declaration.isExpect) return
super.check(declaration, context, reporter)
}
}
override fun check(declaration: FirRegularClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (!declaration.isInterface || !declaration.isFun) return
@@ -40,8 +40,20 @@ import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.TypeCheckerState
import org.jetbrains.kotlin.utils.addIfNotNull
// TODO: extract common checker for expect interfaces
object FirImplementationMismatchChecker : FirClassChecker(MppCheckerKind.Platform) {
sealed class FirImplementationMismatchChecker(mppKind: MppCheckerKind) : FirClassChecker(mppKind) {
object Regular : FirImplementationMismatchChecker(MppCheckerKind.Platform) {
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (declaration.isExpect) return
super.check(declaration, context, reporter)
}
}
object ForExpectClass : FirImplementationMismatchChecker(MppCheckerKind.Common) {
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (!declaration.isExpect) return
super.check(declaration, context, reporter)
}
}
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
val source = declaration.source ?: return
@@ -13,11 +13,25 @@ import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.utils.isExpect
import org.jetbrains.kotlin.fir.resolve.lookupSuperTypes
import org.jetbrains.kotlin.fir.types.functionTypeKind
// TODO: extract common checker for expect interfaces
object FirMixedFunctionalTypesInSupertypesChecker : FirClassChecker(MppCheckerKind.Platform) {
sealed class FirMixedFunctionalTypesInSupertypesChecker(mppKind: MppCheckerKind) : FirClassChecker(mppKind) {
object Regular : FirMixedFunctionalTypesInSupertypesChecker(MppCheckerKind.Platform) {
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (declaration.isExpect) return
super.check(declaration, context, reporter)
}
}
object ForExpectClass : FirMixedFunctionalTypesInSupertypesChecker(MppCheckerKind.Common) {
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (!declaration.isExpect) return
super.check(declaration, context, reporter)
}
}
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
val superKinds = lookupSuperTypes(declaration.symbol, lookupInterfaces = true, deep = true, context.session)
.mapNotNullTo(mutableSetOf()) { it.functionTypeKind(context.session)?.nonReflectKind() }
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.containingClassLookupTag
import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.utils.isExpect
import org.jetbrains.kotlin.fir.declarations.utils.superConeTypes
import org.jetbrains.kotlin.fir.isSubstitutionOverride
import org.jetbrains.kotlin.fir.scopes.processAllFunctions
@@ -24,8 +25,21 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirValueParameterSymbol
import org.jetbrains.kotlin.fir.unwrapSubstitutionOverrides
import org.jetbrains.kotlin.name.Name
// TODO: extract common checker for expect interfaces
object FirMultipleDefaultsInheritedFromSupertypesChecker : FirRegularClassChecker(MppCheckerKind.Platform) {
sealed class FirMultipleDefaultsInheritedFromSupertypesChecker(mppKind: MppCheckerKind) : FirRegularClassChecker(mppKind) {
object Regular : FirMultipleDefaultsInheritedFromSupertypesChecker(MppCheckerKind.Platform) {
override fun check(declaration: FirRegularClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (declaration.isExpect) return
super.check(declaration, context, reporter)
}
}
object ForExpectClass : FirMultipleDefaultsInheritedFromSupertypesChecker(MppCheckerKind.Common) {
override fun check(declaration: FirRegularClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (!declaration.isExpect) return
super.check(declaration, context, reporter)
}
}
override fun check(declaration: FirRegularClass, context: CheckerContext, reporter: DiagnosticReporter) {
declaration.unsubstitutedScope(context).processAllFunctions {
val originalIfSubstitutionOverride = it.unwrapSubstitutionOverrides()
@@ -41,8 +41,20 @@ import org.jetbrains.kotlin.util.ImplementationStatus
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
// TODO: extract common checker for expect interfaces
object FirNotImplementedOverrideChecker : FirClassChecker(MppCheckerKind.Platform) {
sealed class FirNotImplementedOverrideChecker(mppKind: MppCheckerKind) : FirClassChecker(mppKind) {
object Regular : FirNotImplementedOverrideChecker(MppCheckerKind.Platform) {
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (declaration.isExpect) return
super.check(declaration, context, reporter)
}
}
object ForExpectClass : FirNotImplementedOverrideChecker(MppCheckerKind.Common) {
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (!declaration.isExpect) return
super.check(declaration, context, reporter)
}
}
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
val source = declaration.source ?: return
@@ -15,11 +15,25 @@ import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirEnumEntry
import org.jetbrains.kotlin.fir.declarations.utils.isAbstract
import org.jetbrains.kotlin.fir.declarations.utils.isEnumClass
import org.jetbrains.kotlin.fir.declarations.utils.isExpect
import org.jetbrains.kotlin.fir.scopes.processAllCallables
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
// TODO: extract common checker for expect interfaces
object FirNotImplementedOverrideSimpleEnumEntryChecker : FirClassChecker(MppCheckerKind.Common) {
sealed class FirNotImplementedOverrideSimpleEnumEntryChecker(mppKind: MppCheckerKind) : FirClassChecker(mppKind) {
object Regular : FirNotImplementedOverrideSimpleEnumEntryChecker(MppCheckerKind.Platform) {
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (declaration.isExpect) return
super.check(declaration, context, reporter)
}
}
object ForExpectClass : FirNotImplementedOverrideSimpleEnumEntryChecker(MppCheckerKind.Common) {
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (!declaration.isExpect) return
super.check(declaration, context, reporter)
}
}
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (!declaration.isEnumClass) return
@@ -23,10 +23,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.analysis.overridesBackwardCompatibilityHelper
import org.jetbrains.kotlin.fir.containingClassLookupTag
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.isFinal
import org.jetbrains.kotlin.fir.declarations.utils.isOverride
import org.jetbrains.kotlin.fir.declarations.utils.modality
import org.jetbrains.kotlin.fir.declarations.utils.visibility
import org.jetbrains.kotlin.fir.declarations.utils.*
import org.jetbrains.kotlin.fir.languageVersionSettings
import org.jetbrains.kotlin.fir.originalOrSelf
import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap
@@ -107,8 +104,21 @@ abstract class FirAbstractOverrideChecker(mppKind: MppCheckerKind) : FirClassChe
}
}
// TODO: extract common checker for expect interfaces
object FirOverrideChecker : FirAbstractOverrideChecker(MppCheckerKind.Platform) {
sealed class FirOverrideChecker(mppKind: MppCheckerKind) : FirAbstractOverrideChecker(mppKind) {
object Regular : FirOverrideChecker(MppCheckerKind.Platform) {
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (declaration.isExpect) return
super.check(declaration, context, reporter)
}
}
object ForExpectClass : FirOverrideChecker(MppCheckerKind.Common) {
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (!declaration.isExpect) return
super.check(declaration, context, reporter)
}
}
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
val typeCheckerState = context.session.typeContext.newTypeCheckerState(
errorTypesEqualToAnything = false,
@@ -13,14 +13,29 @@ import org.jetbrains.kotlin.fir.analysis.checkers.*
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.isExpect
import org.jetbrains.kotlin.fir.declarations.utils.isOverride
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.TypeCheckerProviderContext
// TODO: extract common checker for expect interfaces
object FirTypeParameterBoundsChecker : FirTypeParameterChecker(MppCheckerKind.Platform) {
sealed class FirTypeParameterBoundsChecker(mppKind: MppCheckerKind) : FirTypeParameterChecker(mppKind) {
object Regular : FirTypeParameterBoundsChecker(MppCheckerKind.Platform) {
override fun check(declaration: FirTypeParameter, context: CheckerContext, reporter: DiagnosticReporter) {
val containingDeclaration = context.containingDeclarations.lastOrNull() ?: return
if ((containingDeclaration as? FirMemberDeclaration)?.isExpect == true) return
check(declaration, containingDeclaration, context, reporter)
}
}
object ForExpectClass : FirTypeParameterBoundsChecker(MppCheckerKind.Common) {
override fun check(declaration: FirTypeParameter, context: CheckerContext, reporter: DiagnosticReporter) {
val containingDeclaration = context.containingDeclarations.lastOrNull() ?: return
if ((containingDeclaration as? FirMemberDeclaration)?.isExpect != true) return
check(declaration, containingDeclaration, context, reporter)
}
}
private val classKinds = setOf(
ClassKind.CLASS,
@@ -28,8 +43,12 @@ object FirTypeParameterBoundsChecker : FirTypeParameterChecker(MppCheckerKind.Pl
ClassKind.OBJECT
)
override fun check(declaration: FirTypeParameter, context: CheckerContext, reporter: DiagnosticReporter) {
val containingDeclaration = context.containingDeclarations.lastOrNull() ?: return
protected fun check(
declaration: FirTypeParameter,
containingDeclaration: FirDeclaration,
context: CheckerContext,
reporter: DiagnosticReporter,
) {
if (containingDeclaration is FirConstructor) return
checkFinalUpperBounds(declaration, containingDeclaration, context, reporter)
@@ -33,13 +33,27 @@ import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.StandardClassIds
// TODO: extract common checker for expect interfaces
object FirValueClassDeclarationChecker : FirRegularClassChecker(MppCheckerKind.Platform) {
sealed class FirValueClassDeclarationChecker(mppKind: MppCheckerKind) : FirRegularClassChecker(mppKind) {
object Regular : FirValueClassDeclarationChecker(MppCheckerKind.Platform) {
override fun check(declaration: FirRegularClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (declaration.isExpect) return
super.check(declaration, context, reporter)
}
}
private val boxAndUnboxNames = setOf("box", "unbox")
private val equalsAndHashCodeNames = setOf("equals", "hashCode")
private val javaLangFqName = FqName("java.lang")
private val cloneableFqName = FqName("Cloneable")
object ForExpectClass : FirValueClassDeclarationChecker(MppCheckerKind.Common) {
override fun check(declaration: FirRegularClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (!declaration.isExpect) return
super.check(declaration, context, reporter)
}
}
companion object {
private val boxAndUnboxNames = setOf("box", "unbox")
private val equalsAndHashCodeNames = setOf("equals", "hashCode")
private val javaLangFqName = FqName("java.lang")
private val cloneableFqName = FqName("Cloneable")
}
override fun check(declaration: FirRegularClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (!declaration.symbol.isInlineOrValueClass()) {