diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/ClassInheritsJavaSealedClassChecker.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/ClassInheritsJavaSealedClassChecker.kt new file mode 100644 index 00000000000..fb95e10f266 --- /dev/null +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/ClassInheritsJavaSealedClassChecker.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.resolve.jvm.checkers + +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaClassDescriptor +import org.jetbrains.kotlin.psi.KtClassOrObject +import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.resolve.bindingContextUtil.getAbbreviatedTypeOrType +import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker +import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext + +object ClassInheritsJavaSealedClassChecker : DeclarationChecker { + override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) { + if (descriptor !is ClassDescriptor || declaration !is KtClassOrObject) return + for (superTypeListEntry in declaration.superTypeListEntries) { + val typeReference = superTypeListEntry.typeReference ?: continue + val superType = typeReference.getAbbreviatedTypeOrType(context.trace.bindingContext)?.unwrap() ?: continue + val superClass = superType.constructor.declarationDescriptor as? LazyJavaClassDescriptor ?: continue + if (superClass.jClass.isSealed) { + context.trace.report(Errors.CLASS_INHERITS_JAVA_SEALED_CLASS.on(typeReference)) + } + } + } +} diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt index 3f4fe948f76..9c0addcdca7 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt @@ -41,7 +41,8 @@ object JvmPlatformConfigurator : PlatformConfiguratorBase( JvmMultifileClassStateChecker, SynchronizedOnInlineMethodChecker, DefaultCheckerInTailrec, - FunctionDelegateMemberNameClashChecker + FunctionDelegateMemberNameClashChecker, + ClassInheritsJavaSealedClassChecker ), additionalCallCheckers = listOf( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 3cb26257efd..7daa75e5199 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -427,6 +427,7 @@ public interface Errors { DiagnosticFactory0 SEALED_SUPERTYPE = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 SEALED_SUPERTYPE_IN_LOCAL_CLASS = DiagnosticFactory0.create(ERROR); DiagnosticFactory2 SEALED_INHERITOR_IN_DIFFERENT_PACKAGE = DiagnosticFactory2.create(ERROR); + DiagnosticFactory0 CLASS_INHERITS_JAVA_SEALED_CLASS = DiagnosticFactory0.create(ERROR); // Companion objects diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index c6d18ba488d..330b13fc9ed 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -634,6 +634,7 @@ public class DefaultErrorMessages { 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 {1} but it must be in package {2} where base class is declared", TO_STRING, TO_STRING); + MAP.put(CLASS_INHERITS_JAVA_SEALED_CLASS, "Inheritance of java sealed classes is prohibited"); MAP.put(SINGLETON_IN_SUPERTYPE, "Cannot inherit from a singleton"); MAP.put(CLASS_CANNOT_BE_EXTENDED_DIRECTLY, "Class {0} cannot be extended directly", NAME); diff --git a/compiler/testData/diagnostics/testsWithJava15/sealedClasses/kotlinInheritsJavaClass.kt b/compiler/testData/diagnostics/testsWithJava15/sealedClasses/kotlinInheritsJavaClass.kt new file mode 100644 index 00000000000..3c2e16a6c76 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJava15/sealedClasses/kotlinInheritsJavaClass.kt @@ -0,0 +1,11 @@ +// 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/testsWithJava15/sealedClasses/kotlinInheritsJavaClass.txt b/compiler/testData/diagnostics/testsWithJava15/sealedClasses/kotlinInheritsJavaClass.txt new file mode 100644 index 00000000000..0b902f9c44b --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJava15/sealedClasses/kotlinInheritsJavaClass.txt @@ -0,0 +1,22 @@ +package + +public final class A : Base { + public constructor A() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class B : Base { + public constructor B() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public sealed class Base { + public constructor Base() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/testsWithJava15/sealedClasses/kotlinInheritsJavaInterface.kt b/compiler/testData/diagnostics/testsWithJava15/sealedClasses/kotlinInheritsJavaInterface.kt new file mode 100644 index 00000000000..ab37a583b01 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJava15/sealedClasses/kotlinInheritsJavaInterface.kt @@ -0,0 +1,11 @@ +// 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/testsWithJava15/sealedClasses/kotlinInheritsJavaInterface.txt b/compiler/testData/diagnostics/testsWithJava15/sealedClasses/kotlinInheritsJavaInterface.txt new file mode 100644 index 00000000000..5e98dbfb22d --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJava15/sealedClasses/kotlinInheritsJavaInterface.txt @@ -0,0 +1,21 @@ +package + +public final class A : Base { + public constructor A() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class B : Base { + public constructor B() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public sealed interface Base { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +}