[FE] Make whens on expect sealed classes and enums not exhaustive

This commit is contained in:
Dmitriy Novozhilov
2021-02-24 16:44:48 +03:00
parent 1cf73203c7
commit 4222bb9af2
29 changed files with 768 additions and 11 deletions
@@ -17989,6 +17989,34 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
}
}
@Nested
@TestMetadata("compiler/testData/diagnostics/tests/multiplatform/exhaustiveness")
@TestDataPath("$PROJECT_ROOT")
public class Exhaustiveness {
@Test
public void testAllFilesPresentInExhaustiveness() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/multiplatform/exhaustiveness"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
}
@Test
@TestMetadata("expectEnum.kt")
public void testExpectEnum() throws Exception {
runTest("compiler/testData/diagnostics/tests/multiplatform/exhaustiveness/expectEnum.kt");
}
@Test
@TestMetadata("expectSealedClass.kt")
public void testExpectSealedClass() throws Exception {
runTest("compiler/testData/diagnostics/tests/multiplatform/exhaustiveness/expectSealedClass.kt");
}
@Test
@TestMetadata("expectSealedInterface.kt")
public void testExpectSealedInterface() throws Exception {
runTest("compiler/testData/diagnostics/tests/multiplatform/exhaustiveness/expectSealedInterface.kt");
}
}
@Nested
@TestMetadata("compiler/testData/diagnostics/tests/multiplatform/generic")
@TestDataPath("$PROJECT_ROOT")
@@ -24594,6 +24594,34 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/multiplatform/exhaustiveness")
@TestDataPath("$PROJECT_ROOT")
public class Exhaustiveness {
@Test
public void testAllFilesPresentInExhaustiveness() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/multiplatform/exhaustiveness"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@Test
@TestMetadata("commonEnum.kt")
public void testCommonEnum() throws Exception {
runTest("compiler/testData/codegen/box/multiplatform/exhaustiveness/commonEnum.kt");
}
@Test
@TestMetadata("commonSealedClass.kt")
public void testCommonSealedClass() throws Exception {
runTest("compiler/testData/codegen/box/multiplatform/exhaustiveness/commonSealedClass.kt");
}
@Test
@TestMetadata("commonSealedInterface.kt")
public void testCommonSealedInterface() throws Exception {
runTest("compiler/testData/codegen/box/multiplatform/exhaustiveness/commonSealedInterface.kt");
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/multiplatform/multiModule")
@TestDataPath("$PROJECT_ROOT")
@@ -17,6 +17,16 @@ sealed class WhenMissingCase {
override val branchConditionText: String = "else"
}
sealed class ConditionTypeIsExpect(val typeOfDeclaration: String) : WhenMissingCase() {
object SealedClass : ConditionTypeIsExpect("sealed class")
object SealedInterface : ConditionTypeIsExpect("sealed interface")
object Enum : ConditionTypeIsExpect("enum")
override val branchConditionText: String = "else"
override fun toString(): String = "unknown"
}
object NullIsMissing : WhenMissingCase() {
override val branchConditionText: String = "null"
}
@@ -51,4 +61,4 @@ sealed class WhenMissingCase {
override fun toString(): String {
return branchConditionText
}
}
}
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.diagnostics.Errors.*
import org.jetbrains.kotlin.diagnostics.WhenMissingCase
import org.jetbrains.kotlin.idea.MainFunctionDetector
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
@@ -1003,6 +1004,10 @@ class ControlFlowInformationProviderImpl private constructor(
if (usedAsExpression && missingCases.isNotEmpty()) {
if (elseEntry != null) continue
trace.report(NO_ELSE_IN_WHEN.on(element, missingCases))
missingCases.firstOrNull { it is WhenMissingCase.ConditionTypeIsExpect }?.let {
require(it is WhenMissingCase.ConditionTypeIsExpect)
trace.report(EXPECT_TYPE_IN_WHEN_WITHOUT_ELSE.on(element, it.typeOfDeclaration))
}
} else if (subjectExpression != null) {
val subjectType = trace.getType(subjectExpression)
if (elseEntry != null) {
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.cfg
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.cfg.WhenOnEnumExhaustivenessChecker.enumEntries
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
@@ -40,11 +41,13 @@ import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluat
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.addToStdlib.runIf
import java.util.*
val List<WhenMissingCase>.hasUnknown: Boolean
get() = firstOrNull() == WhenMissingCase.Unknown
get() = any { it == WhenMissingCase.Unknown || it is WhenMissingCase.ConditionTypeIsExpect }
private interface WhenExhaustivenessChecker {
fun getMissingCases(
@@ -57,6 +60,21 @@ private interface WhenExhaustivenessChecker {
fun isApplicable(subjectType: KotlinType): Boolean = false
}
// It's not a regular exhaustiveness checker, invoke it only inside other checkers
private object WhenOnExpectExhaustivenessChecker {
fun getMissingCase(subjectDescriptor: ClassDescriptor?): WhenMissingCase? {
return runIf(subjectDescriptor?.isExpect == true) {
when (subjectDescriptor!!.kind) {
ClassKind.CLASS -> WhenMissingCase.ConditionTypeIsExpect.SealedClass
ClassKind.INTERFACE -> WhenMissingCase.ConditionTypeIsExpect.SealedInterface
ClassKind.ENUM_CLASS -> WhenMissingCase.ConditionTypeIsExpect.Enum
else -> WhenMissingCase.Unknown
}
}
}
}
// It's not a regular exhaustiveness checker, invoke it only inside other checkers
private object WhenOnNullableExhaustivenessChecker /* : WhenExhaustivenessChecker*/ {
fun getMissingCases(expression: KtWhenExpression, context: BindingContext, nullable: Boolean) =
@@ -209,6 +227,7 @@ internal abstract class WhenOnClassExhaustivenessChecker : WhenExhaustivenessChe
}
private object WhenOnEnumExhaustivenessChecker : WhenOnClassExhaustivenessChecker() {
@OptIn(ExperimentalStdlibApi::class)
override fun getMissingCases(
expression: KtWhenExpression,
context: BindingContext,
@@ -216,8 +235,11 @@ private object WhenOnEnumExhaustivenessChecker : WhenOnClassExhaustivenessChecke
nullable: Boolean
): List<WhenMissingCase> {
assert(isEnumClass(subjectDescriptor)) { "isWhenOnEnumExhaustive should be called with an enum class descriptor" }
return getMissingClassCases(expression, subjectDescriptor!!.enumEntries, context) +
WhenOnNullableExhaustivenessChecker.getMissingCases(expression, context, nullable)
return buildList {
addAll(getMissingClassCases(expression, subjectDescriptor!!.enumEntries, context))
addAll(WhenOnNullableExhaustivenessChecker.getMissingCases(expression, context, nullable))
addIfNotNull(WhenOnExpectExhaustivenessChecker.getMissingCase(subjectDescriptor))
}
}
override fun isApplicable(subjectType: KotlinType): Boolean {
@@ -226,7 +248,7 @@ private object WhenOnEnumExhaustivenessChecker : WhenOnClassExhaustivenessChecke
}
internal object WhenOnSealedExhaustivenessChecker : WhenOnClassExhaustivenessChecker() {
@OptIn(ExperimentalStdlibApi::class)
override fun getMissingCases(
expression: KtWhenExpression,
context: BindingContext,
@@ -238,8 +260,11 @@ internal object WhenOnSealedExhaustivenessChecker : WhenOnClassExhaustivenessChe
}
val allSubclasses = subjectDescriptor!!.deepSealedSubclasses
return getMissingClassCases(expression, allSubclasses.toSet(), context) +
WhenOnNullableExhaustivenessChecker.getMissingCases(expression, context, nullable)
return buildList {
addAll(getMissingClassCases(expression, allSubclasses.toSet(), context))
addAll(WhenOnNullableExhaustivenessChecker.getMissingCases(expression, context, nullable))
addIfNotNull(WhenOnExpectExhaustivenessChecker.getMissingCase(subjectDescriptor))
}
}
override fun isApplicable(subjectType: KotlinType): Boolean {
@@ -1057,6 +1057,7 @@ public interface Errors {
DiagnosticFactory1<KtWhenExpression, List<WhenMissingCase>> NON_EXHAUSTIVE_WHEN = DiagnosticFactory1.create(WARNING, WHEN_EXPRESSION);
DiagnosticFactory1<KtWhenExpression, List<WhenMissingCase>>
NON_EXHAUSTIVE_WHEN_ON_SEALED_CLASS = DiagnosticFactory1.create(INFO, WHEN_EXPRESSION);
DiagnosticFactory1<KtWhenExpression, String> EXPECT_TYPE_IN_WHEN_WITHOUT_ELSE = DiagnosticFactory1.create(ERROR, WHEN_EXPRESSION);
DiagnosticFactory0<PsiElement> COMMA_IN_WHEN_CONDITION_WITHOUT_ARGUMENT = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> DUPLICATE_LABEL_IN_WHEN = DiagnosticFactory0.create(WARNING);
@@ -610,6 +610,7 @@ public class DefaultErrorMessages {
MAP.put(NO_ELSE_IN_WHEN, "''when'' expression must be exhaustive, add necessary {0}", RENDER_WHEN_MISSING_CASES);
MAP.put(NON_EXHAUSTIVE_WHEN, "''when'' expression on enum is recommended to be exhaustive, add {0}", RENDER_WHEN_MISSING_CASES);
MAP.put(NON_EXHAUSTIVE_WHEN_ON_SEALED_CLASS, "''when'' expression on sealed classes is recommended to be exhaustive, add {0}", RENDER_WHEN_MISSING_CASES);
MAP.put(EXPECT_TYPE_IN_WHEN_WITHOUT_ELSE, "'when' with expect {0} as subject can not be exhaustive without else branch", STRING);
MAP.put(TYPE_MISMATCH_IN_RANGE, "Type mismatch: incompatible types of range and element checked in it");
MAP.put(CYCLIC_INHERITANCE_HIERARCHY, "There's a cycle in the inheritance hierarchy for this type");
@@ -0,0 +1,29 @@
// IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM
// !LANGUAGE: +MultiPlatformProjects
// ISSUE: KT-20306
// MODULE: m1-common
// FILE: common.kt
enum class Base {
A, B
}
fun testCommon(base: Base) {
val x = when (base) { // must be Ok
Base.A -> 1
Base.B -> 2
}
}
// MODULE: m1-jvm(m1-common)
// FILE: main.kt
fun testPlatform(base: Base) {
val x = when (base) { // must be OK
Base.A -> 1
Base.B -> 2
}
}
fun box() = "OK"
@@ -0,0 +1,30 @@
// IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM
// !LANGUAGE: +MultiPlatformProjects
// ISSUE: KT-44474
// MODULE: m1-common
// FILE: common.kt
sealed class Base()
class A : Base()
object B : Base()
fun testCommon(base: Base) {
val x = when (base) { // must be Ok
is A -> 1
B -> 2
}
}
// MODULE: m1-jvm(m1-common)
// FILE: main.kt
fun testPlatform(base: Base) {
val x = when (base) { // must be OK
is A -> 1
B -> 2
}
}
fun box() = "OK"
@@ -0,0 +1,30 @@
// IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM
// !LANGUAGE: +MultiPlatformProjects
// ISSUE: KT-44474
// MODULE: m1-common
// FILE: common.kt
sealed interface Base
interface A : Base
object B : Base
fun testCommon(base: Base) {
val x = when (base) { // must be Ok
is A -> 1
B -> 2
}
}
// MODULE: m1-jvm(m1-common)
// FILE: main.kt
fun testPlatform(base: Base) {
val x = when (base) { // must be OK
is A -> 1
B -> 2
}
}
fun box() = "OK"
@@ -0,0 +1,37 @@
// !LANGUAGE: +MultiPlatformProjects
// !DIAGNOSTICS: -UNUSED_VARIABLE
// ISSUE: KT-20306
// MODULE: m1-common
// FILE: common.kt
expect enum class Base {
A, B
}
fun testCommon(base: Base) {
val x = when (base) { // must be an error
Base.A -> 1
Base.B -> 2
}
}
// MODULE: m1-jvm(m1-common)
// FILE: Base.kt
actual enum class Base {
A, B, C
}
fun testPlatformGood(base: Base) {
val x = when (base) { // must be OK
Base.A -> 1
Base.B -> 2
Base.C -> 3
}
}
fun testPlatformBad(base: Base) {
val x = <!NO_ELSE_IN_WHEN!>when<!> (base) { // must be an error
Base.A -> 1
Base.B -> 2
}
}
@@ -0,0 +1,37 @@
// !LANGUAGE: +MultiPlatformProjects
// !DIAGNOSTICS: -UNUSED_VARIABLE
// ISSUE: KT-20306
// MODULE: m1-common
// FILE: common.kt
expect enum class Base {
A, B
}
fun testCommon(base: Base) {
val x = <!EXPECT_TYPE_IN_WHEN_WITHOUT_ELSE("enum"), NO_ELSE_IN_WHEN("'else' branch"), NO_ELSE_IN_WHEN{JVM}("'C' branch or 'else' branch instead")!>when<!> (base) { // must be an error
Base.A -> 1
Base.B -> 2
}
}
// MODULE: m1-jvm(m1-common)
// FILE: Base.kt
actual enum class Base {
A, B, C
}
fun testPlatformGood(base: Base) {
val x = when (base) { // must be OK
Base.A -> 1
Base.B -> 2
Base.C -> 3
}
}
fun testPlatformBad(base: Base) {
val x = <!NO_ELSE_IN_WHEN!>when<!> (base) { // must be an error
Base.A -> 1
Base.B -> 2
}
}
@@ -0,0 +1,52 @@
// -- Module: <m1-common> --
package
public fun testCommon(/*0*/ base: Base): kotlin.Unit
public final expect enum class Base : kotlin.Enum<Base> {
expect enum entry A
expect enum entry B
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: Base): kotlin.Int
public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
// Static members
public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): Base
public final /*synthesized*/ fun values(): kotlin.Array<Base>
}
// -- Module: <m1-jvm> --
package
public fun testCommon(/*0*/ base: Base): kotlin.Unit
public fun testPlatformBad(/*0*/ base: Base): kotlin.Unit
public fun testPlatformGood(/*0*/ base: Base): kotlin.Unit
public final actual enum class Base : kotlin.Enum<Base> {
enum entry A
enum entry B
enum entry C
private constructor Base()
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: Base): kotlin.Int
public final override /*1*/ /*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<Base!>!
public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
// Static members
public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): Base
public final /*synthesized*/ fun values(): kotlin.Array<Base>
}
@@ -0,0 +1,40 @@
// !LANGUAGE: +MultiPlatformProjects
// !DIAGNOSTICS: -UNUSED_VARIABLE
// ISSUE: KT-44474
// MODULE: m1-common
// FILE: common.kt
expect sealed class Base()
class A : Base()
object B : Base()
fun testCommon(base: Base) {
val x = when (base) { // must be an error
is A -> 1
B -> 2
}
}
// MODULE: m1-jvm(m1-common)
// FILE: Base.kt
actual sealed class Base
// FILE: C.kt
class C : Base()
fun testPlatformGood(base: Base) {
val x = when (base) { // must be OK
is A -> 1
B -> 2
is C -> 3
}
}
fun testPlatformBad(base: Base) {
val x = <!NO_ELSE_IN_WHEN!>when<!> (base) { // must be an error
is A -> 1
B -> 2
}
}
@@ -0,0 +1,40 @@
// !LANGUAGE: +MultiPlatformProjects
// !DIAGNOSTICS: -UNUSED_VARIABLE
// ISSUE: KT-44474
// MODULE: m1-common
// FILE: common.kt
expect sealed class Base()
class A : Base()
object B : Base()
fun testCommon(base: Base) {
val x = <!EXPECT_TYPE_IN_WHEN_WITHOUT_ELSE("sealed class"), NO_ELSE_IN_WHEN("'else' branch"), NO_ELSE_IN_WHEN{JVM}("'is C' branch or 'else' branch instead")!>when<!> (base) { // must be an error
is A -> 1
B -> 2
}
}
// MODULE: m1-jvm(m1-common)
// FILE: Base.kt
actual sealed class Base
// FILE: C.kt
class C : Base()
fun testPlatformGood(base: Base) {
val x = when (base) { // must be OK
is A -> 1
B -> 2
is C -> 3
}
}
fun testPlatformBad(base: Base) {
val x = <!NO_ELSE_IN_WHEN!>when<!> (base) { // must be an error
is A -> 1
B -> 2
}
}
@@ -0,0 +1,60 @@
// -- Module: <m1-common> --
package
public fun testCommon(/*0*/ base: Base): kotlin.Unit
public final class A : Base {
public constructor A()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public object B : Base {
private constructor B()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public sealed expect class Base {
protected constructor Base()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
// -- Module: <m1-jvm> --
package
public fun testCommon(/*0*/ base: Base): kotlin.Unit
public fun testPlatformBad(/*0*/ base: Base): kotlin.Unit
public fun testPlatformGood(/*0*/ base: Base): kotlin.Unit
public final class A : Base {
public constructor A()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public object B : Base {
private constructor B()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public sealed actual class Base {
protected constructor Base()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final class C : Base {
public constructor C()
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,40 @@
// !LANGUAGE: +MultiPlatformProjects
// !DIAGNOSTICS: -UNUSED_VARIABLE
// ISSUE: KT-44474
// MODULE: m1-common
// FILE: common.kt
expect sealed interface Base
class A : Base
object B : Base
fun testCommon(base: Base) {
val x = when (base) { // must be an error
is A -> 1
B -> 2
}
}
// MODULE: m1-jvm(m1-common)
// FILE: Base.kt
actual sealed interface Base
// FILE: C.kt
interface C : Base
fun testPlatformGood(base: Base) {
val x = when (base) { // must be OK
is A -> 1
B -> 2
is C -> 3
}
}
fun testPlatformBad(base: Base) {
val x = <!NO_ELSE_IN_WHEN!>when<!> (base) { // must be an error
is A -> 1
B -> 2
}
}
@@ -0,0 +1,40 @@
// !LANGUAGE: +MultiPlatformProjects
// !DIAGNOSTICS: -UNUSED_VARIABLE
// ISSUE: KT-44474
// MODULE: m1-common
// FILE: common.kt
expect sealed interface Base
class A : Base
object B : Base
fun testCommon(base: Base) {
val x = <!EXPECT_TYPE_IN_WHEN_WITHOUT_ELSE("sealed interface"), NO_ELSE_IN_WHEN("'else' branch"), NO_ELSE_IN_WHEN{JVM}("'is C' branch or 'else' branch instead")!>when<!> (base) { // must be an error
is A -> 1
B -> 2
}
}
// MODULE: m1-jvm(m1-common)
// FILE: Base.kt
actual sealed interface Base
// FILE: C.kt
interface C : Base
fun testPlatformGood(base: Base) {
val x = when (base) { // must be OK
is A -> 1
B -> 2
is C -> 3
}
}
fun testPlatformBad(base: Base) {
val x = <!NO_ELSE_IN_WHEN!>when<!> (base) { // must be an error
is A -> 1
B -> 2
}
}
@@ -0,0 +1,58 @@
// -- Module: <m1-common> --
package
public fun testCommon(/*0*/ base: Base): kotlin.Unit
public final class A : Base {
public constructor A()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public object B : Base {
private constructor B()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public sealed expect 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
}
// -- Module: <m1-jvm> --
package
public fun testCommon(/*0*/ base: Base): kotlin.Unit
public fun testPlatformBad(/*0*/ base: Base): kotlin.Unit
public fun testPlatformGood(/*0*/ base: Base): kotlin.Unit
public final class A : Base {
public constructor A()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public object B : Base {
private constructor B()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public sealed actual 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 interface C : 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
}
@@ -17995,6 +17995,34 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
}
}
@Nested
@TestMetadata("compiler/testData/diagnostics/tests/multiplatform/exhaustiveness")
@TestDataPath("$PROJECT_ROOT")
public class Exhaustiveness {
@Test
public void testAllFilesPresentInExhaustiveness() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/multiplatform/exhaustiveness"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
}
@Test
@TestMetadata("expectEnum.kt")
public void testExpectEnum() throws Exception {
runTest("compiler/testData/diagnostics/tests/multiplatform/exhaustiveness/expectEnum.kt");
}
@Test
@TestMetadata("expectSealedClass.kt")
public void testExpectSealedClass() throws Exception {
runTest("compiler/testData/diagnostics/tests/multiplatform/exhaustiveness/expectSealedClass.kt");
}
@Test
@TestMetadata("expectSealedInterface.kt")
public void testExpectSealedInterface() throws Exception {
runTest("compiler/testData/diagnostics/tests/multiplatform/exhaustiveness/expectSealedInterface.kt");
}
}
@Nested
@TestMetadata("compiler/testData/diagnostics/tests/multiplatform/generic")
@TestDataPath("$PROJECT_ROOT")
@@ -24594,6 +24594,34 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/multiplatform/exhaustiveness")
@TestDataPath("$PROJECT_ROOT")
public class Exhaustiveness {
@Test
public void testAllFilesPresentInExhaustiveness() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/multiplatform/exhaustiveness"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
}
@Test
@TestMetadata("commonEnum.kt")
public void testCommonEnum() throws Exception {
runTest("compiler/testData/codegen/box/multiplatform/exhaustiveness/commonEnum.kt");
}
@Test
@TestMetadata("commonSealedClass.kt")
public void testCommonSealedClass() throws Exception {
runTest("compiler/testData/codegen/box/multiplatform/exhaustiveness/commonSealedClass.kt");
}
@Test
@TestMetadata("commonSealedInterface.kt")
public void testCommonSealedInterface() throws Exception {
runTest("compiler/testData/codegen/box/multiplatform/exhaustiveness/commonSealedInterface.kt");
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/multiplatform/multiModule")
@TestDataPath("$PROJECT_ROOT")
@@ -24594,6 +24594,34 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/multiplatform/exhaustiveness")
@TestDataPath("$PROJECT_ROOT")
public class Exhaustiveness {
@Test
public void testAllFilesPresentInExhaustiveness() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/multiplatform/exhaustiveness"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@Test
@TestMetadata("commonEnum.kt")
public void testCommonEnum() throws Exception {
runTest("compiler/testData/codegen/box/multiplatform/exhaustiveness/commonEnum.kt");
}
@Test
@TestMetadata("commonSealedClass.kt")
public void testCommonSealedClass() throws Exception {
runTest("compiler/testData/codegen/box/multiplatform/exhaustiveness/commonSealedClass.kt");
}
@Test
@TestMetadata("commonSealedInterface.kt")
public void testCommonSealedInterface() throws Exception {
runTest("compiler/testData/codegen/box/multiplatform/exhaustiveness/commonSealedInterface.kt");
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/multiplatform/multiModule")
@TestDataPath("$PROJECT_ROOT")
@@ -20877,6 +20877,34 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
}
}
@TestMetadata("compiler/testData/codegen/box/multiplatform/exhaustiveness")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Exhaustiveness extends AbstractLightAnalysisModeTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath);
}
public void testAllFilesPresentInExhaustiveness() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/multiplatform/exhaustiveness"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
}
@TestMetadata("commonEnum.kt")
public void testCommonEnum() throws Exception {
runTest("compiler/testData/codegen/box/multiplatform/exhaustiveness/commonEnum.kt");
}
@TestMetadata("commonSealedClass.kt")
public void testCommonSealedClass() throws Exception {
runTest("compiler/testData/codegen/box/multiplatform/exhaustiveness/commonSealedClass.kt");
}
@TestMetadata("commonSealedInterface.kt")
public void testCommonSealedInterface() throws Exception {
runTest("compiler/testData/codegen/box/multiplatform/exhaustiveness/commonSealedInterface.kt");
}
}
@TestMetadata("compiler/testData/codegen/box/multiplatform/multiModule")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -90,8 +90,10 @@ class AddWhenRemainingBranchesFix(
(whenCloseBrace.prevSibling as? PsiWhiteSpace)?.replace(psiFactory.createNewLine())
for (case in missingCases) {
val branchConditionText = when (case) {
WhenMissingCase.Unknown, WhenMissingCase.NullIsMissing, is WhenMissingCase.BooleanIsMissing ->
case.branchConditionText
WhenMissingCase.Unknown,
WhenMissingCase.NullIsMissing,
is WhenMissingCase.BooleanIsMissing,
is WhenMissingCase.ConditionTypeIsExpect -> case.branchConditionText
is WhenMissingCase.IsTypeCheckIsMissing ->
if (case.isSingleton) {
""
@@ -126,4 +128,4 @@ class AddWhenRemainingBranchesFix(
}
}
}
}
}
@@ -2,6 +2,6 @@ expect sealed class <!LINE_MARKER("descr='Is subclassed by CommonAImplTestClass
class CommonImplTestClass: TestClass()
fun checkCommon(t: TestClass): Int = when (t) {
fun checkCommon(t: TestClass): Int = <!EXPECT_TYPE_IN_WHEN_WITHOUT_ELSE, NO_ELSE_IN_WHEN!>when<!> (t) {
is CommonImplTestClass -> 0
}
@@ -16457,6 +16457,19 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes
}
}
@TestMetadata("compiler/testData/codegen/box/multiplatform/exhaustiveness")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Exhaustiveness extends AbstractIrJsCodegenBoxES6Test {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR_ES6, testDataFilePath);
}
public void testAllFilesPresentInExhaustiveness() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/multiplatform/exhaustiveness"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR_ES6, true);
}
}
@TestMetadata("compiler/testData/codegen/box/multiplatform/multiModule")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -15942,6 +15942,19 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
}
}
@TestMetadata("compiler/testData/codegen/box/multiplatform/exhaustiveness")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Exhaustiveness extends AbstractIrJsCodegenBoxTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR, testDataFilePath);
}
public void testAllFilesPresentInExhaustiveness() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/multiplatform/exhaustiveness"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true);
}
}
@TestMetadata("compiler/testData/codegen/box/multiplatform/multiModule")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -16007,6 +16007,19 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
}
}
@TestMetadata("compiler/testData/codegen/box/multiplatform/exhaustiveness")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Exhaustiveness extends AbstractJsCodegenBoxTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS, testDataFilePath);
}
public void testAllFilesPresentInExhaustiveness() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/multiplatform/exhaustiveness"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS, true);
}
}
@TestMetadata("compiler/testData/codegen/box/multiplatform/multiModule")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -9833,6 +9833,19 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
}
}
@TestMetadata("compiler/testData/codegen/box/multiplatform/exhaustiveness")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Exhaustiveness extends AbstractIrCodegenBoxWasmTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest0(this::doTest, TargetBackend.WASM, testDataFilePath);
}
public void testAllFilesPresentInExhaustiveness() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/multiplatform/exhaustiveness"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.WASM, true);
}
}
@TestMetadata("compiler/testData/codegen/box/multiplatform/multiModule")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)