FIR: check subclass of sealed class
This commit is contained in:
committed by
TeamCityServer
parent
ed152e74a0
commit
10d4dfef04
+6
@@ -160,6 +160,12 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
|
||||
val CLASS_IN_SUPERTYPE_FOR_ENUM by error<KtTypeReference>()
|
||||
val SEALED_SUPERTYPE by error<KtTypeReference>()
|
||||
val SEALED_SUPERTYPE_IN_LOCAL_CLASS by error<KtTypeReference>()
|
||||
val SEALED_INHERITOR_IN_DIFFERENT_PACKAGE by error<KtTypeReference> {
|
||||
parameter<FqName>("subclassPackage")
|
||||
parameter<FqName>("basePackage")
|
||||
}
|
||||
val SEALED_INHERITOR_IN_DIFFERENT_MODULE by error<KtTypeReference>()
|
||||
val CLASS_INHERITS_JAVA_SEALED_CLASS by error<KtTypeReference>()
|
||||
val SUPERTYPE_NOT_A_CLASS_OR_INTERFACE by error<KtElement> {
|
||||
parameter<String>("reason")
|
||||
}
|
||||
|
||||
@@ -168,6 +168,9 @@ object FirErrors {
|
||||
val CLASS_IN_SUPERTYPE_FOR_ENUM by error0<KtTypeReference>()
|
||||
val SEALED_SUPERTYPE by error0<KtTypeReference>()
|
||||
val SEALED_SUPERTYPE_IN_LOCAL_CLASS by error0<KtTypeReference>()
|
||||
val SEALED_INHERITOR_IN_DIFFERENT_PACKAGE by error2<KtTypeReference, FqName, FqName>()
|
||||
val SEALED_INHERITOR_IN_DIFFERENT_MODULE by error0<KtTypeReference>()
|
||||
val CLASS_INHERITS_JAVA_SEALED_CLASS by error0<KtTypeReference>()
|
||||
val SUPERTYPE_NOT_A_CLASS_OR_INTERFACE by error1<KtElement, String>()
|
||||
val CYCLIC_INHERITANCE_HIERARCHY by error0<PsiElement>()
|
||||
val EXPANDED_TYPE_CANNOT_BE_INHERITED by error1<KtTypeReference, ConeKotlinType>()
|
||||
|
||||
+24
-6
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+12
@@ -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(
|
||||
|
||||
+1
-1
@@ -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 : <!SEALED_INHERITOR_IN_DIFFERENT_MODULE!>Base<!>() // must be an error
|
||||
|
||||
fun test_2(b: Base) = when (b) {
|
||||
is Derived -> 1
|
||||
|
||||
+2
-2
@@ -13,10 +13,10 @@ expect sealed class SealedWithPlatformActuals : SealedWithSharedActual
|
||||
package foo
|
||||
|
||||
actual sealed class SealedWithSharedActual
|
||||
class SimpleShared : <!UNRESOLVED_REFERENCE!>SealedWithPlatformActuals<!>()
|
||||
class SimpleShared : <!SEALED_INHERITOR_IN_DIFFERENT_MODULE, UNRESOLVED_REFERENCE!>SealedWithPlatformActuals<!>()
|
||||
|
||||
// MODULE: main()()(intermediate)
|
||||
// TARGET_PLATFORM: JVM
|
||||
package foo
|
||||
|
||||
actual sealed class SealedWithPlatformActuals <!ACTUAL_WITHOUT_EXPECT, NON_PRIVATE_OR_PROTECTED_CONSTRUCTOR_IN_SEALED!>actual constructor()<!>: SealedWithSharedActual()
|
||||
actual sealed class SealedWithPlatformActuals <!ACTUAL_WITHOUT_EXPECT, NON_PRIVATE_OR_PROTECTED_CONSTRUCTOR_IN_SEALED!>actual constructor()<!>: <!SEALED_INHERITOR_IN_DIFFERENT_MODULE!>SealedWithSharedActual<!>()
|
||||
|
||||
@@ -37,4 +37,4 @@ package bar
|
||||
|
||||
import foo.Base
|
||||
|
||||
class E : <!SEALED_SUPERTYPE!>Base<!>()
|
||||
class E : <!SEALED_INHERITOR_IN_DIFFERENT_PACKAGE!>Base<!>()
|
||||
|
||||
@@ -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()
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// ISSUE: KT-20423
|
||||
// !LANGUAGE: +SealedInterfaces +AllowSealedInheritorsInDifferentFilesOfSamePackage
|
||||
|
||||
|
||||
-17
@@ -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
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// ISSUE: KT-20423
|
||||
// !LANGUAGE: +SealedInterfaces +AllowSealedInheritorsInDifferentFilesOfSamePackage
|
||||
|
||||
|
||||
Vendored
-11
@@ -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()
|
||||
Vendored
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// ISSUE: KT-41215
|
||||
|
||||
// FILE: Base.java
|
||||
|
||||
compiler/testData/diagnostics/tests/testsWithJava17/sealedClasses/kotlinInheritsJavaInterface.fir.kt
Vendored
-11
@@ -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
|
||||
Vendored
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// ISSUE: KT-41215
|
||||
|
||||
// FILE: Base.java
|
||||
|
||||
+20
@@ -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,
|
||||
|
||||
+14
@@ -341,6 +341,20 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
|
||||
override val diagnosticClass get() = SealedSupertypeInLocalClass::class
|
||||
}
|
||||
|
||||
abstract class SealedInheritorInDifferentPackage : KtFirDiagnostic<KtTypeReference>() {
|
||||
override val diagnosticClass get() = SealedInheritorInDifferentPackage::class
|
||||
abstract val subclassPackage: FqName
|
||||
abstract val basePackage: FqName
|
||||
}
|
||||
|
||||
abstract class SealedInheritorInDifferentModule : KtFirDiagnostic<KtTypeReference>() {
|
||||
override val diagnosticClass get() = SealedInheritorInDifferentModule::class
|
||||
}
|
||||
|
||||
abstract class ClassInheritsJavaSealedClass : KtFirDiagnostic<KtTypeReference>() {
|
||||
override val diagnosticClass get() = ClassInheritsJavaSealedClass::class
|
||||
}
|
||||
|
||||
abstract class SupertypeNotAClassOrInterface : KtFirDiagnostic<KtElement>() {
|
||||
override val diagnosticClass get() = SupertypeNotAClassOrInterface::class
|
||||
abstract val reason: String
|
||||
|
||||
+23
@@ -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<KtTypeReference> {
|
||||
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class SealedInheritorInDifferentModuleImpl(
|
||||
firDiagnostic: FirPsiDiagnostic,
|
||||
override val token: ValidityToken,
|
||||
) : KtFirDiagnostic.SealedInheritorInDifferentModule(), KtAbstractFirDiagnostic<KtTypeReference> {
|
||||
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class ClassInheritsJavaSealedClassImpl(
|
||||
firDiagnostic: FirPsiDiagnostic,
|
||||
override val token: ValidityToken,
|
||||
) : KtFirDiagnostic.ClassInheritsJavaSealedClass(), KtAbstractFirDiagnostic<KtTypeReference> {
|
||||
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class SupertypeNotAClassOrInterfaceImpl(
|
||||
override val reason: String,
|
||||
firDiagnostic: FirPsiDiagnostic,
|
||||
|
||||
Reference in New Issue
Block a user