[FE] Allow using sealed modifier on interface and compute sealed modality for them

#KT-20423
This commit is contained in:
Dmitriy Novozhilov
2020-11-16 17:38:18 +03:00
committed by TeamCityServer
parent d605c7e491
commit 9609954560
14 changed files with 254 additions and 2 deletions
@@ -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")
@@ -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,
@@ -42,6 +42,7 @@ private val DEFAULT_DECLARATION_CHECKERS = listOf(
ContractDescriptionBlockChecker,
PrivateInlineFunctionsReturningAnonymousObjectsChecker,
SealedInheritorInSamePackageChecker,
SealedInterfaceAllowedChecker,
)
private val DEFAULT_CALL_CHECKERS = listOf(
@@ -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))
}
}
@@ -0,0 +1,5 @@
// ISSUE: KT-20423
// !LANGUAGE: -SealedInterfaces
// !DIAGNOSTICS: -UNUSED_VARIABLE
sealed interface Base
@@ -0,0 +1,5 @@
// ISSUE: KT-20423
// !LANGUAGE: -SealedInterfaces
// !DIAGNOSTICS: -UNUSED_VARIABLE
<!WRONG_MODIFIER_TARGET!>sealed<!> interface Base
@@ -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
}
@@ -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
}
}
@@ -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 = <!NO_ELSE_IN_WHEN!>when<!> (base) {
is A -> 1
is B.First -> 2
is B.Second -> 3
C.SomeValue -> 4
C.AnotherValue -> 5
D -> 6
}
}
@@ -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<C>, 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<C!>!
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<C>
}
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
}
@@ -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")
@@ -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")
@@ -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),
@@ -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) {