From f6872dfbea85c1d923ef9c5af71560124bfa4802 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Tue, 12 May 2015 18:07:17 +0300 Subject: [PATCH] Exhaustive when support for sealed classes #KT-7606 Fixed When on sealed can use is (both for derived classes and objects) or just comparison (only for derived objects). A pack of tests provided. --- .../org/jetbrains/kotlin/cfg/WhenChecker.java | 92 ++++++++++++++- .../tests/sealed/ExhaustiveWhen.kt | 16 +++ .../tests/sealed/ExhaustiveWhen.txt | 46 ++++++++ .../tests/sealed/ExhaustiveWhenDoubleInner.kt | 16 +++ .../sealed/ExhaustiveWhenDoubleInner.txt | 45 ++++++++ .../sealed/ExhaustiveWhenMultipleInner.kt | 47 ++++++++ .../sealed/ExhaustiveWhenMultipleInner.txt | 69 +++++++++++ .../tests/sealed/ExhaustiveWhenNegated.kt | 16 +++ .../tests/sealed/ExhaustiveWhenNegated.txt | 46 ++++++++ .../sealed/ExhaustiveWhenNegatedTwice.kt | 17 +++ .../sealed/ExhaustiveWhenNegatedTwice.txt | 54 +++++++++ .../sealed/ExhaustiveWhenOnNestedSealed.kt | 20 ++++ .../sealed/ExhaustiveWhenOnNestedSealed.txt | 45 ++++++++ .../tests/sealed/ExhaustiveWhenOnNullable.kt | 17 +++ .../tests/sealed/ExhaustiveWhenOnNullable.txt | 46 ++++++++ .../ExhaustiveWhenWithAdditionalMember.kt | 18 +++ .../ExhaustiveWhenWithAdditionalMember.txt | 58 ++++++++++ .../tests/sealed/ExhaustiveWhenWithElse.kt | 15 +++ .../tests/sealed/ExhaustiveWhenWithElse.txt | 46 ++++++++ .../tests/sealed/NonExhaustiveWhen.kt | 14 +++ .../tests/sealed/NonExhaustiveWhen.txt | 46 ++++++++ .../tests/sealed/NonExhaustiveWhenNegated.kt | 14 +++ .../tests/sealed/NonExhaustiveWhenNegated.txt | 46 ++++++++ .../NonExhaustiveWhenWithAdditionalCase.kt | 22 ++++ .../NonExhaustiveWhenWithAdditionalCase.txt | 63 ++++++++++ .../sealed/NonExhaustiveWhenWithAnyCase.kt | 15 +++ .../sealed/NonExhaustiveWhenWithAnyCase.txt | 38 ++++++ .../diagnostics/tests/sealed/OperationWhen.kt | 20 ++++ .../tests/sealed/OperationWhen.txt | 53 +++++++++ .../diagnostics/tests/sealed/TreeWhen.kt | 13 +++ .../diagnostics/tests/sealed/TreeWhen.txt | 36 ++++++ .../tests/sealed/TreeWhenFunctional.kt | 11 ++ .../tests/sealed/TreeWhenFunctional.txt | 36 ++++++ .../tests/sealed/TreeWhenFunctionalNoIs.kt | 23 ++++ .../tests/sealed/TreeWhenFunctionalNoIs.txt | 44 +++++++ .../tests/sealed/WhenOnEmptySealed.kt | 11 ++ .../tests/sealed/WhenOnEmptySealed.txt | 10 ++ .../checkers/JetDiagnosticsTestGenerated.java | 108 ++++++++++++++++++ .../kotlin/resolve/DescriptorUtils.java | 24 +++- 39 files changed, 1368 insertions(+), 8 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/sealed/ExhaustiveWhen.kt create mode 100644 compiler/testData/diagnostics/tests/sealed/ExhaustiveWhen.txt create mode 100644 compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenDoubleInner.kt create mode 100644 compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenDoubleInner.txt create mode 100644 compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenMultipleInner.kt create mode 100644 compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenMultipleInner.txt create mode 100644 compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenNegated.kt create mode 100644 compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenNegated.txt create mode 100644 compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenNegatedTwice.kt create mode 100644 compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenNegatedTwice.txt create mode 100644 compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenOnNestedSealed.kt create mode 100644 compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenOnNestedSealed.txt create mode 100644 compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenOnNullable.kt create mode 100644 compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenOnNullable.txt create mode 100644 compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenWithAdditionalMember.kt create mode 100644 compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenWithAdditionalMember.txt create mode 100644 compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenWithElse.kt create mode 100644 compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenWithElse.txt create mode 100644 compiler/testData/diagnostics/tests/sealed/NonExhaustiveWhen.kt create mode 100644 compiler/testData/diagnostics/tests/sealed/NonExhaustiveWhen.txt create mode 100644 compiler/testData/diagnostics/tests/sealed/NonExhaustiveWhenNegated.kt create mode 100644 compiler/testData/diagnostics/tests/sealed/NonExhaustiveWhenNegated.txt create mode 100644 compiler/testData/diagnostics/tests/sealed/NonExhaustiveWhenWithAdditionalCase.kt create mode 100644 compiler/testData/diagnostics/tests/sealed/NonExhaustiveWhenWithAdditionalCase.txt create mode 100644 compiler/testData/diagnostics/tests/sealed/NonExhaustiveWhenWithAnyCase.kt create mode 100644 compiler/testData/diagnostics/tests/sealed/NonExhaustiveWhenWithAnyCase.txt create mode 100644 compiler/testData/diagnostics/tests/sealed/OperationWhen.kt create mode 100644 compiler/testData/diagnostics/tests/sealed/OperationWhen.txt create mode 100644 compiler/testData/diagnostics/tests/sealed/TreeWhen.kt create mode 100644 compiler/testData/diagnostics/tests/sealed/TreeWhen.txt create mode 100644 compiler/testData/diagnostics/tests/sealed/TreeWhenFunctional.kt create mode 100644 compiler/testData/diagnostics/tests/sealed/TreeWhenFunctional.txt create mode 100644 compiler/testData/diagnostics/tests/sealed/TreeWhenFunctionalNoIs.kt create mode 100644 compiler/testData/diagnostics/tests/sealed/TreeWhenFunctionalNoIs.txt create mode 100644 compiler/testData/diagnostics/tests/sealed/WhenOnEmptySealed.kt create mode 100644 compiler/testData/diagnostics/tests/sealed/WhenOnEmptySealed.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/WhenChecker.java b/compiler/frontend/src/org/jetbrains/kotlin/cfg/WhenChecker.java index 2c5e635f66c..9b3949d7cd1 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/WhenChecker.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/WhenChecker.java @@ -24,10 +24,14 @@ import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.BindingTrace; import org.jetbrains.kotlin.resolve.CompileTimeConstantUtils; +import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.resolve.bindingContextUtil.BindingContextUtilPackage; import org.jetbrains.kotlin.types.JetType; import org.jetbrains.kotlin.types.TypeUtils; +import java.util.HashSet; +import java.util.Set; + import static org.jetbrains.kotlin.resolve.DescriptorUtils.isEnumEntry; import static org.jetbrains.kotlin.resolve.DescriptorUtils.isEnumClass; import static org.jetbrains.kotlin.types.TypesPackage.isFlexible; @@ -82,8 +86,12 @@ public final class WhenChecker { } public static boolean isWhenOnEnumExhaustive( - @NotNull JetWhenExpression expression, @NotNull BindingTrace trace, @NotNull ClassDescriptor enumClassDescriptor) { - assert isEnumClass(enumClassDescriptor); + @NotNull JetWhenExpression expression, + @NotNull BindingTrace trace, + @NotNull ClassDescriptor enumClassDescriptor + ) { + assert isEnumClass(enumClassDescriptor) : + "isWhenOnEnumExhaustive should be called with an enum class descriptor"; boolean notEmpty = false; for (DeclarationDescriptor descriptor : enumClassDescriptor.getUnsubstitutedInnerClassesScope().getAllDescriptors()) { if (isEnumEntry(descriptor)) { @@ -96,6 +104,35 @@ public final class WhenChecker { return notEmpty; } + private static void collectNestedSubclasses( + @NotNull ClassDescriptor baseDescriptor, + @NotNull ClassDescriptor currentDescriptor, + @NotNull Set subclasses + ) { + for (DeclarationDescriptor descriptor : currentDescriptor.getUnsubstitutedInnerClassesScope().getAllDescriptors()) { + if (descriptor instanceof ClassDescriptor) { + ClassDescriptor memberClassDescriptor = (ClassDescriptor) descriptor; + if (DescriptorUtils.isDirectSubclass(memberClassDescriptor, baseDescriptor)) { + subclasses.add(memberClassDescriptor); + } + collectNestedSubclasses(baseDescriptor, memberClassDescriptor, subclasses); + } + } + } + + private static boolean isWhenOnSealedClassExhaustive( + @NotNull JetWhenExpression expression, + @NotNull BindingTrace trace, + @NotNull ClassDescriptor classDescriptor + ) { + assert classDescriptor.getModality() == Modality.SEALED : + "isWhenOnSealedClassExhaustive should be called with a sealed class descriptor"; + Set memberClassDescriptors = new HashSet(); + collectNestedSubclasses(classDescriptor, classDescriptor, memberClassDescriptors); + // When on a sealed class without derived members is considered non-exhaustive (see test WhenOnEmptySealed) + return !memberClassDescriptors.isEmpty() && containsAllClassCases(expression, memberClassDescriptors, trace); + } + /** * It's assumed that function is called for a final type. In this case the only possible smart cast is to not nullable type. * @return true if type is nullable, and cannot be smart casted @@ -128,8 +165,10 @@ public final class WhenChecker { exhaustive = isWhenOnBooleanExhaustive(expression, trace); } else { - // TODO: sealed hierarchies, etc. - exhaustive = false; + ClassDescriptor classDescriptor = TypeUtils.getClassDescriptor(type); + exhaustive = (classDescriptor != null + && classDescriptor.getModality() == Modality.SEALED + && isWhenOnSealedClassExhaustive(expression, trace, classDescriptor)); } } else { @@ -167,6 +206,51 @@ public final class WhenChecker { return false; } + private static boolean containsAllClassCases( + @NotNull JetWhenExpression whenExpression, + @NotNull Set memberDescriptors, + @NotNull BindingTrace trace + ) { + Set checkedDescriptors = new HashSet(); + for (JetWhenEntry whenEntry : whenExpression.getEntries()) { + for (JetWhenCondition condition : whenEntry.getConditions()) { + boolean negated = false; + JetType checkedType = null; + if (condition instanceof JetWhenConditionIsPattern) { + JetWhenConditionIsPattern conditionIsPattern = (JetWhenConditionIsPattern) condition; + checkedType = trace.get(BindingContext.TYPE, conditionIsPattern.getTypeReference()); + negated = conditionIsPattern.isNegated(); + } + else if (condition instanceof JetWhenConditionWithExpression) { + JetWhenConditionWithExpression conditionWithExpression = (JetWhenConditionWithExpression) condition; + if (conditionWithExpression.getExpression() != null) { + checkedType = trace.getBindingContext().getType(conditionWithExpression.getExpression()); + } + } + if (checkedType == null) { + continue; + } + ClassDescriptor checkedDescriptor = TypeUtils.getClassDescriptor(checkedType); + // Checks are important only for nested subclasses of the sealed class + // In additional, check without "is" is important only for objects + if (checkedDescriptor == null + || !memberDescriptors.contains(checkedDescriptor) + || (condition instanceof JetWhenConditionWithExpression && !DescriptorUtils.isObject(checkedDescriptor))) { + continue; + } + if (negated) { + if (checkedDescriptors.contains(checkedDescriptor)) return true; // all members are already there + checkedDescriptors.addAll(memberDescriptors); + checkedDescriptors.remove(checkedDescriptor); + } + else { + checkedDescriptors.add(checkedDescriptor); + } + } + } + return checkedDescriptors.containsAll(memberDescriptors); + } + public static boolean containsNullCase(@NotNull JetWhenExpression expression, @NotNull BindingTrace trace) { for (JetWhenEntry entry : expression.getEntries()) { for (JetWhenCondition condition : entry.getConditions()) { diff --git a/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhen.kt b/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhen.kt new file mode 100644 index 00000000000..cd9a104257f --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhen.kt @@ -0,0 +1,16 @@ +sealed class Sealed(val x: Int) { + object First: Sealed(12) + open class NonFirst(x: Int, val y: Int): Sealed(x) { + object Second: NonFirst(34, 2) + object Third: NonFirst(56, 3) + } +} + +fun foo(s: Sealed): Int { + return when(s) { + is Sealed.First -> 1 + is Sealed.NonFirst -> 0 + // no else required + } +} + diff --git a/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhen.txt b/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhen.txt new file mode 100644 index 00000000000..82857529aa1 --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhen.txt @@ -0,0 +1,46 @@ +package + +internal fun foo(/*0*/ s: Sealed): kotlin.Int + +internal sealed class Sealed { + public constructor Sealed(/*0*/ x: kotlin.Int) + internal final val x: kotlin.Int + 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 + + internal object First : Sealed { + private constructor First() + internal final override /*1*/ /*fake_override*/ val x: kotlin.Int + 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 + } + + internal open class NonFirst : Sealed { + public constructor NonFirst(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Int) + internal final override /*1*/ /*fake_override*/ val x: kotlin.Int + internal final val y: kotlin.Int + 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 + + internal object Second : Sealed.NonFirst { + private constructor Second() + internal final override /*1*/ /*fake_override*/ val x: kotlin.Int + internal final override /*1*/ /*fake_override*/ val y: kotlin.Int + 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 + } + + internal object Third : Sealed.NonFirst { + private constructor Third() + internal final override /*1*/ /*fake_override*/ val x: kotlin.Int + internal final override /*1*/ /*fake_override*/ val y: kotlin.Int + 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/ExhaustiveWhenDoubleInner.kt b/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenDoubleInner.kt new file mode 100644 index 00000000000..1a4fc73d191 --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenDoubleInner.kt @@ -0,0 +1,16 @@ +sealed class Sealed() { + object First: Sealed() + open class NonFirst: Sealed() { + object Second: NonFirst() + object Third: NonFirst() + // It's ALLOWED to inherit Sealed also from here + object Fourth: Sealed() + } +} + +fun foo(s: Sealed) = when(s) { + Sealed.First -> 1 + is Sealed.NonFirst -> 2 + Sealed.NonFirst.Fourth -> 4 + // no else required +} diff --git a/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenDoubleInner.txt b/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenDoubleInner.txt new file mode 100644 index 00000000000..c9e8163d7a4 --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenDoubleInner.txt @@ -0,0 +1,45 @@ +package + +internal fun foo(/*0*/ s: Sealed): kotlin.Int + +internal sealed class Sealed { + public constructor Sealed() + 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 + + internal object First : Sealed { + private 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 + } + + internal open class NonFirst : Sealed { + public constructor NonFirst() + 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 + + internal object Fourth : Sealed { + private constructor Fourth() + 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 + } + + internal object Second : Sealed.NonFirst { + private 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 + } + + internal object Third : Sealed.NonFirst { + private constructor Third() + 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/ExhaustiveWhenMultipleInner.kt b/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenMultipleInner.kt new file mode 100644 index 00000000000..b7afa1df5ac --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenMultipleInner.kt @@ -0,0 +1,47 @@ +sealed class Sealed() { + object First: Sealed() + open class NonFirst: Sealed() { + class NonSecond: NonFirst() { + object Third: Sealed() + class NonThird: Sealed() { + object Fourth: NonFirst() + class Fifth: Sealed() + } + } + object Second: Sealed() + } +} + +fun foo(s: Sealed) = when(s) { + Sealed.First -> 1 + is Sealed.NonFirst -> 2 + Sealed.NonFirst.Second -> 4 + Sealed.NonFirst.NonSecond.Third -> 6 + is Sealed.NonFirst.NonSecond.NonThird -> 8 + is Sealed.NonFirst.NonSecond.NonThird.Fifth -> 10 + // no else required +} + +fun fooWithElse(s: Sealed) = when(s) { + Sealed.First -> 1 + Sealed.NonFirst.NonSecond.Third -> 6 + is Sealed.NonFirst.NonSecond.NonThird.Fifth -> 10 + else -> 0 +} + +fun fooWithoutElse(s: Sealed) = when(s) { + Sealed.First -> 1 + is Sealed.NonFirst -> 2 + Sealed.NonFirst.NonSecond.Third -> 6 + is Sealed.NonFirst.NonSecond.NonThird -> 8 + is Sealed.NonFirst.NonSecond.NonThird.Fifth -> 10 +} + +fun barWithoutElse(s: Sealed) = when(s) { + Sealed.First -> 1 + is Sealed.NonFirst -> 2 + Sealed.NonFirst.Second -> 4 + is Sealed.NonFirst.NonSecond.NonThird -> 8 + is Sealed.NonFirst.NonSecond.NonThird.Fifth -> 10 +} + diff --git a/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenMultipleInner.txt b/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenMultipleInner.txt new file mode 100644 index 00000000000..cc43123d220 --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenMultipleInner.txt @@ -0,0 +1,69 @@ +package + +internal fun barWithoutElse(/*0*/ s: Sealed): kotlin.Int +internal fun foo(/*0*/ s: Sealed): kotlin.Int +internal fun fooWithElse(/*0*/ s: Sealed): kotlin.Int +internal fun fooWithoutElse(/*0*/ s: Sealed): kotlin.Int + +internal sealed class Sealed { + public constructor Sealed() + 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 + + internal object First : Sealed { + private 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 + } + + internal open class NonFirst : Sealed { + public constructor NonFirst() + 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 + + internal final class NonSecond : Sealed.NonFirst { + public constructor NonSecond() + 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 + + internal final class NonThird : Sealed { + public constructor NonThird() + 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 + + internal final class Fifth : Sealed { + public constructor Fifth() + 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 + } + + internal object Fourth : Sealed.NonFirst { + private constructor Fourth() + 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 + } + } + + internal object Third : Sealed { + private constructor Third() + 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 + } + } + + internal object Second : Sealed { + private 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 + } + } +} diff --git a/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenNegated.kt b/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenNegated.kt new file mode 100644 index 00000000000..1e0f78ef81c --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenNegated.kt @@ -0,0 +1,16 @@ +sealed class Sealed(val x: Int) { + object First: Sealed(12) + open class NonFirst(x: Int, val y: Int): Sealed(x) { + object Second: NonFirst(34, 2) + object Third: NonFirst(56, 3) + } +} + +fun foo(s: Sealed): Int { + return when(s) { + is Sealed.First -> 1 + !is Sealed.First -> 0 + // no else required + } +} + diff --git a/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenNegated.txt b/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenNegated.txt new file mode 100644 index 00000000000..82857529aa1 --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenNegated.txt @@ -0,0 +1,46 @@ +package + +internal fun foo(/*0*/ s: Sealed): kotlin.Int + +internal sealed class Sealed { + public constructor Sealed(/*0*/ x: kotlin.Int) + internal final val x: kotlin.Int + 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 + + internal object First : Sealed { + private constructor First() + internal final override /*1*/ /*fake_override*/ val x: kotlin.Int + 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 + } + + internal open class NonFirst : Sealed { + public constructor NonFirst(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Int) + internal final override /*1*/ /*fake_override*/ val x: kotlin.Int + internal final val y: kotlin.Int + 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 + + internal object Second : Sealed.NonFirst { + private constructor Second() + internal final override /*1*/ /*fake_override*/ val x: kotlin.Int + internal final override /*1*/ /*fake_override*/ val y: kotlin.Int + 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 + } + + internal object Third : Sealed.NonFirst { + private constructor Third() + internal final override /*1*/ /*fake_override*/ val x: kotlin.Int + internal final override /*1*/ /*fake_override*/ val y: kotlin.Int + 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/ExhaustiveWhenNegatedTwice.kt b/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenNegatedTwice.kt new file mode 100644 index 00000000000..949ccc9b67c --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenNegatedTwice.kt @@ -0,0 +1,17 @@ +sealed class Sealed(val x: Int) { + object First: Sealed(12) + open class NonFirst(x: Int, val y: Int): Sealed(x) { + object Second: NonFirst(34, 2) + object Third: NonFirst(56, 3) + } + object Last: Sealed(78) +} + +fun foo(s: Sealed): Int { + return when(s) { + !is Sealed.First -> 1 + !is Sealed.Last -> 0 + // no else required + } +} + diff --git a/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenNegatedTwice.txt b/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenNegatedTwice.txt new file mode 100644 index 00000000000..4a5b648c715 --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenNegatedTwice.txt @@ -0,0 +1,54 @@ +package + +internal fun foo(/*0*/ s: Sealed): kotlin.Int + +internal sealed class Sealed { + public constructor Sealed(/*0*/ x: kotlin.Int) + internal final val x: kotlin.Int + 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 + + internal object First : Sealed { + private constructor First() + internal final override /*1*/ /*fake_override*/ val x: kotlin.Int + 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 + } + + internal object Last : Sealed { + private constructor Last() + internal final override /*1*/ /*fake_override*/ val x: kotlin.Int + 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 + } + + internal open class NonFirst : Sealed { + public constructor NonFirst(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Int) + internal final override /*1*/ /*fake_override*/ val x: kotlin.Int + internal final val y: kotlin.Int + 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 + + internal object Second : Sealed.NonFirst { + private constructor Second() + internal final override /*1*/ /*fake_override*/ val x: kotlin.Int + internal final override /*1*/ /*fake_override*/ val y: kotlin.Int + 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 + } + + internal object Third : Sealed.NonFirst { + private constructor Third() + internal final override /*1*/ /*fake_override*/ val x: kotlin.Int + internal final override /*1*/ /*fake_override*/ val y: kotlin.Int + 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/ExhaustiveWhenOnNestedSealed.kt b/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenOnNestedSealed.kt new file mode 100644 index 00000000000..d11c19bd2ec --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenOnNestedSealed.kt @@ -0,0 +1,20 @@ +sealed class Sealed { + object First: Sealed() + sealed class NonFirst { + object Second: NonFirst() + object Third: NonFirst() + object Fourth: Sealed() + } +} + +fun foo(s: Sealed, nf: Sealed.NonFirst): Int { + val si = when(s) { + Sealed.First -> 1 + Sealed.NonFirst.Fourth -> 4 + } + val nfi = when(nf) { + Sealed.NonFirst.Second -> 2 + Sealed.NonFirst.Third -> 3 + } + return si + nfi +} diff --git a/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenOnNestedSealed.txt b/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenOnNestedSealed.txt new file mode 100644 index 00000000000..4f4f54103b5 --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenOnNestedSealed.txt @@ -0,0 +1,45 @@ +package + +internal fun foo(/*0*/ s: Sealed, /*1*/ nf: Sealed.NonFirst): kotlin.Int + +internal sealed class Sealed { + public constructor Sealed() + 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 + + internal object First : Sealed { + private 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 + } + + internal sealed class NonFirst { + public constructor NonFirst() + 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 + + internal object Fourth : Sealed { + private constructor Fourth() + 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 + } + + internal object Second : Sealed.NonFirst { + private 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 + } + + internal object Third : Sealed.NonFirst { + private constructor Third() + 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/ExhaustiveWhenOnNullable.kt b/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenOnNullable.kt new file mode 100644 index 00000000000..38c493ca263 --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenOnNullable.kt @@ -0,0 +1,17 @@ +sealed class Sealed(val x: Int) { + object First: Sealed(12) + open class NonFirst(x: Int, val y: Int): Sealed(x) { + object Second: NonFirst(34, 2) + object Third: NonFirst(56, 3) + } +} + +fun foo(s: Sealed?): Int { + return when(s) { + is Sealed.First -> 1 + is Sealed.NonFirst -> 0 + null -> -1 + // no else required + } +} + diff --git a/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenOnNullable.txt b/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenOnNullable.txt new file mode 100644 index 00000000000..256f59a31a1 --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenOnNullable.txt @@ -0,0 +1,46 @@ +package + +internal fun foo(/*0*/ s: Sealed?): kotlin.Int + +internal sealed class Sealed { + public constructor Sealed(/*0*/ x: kotlin.Int) + internal final val x: kotlin.Int + 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 + + internal object First : Sealed { + private constructor First() + internal final override /*1*/ /*fake_override*/ val x: kotlin.Int + 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 + } + + internal open class NonFirst : Sealed { + public constructor NonFirst(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Int) + internal final override /*1*/ /*fake_override*/ val x: kotlin.Int + internal final val y: kotlin.Int + 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 + + internal object Second : Sealed.NonFirst { + private constructor Second() + internal final override /*1*/ /*fake_override*/ val x: kotlin.Int + internal final override /*1*/ /*fake_override*/ val y: kotlin.Int + 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 + } + + internal object Third : Sealed.NonFirst { + private constructor Third() + internal final override /*1*/ /*fake_override*/ val x: kotlin.Int + internal final override /*1*/ /*fake_override*/ val y: kotlin.Int + 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/ExhaustiveWhenWithAdditionalMember.kt b/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenWithAdditionalMember.kt new file mode 100644 index 00000000000..1f12821e8ba --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenWithAdditionalMember.kt @@ -0,0 +1,18 @@ +sealed class Sealed(val x: Int) { + data class Tuple(val x: Int, val y: Int) + object First: Sealed(12) + open class NonFirst(tuple: Tuple): Sealed(tuple.x) { + val y: Int = tuple.y + object Second: NonFirst(Tuple(34, 2)) + object Third: NonFirst(Tuple(56, 3)) + } +} + +fun foo(s: Sealed): Int { + return when(s) { + is Sealed.First -> 1 + is Sealed.NonFirst -> 0 + // no else required + } +} + diff --git a/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenWithAdditionalMember.txt b/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenWithAdditionalMember.txt new file mode 100644 index 00000000000..7f0e2dbff4d --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenWithAdditionalMember.txt @@ -0,0 +1,58 @@ +package + +internal fun foo(/*0*/ s: Sealed): kotlin.Int + +internal sealed class Sealed { + public constructor Sealed(/*0*/ x: kotlin.Int) + internal final val x: kotlin.Int + 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 + + internal object First : Sealed { + private constructor First() + internal final override /*1*/ /*fake_override*/ val x: kotlin.Int + 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 + } + + internal open class NonFirst : Sealed { + public constructor NonFirst(/*0*/ tuple: Sealed.Tuple) + internal final override /*1*/ /*fake_override*/ val x: kotlin.Int + internal final val y: kotlin.Int + 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 + + internal object Second : Sealed.NonFirst { + private constructor Second() + internal final override /*1*/ /*fake_override*/ val x: kotlin.Int + internal final override /*1*/ /*fake_override*/ val y: kotlin.Int + 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 + } + + internal object Third : Sealed.NonFirst { + private constructor Third() + internal final override /*1*/ /*fake_override*/ val x: kotlin.Int + internal final override /*1*/ /*fake_override*/ val y: kotlin.Int + 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 + } + } + + kotlin.data() internal final class Tuple { + public constructor Tuple(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Int) + internal final val x: kotlin.Int + internal final val y: kotlin.Int + internal final /*synthesized*/ fun component1(): kotlin.Int + internal final /*synthesized*/ fun component2(): kotlin.Int + public final /*synthesized*/ fun copy(/*0*/ x: kotlin.Int = ..., /*1*/ y: kotlin.Int = ...): Sealed.Tuple + 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/ExhaustiveWhenWithElse.kt b/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenWithElse.kt new file mode 100644 index 00000000000..711f599a637 --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenWithElse.kt @@ -0,0 +1,15 @@ +sealed class Sealed(val x: Int) { + object First: Sealed(12) + open class NonFirst(x: Int, val y: Int): Sealed(x) { + object Second: NonFirst(34, 2) + object Third: NonFirst(56, 3) + } +} + +fun foo(s: Sealed): Int { + return when(s) { + is Sealed.NonFirst -> 0 + else -> -1 + } +} + diff --git a/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenWithElse.txt b/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenWithElse.txt new file mode 100644 index 00000000000..82857529aa1 --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenWithElse.txt @@ -0,0 +1,46 @@ +package + +internal fun foo(/*0*/ s: Sealed): kotlin.Int + +internal sealed class Sealed { + public constructor Sealed(/*0*/ x: kotlin.Int) + internal final val x: kotlin.Int + 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 + + internal object First : Sealed { + private constructor First() + internal final override /*1*/ /*fake_override*/ val x: kotlin.Int + 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 + } + + internal open class NonFirst : Sealed { + public constructor NonFirst(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Int) + internal final override /*1*/ /*fake_override*/ val x: kotlin.Int + internal final val y: kotlin.Int + 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 + + internal object Second : Sealed.NonFirst { + private constructor Second() + internal final override /*1*/ /*fake_override*/ val x: kotlin.Int + internal final override /*1*/ /*fake_override*/ val y: kotlin.Int + 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 + } + + internal object Third : Sealed.NonFirst { + private constructor Third() + internal final override /*1*/ /*fake_override*/ val x: kotlin.Int + internal final override /*1*/ /*fake_override*/ val y: kotlin.Int + 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/NonExhaustiveWhen.kt b/compiler/testData/diagnostics/tests/sealed/NonExhaustiveWhen.kt new file mode 100644 index 00000000000..439de1ee3fe --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/NonExhaustiveWhen.kt @@ -0,0 +1,14 @@ +sealed class Sealed(val x: Int) { + object First: Sealed(12) + open class NonFirst(x: Int, val y: Int): Sealed(x) { + object Second: NonFirst(34, 2) + object Third: NonFirst(56, 3) + } +} + +fun foo(s: Sealed): Int { + return when(s) { + is Sealed.NonFirst -> 0 + } +} + diff --git a/compiler/testData/diagnostics/tests/sealed/NonExhaustiveWhen.txt b/compiler/testData/diagnostics/tests/sealed/NonExhaustiveWhen.txt new file mode 100644 index 00000000000..82857529aa1 --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/NonExhaustiveWhen.txt @@ -0,0 +1,46 @@ +package + +internal fun foo(/*0*/ s: Sealed): kotlin.Int + +internal sealed class Sealed { + public constructor Sealed(/*0*/ x: kotlin.Int) + internal final val x: kotlin.Int + 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 + + internal object First : Sealed { + private constructor First() + internal final override /*1*/ /*fake_override*/ val x: kotlin.Int + 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 + } + + internal open class NonFirst : Sealed { + public constructor NonFirst(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Int) + internal final override /*1*/ /*fake_override*/ val x: kotlin.Int + internal final val y: kotlin.Int + 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 + + internal object Second : Sealed.NonFirst { + private constructor Second() + internal final override /*1*/ /*fake_override*/ val x: kotlin.Int + internal final override /*1*/ /*fake_override*/ val y: kotlin.Int + 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 + } + + internal object Third : Sealed.NonFirst { + private constructor Third() + internal final override /*1*/ /*fake_override*/ val x: kotlin.Int + internal final override /*1*/ /*fake_override*/ val y: kotlin.Int + 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/NonExhaustiveWhenNegated.kt b/compiler/testData/diagnostics/tests/sealed/NonExhaustiveWhenNegated.kt new file mode 100644 index 00000000000..0d02f2c6757 --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/NonExhaustiveWhenNegated.kt @@ -0,0 +1,14 @@ +sealed class Sealed(val x: Int) { + object First: Sealed(12) + open class NonFirst(x: Int, val y: Int): Sealed(x) { + object Second: NonFirst(34, 2) + object Third: NonFirst(56, 3) + } +} + +fun foo(s: Sealed): Int { + return when(s) { + !is Sealed.NonFirst -> 1 + } +} + diff --git a/compiler/testData/diagnostics/tests/sealed/NonExhaustiveWhenNegated.txt b/compiler/testData/diagnostics/tests/sealed/NonExhaustiveWhenNegated.txt new file mode 100644 index 00000000000..82857529aa1 --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/NonExhaustiveWhenNegated.txt @@ -0,0 +1,46 @@ +package + +internal fun foo(/*0*/ s: Sealed): kotlin.Int + +internal sealed class Sealed { + public constructor Sealed(/*0*/ x: kotlin.Int) + internal final val x: kotlin.Int + 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 + + internal object First : Sealed { + private constructor First() + internal final override /*1*/ /*fake_override*/ val x: kotlin.Int + 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 + } + + internal open class NonFirst : Sealed { + public constructor NonFirst(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Int) + internal final override /*1*/ /*fake_override*/ val x: kotlin.Int + internal final val y: kotlin.Int + 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 + + internal object Second : Sealed.NonFirst { + private constructor Second() + internal final override /*1*/ /*fake_override*/ val x: kotlin.Int + internal final override /*1*/ /*fake_override*/ val y: kotlin.Int + 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 + } + + internal object Third : Sealed.NonFirst { + private constructor Third() + internal final override /*1*/ /*fake_override*/ val x: kotlin.Int + internal final override /*1*/ /*fake_override*/ val y: kotlin.Int + 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/NonExhaustiveWhenWithAdditionalCase.kt b/compiler/testData/diagnostics/tests/sealed/NonExhaustiveWhenWithAdditionalCase.kt new file mode 100644 index 00000000000..cae5f68b039 --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/NonExhaustiveWhenWithAdditionalCase.kt @@ -0,0 +1,22 @@ +sealed class Sealed(val x: Int) { + interface ITuple { + val x: Int + val y: Int + } + class Tuple(override val x: Int, override val y: Int): ITuple + object First: Sealed(12) + open class NonFirst(tuple: Tuple): Sealed(tuple.x), ITuple { + override val y: Int = tuple.y + object Second: NonFirst(Tuple(34, 2)) + class Third: NonFirst(Tuple(56, 3)) + } +} + +fun foo(s: Sealed): Int { + return when(s) { + is Sealed.First -> 1 + !is Sealed.ITuple -> 0 + // else required + } +} + diff --git a/compiler/testData/diagnostics/tests/sealed/NonExhaustiveWhenWithAdditionalCase.txt b/compiler/testData/diagnostics/tests/sealed/NonExhaustiveWhenWithAdditionalCase.txt new file mode 100644 index 00000000000..9795e30d009 --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/NonExhaustiveWhenWithAdditionalCase.txt @@ -0,0 +1,63 @@ +package + +internal fun foo(/*0*/ s: Sealed): kotlin.Int + +internal sealed class Sealed { + public constructor Sealed(/*0*/ x: kotlin.Int) + internal final val x: kotlin.Int + 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 + + internal object First : Sealed { + private constructor First() + internal final override /*1*/ /*fake_override*/ val x: kotlin.Int + 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 + } + + internal interface ITuple { + internal abstract val x: kotlin.Int + internal abstract val y: kotlin.Int + 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 + } + + internal open class NonFirst : Sealed, Sealed.ITuple { + public constructor NonFirst(/*0*/ tuple: Sealed.Tuple) + internal final override /*2*/ /*fake_override*/ val x: kotlin.Int + internal open override /*1*/ val y: kotlin.Int + public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String + + internal object Second : Sealed.NonFirst { + private constructor Second() + internal final override /*1*/ /*fake_override*/ val x: kotlin.Int + internal open override /*1*/ /*fake_override*/ val y: kotlin.Int + 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 + } + + internal final class Third : Sealed.NonFirst { + public constructor Third() + internal final override /*1*/ /*fake_override*/ val x: kotlin.Int + internal open override /*1*/ /*fake_override*/ val y: kotlin.Int + 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 + } + } + + internal final class Tuple : Sealed.ITuple { + public constructor Tuple(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Int) + internal open override /*1*/ val x: kotlin.Int + internal open override /*1*/ val y: kotlin.Int + 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/NonExhaustiveWhenWithAnyCase.kt b/compiler/testData/diagnostics/tests/sealed/NonExhaustiveWhenWithAnyCase.kt new file mode 100644 index 00000000000..fc235670129 --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/NonExhaustiveWhenWithAnyCase.kt @@ -0,0 +1,15 @@ +sealed class Sealed { + object First: Sealed() + open class NonFirst: Sealed() { + object Second: NonFirst() + object Third: NonFirst() + } +} + +fun foo(s: Sealed): Int { + return when(s) { + is Sealed.First -> 1 + !is Any -> 0 + } +} + diff --git a/compiler/testData/diagnostics/tests/sealed/NonExhaustiveWhenWithAnyCase.txt b/compiler/testData/diagnostics/tests/sealed/NonExhaustiveWhenWithAnyCase.txt new file mode 100644 index 00000000000..2289d175d38 --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/NonExhaustiveWhenWithAnyCase.txt @@ -0,0 +1,38 @@ +package + +internal fun foo(/*0*/ s: Sealed): kotlin.Int + +internal sealed class Sealed { + public constructor Sealed() + 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 + + internal object First : Sealed { + private 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 + } + + internal open class NonFirst : Sealed { + public constructor NonFirst() + 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 + + internal object Second : Sealed.NonFirst { + private 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 + } + + internal object Third : Sealed.NonFirst { + private constructor Third() + 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/OperationWhen.kt b/compiler/testData/diagnostics/tests/sealed/OperationWhen.kt new file mode 100644 index 00000000000..c45fa8412f7 --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/OperationWhen.kt @@ -0,0 +1,20 @@ +sealed class Operation(val left: Int, val right: Int) { + abstract fun exec(): Int + class Plus(left: Int, right: Int): Operation(left, right) { + override fun exec(): Int = left + right + } + class Minus(left: Int, right: Int): Operation(left, right) { + override fun exec(): Int = left - right + } + class Times(left: Int, right: Int): Operation(left, right) { + override fun exec(): Int = left * right + } + class Slash(left: Int, right: Int): Operation(left, right) { + override fun exec(): Int = left / right + } +} + +fun priority(op: Operation) = when(op) { + is Operation.Plus, is Operation.Minus -> 1 + is Operation.Times, is Operation.Slash -> 2 +} diff --git a/compiler/testData/diagnostics/tests/sealed/OperationWhen.txt b/compiler/testData/diagnostics/tests/sealed/OperationWhen.txt new file mode 100644 index 00000000000..a6fefd2f7c6 --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/OperationWhen.txt @@ -0,0 +1,53 @@ +package + +internal fun priority(/*0*/ op: Operation): kotlin.Int + +internal sealed class Operation { + public constructor Operation(/*0*/ left: kotlin.Int, /*1*/ right: kotlin.Int) + internal final val left: kotlin.Int + internal final val right: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + internal abstract fun exec(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + internal final class Minus : Operation { + public constructor Minus(/*0*/ left: kotlin.Int, /*1*/ right: kotlin.Int) + internal final override /*1*/ /*fake_override*/ val left: kotlin.Int + internal final override /*1*/ /*fake_override*/ val right: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + internal open override /*1*/ fun exec(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + internal final class Plus : Operation { + public constructor Plus(/*0*/ left: kotlin.Int, /*1*/ right: kotlin.Int) + internal final override /*1*/ /*fake_override*/ val left: kotlin.Int + internal final override /*1*/ /*fake_override*/ val right: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + internal open override /*1*/ fun exec(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + internal final class Slash : Operation { + public constructor Slash(/*0*/ left: kotlin.Int, /*1*/ right: kotlin.Int) + internal final override /*1*/ /*fake_override*/ val left: kotlin.Int + internal final override /*1*/ /*fake_override*/ val right: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + internal open override /*1*/ fun exec(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + internal final class Times : Operation { + public constructor Times(/*0*/ left: kotlin.Int, /*1*/ right: kotlin.Int) + internal final override /*1*/ /*fake_override*/ val left: kotlin.Int + internal final override /*1*/ /*fake_override*/ val right: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + internal open override /*1*/ fun exec(): kotlin.Int + 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/TreeWhen.kt b/compiler/testData/diagnostics/tests/sealed/TreeWhen.kt new file mode 100644 index 00000000000..e27f7b50fda --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/TreeWhen.kt @@ -0,0 +1,13 @@ +sealed class Tree { + object Empty: Tree() + class Leaf(val x: Int): Tree() + class Node(val left: Tree, val right: Tree): Tree() + + fun max(): Int { + when(this) { + is Empty -> return -1 + is Leaf -> return this.x + is Node -> return this.left.max() + } + } +} diff --git a/compiler/testData/diagnostics/tests/sealed/TreeWhen.txt b/compiler/testData/diagnostics/tests/sealed/TreeWhen.txt new file mode 100644 index 00000000000..02f60cae367 --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/TreeWhen.txt @@ -0,0 +1,36 @@ +package + +internal sealed class Tree { + public constructor Tree() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + internal final fun max(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + internal object Empty : Tree { + private constructor Empty() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + internal final override /*1*/ /*fake_override*/ fun max(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + internal final class Leaf : Tree { + public constructor Leaf(/*0*/ x: kotlin.Int) + internal final val x: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + internal final override /*1*/ /*fake_override*/ fun max(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + internal final class Node : Tree { + public constructor Node(/*0*/ left: Tree, /*1*/ right: Tree) + internal final val left: Tree + internal final val right: Tree + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + internal final override /*1*/ /*fake_override*/ fun max(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } +} diff --git a/compiler/testData/diagnostics/tests/sealed/TreeWhenFunctional.kt b/compiler/testData/diagnostics/tests/sealed/TreeWhenFunctional.kt new file mode 100644 index 00000000000..42906a8a0e0 --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/TreeWhenFunctional.kt @@ -0,0 +1,11 @@ +sealed class Tree { + object Empty: Tree() + class Leaf(val x: Int): Tree() + class Node(val left: Tree, val right: Tree): Tree() + + fun max(): Int = when(this) { + is Empty -> -1 + is Leaf -> this.x + is Node -> this.left.max() + } +} diff --git a/compiler/testData/diagnostics/tests/sealed/TreeWhenFunctional.txt b/compiler/testData/diagnostics/tests/sealed/TreeWhenFunctional.txt new file mode 100644 index 00000000000..02f60cae367 --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/TreeWhenFunctional.txt @@ -0,0 +1,36 @@ +package + +internal sealed class Tree { + public constructor Tree() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + internal final fun max(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + internal object Empty : Tree { + private constructor Empty() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + internal final override /*1*/ /*fake_override*/ fun max(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + internal final class Leaf : Tree { + public constructor Leaf(/*0*/ x: kotlin.Int) + internal final val x: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + internal final override /*1*/ /*fake_override*/ fun max(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + internal final class Node : Tree { + public constructor Node(/*0*/ left: Tree, /*1*/ right: Tree) + internal final val left: Tree + internal final val right: Tree + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + internal final override /*1*/ /*fake_override*/ fun max(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } +} diff --git a/compiler/testData/diagnostics/tests/sealed/TreeWhenFunctionalNoIs.kt b/compiler/testData/diagnostics/tests/sealed/TreeWhenFunctionalNoIs.kt new file mode 100644 index 00000000000..24b303294cc --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/TreeWhenFunctionalNoIs.kt @@ -0,0 +1,23 @@ +sealed class Tree { + object Empty: Tree() + class Leaf(val x: Int): Tree() + class Node(val left: Tree, val right: Tree): Tree() + + fun max(): Int = when(this) { + Empty -> -1 + is Leaf -> this.x + is Node -> this.left.max() + } + + fun maxIsClass(): Int = when(this) { + Empty -> -1 + Leaf -> 0 + is Node -> this.left.max() + } + + fun maxWithElse(): Int = when(this) { + is Leaf -> this.x + is Node -> this.left.max() + else -> -1 + } +} diff --git a/compiler/testData/diagnostics/tests/sealed/TreeWhenFunctionalNoIs.txt b/compiler/testData/diagnostics/tests/sealed/TreeWhenFunctionalNoIs.txt new file mode 100644 index 00000000000..87e5e4f7c79 --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/TreeWhenFunctionalNoIs.txt @@ -0,0 +1,44 @@ +package + +internal sealed class Tree { + public constructor Tree() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + internal final fun max(): kotlin.Int + internal final fun maxIsClass(): kotlin.Int + internal final fun maxWithElse(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + internal object Empty : Tree { + private constructor Empty() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + internal final override /*1*/ /*fake_override*/ fun max(): kotlin.Int + internal final override /*1*/ /*fake_override*/ fun maxIsClass(): kotlin.Int + internal final override /*1*/ /*fake_override*/ fun maxWithElse(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + internal final class Leaf : Tree { + public constructor Leaf(/*0*/ x: kotlin.Int) + internal final val x: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + internal final override /*1*/ /*fake_override*/ fun max(): kotlin.Int + internal final override /*1*/ /*fake_override*/ fun maxIsClass(): kotlin.Int + internal final override /*1*/ /*fake_override*/ fun maxWithElse(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + internal final class Node : Tree { + public constructor Node(/*0*/ left: Tree, /*1*/ right: Tree) + internal final val left: Tree + internal final val right: Tree + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + internal final override /*1*/ /*fake_override*/ fun max(): kotlin.Int + internal final override /*1*/ /*fake_override*/ fun maxIsClass(): kotlin.Int + internal final override /*1*/ /*fake_override*/ fun maxWithElse(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } +} diff --git a/compiler/testData/diagnostics/tests/sealed/WhenOnEmptySealed.kt b/compiler/testData/diagnostics/tests/sealed/WhenOnEmptySealed.kt new file mode 100644 index 00000000000..ea45861d7cd --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/WhenOnEmptySealed.kt @@ -0,0 +1,11 @@ +// !DIAGNOSTICS: -UNUSED_EXPRESSION +sealed class Sealed { + +} + +fun foo(s: Sealed): Int { + return when(s) { + // We do not return anything, so else branch must be here + } +} + diff --git a/compiler/testData/diagnostics/tests/sealed/WhenOnEmptySealed.txt b/compiler/testData/diagnostics/tests/sealed/WhenOnEmptySealed.txt new file mode 100644 index 00000000000..112b361bd17 --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/WhenOnEmptySealed.txt @@ -0,0 +1,10 @@ +package + +internal fun foo(/*0*/ s: Sealed): kotlin.Int + +internal sealed class Sealed { + public constructor Sealed() + 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/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java index 9640b8a8b48..7d0ce4a3108 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java @@ -11439,6 +11439,60 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } + @TestMetadata("ExhaustiveWhen.kt") + public void testExhaustiveWhen() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/sealed/ExhaustiveWhen.kt"); + doTest(fileName); + } + + @TestMetadata("ExhaustiveWhenDoubleInner.kt") + public void testExhaustiveWhenDoubleInner() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenDoubleInner.kt"); + doTest(fileName); + } + + @TestMetadata("ExhaustiveWhenMultipleInner.kt") + public void testExhaustiveWhenMultipleInner() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenMultipleInner.kt"); + doTest(fileName); + } + + @TestMetadata("ExhaustiveWhenNegated.kt") + public void testExhaustiveWhenNegated() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenNegated.kt"); + doTest(fileName); + } + + @TestMetadata("ExhaustiveWhenNegatedTwice.kt") + public void testExhaustiveWhenNegatedTwice() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenNegatedTwice.kt"); + doTest(fileName); + } + + @TestMetadata("ExhaustiveWhenOnNestedSealed.kt") + public void testExhaustiveWhenOnNestedSealed() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenOnNestedSealed.kt"); + doTest(fileName); + } + + @TestMetadata("ExhaustiveWhenOnNullable.kt") + public void testExhaustiveWhenOnNullable() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenOnNullable.kt"); + doTest(fileName); + } + + @TestMetadata("ExhaustiveWhenWithAdditionalMember.kt") + public void testExhaustiveWhenWithAdditionalMember() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenWithAdditionalMember.kt"); + doTest(fileName); + } + + @TestMetadata("ExhaustiveWhenWithElse.kt") + public void testExhaustiveWhenWithElse() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenWithElse.kt"); + doTest(fileName); + } + @TestMetadata("Local.kt") public void testLocal() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/sealed/Local.kt"); @@ -11487,11 +11541,65 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } + @TestMetadata("NonExhaustiveWhen.kt") + public void testNonExhaustiveWhen() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/sealed/NonExhaustiveWhen.kt"); + doTest(fileName); + } + + @TestMetadata("NonExhaustiveWhenNegated.kt") + public void testNonExhaustiveWhenNegated() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/sealed/NonExhaustiveWhenNegated.kt"); + doTest(fileName); + } + + @TestMetadata("NonExhaustiveWhenWithAdditionalCase.kt") + public void testNonExhaustiveWhenWithAdditionalCase() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/sealed/NonExhaustiveWhenWithAdditionalCase.kt"); + doTest(fileName); + } + + @TestMetadata("NonExhaustiveWhenWithAnyCase.kt") + public void testNonExhaustiveWhenWithAnyCase() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/sealed/NonExhaustiveWhenWithAnyCase.kt"); + doTest(fileName); + } + + @TestMetadata("OperationWhen.kt") + public void testOperationWhen() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/sealed/OperationWhen.kt"); + doTest(fileName); + } + @TestMetadata("RedundantAbstract.kt") public void testRedundantAbstract() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/sealed/RedundantAbstract.kt"); doTest(fileName); } + + @TestMetadata("TreeWhen.kt") + public void testTreeWhen() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/sealed/TreeWhen.kt"); + doTest(fileName); + } + + @TestMetadata("TreeWhenFunctional.kt") + public void testTreeWhenFunctional() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/sealed/TreeWhenFunctional.kt"); + doTest(fileName); + } + + @TestMetadata("TreeWhenFunctionalNoIs.kt") + public void testTreeWhenFunctionalNoIs() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/sealed/TreeWhenFunctionalNoIs.kt"); + doTest(fileName); + } + + @TestMetadata("WhenOnEmptySealed.kt") + public void testWhenOnEmptySealed() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/sealed/WhenOnEmptySealed.kt"); + doTest(fileName); + } } @TestMetadata("compiler/testData/diagnostics/tests/secondaryConstructors") diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java index cde01bc8dcd..da0784646de 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java @@ -234,21 +234,35 @@ public class DescriptorUtils { return false; } + public static boolean isDirectSubclass(@NotNull ClassDescriptor subClass, @NotNull ClassDescriptor superClass) { + for (JetType superType : subClass.getTypeConstructor().getSupertypes()) { + if (isSameClass(superType, superClass.getOriginal())) { + return true; + } + } + return false; + } + public static boolean isSubclass(@NotNull ClassDescriptor subClass, @NotNull ClassDescriptor superClass) { return isSubtypeOfClass(subClass.getDefaultType(), superClass.getOriginal()); } - private static boolean isSubtypeOfClass(@NotNull JetType type, @NotNull DeclarationDescriptor superClass) { + private static boolean isSameClass(@NotNull JetType type, @NotNull DeclarationDescriptor other) { DeclarationDescriptor descriptor = type.getConstructor().getDeclarationDescriptor(); if (descriptor != null) { DeclarationDescriptor originalDescriptor = descriptor.getOriginal(); if (originalDescriptor instanceof ClassifierDescriptor - && superClass instanceof ClassifierDescriptor - && ((ClassifierDescriptor) superClass).getTypeConstructor().equals(((ClassifierDescriptor) originalDescriptor).getTypeConstructor())) { + && other instanceof ClassifierDescriptor + && ((ClassifierDescriptor) other).getTypeConstructor().equals( + ((ClassifierDescriptor) originalDescriptor).getTypeConstructor())) { return true; } } + return false; + } + private static boolean isSubtypeOfClass(@NotNull JetType type, @NotNull DeclarationDescriptor superClass) { + if (isSameClass(type, superClass)) return true; for (JetType superType : type.getConstructor().getSupertypes()) { if (isSubtypeOfClass(superType, superClass)) { return true; @@ -446,7 +460,9 @@ public class DescriptorUtils { } public static boolean classCanHaveAbstractMembers(@NotNull ClassDescriptor classDescriptor) { - return classDescriptor.getModality() == Modality.ABSTRACT || classDescriptor.getKind() == ClassKind.ENUM_CLASS; + return classDescriptor.getModality() == Modality.ABSTRACT + || classDescriptor.getModality() == Modality.SEALED + || classDescriptor.getKind() == ClassKind.ENUM_CLASS; } public static boolean classCanHaveOpenMembers(@NotNull ClassDescriptor classDescriptor) {