diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java index ce1d6ddf0cf..16395f5f8a4 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java @@ -20897,6 +20897,29 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirOldFronte public void testWithInterface() throws Exception { runTest("compiler/testData/diagnostics/tests/sealed/WithInterface.kt"); } + + @TestMetadata("compiler/testData/diagnostics/tests/sealed/interfaces") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Interfaces extends AbstractFirOldFrontendDiagnosticsTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInInterfaces() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/sealed/interfaces"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); + } + + @TestMetadata("sealedInterfacesDisabled.kt") + public void testSealedInterfacesDisabled() throws Exception { + runTest("compiler/testData/diagnostics/tests/sealed/interfaces/sealedInterfacesDisabled.kt"); + } + + @TestMetadata("simpleSealedInterface.kt") + public void testSimpleSealedInterface() throws Exception { + runTest("compiler/testData/diagnostics/tests/sealed/interfaces/simpleSealedInterface.kt"); + } + } } @TestMetadata("compiler/testData/diagnostics/tests/secondaryConstructors") diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.kt index cd212c74625..bed41c3165b 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.kt @@ -52,7 +52,7 @@ object ModifierCheckerCore { ABSTRACT_KEYWORD to EnumSet.of(CLASS_ONLY, LOCAL_CLASS, INTERFACE, MEMBER_PROPERTY, MEMBER_FUNCTION), OPEN_KEYWORD to EnumSet.of(CLASS_ONLY, LOCAL_CLASS, INTERFACE, MEMBER_PROPERTY, MEMBER_FUNCTION), FINAL_KEYWORD to EnumSet.of(CLASS_ONLY, LOCAL_CLASS, ENUM_CLASS, OBJECT, MEMBER_PROPERTY, MEMBER_FUNCTION), - SEALED_KEYWORD to EnumSet.of(CLASS_ONLY), + SEALED_KEYWORD to EnumSet.of(CLASS_ONLY, INTERFACE), INNER_KEYWORD to EnumSet.of(CLASS_ONLY), OVERRIDE_KEYWORD to EnumSet.of(MEMBER_PROPERTY, MEMBER_FUNCTION), PRIVATE_KEYWORD to defaultVisibilityTargets, diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt index 34391d681cc..43734e9792a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt @@ -42,6 +42,7 @@ private val DEFAULT_DECLARATION_CHECKERS = listOf( ContractDescriptionBlockChecker, PrivateInlineFunctionsReturningAnonymousObjectsChecker, SealedInheritorInSamePackageChecker, + SealedInterfaceAllowedChecker, ) private val DEFAULT_CALL_CHECKERS = listOf( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/SealedInterfaceAllowedChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/SealedInterfaceAllowedChecker.kt new file mode 100644 index 00000000000..ae9f92c84a8 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/SealedInterfaceAllowedChecker.kt @@ -0,0 +1,23 @@ +/* + * 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.checkers + +import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.KtDeclaration + +object SealedInterfaceAllowedChecker : DeclarationChecker { + override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) { + if (context.languageVersionSettings.supportsFeature(LanguageFeature.SealedInterfaces)) return + if (descriptor !is ClassDescriptor) return + if (descriptor.kind != ClassKind.INTERFACE) return + val keyword = declaration.modifierList?.getModifier(KtTokens.SEALED_KEYWORD) ?: return + context.trace.report(Errors.WRONG_MODIFIER_TARGET.on(keyword, KtTokens.SEALED_KEYWORD, KotlinTarget.INTERFACE.description)) + } +} diff --git a/compiler/testData/diagnostics/tests/sealed/interfaces/sealedInterfacesDisabled.fir.kt b/compiler/testData/diagnostics/tests/sealed/interfaces/sealedInterfacesDisabled.fir.kt new file mode 100644 index 00000000000..300a769e364 --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/interfaces/sealedInterfacesDisabled.fir.kt @@ -0,0 +1,5 @@ +// ISSUE: KT-20423 +// !LANGUAGE: -SealedInterfaces +// !DIAGNOSTICS: -UNUSED_VARIABLE + +sealed interface Base diff --git a/compiler/testData/diagnostics/tests/sealed/interfaces/sealedInterfacesDisabled.kt b/compiler/testData/diagnostics/tests/sealed/interfaces/sealedInterfacesDisabled.kt new file mode 100644 index 00000000000..2ba0a7ccbd9 --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/interfaces/sealedInterfacesDisabled.kt @@ -0,0 +1,5 @@ +// ISSUE: KT-20423 +// !LANGUAGE: -SealedInterfaces +// !DIAGNOSTICS: -UNUSED_VARIABLE + +sealed interface Base diff --git a/compiler/testData/diagnostics/tests/sealed/interfaces/sealedInterfacesDisabled.txt b/compiler/testData/diagnostics/tests/sealed/interfaces/sealedInterfacesDisabled.txt new file mode 100644 index 00000000000..f2ed48d03d4 --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/interfaces/sealedInterfacesDisabled.txt @@ -0,0 +1,7 @@ +package + +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 +} diff --git a/compiler/testData/diagnostics/tests/sealed/interfaces/simpleSealedInterface.fir.kt b/compiler/testData/diagnostics/tests/sealed/interfaces/simpleSealedInterface.fir.kt new file mode 100644 index 00000000000..2d63915cbcb --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/interfaces/simpleSealedInterface.fir.kt @@ -0,0 +1,38 @@ +// ISSUE: KT-20423 +// !LANGUAGE: +FreedomForSealedClasses +SealedInterfaces +// !DIAGNOSTICS: -UNUSED_VARIABLE + +sealed interface Base + +interface A : Base + +sealed class B : Base { + class First : B() + class Second : B() +} + +enum class C : Base { + SomeValue, AnotherValue +} + +object D : Base + +fun test_1(base: Base) { + val x = when (base) { + is A -> 1 + is B -> 2 + is C -> 3 + is D -> 4 + } +} + +fun test_2(base: Base) { + val x = when (base) { + is A -> 1 + is B.First -> 2 + is B.Second -> 3 + C.SomeValue -> 4 + C.AnotherValue -> 5 + D -> 6 + } +} diff --git a/compiler/testData/diagnostics/tests/sealed/interfaces/simpleSealedInterface.kt b/compiler/testData/diagnostics/tests/sealed/interfaces/simpleSealedInterface.kt new file mode 100644 index 00000000000..4f87a029760 --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/interfaces/simpleSealedInterface.kt @@ -0,0 +1,38 @@ +// ISSUE: KT-20423 +// !LANGUAGE: +FreedomForSealedClasses +SealedInterfaces +// !DIAGNOSTICS: -UNUSED_VARIABLE + +sealed interface Base + +interface A : Base + +sealed class B : Base { + class First : B() + class Second : B() +} + +enum class C : Base { + SomeValue, AnotherValue +} + +object D : Base + +fun test_1(base: Base) { + val x = when (base) { + is A -> 1 + is B -> 2 + is C -> 3 + is D -> 4 + } +} + +fun test_2(base: Base) { + val x = when (base) { + is A -> 1 + is B.First -> 2 + is B.Second -> 3 + C.SomeValue -> 4 + C.AnotherValue -> 5 + D -> 6 + } +} diff --git a/compiler/testData/diagnostics/tests/sealed/interfaces/simpleSealedInterface.txt b/compiler/testData/diagnostics/tests/sealed/interfaces/simpleSealedInterface.txt new file mode 100644 index 00000000000..1cc0a6b869a --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/interfaces/simpleSealedInterface.txt @@ -0,0 +1,65 @@ +package + +public fun test_1(/*0*/ base: Base): kotlin.Unit +public fun test_2(/*0*/ base: Base): kotlin.Unit + +public interface A : 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 +} + +public sealed class B : Base { + internal 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 final class First : B { + public constructor First() + 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 Second : B { + public constructor Second() + 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 +} + +public final enum class C : kotlin.Enum, Base { + enum entry SomeValue + + enum entry AnotherValue + + private constructor C() + public final override /*1*/ /*fake_override*/ val name: kotlin.String + public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int + protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: C): kotlin.Int + public final override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + protected/*protected and package*/ final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun finalize(): kotlin.Unit + public final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun getDeclaringClass(): java.lang.Class! + public final override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String + + // Static members + public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): C + public final /*synthesized*/ fun values(): kotlin.Array +} + +public object D : Base { + private constructor D() + 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/tests-gen/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 5c1521aa986..04fa8c6fbbc 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -20974,6 +20974,29 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTestWithFirVali public void testWithInterface() throws Exception { runTest("compiler/testData/diagnostics/tests/sealed/WithInterface.kt"); } + + @TestMetadata("compiler/testData/diagnostics/tests/sealed/interfaces") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Interfaces extends AbstractDiagnosticsTestWithFirValidation { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInInterfaces() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/sealed/interfaces"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); + } + + @TestMetadata("sealedInterfacesDisabled.kt") + public void testSealedInterfacesDisabled() throws Exception { + runTest("compiler/testData/diagnostics/tests/sealed/interfaces/sealedInterfacesDisabled.kt"); + } + + @TestMetadata("simpleSealedInterface.kt") + public void testSimpleSealedInterface() throws Exception { + runTest("compiler/testData/diagnostics/tests/sealed/interfaces/simpleSealedInterface.kt"); + } + } } @TestMetadata("compiler/testData/diagnostics/tests/secondaryConstructors") diff --git a/compiler/tests-gen/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java index 6b56a052b0f..44bf9026e50 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java @@ -20899,6 +20899,29 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing public void testWithInterface() throws Exception { runTest("compiler/testData/diagnostics/tests/sealed/WithInterface.kt"); } + + @TestMetadata("compiler/testData/diagnostics/tests/sealed/interfaces") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Interfaces extends AbstractDiagnosticsUsingJavacTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInInterfaces() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/sealed/interfaces"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); + } + + @TestMetadata("sealedInterfacesDisabled.kt") + public void testSealedInterfacesDisabled() throws Exception { + runTest("compiler/testData/diagnostics/tests/sealed/interfaces/sealedInterfacesDisabled.kt"); + } + + @TestMetadata("simpleSealedInterface.kt") + public void testSimpleSealedInterface() throws Exception { + runTest("compiler/testData/diagnostics/tests/sealed/interfaces/simpleSealedInterface.kt"); + } + } } @TestMetadata("compiler/testData/diagnostics/tests/secondaryConstructors") diff --git a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt index 487e7a6e9ef..6b8aff23492 100644 --- a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt +++ b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt @@ -144,6 +144,7 @@ enum class LanguageFeature( JvmRecordSupport(KOTLIN_1_5), FreedomForSealedClasses(KOTLIN_1_5), + SealedInterfaces(KOTLIN_1_5), // Temporarily disabled, see KT-27084/KT-22379 SoundSmartcastFromLoopConditionForLoopAssignedVariables(sinceVersion = null, kind = BUG_FIX), diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java index 005b58cf637..51f6c92f8f2 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java @@ -270,7 +270,7 @@ public class DescriptorUtils { } public static boolean isSealedClass(@Nullable DeclarationDescriptor descriptor) { - return isKindOf(descriptor, ClassKind.CLASS) && ((ClassDescriptor) descriptor).getModality() == Modality.SEALED; + return (isKindOf(descriptor, ClassKind.CLASS) || isKindOf(descriptor, ClassKind.INTERFACE)) && ((ClassDescriptor) descriptor).getModality() == Modality.SEALED; } public static boolean isAnonymousObject(@NotNull DeclarationDescriptor descriptor) {