[FIR] Implement checker for exhaustive when's in expression position

This commit is contained in:
Dmitriy Novozhilov
2021-02-04 15:41:57 +03:00
parent 3f715e671d
commit 8dd9d98129
103 changed files with 919 additions and 617 deletions
@@ -0,0 +1,55 @@
FILE: missingBooleanBranch.kt
public final fun test_1(cond: R|kotlin/Boolean|): R|kotlin/Unit| {
lval x: R|kotlin/Unit| = when (R|<local>/cond|) {
==($subj$, Boolean(true)) -> {
Int(1)
}
}
lval y: R|kotlin/Unit| = when (R|<local>/cond|) {
==($subj$, Boolean(false)) -> {
Int(2)
}
}
lval z: R|kotlin/Int| = when (R|<local>/cond|) {
==($subj$, Boolean(true)) -> {
Int(1)
}
==($subj$, Boolean(false)) -> {
Int(2)
}
}
}
public final fun test_2(cond: R|kotlin/Boolean?|): R|kotlin/Unit| {
lval x: R|kotlin/Unit| = when (R|<local>/cond|) {
==($subj$, Boolean(true)) -> {
Int(1)
}
==($subj$, Boolean(false)) -> {
Int(2)
}
}
lval x: R|kotlin/Int| = when (R|<local>/cond|) {
==($subj$, Boolean(true)) -> {
Int(1)
}
==($subj$, Boolean(false)) -> {
Int(2)
}
==($subj$, Null(null)) -> {
Int(3)
}
}
}
public final fun test_3(cond: R|kotlin/Boolean|): R|kotlin/Unit| {
when (R|<local>/cond|) {
==($subj$, Boolean(true)) -> {
Int(1)
}
}
}
@@ -0,0 +1,33 @@
fun test_1(cond: Boolean) {
val x = <!NO_ELSE_IN_WHEN!>when<!> (cond) {
true -> 1
}
val y = <!NO_ELSE_IN_WHEN!>when<!> (cond) {
false -> 2
}
val z = when (cond) {
true -> 1
false -> 2
}
}
fun test_2(cond: Boolean?) {
val x = <!NO_ELSE_IN_WHEN!>when<!> (cond) {
true -> 1
false -> 2
}
val x = when (cond) {
true -> 1
false -> 2
null -> 3
}
}
fun test_3(cond: Boolean) {
when (cond) {
true -> 1
}
}
@@ -0,0 +1,41 @@
FILE: missingElse.kt
public final fun test(a: R|kotlin/Any|): R|kotlin/Unit| {
lval x: R|kotlin/Unit| = when (R|<local>/a|) {
($subj$ is R|kotlin/Int|) -> {
Int(1)
}
($subj$ is R|kotlin/String|) -> {
Int(2)
}
}
lval y: R|kotlin/Int| = when (R|<local>/a|) {
else -> {
Int(1)
}
}
lval z: R|kotlin/Int| = when (R|<local>/a|) {
($subj$ is R|kotlin/Int|) -> {
Int(1)
}
($subj$ is R|kotlin/String|) -> {
Int(2)
}
else -> {
Int(3)
}
}
}
public final fun test_2(a: R|kotlin/Any|): R|kotlin/Unit| {
when (R|<local>/a|) {
($subj$ is R|kotlin/String|) -> {
Int(1)
}
($subj$ is R|kotlin/Int|) -> {
Int(2)
}
}
}
@@ -0,0 +1,23 @@
fun test(a: Any) {
val x = <!NO_ELSE_IN_WHEN!>when<!> (a) {
is Int -> 1
is String -> 2
}
val y = when (a) {
else -> 1
}
val z = when (a) {
is Int -> 1
is String -> 2
else -> 3
}
}
fun test_2(a: Any) {
when (a) {
is String -> 1
is Int -> 2
}
}
@@ -0,0 +1,63 @@
FILE: missingEnumEntry.kt
public final enum class SomeEnum : R|kotlin/Enum<SomeEnum>| {
private constructor(): R|SomeEnum| {
super<R|kotlin/Enum<SomeEnum>|>()
}
public final static enum entry A: R|SomeEnum|
public final static enum entry B: R|SomeEnum|
public final static fun values(): R|kotlin/Array<SomeEnum>| {
}
public final static fun valueOf(value: R|kotlin/String|): R|SomeEnum| {
}
}
public final fun test_1(enum: R|SomeEnum|): R|kotlin/Unit| {
lval x: R|kotlin/Unit| = when (R|<local>/enum|) {
==($subj$, Q|SomeEnum|.R|/SomeEnum.A|) -> {
Int(1)
}
}
lval y: R|kotlin/Int| = when (R|<local>/enum|) {
==($subj$, Q|SomeEnum|.R|/SomeEnum.A|) -> {
Int(1)
}
==($subj$, Q|SomeEnum|.R|/SomeEnum.B|) -> {
Int(2)
}
}
}
public final fun test_2(enum: R|SomeEnum?|): R|kotlin/Unit| {
lval x: R|kotlin/Unit| = when (R|<local>/enum|) {
==($subj$, Q|SomeEnum|.R|/SomeEnum.A|) -> {
Int(1)
}
==($subj$, Q|SomeEnum|.R|/SomeEnum.B|) -> {
Int(2)
}
}
lval y: R|kotlin/Int| = when (R|<local>/enum|) {
==($subj$, Q|SomeEnum|.R|/SomeEnum.A|) -> {
Int(1)
}
==($subj$, Q|SomeEnum|.R|/SomeEnum.B|) -> {
Int(2)
}
==($subj$, Null(null)) -> {
Int(3)
}
}
}
public final fun test_3(enum: R|SomeEnum|): R|kotlin/Unit| {
when (R|<local>/enum|) {
==($subj$, Q|SomeEnum|.R|/SomeEnum.A|) -> {
Int(1)
}
}
}
@@ -0,0 +1,33 @@
enum class SomeEnum {
A, B
}
fun test_1(enum: SomeEnum) {
val x = <!NO_ELSE_IN_WHEN!>when<!> (enum) {
SomeEnum.A -> 1
}
val y = when (enum) {
SomeEnum.A -> 1
SomeEnum.B -> 2
}
}
fun test_2(enum: SomeEnum?) {
val x = <!NO_ELSE_IN_WHEN!>when<!> (enum) {
SomeEnum.A -> 1
SomeEnum.B -> 2
}
val y = when (enum) {
SomeEnum.A -> 1
SomeEnum.B -> 2
null -> 3
}
}
fun test_3(enum: SomeEnum) {
when (enum) {
SomeEnum.A -> 1
}
}
@@ -0,0 +1,84 @@
FILE: a.kt
public sealed class Base : R|kotlin/Any| {
private constructor(): R|Base| {
super<R|kotlin/Any|>()
}
}
public final class A : R|Base| {
public constructor(): R|A| {
super<R|Base|>()
}
}
FILE: b.kt
public final object B : R|Base| {
private constructor(): R|B| {
super<R|Base|>()
}
}
FILE: c.kt
public final fun test_1(base: R|Base|): R|kotlin/Unit| {
lval x: R|kotlin/Unit| = when (R|<local>/base|) {
($subj$ is R|A|) -> {
Int(1)
}
}
lval y: R|kotlin/Unit| = when (R|<local>/base|) {
==($subj$, Q|B|) -> {
Int(1)
}
}
lval z: R|kotlin/Int| = when (R|<local>/base|) {
($subj$ is R|A|) -> {
Int(1)
}
==($subj$, Q|B|) -> {
Int(2)
}
}
}
public final fun test_2(base: R|Base?|): R|kotlin/Unit| {
lval x: R|kotlin/Unit| = when (R|<local>/base|) {
($subj$ is R|A|) -> {
Int(1)
}
($subj$ is R|B|) -> {
Int(2)
}
}
lval y: R|kotlin/Unit| = when (R|<local>/base|) {
($subj$ is R|A|) -> {
Int(1)
}
==($subj$, Q|B|) -> {
Int(2)
}
}
lval z: R|kotlin/Int| = when (R|<local>/base|) {
($subj$ is R|A|) -> {
Int(1)
}
==($subj$, Q|B|) -> {
Int(2)
}
==($subj$, Null(null)) -> {
Int(3)
}
}
}
public final fun test_3(base: R|Base|): R|kotlin/Unit| {
when (R|<local>/base|) {
($subj$ is R|A|) -> {
Int(1)
}
}
}
@@ -0,0 +1,50 @@
// FILE: a.kt
sealed class Base
class A : Base
// FILE: b.kt
object B : Base()
// FILE: c.kt
fun test_1(base: Base) {
val x = <!NO_ELSE_IN_WHEN!>when<!> (base) {
is A -> 1
}
val y = <!NO_ELSE_IN_WHEN!>when<!> (base) {
B -> 1
}
val z = when (base) {
is A -> 1
B -> 2
}
}
fun test_2(base: Base?) {
val x = <!NO_ELSE_IN_WHEN!>when<!> (base) {
is A -> 1
is B -> 2
}
val y = <!NO_ELSE_IN_WHEN!>when<!> (base) {
is A -> 1
B -> 2
}
val z = when (base) {
is A -> 1
B -> 2
null -> 3
}
}
fun test_3(base: Base) {
when (base) {
is A -> 1
}
}
@@ -0,0 +1,345 @@
digraph exhaustiveWhenAndDNNType_kt {
graph [nodesep=3]
node [shape=box penwidth=2]
edge [penwidth=2]
subgraph cluster_0 {
color=red
0 [label="Enter class SomeEnum" style="filled" fillcolor=red];
1 [label="Exit class SomeEnum" style="filled" fillcolor=red];
}
0 -> {1} [color=green];
subgraph cluster_1 {
color=red
2 [label="Enter function <init>" style="filled" fillcolor=red];
3 [label="Delegated constructor call: super<R|kotlin/Enum<SomeEnum>|>()"];
4 [label="Exit function <init>" style="filled" fillcolor=red];
}
2 -> {3};
3 -> {4};
subgraph cluster_2 {
color=red
5 [label="Enter function values" style="filled" fillcolor=red];
subgraph cluster_3 {
color=blue
6 [label="Enter block"];
7 [label="Exit block"];
}
8 [label="Exit function values" style="filled" fillcolor=red];
}
5 -> {6};
6 -> {7};
7 -> {8};
subgraph cluster_4 {
color=red
9 [label="Enter function valueOf" style="filled" fillcolor=red];
subgraph cluster_5 {
color=blue
10 [label="Enter block"];
11 [label="Exit block"];
}
12 [label="Exit function valueOf" style="filled" fillcolor=red];
}
9 -> {10};
10 -> {11};
11 -> {12};
subgraph cluster_6 {
color=red
13 [label="Enter class B" style="filled" fillcolor=red];
14 [label="Exit class B" style="filled" fillcolor=red];
}
13 -> {14} [color=green];
subgraph cluster_7 {
color=red
15 [label="Enter function <init>" style="filled" fillcolor=red];
16 [label="Delegated constructor call: super<R|kotlin/Any|>()"];
17 [label="Exit function <init>" style="filled" fillcolor=red];
}
15 -> {16};
16 -> {17};
subgraph cluster_8 {
color=red
18 [label="Enter function takeB" style="filled" fillcolor=red];
subgraph cluster_9 {
color=blue
19 [label="Enter block"];
20 [label="Exit block"];
}
21 [label="Exit function takeB" style="filled" fillcolor=red];
}
18 -> {19};
19 -> {20};
20 -> {21};
subgraph cluster_10 {
color=red
22 [label="Enter function test_1" style="filled" fillcolor=red];
subgraph cluster_11 {
color=blue
23 [label="Enter block"];
24 [label="Access qualifier /SomeEnum"];
25 [label="Access variable R|/SomeEnum.A1|"];
26 [label="Variable declaration: lval flag: R|SomeEnum|"];
subgraph cluster_12 {
color=blue
27 [label="Enter when"];
28 [label="Access variable R|<local>/flag|"];
29 [label="Check not null: R|<local>/flag|!!"];
subgraph cluster_13 {
color=blue
30 [label="Enter when branch condition "];
31 [label="Access qualifier /SomeEnum"];
32 [label="Access variable R|/SomeEnum.A1|"];
33 [label="Equality operator =="];
34 [label="Exit when branch condition"];
}
subgraph cluster_14 {
color=blue
35 [label="Enter when branch condition "];
36 [label="Access qualifier /SomeEnum"];
37 [label="Access variable R|/SomeEnum.A2|"];
38 [label="Equality operator =="];
39 [label="Exit when branch condition"];
}
40 [label="Enter when branch result"];
subgraph cluster_15 {
color=blue
41 [label="Enter block"];
42 [label="Function call: R|/B.B|()"];
43 [label="Exit block"];
}
44 [label="Exit when branch result"];
45 [label="Enter when branch result"];
subgraph cluster_16 {
color=blue
46 [label="Enter block"];
47 [label="Function call: R|/B.B|()"];
48 [label="Exit block"];
}
49 [label="Exit when branch result"];
50 [label="Exit when"];
}
51 [label="Variable declaration: lval b: R|B|"];
52 [label="Access variable R|<local>/b|"];
53 [label="Function call: R|/takeB|(...)"];
54 [label="Exit block"];
}
55 [label="Exit function test_1" style="filled" fillcolor=red];
}
22 -> {23};
23 -> {24};
24 -> {25};
25 -> {26};
26 -> {27};
27 -> {28};
28 -> {29};
29 -> {30};
30 -> {31};
31 -> {32};
32 -> {33};
33 -> {34};
34 -> {45 35};
35 -> {36};
36 -> {37};
37 -> {38};
38 -> {39};
39 -> {40};
40 -> {41};
41 -> {42};
42 -> {43};
43 -> {44};
44 -> {50};
45 -> {46};
46 -> {47};
47 -> {48};
48 -> {49};
49 -> {50};
50 -> {51};
51 -> {52};
52 -> {53};
53 -> {54};
54 -> {55};
subgraph cluster_17 {
color=red
56 [label="Enter function test_2" style="filled" fillcolor=red];
subgraph cluster_18 {
color=blue
57 [label="Enter block"];
58 [label="Access qualifier /SomeEnum"];
59 [label="Access variable R|/SomeEnum.A1|"];
60 [label="Variable declaration: lval flag: R|SomeEnum|"];
subgraph cluster_19 {
color=blue
61 [label="Enter when"];
62 [label="Access variable R|<local>/flag|"];
63 [label="Check not null: R|<local>/flag|!!"];
subgraph cluster_20 {
color=blue
64 [label="Enter when branch condition "];
65 [label="Access qualifier /SomeEnum"];
66 [label="Access variable R|/SomeEnum.A1|"];
67 [label="Equality operator =="];
68 [label="Exit when branch condition"];
}
subgraph cluster_21 {
color=blue
69 [label="Enter when branch condition "];
70 [label="Access qualifier /SomeEnum"];
71 [label="Access variable R|/SomeEnum.A2|"];
72 [label="Equality operator =="];
73 [label="Exit when branch condition"];
}
74 [label="Enter when branch result"];
subgraph cluster_22 {
color=blue
75 [label="Enter block"];
76 [label="Function call: R|/B.B|()"];
77 [label="Exit block"];
}
78 [label="Exit when branch result"];
79 [label="Enter when branch result"];
subgraph cluster_23 {
color=blue
80 [label="Enter block"];
81 [label="Function call: R|/B.B|()"];
82 [label="Exit block"];
}
83 [label="Exit when branch result"];
84 [label="Exit when"];
}
85 [label="Variable declaration: lval b: R|B|"];
86 [label="Access variable R|<local>/b|"];
87 [label="Function call: R|/takeB|(...)"];
88 [label="Exit block"];
}
89 [label="Exit function test_2" style="filled" fillcolor=red];
}
56 -> {57};
57 -> {58};
58 -> {59};
59 -> {60};
60 -> {61};
61 -> {62};
62 -> {63};
63 -> {64};
64 -> {65};
65 -> {66};
66 -> {67};
67 -> {68};
68 -> {79 69};
69 -> {70};
70 -> {71};
71 -> {72};
72 -> {73};
73 -> {74};
74 -> {75};
75 -> {76};
76 -> {77};
77 -> {78};
78 -> {84};
79 -> {80};
80 -> {81};
81 -> {82};
82 -> {83};
83 -> {84};
84 -> {85};
85 -> {86};
86 -> {87};
87 -> {88};
88 -> {89};
subgraph cluster_24 {
color=red
90 [label="Enter function test_3" style="filled" fillcolor=red];
subgraph cluster_25 {
color=blue
91 [label="Enter block"];
92 [label="Access qualifier /SomeEnum"];
93 [label="Access variable R|/SomeEnum.A1|"];
94 [label="Variable declaration: lval flag: R|SomeEnum|"];
subgraph cluster_26 {
color=blue
95 [label="Enter when"];
96 [label="Access variable R|<local>/flag|"];
subgraph cluster_27 {
color=blue
97 [label="Enter when branch condition "];
98 [label="Access qualifier /SomeEnum"];
99 [label="Access variable R|/SomeEnum.A1|"];
100 [label="Equality operator =="];
101 [label="Exit when branch condition"];
}
subgraph cluster_28 {
color=blue
102 [label="Enter when branch condition "];
103 [label="Access qualifier /SomeEnum"];
104 [label="Access variable R|/SomeEnum.A2|"];
105 [label="Equality operator =="];
106 [label="Exit when branch condition"];
}
107 [label="Enter when branch result"];
subgraph cluster_29 {
color=blue
108 [label="Enter block"];
109 [label="Function call: R|/B.B|()"];
110 [label="Exit block"];
}
111 [label="Exit when branch result"];
112 [label="Enter when branch result"];
subgraph cluster_30 {
color=blue
113 [label="Enter block"];
114 [label="Function call: R|/B.B|()"];
115 [label="Exit block"];
}
116 [label="Exit when branch result"];
117 [label="Exit when"];
}
118 [label="Variable declaration: lval b: R|B|"];
119 [label="Access variable R|<local>/b|"];
120 [label="Function call: R|/takeB|(...)"];
121 [label="Exit block"];
}
122 [label="Exit function test_3" style="filled" fillcolor=red];
}
90 -> {91};
91 -> {92};
92 -> {93};
93 -> {94};
94 -> {95};
95 -> {96};
96 -> {97};
97 -> {98};
98 -> {99};
99 -> {100};
100 -> {101};
101 -> {112 102};
102 -> {103};
103 -> {104};
104 -> {105};
105 -> {106};
106 -> {107};
107 -> {108};
108 -> {109};
109 -> {110};
110 -> {111};
111 -> {117};
112 -> {113};
113 -> {114};
114 -> {115};
115 -> {116};
116 -> {117};
117 -> {118};
118 -> {119};
119 -> {120};
120 -> {121};
121 -> {122};
}
@@ -0,0 +1,62 @@
FILE: exhaustiveWhenAndDNNType.kt
public final enum class SomeEnum : R|kotlin/Enum<SomeEnum>| {
private constructor(): R|SomeEnum| {
super<R|kotlin/Enum<SomeEnum>|>()
}
public final static enum entry A1: R|SomeEnum|
public final static enum entry A2: R|SomeEnum|
public final static fun values(): R|kotlin/Array<SomeEnum>| {
}
public final static fun valueOf(value: R|kotlin/String|): R|SomeEnum| {
}
}
public final class B : R|kotlin/Any| {
public constructor(): R|B| {
super<R|kotlin/Any|>()
}
}
public final fun takeB(b: R|B|): R|kotlin/Unit| {
}
public final fun test_1(): R|kotlin/Unit| {
lval flag: R|SomeEnum| = Q|SomeEnum|.R|/SomeEnum.A1|
lval b: R|B| = when (R|<local>/flag|!!) {
==($subj$, Q|SomeEnum|.R|/SomeEnum.A1|) -> {
R|/B.B|()
}
==($subj$, Q|SomeEnum|.R|/SomeEnum.A2|) -> {
R|/B.B|()
}
}
R|/takeB|(R|<local>/b|)
}
public final fun test_2(): R|kotlin/Unit| {
lval flag: R|SomeEnum| = Q|SomeEnum|.R|/SomeEnum.A1|
lval b: R|B| = when (R|<local>/flag|!!) {
==($subj$, Q|SomeEnum|.R|/SomeEnum.A1|) -> {
R|/B.B|()
}
==($subj$, Q|SomeEnum|.R|/SomeEnum.A2|) -> {
R|/B.B|()
}
}
R|/takeB|(R|<local>/b|)
}
public final fun test_3(): R|kotlin/Unit| {
lval flag: R|SomeEnum| = Q|SomeEnum|.R|/SomeEnum.A1|
lval b: R|B| = when (R|<local>/flag|) {
==($subj$, Q|SomeEnum|.R|/SomeEnum.A1|) -> {
R|/B.B|()
}
==($subj$, Q|SomeEnum|.R|/SomeEnum.A2|) -> {
R|/B.B|()
}
}
R|/takeB|(R|<local>/b|)
}
@@ -0,0 +1,38 @@
// !DUMP_CFG
// ISSUE: KT-37091
enum class SomeEnum {
A1, A2;
}
class B()
fun takeB(b: B) {}
fun test_1() {
val flag = SomeEnum.A1
val b: B = when (flag!!) {
SomeEnum.A1 -> B()
SomeEnum.A2 -> B()
}
takeB(b) // should be OK
}
fun test_2() {
val flag = SomeEnum.A1
val b = when (flag!!) {
SomeEnum.A1 -> B()
SomeEnum.A2 -> B()
}
takeB(b) // should be OK
}
fun test_3() {
val flag = SomeEnum.A1
val b = when (flag) { //there is no null-assertion! , no explicit type
SomeEnum.A1 -> B()
SomeEnum.A2 -> B()
}
takeB(b) // should be OK
}
@@ -0,0 +1,51 @@
FILE: main.kt
public final enum class E : R|kotlin/Enum<E>| {
private constructor(): R|E| {
super<R|kotlin/Enum<E>|>()
}
public final static enum entry A: R|E|
public final static enum entry B: R|E|
public final static enum entry C: R|E|
public final static fun values(): R|kotlin/Array<E>| {
}
public final static fun valueOf(value: R|kotlin/String|): R|E| {
}
}
public final fun test_1(): R|kotlin/Unit| {
lval e: R|ft<@FlexibleNullability E, E?>!| = Q|Utils|.R|/Utils.getEnum|()
lval s: R|kotlin/String| = when (R|<local>/e|) {
==($subj$, Null(null)) -> {
^test_1 Unit
}
==($subj$, Q|E|.R|/E.A|) -> {
String()
}
==($subj$, Q|E|.R|/E.B|) -> {
String()
}
==($subj$, Q|E|.R|/E.C|) -> {
String()
}
}
R|<local>/s|.R|kotlin/String.length|
}
public final fun test_2(): R|kotlin/Unit| {
lval e: R|ft<@FlexibleNullability E, E?>!| = Q|Utils|.R|/Utils.getEnum|()
lval s: R|kotlin/String| = when (R|<local>/e|) {
==($subj$, Q|E|.R|/E.A|) -> {
String()
}
==($subj$, Q|E|.R|/E.B|) -> {
String()
}
==($subj$, Q|E|.R|/E.C|) -> {
String()
}
}
R|<local>/s|.R|kotlin/String.length|
}
@@ -0,0 +1,36 @@
// IGNORE_LIGHT_TREE
// FILE: Utils.java
public class Utils {
public static E getEnum() {
return null;
}
}
// FILE: main.kt
enum class E {
A, B, C
}
fun test_1() {
val e = Utils.getEnum()
val s = when (e) {
null -> return
E.A -> ""
E.B -> ""
E.C -> ""
}
s.length
}
fun test_2() {
val e = Utils.getEnum()
val s = when (e) {
E.A -> ""
E.B -> ""
E.C -> ""
}
s.length
}
@@ -0,0 +1,50 @@
FILE: exhaustiveness_boolean.kt
public final fun test_1(b: R|kotlin/Boolean|): R|kotlin/Unit| {
lval x: R|kotlin/Unit| = when (R|<local>/b|) {
==($subj$, Boolean(true)) -> {
Int(1)
}
}
lval y: R|kotlin/Int| = when (R|<local>/b|) {
==($subj$, Boolean(true)) -> {
Int(1)
}
==($subj$, Boolean(false)) -> {
Int(2)
}
}
lval z: R|kotlin/Int| = when (R|<local>/b|) {
==($subj$, Boolean(true)) -> {
Int(1)
}
else -> {
Int(2)
}
}
}
public final fun test_2(b: R|kotlin/Boolean?|): R|kotlin/Unit| {
lval x: R|kotlin/Unit| = when (R|<local>/b|) {
==($subj$, Boolean(true)) -> {
Int(1)
}
==($subj$, Boolean(false)) -> {
Int(2)
}
}
lval y: R|kotlin/Int| = when (R|<local>/b|) {
==($subj$, Boolean(true)) -> {
Int(1)
}
==($subj$, Boolean(false)) -> {
Int(2)
}
==($subj$, Null(null)) -> {
Int(3)
}
}
}
@@ -0,0 +1,25 @@
fun test_1(b: Boolean) {
val x = <!NO_ELSE_IN_WHEN!>when<!> (b) {
true -> 1
}
val y = when (b) {
true -> 1
false -> 2
}
val z = when (b) {
true -> 1
else -> 2
}
}
fun test_2(b: Boolean?) {
val x = <!NO_ELSE_IN_WHEN!>when<!> (b) {
true -> 1
false -> 2
}
val y = when (b) {
true -> 1
false -> 2
null -> 3
}
}
@@ -0,0 +1,115 @@
FILE: exhaustiveness_enum.kt
public final enum class Enum : R|kotlin/Enum<Enum>| {
private constructor(): R|Enum| {
super<R|kotlin/Enum<Enum>|>()
}
public final static enum entry A: R|Enum|
public final static enum entry B: R|Enum|
public final static enum entry C: R|Enum|
public final static fun values(): R|kotlin/Array<Enum>| {
}
public final static fun valueOf(value: R|kotlin/String|): R|Enum| {
}
}
public final fun test_1(e: R|Enum|): R|kotlin/Unit| {
lval a: R|kotlin/Unit| = when (R|<local>/e|) {
==($subj$, Q|Enum|.R|/Enum.A|) -> {
Int(1)
}
==($subj$, Q|Enum|.R|/Enum.B|) -> {
Int(2)
}
}
lval b: R|kotlin/Unit| = when (R|<local>/e|) {
==($subj$, Q|Enum|.R|/Enum.A|) -> {
Int(1)
}
==($subj$, Q|Enum|.R|/Enum.B|) -> {
Int(2)
}
($subj$ is R|kotlin/String|) -> {
Int(3)
}
}
lval c: R|kotlin/Int| = when (R|<local>/e|) {
==($subj$, Q|Enum|.R|/Enum.A|) -> {
Int(1)
}
==($subj$, Q|Enum|.R|/Enum.B|) -> {
Int(2)
}
==($subj$, Q|Enum|.R|/Enum.C|) -> {
Int(3)
}
}
lval d: R|kotlin/Int| = when (R|<local>/e|) {
==($subj$, Q|Enum|.R|/Enum.A|) -> {
Int(1)
}
else -> {
Int(2)
}
}
}
public final fun test_2(e: R|Enum?|): R|kotlin/Unit| {
lval a: R|kotlin/Unit| = when (R|<local>/e|) {
==($subj$, Q|Enum|.R|/Enum.A|) -> {
Int(1)
}
==($subj$, Q|Enum|.R|/Enum.B|) -> {
Int(2)
}
==($subj$, Q|Enum|.R|/Enum.C|) -> {
Int(3)
}
}
lval a: R|kotlin/Int| = when (R|<local>/e|) {
==($subj$, Q|Enum|.R|/Enum.A|) -> {
Int(1)
}
==($subj$, Q|Enum|.R|/Enum.B|) -> {
Int(2)
}
==($subj$, Q|Enum|.R|/Enum.C|) -> {
Int(3)
}
==($subj$, Null(null)) -> {
Int(4)
}
}
lval a: R|kotlin/Int| = when (R|<local>/e|) {
==($subj$, Q|Enum|.R|/Enum.A|) -> {
Int(1)
}
==($subj$, Q|Enum|.R|/Enum.B|) -> {
Int(2)
}
==($subj$, Q|Enum|.R|/Enum.C|) -> {
Int(3)
}
else -> {
Int(4)
}
}
}
public final fun test_3(e: R|Enum|): R|kotlin/Unit| {
lval a: R|kotlin/Int| = when (R|<local>/e|) {
==($subj$, Q|Enum|.R|/Enum.A|) || ==($subj$, Q|Enum|.R|/Enum.B|) -> {
Int(1)
}
==($subj$, Q|Enum|.R|/Enum.C|) -> {
Int(2)
}
}
}
@@ -0,0 +1,56 @@
enum class Enum {
A, B, C
}
fun test_1(e: Enum) {
val a = <!NO_ELSE_IN_WHEN!>when<!> (e) {
Enum.A -> 1
Enum.B -> 2
}
val b = <!NO_ELSE_IN_WHEN!>when<!> (e) {
Enum.A -> 1
Enum.B -> 2
is String -> 3
}
val c = when (e) {
Enum.A -> 1
Enum.B -> 2
Enum.C -> 3
}
val d = when (e) {
Enum.A -> 1
else -> 2
}
}
fun test_2(e: Enum?) {
val a = <!NO_ELSE_IN_WHEN!>when<!> (e) {
Enum.A -> 1
Enum.B -> 2
Enum.C -> 3
}
val a = when (e) {
Enum.A -> 1
Enum.B -> 2
Enum.C -> 3
null -> 4
}
val a = when (e) {
Enum.A -> 1
Enum.B -> 2
Enum.C -> 3
else -> 4
}
}
fun test_3(e: Enum) {
val a = when (e) {
Enum.A, Enum.B -> 1
Enum.C -> 2
}
}
@@ -0,0 +1,100 @@
FILE: main.kt
public final fun test_1(e: R|JavaEnum|): R|kotlin/Unit| {
lval a: R|ERROR CLASS: Unresolved name: plus| = when (R|<local>/e|) {
==($subj$, Q|JavaEnum|.R|/JavaEnum.A|) -> {
Int(1)
}
==($subj$, Q|JavaEnum|.R|/JavaEnum.B|) -> {
Int(2)
}
}
.<Unresolved name: plus>#(Int(0))
lval b: R|ERROR CLASS: Unresolved name: plus| = when (R|<local>/e|) {
==($subj$, Q|JavaEnum|.R|/JavaEnum.A|) -> {
Int(1)
}
==($subj$, Q|JavaEnum|.R|/JavaEnum.B|) -> {
Int(2)
}
($subj$ is R|kotlin/String|) -> {
Int(3)
}
}
.<Unresolved name: plus>#(Int(0))
lval c: R|kotlin/Int| = when (R|<local>/e|) {
==($subj$, Q|JavaEnum|.R|/JavaEnum.A|) -> {
Int(1)
}
==($subj$, Q|JavaEnum|.R|/JavaEnum.B|) -> {
Int(2)
}
==($subj$, Q|JavaEnum|.R|/JavaEnum.C|) -> {
Int(3)
}
}
.R|kotlin/Int.plus|(Int(0))
lval d: R|kotlin/Int| = when (R|<local>/e|) {
==($subj$, Q|JavaEnum|.R|/JavaEnum.A|) -> {
Int(1)
}
else -> {
Int(2)
}
}
.R|kotlin/Int.plus|(Int(0))
}
public final fun test_2(e: R|JavaEnum?|): R|kotlin/Unit| {
lval a: R|ERROR CLASS: Unresolved name: plus| = when (R|<local>/e|) {
==($subj$, Q|JavaEnum|.R|/JavaEnum.A|) -> {
Int(1)
}
==($subj$, Q|JavaEnum|.R|/JavaEnum.B|) -> {
Int(2)
}
==($subj$, Q|JavaEnum|.R|/JavaEnum.C|) -> {
Int(3)
}
}
.<Unresolved name: plus>#(Int(0))
lval a: R|kotlin/Int| = when (R|<local>/e|) {
==($subj$, Q|JavaEnum|.R|/JavaEnum.A|) -> {
Int(1)
}
==($subj$, Q|JavaEnum|.R|/JavaEnum.B|) -> {
Int(2)
}
==($subj$, Q|JavaEnum|.R|/JavaEnum.C|) -> {
Int(3)
}
==($subj$, Null(null)) -> {
Int(4)
}
}
.R|kotlin/Int.plus|(Int(0))
lval a: R|kotlin/Int| = when (R|<local>/e|) {
==($subj$, Q|JavaEnum|.R|/JavaEnum.A|) -> {
Int(1)
}
==($subj$, Q|JavaEnum|.R|/JavaEnum.B|) -> {
Int(2)
}
==($subj$, Q|JavaEnum|.R|/JavaEnum.C|) -> {
Int(3)
}
else -> {
Int(4)
}
}
.R|kotlin/Int.plus|(Int(0))
}
public final fun test_3(e: R|JavaEnum|): R|kotlin/Unit| {
lval a: R|kotlin/Int| = when (R|<local>/e|) {
==($subj$, Q|JavaEnum|.R|/JavaEnum.A|) || ==($subj$, Q|JavaEnum|.R|/JavaEnum.B|) -> {
Int(1)
}
==($subj$, Q|JavaEnum|.R|/JavaEnum.C|) -> {
Int(2)
}
}
.R|kotlin/Int.plus|(Int(0))
}
@@ -0,0 +1,60 @@
// FILE: JavaEnum.java
public enum JavaEnum {
A, B, C;
public int i = 0;
}
// FILE: main.kt
fun test_1(e: JavaEnum) {
val a = <!NO_ELSE_IN_WHEN!>when<!> (e) {
JavaEnum.A -> 1
JavaEnum.B -> 2
}.<!UNRESOLVED_REFERENCE{LT}!><!UNRESOLVED_REFERENCE{PSI}!>plus<!>(0)<!>
val b = <!NO_ELSE_IN_WHEN!>when<!> (e) {
JavaEnum.A -> 1
JavaEnum.B -> 2
is String -> 3
}.<!UNRESOLVED_REFERENCE{LT}!><!UNRESOLVED_REFERENCE{PSI}!>plus<!>(0)<!>
val c = when (e) {
JavaEnum.A -> 1
JavaEnum.B -> 2
JavaEnum.C -> 3
}.plus(0)
val d = when (e) {
JavaEnum.A -> 1
else -> 2
}.plus(0)
}
fun test_2(e: JavaEnum?) {
val a = <!NO_ELSE_IN_WHEN!>when<!> (e) {
JavaEnum.A -> 1
JavaEnum.B -> 2
JavaEnum.C -> 3
}.<!UNRESOLVED_REFERENCE{LT}!><!UNRESOLVED_REFERENCE{PSI}!>plus<!>(0)<!>
val a = when (e) {
JavaEnum.A -> 1
JavaEnum.B -> 2
JavaEnum.C -> 3
null -> 4
}.plus(0)
val a = when (e) {
JavaEnum.A -> 1
JavaEnum.B -> 2
JavaEnum.C -> 3
else -> 4
}.plus(0)
}
fun test_3(e: JavaEnum) {
val a = when (e) {
JavaEnum.A, JavaEnum.B -> 1
JavaEnum.C -> 2
}.plus(0)
}
@@ -0,0 +1,126 @@
FILE: exhaustiveness_sealedClass.kt
public sealed class Base : R|kotlin/Any| {
private constructor(): R|Base| {
super<R|kotlin/Any|>()
}
public final class A : R|Base| {
public constructor(): R|Base.A| {
super<R|Base|>()
}
public final class B : R|Base| {
public constructor(): R|Base.A.B| {
super<R|Base|>()
}
}
}
}
public final class C : R|Base| {
public constructor(): R|C| {
super<R|Base|>()
}
}
public final fun test_1(e: R|Base|): R|kotlin/Unit| {
lval a: R|kotlin/Unit| = when (R|<local>/e|) {
($subj$ is R|Base.A|) -> {
Int(1)
}
($subj$ is R|Base.A.B|) -> {
Int(2)
}
}
lval b: R|kotlin/Unit| = when (R|<local>/e|) {
($subj$ is R|Base.A|) -> {
Int(1)
}
($subj$ is R|Base.A.B|) -> {
Int(2)
}
($subj$ is R|kotlin/String|) -> {
Int(3)
}
}
lval c: R|kotlin/Int| = when (R|<local>/e|) {
($subj$ is R|Base.A|) -> {
Int(1)
}
($subj$ is R|Base.A.B|) -> {
Int(2)
}
($subj$ is R|C|) -> {
Int(3)
}
}
lval d: R|kotlin/Int| = when (R|<local>/e|) {
($subj$ is R|Base.A|) -> {
Int(1)
}
else -> {
Int(2)
}
}
}
public final fun test_2(e: R|Base?|): R|kotlin/Unit| {
lval a: R|kotlin/Unit| = when (R|<local>/e|) {
($subj$ is R|Base.A|) -> {
Int(1)
}
($subj$ is R|Base.A.B|) -> {
Int(2)
}
($subj$ is R|C|) -> {
Int(3)
}
}
lval b: R|kotlin/Int| = when (R|<local>/e|) {
($subj$ is R|Base.A|) -> {
Int(1)
}
($subj$ is R|Base.A.B|) -> {
Int(2)
}
($subj$ is R|C|) -> {
Int(3)
}
==($subj$, Null(null)) -> {
Int(4)
}
}
lval c: R|kotlin/Int| = when (R|<local>/e|) {
($subj$ is R|Base.A|) -> {
Int(1)
}
($subj$ is R|Base.A.B|) -> {
Int(2)
}
($subj$ is R|C|) -> {
Int(3)
}
else -> {
Int(4)
}
}
}
public final fun test_3(e: R|Base|): R|kotlin/Unit| {
lval a: R|kotlin/Int| = when (R|<local>/e|) {
($subj$ is R|Base.A|) || ($subj$ is R|Base.A.B|) -> {
Int(1)
}
($subj$ is R|C|) -> {
Int(2)
}
}
}
@@ -0,0 +1,60 @@
sealed class Base {
class A : Base() {
class B : Base()
}
}
class C : Base()
fun test_1(e: Base) {
val a = <!NO_ELSE_IN_WHEN!>when<!> (e) {
is Base.A -> 1
is Base.A.B -> 2
}
val b = <!NO_ELSE_IN_WHEN!>when<!> (e) {
is Base.A -> 1
is Base.A.B -> 2
is String -> 3
}
val c = when (e) {
is Base.A -> 1
is Base.A.B -> 2
is C -> 3
}
val d = when (e) {
is Base.A -> 1
else -> 2
}
}
fun test_2(e: Base?) {
val a = <!NO_ELSE_IN_WHEN!>when<!> (e) {
is Base.A -> 1
is Base.A.B -> 2
is C -> 3
}
val b = when (e) {
is Base.A -> 1
is Base.A.B -> 2
is C -> 3
null -> 4
}
val c = when (e) {
is Base.A -> 1
is Base.A.B -> 2
is C -> 3
else -> 4
}
}
fun test_3(e: Base) {
val a = when (e) {
is Base.A, is Base.A.B -> 1
is C -> 2
}
}
@@ -0,0 +1,45 @@
FILE: exhaustiveness_sealedObject.kt
public sealed class A : R|kotlin/Any| {
private constructor(): R|A| {
super<R|kotlin/Any|>()
}
}
public final class B : R|A| {
public constructor(): R|B| {
super<R|A|>()
}
}
public final object C : R|A| {
private constructor(): R|C| {
super<R|A|>()
}
}
public final fun takeString(s: R|kotlin/String|): R|kotlin/Unit| {
}
public final fun test_1(a: R|A|): R|kotlin/Unit| {
lval s: R|kotlin/String| = when (R|<local>/a|) {
($subj$ is R|B|) -> {
String()
}
($subj$ is R|C|) -> {
String()
}
}
R|/takeString|(R|<local>/s|)
}
public final fun test_2(a: R|A|): R|kotlin/Unit| {
lval s: R|kotlin/String| = when (R|<local>/a|) {
($subj$ is R|B|) -> {
String()
}
==($subj$, Q|C|) -> {
String()
}
}
R|/takeString|(R|<local>/s|)
}
@@ -0,0 +1,24 @@
// ISSUE: KT-37488
sealed class A
class B : A()
object C : A()
fun takeString(s: String) {}
fun test_1(a: A) {
val s = when(a) {
is B -> ""
is C -> ""
}
takeString(s)
}
fun test_2(a: A) {
val s = when(a) {
is B -> ""
C -> ""
}
takeString(s)
}
@@ -0,0 +1,116 @@
FILE: exhaustiveness_sealedSubClass.kt
public sealed class A : R|kotlin/Any| {
private constructor(): R|A| {
super<R|kotlin/Any|>()
}
}
public sealed class B : R|A| {
private constructor(): R|B| {
super<R|A|>()
}
}
public final class C : R|A| {
public constructor(): R|C| {
super<R|A|>()
}
}
public sealed class D : R|B| {
private constructor(): R|D| {
super<R|B|>()
}
}
public sealed class E : R|B| {
private constructor(): R|E| {
super<R|B|>()
}
}
public final fun test_1(e: R|A|): R|kotlin/Unit| {
lval a: R|kotlin/Int| = when (R|<local>/e|) {
($subj$ is R|C|) -> {
Int(1)
}
($subj$ is R|D|) -> {
Int(2)
}
($subj$ is R|E|) -> {
Int(3)
}
}
.R|kotlin/Int.plus|(Int(0))
lval b: R|kotlin/Int| = when (R|<local>/e|) {
($subj$ is R|B|) -> {
Int(1)
}
($subj$ is R|C|) -> {
Int(2)
}
}
.R|kotlin/Int.plus|(Int(0))
lval c: R|kotlin/Int| = when (R|<local>/e|) {
($subj$ is R|B|) -> {
Int(1)
}
($subj$ is R|C|) -> {
Int(2)
}
($subj$ is R|E|) -> {
Int(3)
}
($subj$ is R|D|) -> {
Int(4)
}
}
.R|kotlin/Int.plus|(Int(0))
lval d: R|kotlin/Int| = when (R|<local>/e|) {
($subj$ is R|E|) -> {
Int(1)
}
($subj$ is R|A|) -> {
Int(2)
}
}
.R|kotlin/Int.plus|(Int(0))
}
public final fun test_2(e: R|A|): R|kotlin/Unit| {
lval a: R|ERROR CLASS: Unresolved name: plus| = when (R|<local>/e|) {
($subj$ is R|D|) -> {
Int(1)
}
($subj$ is R|E|) -> {
Int(2)
}
}
.<Unresolved name: plus>#(Int(0))
lval b: R|ERROR CLASS: Unresolved name: plus| = when (R|<local>/e|) {
($subj$ is R|B|) -> {
Int(1)
}
($subj$ is R|D|) -> {
Int(2)
}
($subj$ is R|E|) -> {
Int(3)
}
}
.<Unresolved name: plus>#(Int(0))
lval c: R|ERROR CLASS: Unresolved name: plus| = when (R|<local>/e|) {
($subj$ is R|B|) -> {
Int(1)
}
($subj$ is R|D|) -> {
Int(2)
}
}
.<Unresolved name: plus>#(Int(0))
lval d: R|ERROR CLASS: Unresolved name: plus| = when (R|<local>/e|) {
($subj$ is R|C|) -> {
Int(1)
}
}
.<Unresolved name: plus>#(Int(0))
}
@@ -0,0 +1,54 @@
sealed class A
sealed class B : A()
class C : A()
sealed class D : B()
sealed class E : B()
fun test_1(e: A) {
val a = when (e) {
is C -> 1
is D -> 2
is E -> 3
}.plus(0)
val b = when (e) {
is B -> 1
is C -> 2
}.plus(0)
val c = when (e) {
is B -> 1
is C -> 2
is E -> 3
is D -> 4
}.plus(0)
val d = when (e) {
is E -> 1
is A -> 2
}.plus(0)
}
fun test_2(e: A) {
val a = <!NO_ELSE_IN_WHEN!>when<!> (e) {
is D -> 1
is E -> 2
}.<!UNRESOLVED_REFERENCE{LT}!><!UNRESOLVED_REFERENCE{PSI}!>plus<!>(0)<!>
val b = <!NO_ELSE_IN_WHEN!>when<!> (e) {
is B -> 1
is D -> 2
is E -> 3
}.<!UNRESOLVED_REFERENCE{LT}!><!UNRESOLVED_REFERENCE{PSI}!>plus<!>(0)<!>
val c = <!NO_ELSE_IN_WHEN!>when<!> (e) {
is B -> 1
is D -> 2
}.<!UNRESOLVED_REFERENCE{LT}!><!UNRESOLVED_REFERENCE{PSI}!>plus<!>(0)<!>
val d = <!NO_ELSE_IN_WHEN!>when<!> (e) {
is C -> 1
}.<!UNRESOLVED_REFERENCE{LT}!><!UNRESOLVED_REFERENCE{PSI}!>plus<!>(0)<!>
}