diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt index fd82f8b4fe7..5d4633c6563 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt @@ -160,6 +160,12 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { val CLASS_IN_SUPERTYPE_FOR_ENUM by error() val SEALED_SUPERTYPE by error() val SEALED_SUPERTYPE_IN_LOCAL_CLASS by error() + val SEALED_INHERITOR_IN_DIFFERENT_PACKAGE by error { + parameter("subclassPackage") + parameter("basePackage") + } + val SEALED_INHERITOR_IN_DIFFERENT_MODULE by error() + val CLASS_INHERITS_JAVA_SEALED_CLASS by error() val SUPERTYPE_NOT_A_CLASS_OR_INTERFACE by error { parameter("reason") } diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index c390c681338..2b3b1879a5f 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -168,6 +168,9 @@ object FirErrors { val CLASS_IN_SUPERTYPE_FOR_ENUM by error0() val SEALED_SUPERTYPE by error0() val SEALED_SUPERTYPE_IN_LOCAL_CLASS by error0() + val SEALED_INHERITOR_IN_DIFFERENT_PACKAGE by error2() + val SEALED_INHERITOR_IN_DIFFERENT_MODULE by error0() + val CLASS_INHERITS_JAVA_SEALED_CLASS by error0() val SUPERTYPE_NOT_A_CLASS_OR_INTERFACE by error1() val CYCLIC_INHERITANCE_HIERARCHY by error0() val EXPANDED_TYPE_CANNOT_BE_INHERITED by error1() diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirSealedSupertypeChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirSealedSupertypeChecker.kt index 0d1f97a5444..d6ae8244f9b 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirSealedSupertypeChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirSealedSupertypeChecker.kt @@ -11,7 +11,9 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn import org.jetbrains.kotlin.fir.declarations.FirClass +import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin import org.jetbrains.kotlin.fir.declarations.utils.classId +import org.jetbrains.kotlin.fir.declarations.utils.isSealed import org.jetbrains.kotlin.fir.declarations.utils.modality import org.jetbrains.kotlin.fir.resolve.symbolProvider import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol @@ -29,19 +31,35 @@ object FirSealedSupertypeChecker : FirClassChecker() { } private fun checkGlobalDeclaration(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) { - for (it in declaration.superTypeRefs) { - val classId = it.coneType.classId ?: continue + val subclassPackage = declaration.classId.packageFqName + for (superTypeRef in declaration.superTypeRefs) { + val superClassId = superTypeRef.coneType.classId ?: continue - if (classId.isLocal) { + if (superClassId.isLocal) { continue } - val classSymbol = context.session.symbolProvider.getClassLikeSymbolByFqName(classId) as? FirRegularClassSymbol ?: continue + val superClass = context.session.symbolProvider.getClassLikeSymbolByFqName(superClassId) as? FirRegularClassSymbol ?: continue - if (classSymbol.modality == Modality.SEALED && declaration.classId.packageFqName != classSymbol.classId.packageFqName) { - reporter.reportOn(it.source, FirErrors.SEALED_SUPERTYPE, context) + if (!superClass.isSealed) continue + if (superClass.origin == FirDeclarationOrigin.Java) { + reporter.reportOn(superTypeRef.source, FirErrors.CLASS_INHERITS_JAVA_SEALED_CLASS, context) continue } + val superClassPackage = superClass.classId.packageFqName + if (superClassPackage != subclassPackage) { + reporter.reportOn( + superTypeRef.source, + FirErrors.SEALED_INHERITOR_IN_DIFFERENT_PACKAGE, + subclassPackage, + superClassPackage, + context + ) + } + if (superClass.moduleData != declaration.moduleData) { + // TODO: implement logic like in org.jetbrains.kotlin.resolve.checkers.SealedInheritorInSameModuleChecker for MPP support. + reporter.reportOn(superTypeRef.source, FirErrors.SEALED_INHERITOR_IN_DIFFERENT_MODULE, context) + } } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt index e461c5dc848..d9d6099e818 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt @@ -88,6 +88,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CAPTURED_MEMBER_V import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CAPTURED_VAL_INITIALIZATION import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CATCH_PARAMETER_WITH_DEFAULT_VALUE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CLASS_CANNOT_BE_EXTENDED_DIRECTLY +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CLASS_INHERITS_JAVA_SEALED_CLASS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CLASS_IN_SUPERTYPE_FOR_ENUM import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CLASS_LITERAL_LHS_NOT_A_CLASS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.COMMA_IN_WHEN_CONDITION_WITHOUT_ARGUMENT @@ -372,6 +373,8 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RETURN_TYPE_MISMA import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RETURN_TYPE_MISMATCH_ON_INHERITANCE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RETURN_TYPE_MISMATCH_ON_OVERRIDE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SEALED_CLASS_CONSTRUCTOR_CALL +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SEALED_INHERITOR_IN_DIFFERENT_MODULE +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SEALED_INHERITOR_IN_DIFFERENT_PACKAGE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SEALED_SUPERTYPE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SEALED_SUPERTYPE_IN_LOCAL_CLASS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SECONDARY_CONSTRUCTOR_WITH_BODY_INSIDE_INLINE_CLASS @@ -570,6 +573,15 @@ class FirDefaultErrorMessages { map.put(CLASS_IN_SUPERTYPE_FOR_ENUM, "Enum class cannot inherit from classes") map.put(SEALED_SUPERTYPE, "This type is sealed, so it can be inherited by only its own nested classes or objects") map.put(SEALED_SUPERTYPE_IN_LOCAL_CLASS, "Local class cannot extend a sealed class") + map.put( + SEALED_INHERITOR_IN_DIFFERENT_PACKAGE, + "Inheritor of sealed class or interface declared in package {0} but it must be in package {1} where base class is declared", + TO_STRING, + TO_STRING + ) + map.put(SEALED_INHERITOR_IN_DIFFERENT_MODULE, "Inheritance of sealed classes or interfaces from different module is prohibited") + map.put(CLASS_INHERITS_JAVA_SEALED_CLASS, "Inheritance of java sealed classes is prohibited") + map.put(SUPERTYPE_NOT_A_CLASS_OR_INTERFACE, "Supertype is not a class or interface", TO_STRING) map.put(CYCLIC_INHERITANCE_HIERARCHY, "There's a cycle in the inheritance hierarchy for this type") map.put( diff --git a/compiler/testData/diagnostics/tests/multiplatform/exhaustiveness/commonSealedWithPlatformInheritor.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/exhaustiveness/commonSealedWithPlatformInheritor.fir.kt index c66f25f23a8..82fbffff622 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/exhaustiveness/commonSealedWithPlatformInheritor.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/exhaustiveness/commonSealedWithPlatformInheritor.fir.kt @@ -12,7 +12,7 @@ fun test_1(b: Base) = when (b) { // MODULE: m1-jvm()()(m1-common) -class PlatfromDerived : Base() // must be an error +class PlatfromDerived : Base() // must be an error fun test_2(b: Base) = when (b) { is Derived -> 1 diff --git a/compiler/testData/diagnostics/tests/multiplatform/hmpp/sealedInheritorsInComplexModuleStructure.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/hmpp/sealedInheritorsInComplexModuleStructure.fir.kt index 1de8c07d0d6..1a02e9cd7ed 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/hmpp/sealedInheritorsInComplexModuleStructure.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/hmpp/sealedInheritorsInComplexModuleStructure.fir.kt @@ -13,10 +13,10 @@ expect sealed class SealedWithPlatformActuals : SealedWithSharedActual package foo actual sealed class SealedWithSharedActual -class SimpleShared : SealedWithPlatformActuals() +class SimpleShared : SealedWithPlatformActuals() // MODULE: main()()(intermediate) // TARGET_PLATFORM: JVM package foo -actual sealed class SealedWithPlatformActuals actual constructor(): SealedWithSharedActual() +actual sealed class SealedWithPlatformActuals actual constructor(): SealedWithSharedActual() diff --git a/compiler/testData/diagnostics/tests/sealed/MultipleFiles_enabled.fir.kt b/compiler/testData/diagnostics/tests/sealed/MultipleFiles_enabled.fir.kt index 4dd5bed6c9c..c3a92539fca 100644 --- a/compiler/testData/diagnostics/tests/sealed/MultipleFiles_enabled.fir.kt +++ b/compiler/testData/diagnostics/tests/sealed/MultipleFiles_enabled.fir.kt @@ -37,4 +37,4 @@ package bar import foo.Base -class E : Base() +class E : Base() diff --git a/compiler/testData/diagnostics/tests/sealed/inheritorInDifferentModule.fir.kt b/compiler/testData/diagnostics/tests/sealed/inheritorInDifferentModule.fir.kt deleted file mode 100644 index 6f9e7f14705..00000000000 --- a/compiler/testData/diagnostics/tests/sealed/inheritorInDifferentModule.fir.kt +++ /dev/null @@ -1,17 +0,0 @@ -// ISSUE: KT-20423 -// !LANGUAGE: +SealedInterfaces +AllowSealedInheritorsInDifferentFilesOfSamePackage - -// MODULE: m1 -// FILE: a.kt -package a - -sealed class Base - -class A : Base() - -// MODULE: m2(m1) -// FILE: b.kt - -package a - -class B : Base() diff --git a/compiler/testData/diagnostics/tests/sealed/inheritorInDifferentModule.kt b/compiler/testData/diagnostics/tests/sealed/inheritorInDifferentModule.kt index 0942b62225f..1bb14b07ecb 100644 --- a/compiler/testData/diagnostics/tests/sealed/inheritorInDifferentModule.kt +++ b/compiler/testData/diagnostics/tests/sealed/inheritorInDifferentModule.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // ISSUE: KT-20423 // !LANGUAGE: +SealedInterfaces +AllowSealedInheritorsInDifferentFilesOfSamePackage diff --git a/compiler/testData/diagnostics/tests/sealed/interfaces/inheritorInDifferentModule.fir.kt b/compiler/testData/diagnostics/tests/sealed/interfaces/inheritorInDifferentModule.fir.kt deleted file mode 100644 index d73f4940e35..00000000000 --- a/compiler/testData/diagnostics/tests/sealed/interfaces/inheritorInDifferentModule.fir.kt +++ /dev/null @@ -1,17 +0,0 @@ -// ISSUE: KT-20423 -// !LANGUAGE: +SealedInterfaces +AllowSealedInheritorsInDifferentFilesOfSamePackage - -// MODULE: m1 -// FILE: a.kt -package a - -sealed interface Base - -interface A : Base - -// MODULE: m2(m1) -// FILE: b.kt - -package a - -interface B : Base diff --git a/compiler/testData/diagnostics/tests/sealed/interfaces/inheritorInDifferentModule.kt b/compiler/testData/diagnostics/tests/sealed/interfaces/inheritorInDifferentModule.kt index 74b6512a36d..1942308dff9 100644 --- a/compiler/testData/diagnostics/tests/sealed/interfaces/inheritorInDifferentModule.kt +++ b/compiler/testData/diagnostics/tests/sealed/interfaces/inheritorInDifferentModule.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // ISSUE: KT-20423 // !LANGUAGE: +SealedInterfaces +AllowSealedInheritorsInDifferentFilesOfSamePackage diff --git a/compiler/testData/diagnostics/tests/testsWithJava17/sealedClasses/kotlinInheritsJavaClass.fir.kt b/compiler/testData/diagnostics/tests/testsWithJava17/sealedClasses/kotlinInheritsJavaClass.fir.kt deleted file mode 100644 index 2cce51631b1..00000000000 --- a/compiler/testData/diagnostics/tests/testsWithJava17/sealedClasses/kotlinInheritsJavaClass.fir.kt +++ /dev/null @@ -1,11 +0,0 @@ -// ISSUE: KT-41215 - -// FILE: Base.java -public sealed class Base permits A, B {} - -// FILE: A.java -public final class A extends Base {} - -// FILE: B.kt - -class B : Base() diff --git a/compiler/testData/diagnostics/tests/testsWithJava17/sealedClasses/kotlinInheritsJavaClass.kt b/compiler/testData/diagnostics/tests/testsWithJava17/sealedClasses/kotlinInheritsJavaClass.kt index 3c2e16a6c76..3445a30bc29 100644 --- a/compiler/testData/diagnostics/tests/testsWithJava17/sealedClasses/kotlinInheritsJavaClass.kt +++ b/compiler/testData/diagnostics/tests/testsWithJava17/sealedClasses/kotlinInheritsJavaClass.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // ISSUE: KT-41215 // FILE: Base.java diff --git a/compiler/testData/diagnostics/tests/testsWithJava17/sealedClasses/kotlinInheritsJavaInterface.fir.kt b/compiler/testData/diagnostics/tests/testsWithJava17/sealedClasses/kotlinInheritsJavaInterface.fir.kt deleted file mode 100644 index fd2cf989bd4..00000000000 --- a/compiler/testData/diagnostics/tests/testsWithJava17/sealedClasses/kotlinInheritsJavaInterface.fir.kt +++ /dev/null @@ -1,11 +0,0 @@ -// ISSUE: KT-41215 - -// FILE: Base.java -public sealed interface Base permits A, B {} - -// FILE: A.java -public final class A extends Base {} - -// FILE: B.kt - -class B : Base diff --git a/compiler/testData/diagnostics/tests/testsWithJava17/sealedClasses/kotlinInheritsJavaInterface.kt b/compiler/testData/diagnostics/tests/testsWithJava17/sealedClasses/kotlinInheritsJavaInterface.kt index ab37a583b01..d138fedc8da 100644 --- a/compiler/testData/diagnostics/tests/testsWithJava17/sealedClasses/kotlinInheritsJavaInterface.kt +++ b/compiler/testData/diagnostics/tests/testsWithJava17/sealedClasses/kotlinInheritsJavaInterface.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // ISSUE: KT-41215 // FILE: Base.java diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt index 08624e7c645..dc6843f6118 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -450,6 +450,26 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.SEALED_INHERITOR_IN_DIFFERENT_PACKAGE) { firDiagnostic -> + SealedInheritorInDifferentPackageImpl( + firDiagnostic.a, + firDiagnostic.b, + firDiagnostic as FirPsiDiagnostic, + token, + ) + } + add(FirErrors.SEALED_INHERITOR_IN_DIFFERENT_MODULE) { firDiagnostic -> + SealedInheritorInDifferentModuleImpl( + firDiagnostic as FirPsiDiagnostic, + token, + ) + } + add(FirErrors.CLASS_INHERITS_JAVA_SEALED_CLASS) { firDiagnostic -> + ClassInheritsJavaSealedClassImpl( + firDiagnostic as FirPsiDiagnostic, + token, + ) + } add(FirErrors.SUPERTYPE_NOT_A_CLASS_OR_INTERFACE) { firDiagnostic -> SupertypeNotAClassOrInterfaceImpl( firDiagnostic.a, diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt index 15e14b03974..fc632ffa5c9 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt @@ -341,6 +341,20 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = SealedSupertypeInLocalClass::class } + abstract class SealedInheritorInDifferentPackage : KtFirDiagnostic() { + override val diagnosticClass get() = SealedInheritorInDifferentPackage::class + abstract val subclassPackage: FqName + abstract val basePackage: FqName + } + + abstract class SealedInheritorInDifferentModule : KtFirDiagnostic() { + override val diagnosticClass get() = SealedInheritorInDifferentModule::class + } + + abstract class ClassInheritsJavaSealedClass : KtFirDiagnostic() { + override val diagnosticClass get() = ClassInheritsJavaSealedClass::class + } + abstract class SupertypeNotAClassOrInterface : KtFirDiagnostic() { override val diagnosticClass get() = SupertypeNotAClassOrInterface::class abstract val reason: String diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index ba96a407f13..a045bcfcab0 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -522,6 +522,29 @@ internal class SealedSupertypeInLocalClassImpl( override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) } +internal class SealedInheritorInDifferentPackageImpl( + override val subclassPackage: FqName, + override val basePackage: FqName, + firDiagnostic: FirPsiDiagnostic, + override val token: ValidityToken, +) : KtFirDiagnostic.SealedInheritorInDifferentPackage(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) +} + +internal class SealedInheritorInDifferentModuleImpl( + firDiagnostic: FirPsiDiagnostic, + override val token: ValidityToken, +) : KtFirDiagnostic.SealedInheritorInDifferentModule(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) +} + +internal class ClassInheritsJavaSealedClassImpl( + firDiagnostic: FirPsiDiagnostic, + override val token: ValidityToken, +) : KtFirDiagnostic.ClassInheritsJavaSealedClass(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) +} + internal class SupertypeNotAClassOrInterfaceImpl( override val reason: String, firDiagnostic: FirPsiDiagnostic,