[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
@@ -139,46 +139,6 @@ public class LazyBodyIsNotTouchedTilContractsPhaseTestGenerated extends Abstract
runTest("compiler/fir/analysis-tests/testData/resolve/enumWithCompanion.kt");
}
@TestMetadata("exhaustiveWhenAndDNNType.kt")
public void testExhaustiveWhenAndDNNType() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveWhenAndDNNType.kt");
}
@TestMetadata("exhaustiveWhenAndFlexibleType.kt")
public void testExhaustiveWhenAndFlexibleType() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveWhenAndFlexibleType.kt");
}
@TestMetadata("exhaustiveness_boolean.kt")
public void testExhaustiveness_boolean() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness_boolean.kt");
}
@TestMetadata("exhaustiveness_enum.kt")
public void testExhaustiveness_enum() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness_enum.kt");
}
@TestMetadata("exhaustiveness_enumJava.kt")
public void testExhaustiveness_enumJava() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness_enumJava.kt");
}
@TestMetadata("exhaustiveness_sealedClass.kt")
public void testExhaustiveness_sealedClass() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness_sealedClass.kt");
}
@TestMetadata("exhaustiveness_sealedObject.kt")
public void testExhaustiveness_sealedObject() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness_sealedObject.kt");
}
@TestMetadata("exhaustiveness_sealedSubClass.kt")
public void testExhaustiveness_sealedSubClass() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness_sealedSubClass.kt");
}
@TestMetadata("extension.kt")
public void testExtension() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/extension.kt");
@@ -1271,6 +1231,105 @@ public class LazyBodyIsNotTouchedTilContractsPhaseTestGenerated extends Abstract
}
}
@TestMetadata("compiler/fir/analysis-tests/testData/resolve/exhaustiveness")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Exhaustiveness extends AbstractLazyBodyIsNotTouchedTilContractsPhaseTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInExhaustiveness() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/exhaustiveness"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
}
@TestMetadata("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/negative")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Negative extends AbstractLazyBodyIsNotTouchedTilContractsPhaseTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInNegative() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/negative"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
}
@TestMetadata("missingBooleanBranch.kt")
public void testMissingBooleanBranch() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/negative/missingBooleanBranch.kt");
}
@TestMetadata("missingElse.kt")
public void testMissingElse() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/negative/missingElse.kt");
}
@TestMetadata("missingEnumEntry.kt")
public void testMissingEnumEntry() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/negative/missingEnumEntry.kt");
}
@TestMetadata("missingSealedInheritor.kt")
public void testMissingSealedInheritor() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/negative/missingSealedInheritor.kt");
}
}
@TestMetadata("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/positive")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Positive extends AbstractLazyBodyIsNotTouchedTilContractsPhaseTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInPositive() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/positive"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
}
@TestMetadata("exhaustiveWhenAndDNNType.kt")
public void testExhaustiveWhenAndDNNType() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/positive/exhaustiveWhenAndDNNType.kt");
}
@TestMetadata("exhaustiveWhenAndFlexibleType.kt")
public void testExhaustiveWhenAndFlexibleType() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/positive/exhaustiveWhenAndFlexibleType.kt");
}
@TestMetadata("exhaustiveness_boolean.kt")
public void testExhaustiveness_boolean() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/positive/exhaustiveness_boolean.kt");
}
@TestMetadata("exhaustiveness_enum.kt")
public void testExhaustiveness_enum() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/positive/exhaustiveness_enum.kt");
}
@TestMetadata("exhaustiveness_enumJava.kt")
public void testExhaustiveness_enumJava() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/positive/exhaustiveness_enumJava.kt");
}
@TestMetadata("exhaustiveness_sealedClass.kt")
public void testExhaustiveness_sealedClass() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/positive/exhaustiveness_sealedClass.kt");
}
@TestMetadata("exhaustiveness_sealedObject.kt")
public void testExhaustiveness_sealedObject() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/positive/exhaustiveness_sealedObject.kt");
}
@TestMetadata("exhaustiveness_sealedSubClass.kt")
public void testExhaustiveness_sealedSubClass() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/positive/exhaustiveness_sealedSubClass.kt");
}
}
}
@TestMetadata("compiler/fir/analysis-tests/testData/resolve/expresssions")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -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
}
}
@@ -1,5 +1,5 @@
fun test_1(b: Boolean) {
val x = when (b) {
val x = <!NO_ELSE_IN_WHEN!>when<!> (b) {
true -> 1
}
val y = when (b) {
@@ -13,7 +13,7 @@ fun test_1(b: Boolean) {
}
fun test_2(b: Boolean?) {
val x = when (b) {
val x = <!NO_ELSE_IN_WHEN!>when<!> (b) {
true -> 1
false -> 2
}
@@ -3,12 +3,12 @@ enum class Enum {
}
fun test_1(e: Enum) {
val a = when (e) {
val a = <!NO_ELSE_IN_WHEN!>when<!> (e) {
Enum.A -> 1
Enum.B -> 2
}
val b = when (e) {
val b = <!NO_ELSE_IN_WHEN!>when<!> (e) {
Enum.A -> 1
Enum.B -> 2
is String -> 3
@@ -27,7 +27,7 @@ fun test_1(e: Enum) {
}
fun test_2(e: Enum?) {
val a = when (e) {
val a = <!NO_ELSE_IN_WHEN!>when<!> (e) {
Enum.A -> 1
Enum.B -> 2
Enum.C -> 3
@@ -7,12 +7,12 @@ public enum JavaEnum {
// FILE: main.kt
fun test_1(e: JavaEnum) {
val a = when (e) {
val a = <!NO_ELSE_IN_WHEN!>when<!> (e) {
JavaEnum.A -> 1
JavaEnum.B -> 2
}.<!UNRESOLVED_REFERENCE{LT}!><!UNRESOLVED_REFERENCE{PSI}!>plus<!>(0)<!>
val b = when (e) {
val b = <!NO_ELSE_IN_WHEN!>when<!> (e) {
JavaEnum.A -> 1
JavaEnum.B -> 2
is String -> 3
@@ -31,7 +31,7 @@ fun test_1(e: JavaEnum) {
}
fun test_2(e: JavaEnum?) {
val a = when (e) {
val a = <!NO_ELSE_IN_WHEN!>when<!> (e) {
JavaEnum.A -> 1
JavaEnum.B -> 2
JavaEnum.C -> 3
@@ -7,12 +7,12 @@ sealed class Base {
class C : Base()
fun test_1(e: Base) {
val a = when (e) {
val a = <!NO_ELSE_IN_WHEN!>when<!> (e) {
is Base.A -> 1
is Base.A.B -> 2
}
val b = when (e) {
val b = <!NO_ELSE_IN_WHEN!>when<!> (e) {
is Base.A -> 1
is Base.A.B -> 2
is String -> 3
@@ -31,7 +31,7 @@ fun test_1(e: Base) {
}
fun test_2(e: Base?) {
val a = when (e) {
val a = <!NO_ELSE_IN_WHEN!>when<!> (e) {
is Base.A -> 1
is Base.A.B -> 2
is C -> 3
@@ -32,23 +32,23 @@ fun test_1(e: A) {
}
fun test_2(e: A) {
val a = when (e) {
val a = <!NO_ELSE_IN_WHEN!>when<!> (e) {
is D -> 1
is E -> 2
}.<!UNRESOLVED_REFERENCE{LT}!><!UNRESOLVED_REFERENCE{PSI}!>plus<!>(0)<!>
val b = when (e) {
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 = when (e) {
val c = <!NO_ELSE_IN_WHEN!>when<!> (e) {
is B -> 1
is D -> 2
}.<!UNRESOLVED_REFERENCE{LT}!><!UNRESOLVED_REFERENCE{PSI}!>plus<!>(0)<!>
val d = when (e) {
val d = <!NO_ELSE_IN_WHEN!>when<!> (e) {
is C -> 1
}.<!UNRESOLVED_REFERENCE{LT}!><!UNRESOLVED_REFERENCE{PSI}!>plus<!>(0)<!>
}
+6 -6
View File
@@ -16,12 +16,12 @@ fun get(f: Boolean) = if (f) {A.A1} else {""}
<!CONFLICTING_OVERLOADS!>fun case2()<!> {
val flag: Any = get(false) //string
val l1 = when (flag!!) { // should be NO_ELSE_IN_WHEN
val l1 = <!NO_ELSE_IN_WHEN!>when<!> (flag!!) { // should be NO_ELSE_IN_WHEN
A.A1 -> B()
A.A2 -> B()
}
val l2 = when (flag) {// should be NO_ELSE_IN_WHEN
val l2 = <!NO_ELSE_IN_WHEN!>when<!> (flag) {// should be NO_ELSE_IN_WHEN
A.A1 -> B()
A.A2 -> B()
}
@@ -30,12 +30,12 @@ fun get(f: Boolean) = if (f) {A.A1} else {""}
<!CONFLICTING_OVERLOADS!>fun case2()<!> {
val flag: Any = get(true) //A
val l1 = when (flag!!) {// should be NO_ELSE_IN_WHEN
val l1 = <!NO_ELSE_IN_WHEN!>when<!> (flag!!) {// should be NO_ELSE_IN_WHEN
A.A1 -> B()
A.A2 -> B()
}
val l2 = when (flag) {// should be NO_ELSE_IN_WHEN
val l2 = <!NO_ELSE_IN_WHEN!>when<!> (flag) {// should be NO_ELSE_IN_WHEN
A.A1 -> B()
A.A2 -> B()
}
@@ -44,12 +44,12 @@ fun get(f: Boolean) = if (f) {A.A1} else {""}
fun case3() {
val flag = "" //A
val l1 = when (flag!!) {// should be NO_ELSE_IN_WHEN
val l1 = <!NO_ELSE_IN_WHEN!>when<!> (flag!!) {// should be NO_ELSE_IN_WHEN
A.A1 -> B() //should be INCOMPATIBLE_TYPES
A.A2 -> B() //should be INCOMPATIBLE_TYPES
}
val l2 = when (flag) {// should be NO_ELSE_IN_WHEN
val l2 = <!NO_ELSE_IN_WHEN!>when<!> (flag) {// should be NO_ELSE_IN_WHEN
A.A1 -> B() //should be INCOMPATIBLE_TYPES
A.A2 -> B() //should be INCOMPATIBLE_TYPES
}
@@ -158,54 +158,6 @@ public class FirDiagnosticTestGenerated extends AbstractFirDiagnosticTest {
runTest("compiler/fir/analysis-tests/testData/resolve/enumWithCompanion.kt");
}
@Test
@TestMetadata("exhaustiveWhenAndDNNType.kt")
public void testExhaustiveWhenAndDNNType() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveWhenAndDNNType.kt");
}
@Test
@TestMetadata("exhaustiveWhenAndFlexibleType.kt")
public void testExhaustiveWhenAndFlexibleType() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveWhenAndFlexibleType.kt");
}
@Test
@TestMetadata("exhaustiveness_boolean.kt")
public void testExhaustiveness_boolean() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness_boolean.kt");
}
@Test
@TestMetadata("exhaustiveness_enum.kt")
public void testExhaustiveness_enum() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness_enum.kt");
}
@Test
@TestMetadata("exhaustiveness_enumJava.kt")
public void testExhaustiveness_enumJava() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness_enumJava.kt");
}
@Test
@TestMetadata("exhaustiveness_sealedClass.kt")
public void testExhaustiveness_sealedClass() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness_sealedClass.kt");
}
@Test
@TestMetadata("exhaustiveness_sealedObject.kt")
public void testExhaustiveness_sealedObject() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness_sealedObject.kt");
}
@Test
@TestMetadata("exhaustiveness_sealedSubClass.kt")
public void testExhaustiveness_sealedSubClass() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness_sealedSubClass.kt");
}
@Test
@TestMetadata("extension.kt")
public void testExtension() throws Exception {
@@ -1466,6 +1418,108 @@ public class FirDiagnosticTestGenerated extends AbstractFirDiagnosticTest {
}
}
@Nested
@TestMetadata("compiler/fir/analysis-tests/testData/resolve/exhaustiveness")
@TestDataPath("$PROJECT_ROOT")
public class Exhaustiveness {
@Test
public void testAllFilesPresentInExhaustiveness() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/exhaustiveness"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
}
@Nested
@TestMetadata("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/negative")
@TestDataPath("$PROJECT_ROOT")
public class Negative {
@Test
public void testAllFilesPresentInNegative() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/negative"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
}
@Test
@TestMetadata("missingBooleanBranch.kt")
public void testMissingBooleanBranch() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/negative/missingBooleanBranch.kt");
}
@Test
@TestMetadata("missingElse.kt")
public void testMissingElse() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/negative/missingElse.kt");
}
@Test
@TestMetadata("missingEnumEntry.kt")
public void testMissingEnumEntry() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/negative/missingEnumEntry.kt");
}
@Test
@TestMetadata("missingSealedInheritor.kt")
public void testMissingSealedInheritor() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/negative/missingSealedInheritor.kt");
}
}
@Nested
@TestMetadata("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/positive")
@TestDataPath("$PROJECT_ROOT")
public class Positive {
@Test
public void testAllFilesPresentInPositive() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/positive"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
}
@Test
@TestMetadata("exhaustiveWhenAndDNNType.kt")
public void testExhaustiveWhenAndDNNType() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/positive/exhaustiveWhenAndDNNType.kt");
}
@Test
@TestMetadata("exhaustiveWhenAndFlexibleType.kt")
public void testExhaustiveWhenAndFlexibleType() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/positive/exhaustiveWhenAndFlexibleType.kt");
}
@Test
@TestMetadata("exhaustiveness_boolean.kt")
public void testExhaustiveness_boolean() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/positive/exhaustiveness_boolean.kt");
}
@Test
@TestMetadata("exhaustiveness_enum.kt")
public void testExhaustiveness_enum() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/positive/exhaustiveness_enum.kt");
}
@Test
@TestMetadata("exhaustiveness_enumJava.kt")
public void testExhaustiveness_enumJava() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/positive/exhaustiveness_enumJava.kt");
}
@Test
@TestMetadata("exhaustiveness_sealedClass.kt")
public void testExhaustiveness_sealedClass() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/positive/exhaustiveness_sealedClass.kt");
}
@Test
@TestMetadata("exhaustiveness_sealedObject.kt")
public void testExhaustiveness_sealedObject() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/positive/exhaustiveness_sealedObject.kt");
}
@Test
@TestMetadata("exhaustiveness_sealedSubClass.kt")
public void testExhaustiveness_sealedSubClass() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/positive/exhaustiveness_sealedSubClass.kt");
}
}
}
@Nested
@TestMetadata("compiler/fir/analysis-tests/testData/resolve/expresssions")
@TestDataPath("$PROJECT_ROOT")
@@ -161,54 +161,6 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos
runTest("compiler/fir/analysis-tests/testData/resolve/enumWithCompanion.kt");
}
@Test
@TestMetadata("exhaustiveWhenAndDNNType.kt")
public void testExhaustiveWhenAndDNNType() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveWhenAndDNNType.kt");
}
@Test
@TestMetadata("exhaustiveWhenAndFlexibleType.kt")
public void testExhaustiveWhenAndFlexibleType() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveWhenAndFlexibleType.kt");
}
@Test
@TestMetadata("exhaustiveness_boolean.kt")
public void testExhaustiveness_boolean() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness_boolean.kt");
}
@Test
@TestMetadata("exhaustiveness_enum.kt")
public void testExhaustiveness_enum() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness_enum.kt");
}
@Test
@TestMetadata("exhaustiveness_enumJava.kt")
public void testExhaustiveness_enumJava() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness_enumJava.kt");
}
@Test
@TestMetadata("exhaustiveness_sealedClass.kt")
public void testExhaustiveness_sealedClass() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness_sealedClass.kt");
}
@Test
@TestMetadata("exhaustiveness_sealedObject.kt")
public void testExhaustiveness_sealedObject() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness_sealedObject.kt");
}
@Test
@TestMetadata("exhaustiveness_sealedSubClass.kt")
public void testExhaustiveness_sealedSubClass() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness_sealedSubClass.kt");
}
@Test
@TestMetadata("extension.kt")
public void testExtension() throws Exception {
@@ -1478,6 +1430,111 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos
}
}
@Nested
@TestMetadata("compiler/fir/analysis-tests/testData/resolve/exhaustiveness")
@TestDataPath("$PROJECT_ROOT")
@Execution(ExecutionMode.SAME_THREAD)
public class Exhaustiveness {
@Test
public void testAllFilesPresentInExhaustiveness() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/exhaustiveness"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
}
@Nested
@TestMetadata("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/negative")
@TestDataPath("$PROJECT_ROOT")
@Execution(ExecutionMode.SAME_THREAD)
public class Negative {
@Test
public void testAllFilesPresentInNegative() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/negative"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
}
@Test
@TestMetadata("missingBooleanBranch.kt")
public void testMissingBooleanBranch() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/negative/missingBooleanBranch.kt");
}
@Test
@TestMetadata("missingElse.kt")
public void testMissingElse() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/negative/missingElse.kt");
}
@Test
@TestMetadata("missingEnumEntry.kt")
public void testMissingEnumEntry() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/negative/missingEnumEntry.kt");
}
@Test
@TestMetadata("missingSealedInheritor.kt")
public void testMissingSealedInheritor() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/negative/missingSealedInheritor.kt");
}
}
@Nested
@TestMetadata("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/positive")
@TestDataPath("$PROJECT_ROOT")
@Execution(ExecutionMode.SAME_THREAD)
public class Positive {
@Test
public void testAllFilesPresentInPositive() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/positive"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
}
@Test
@TestMetadata("exhaustiveWhenAndDNNType.kt")
public void testExhaustiveWhenAndDNNType() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/positive/exhaustiveWhenAndDNNType.kt");
}
@Test
@TestMetadata("exhaustiveWhenAndFlexibleType.kt")
public void testExhaustiveWhenAndFlexibleType() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/positive/exhaustiveWhenAndFlexibleType.kt");
}
@Test
@TestMetadata("exhaustiveness_boolean.kt")
public void testExhaustiveness_boolean() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/positive/exhaustiveness_boolean.kt");
}
@Test
@TestMetadata("exhaustiveness_enum.kt")
public void testExhaustiveness_enum() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/positive/exhaustiveness_enum.kt");
}
@Test
@TestMetadata("exhaustiveness_enumJava.kt")
public void testExhaustiveness_enumJava() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/positive/exhaustiveness_enumJava.kt");
}
@Test
@TestMetadata("exhaustiveness_sealedClass.kt")
public void testExhaustiveness_sealedClass() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/positive/exhaustiveness_sealedClass.kt");
}
@Test
@TestMetadata("exhaustiveness_sealedObject.kt")
public void testExhaustiveness_sealedObject() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/positive/exhaustiveness_sealedObject.kt");
}
@Test
@TestMetadata("exhaustiveness_sealedSubClass.kt")
public void testExhaustiveness_sealedSubClass() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/exhaustiveness/positive/exhaustiveness_sealedSubClass.kt");
}
}
}
@Nested
@TestMetadata("compiler/fir/analysis-tests/testData/resolve/expresssions")
@TestDataPath("$PROJECT_ROOT")
@@ -6,11 +6,7 @@
package org.jetbrains.kotlin.fir.checkers.generator
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment
import org.jetbrains.kotlin.fir.expressions.FirTryExpression
import org.jetbrains.kotlin.fir.expressions.*
import java.io.File
fun main(args: Array<String>) {
@@ -23,6 +19,7 @@ fun main(args: Array<String>) {
alias<FirFunctionCall>("FunctionCallChecker")
alias<FirVariableAssignment>("VariableAssignmentChecker")
alias<FirTryExpression>("TryExpressionChecker")
alias<FirWhenExpression>("WhenExpressionChecker")
}
val declarationPackage = "org.jetbrains.kotlin.fir.analysis.checkers.declaration"
@@ -23,12 +23,15 @@ internal class ComposedExpressionCheckers : ExpressionCheckers() {
get() = _variableAssignmentCheckers
override val tryExpressionCheckers: Set<FirTryExpressionChecker>
get() = _tryExpressionCheckers
override val whenExpressionCheckers: Set<FirWhenExpressionChecker>
get() = _whenExpressionCheckers
private val _basicExpressionCheckers: MutableSet<FirBasicExpressionChecker> = mutableSetOf()
private val _qualifiedAccessCheckers: MutableSet<FirQualifiedAccessChecker> = mutableSetOf()
private val _functionCallCheckers: MutableSet<FirFunctionCallChecker> = mutableSetOf()
private val _variableAssignmentCheckers: MutableSet<FirVariableAssignmentChecker> = mutableSetOf()
private val _tryExpressionCheckers: MutableSet<FirTryExpressionChecker> = mutableSetOf()
private val _whenExpressionCheckers: MutableSet<FirWhenExpressionChecker> = mutableSetOf()
@CheckersComponentInternal
internal fun register(checkers: ExpressionCheckers) {
@@ -37,5 +40,6 @@ internal class ComposedExpressionCheckers : ExpressionCheckers() {
_functionCallCheckers += checkers.allFunctionCallCheckers
_variableAssignmentCheckers += checkers.allVariableAssignmentCheckers
_tryExpressionCheckers += checkers.allTryExpressionCheckers
_whenExpressionCheckers += checkers.allWhenExpressionCheckers
}
}
@@ -22,10 +22,12 @@ abstract class ExpressionCheckers {
open val functionCallCheckers: Set<FirFunctionCallChecker> = emptySet()
open val variableAssignmentCheckers: Set<FirVariableAssignmentChecker> = emptySet()
open val tryExpressionCheckers: Set<FirTryExpressionChecker> = emptySet()
open val whenExpressionCheckers: Set<FirWhenExpressionChecker> = emptySet()
@CheckersComponentInternal internal val allBasicExpressionCheckers: Set<FirBasicExpressionChecker> get() = basicExpressionCheckers
@CheckersComponentInternal internal val allQualifiedAccessCheckers: Set<FirQualifiedAccessChecker> get() = qualifiedAccessCheckers + allBasicExpressionCheckers
@CheckersComponentInternal internal val allFunctionCallCheckers: Set<FirFunctionCallChecker> get() = functionCallCheckers + allQualifiedAccessCheckers
@CheckersComponentInternal internal val allVariableAssignmentCheckers: Set<FirVariableAssignmentChecker> get() = variableAssignmentCheckers + allBasicExpressionCheckers
@CheckersComponentInternal internal val allTryExpressionCheckers: Set<FirTryExpressionChecker> get() = tryExpressionCheckers + allBasicExpressionCheckers
@CheckersComponentInternal internal val allWhenExpressionCheckers: Set<FirWhenExpressionChecker> get() = whenExpressionCheckers + allBasicExpressionCheckers
}
@@ -15,9 +15,11 @@ import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.expressions.FirTryExpression
import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment
import org.jetbrains.kotlin.fir.expressions.FirWhenExpression
typealias FirBasicExpressionChecker = FirExpressionChecker<FirStatement>
typealias FirQualifiedAccessChecker = FirExpressionChecker<FirQualifiedAccessExpression>
typealias FirFunctionCallChecker = FirExpressionChecker<FirFunctionCall>
typealias FirVariableAssignmentChecker = FirExpressionChecker<FirVariableAssignment>
typealias FirTryExpressionChecker = FirExpressionChecker<FirTryExpression>
typealias FirWhenExpressionChecker = FirExpressionChecker<FirWhenExpression>
@@ -0,0 +1,31 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.analysis.checkers.expression
import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
import org.jetbrains.kotlin.fir.expressions.FirWhenExpression
object FirExhaustiveWhenChecker : FirWhenExpressionChecker() {
override fun check(expression: FirWhenExpression, context: CheckerContext, reporter: DiagnosticReporter) {
// TODO: add reporting of proper missing clauses, see class WhenMissingCase
if (expression.usedAsExpression && !expression.isExhaustive) {
val factory = if (expression.source?.isIfExpression == true) {
FirErrors.INVALID_IF_AS_EXPRESSION
} else {
FirErrors.NO_ELSE_IN_WHEN
}
reporter.reportOn(expression.source, factory, context)
}
}
private val FirSourceElement.isIfExpression: Boolean
get() = elementType == KtNodeTypes.IF
}
@@ -53,7 +53,7 @@ class ExpressionCheckersDiagnosticComponent(collector: AbstractDiagnosticCollect
}
override fun visitWhenExpression(whenExpression: FirWhenExpression, data: CheckerContext) {
checkers.basicExpressionCheckers.check(whenExpression, data, reporter)
checkers.whenExpressionCheckers.check(whenExpression, data, reporter)
}
override fun visitBinaryLogicExpression(binaryLogicExpression: FirBinaryLogicExpression, data: CheckerContext) {
@@ -84,6 +84,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INITIALIZER_REQUI
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INNER_CLASS_OF_GENERIC_THROWABLE_SUBCLASS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INSTANCE_ACCESS_BEFORE_SUPER_CALL
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INTERFACE_WITH_SUPERCLASS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INVALID_IF_AS_EXPRESSION
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INVALID_TYPE_OF_ANNOTATION_MEMBER
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.LEAKED_IN_PLACE_LAMBDA
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.LOCAL_ANNOTATION_CLASS_ERROR
@@ -100,6 +101,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NOTHING_TO_OVERRI
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NOT_AN_ANNOTATION_CLASS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NOT_A_LOOP_LABEL
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NOT_A_SUPERTYPE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_ELSE_IN_WHEN
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_THIS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_TYPE_FOR_TYPE_PARAMETER
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NULLABLE_TYPE_OF_ANNOTATION_MEMBER
@@ -501,6 +503,10 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension {
// RENDER_TYPE
//)
// When expressions
map.put(NO_ELSE_IN_WHEN, "''when'' expression must be exhaustive")
map.put(INVALID_IF_AS_EXPRESSION, "'if' must have both main and 'else' branches if used as an expression")
// Extended checkers group
map.put(REDUNDANT_VISIBILITY_MODIFIER, "Redundant visibility modifier")
map.put(REDUNDANT_MODALITY_MODIFIER, "Redundant modality modifier")
@@ -230,6 +230,10 @@ object FirErrors {
// TODO: val UNSAFE_OPERATOR_CALL by ...
// TODO: val UNEXPECTED_SAFE_CALL by ...
// When expressions
val NO_ELSE_IN_WHEN by error0<FirSourceElement, KtWhenExpression>(SourceElementPositioningStrategies.WHEN_EXPRESSION)
val INVALID_IF_AS_EXPRESSION by error0<FirSourceElement, KtIfExpression>(SourceElementPositioningStrategies.IF_EXPRESSION)
// Extended checkers group
val REDUNDANT_VISIBILITY_MODIFIER by warning0<FirSourceElement, KtModifierListOwner>(SourceElementPositioningStrategies.VISIBILITY_MODIFIER)
val REDUNDANT_MODALITY_MODIFIER by warning0<FirSourceElement, KtModifierListOwner>(SourceElementPositioningStrategies.MODALITY_MODIFIER)
@@ -334,6 +334,28 @@ object LightTreePositioningStrategies {
return super.mark(node, startOffset, endOffset, tree)
}
}
val WHEN_EXPRESSION = object : LightTreePositioningStrategy() {
override fun mark(
node: LighterASTNode,
startOffset: Int,
endOffset: Int,
tree: FlyweightCapableTreeStructure<LighterASTNode>
): List<TextRange> {
return markElement(tree.whenKeyword(node) ?: node, startOffset, endOffset, tree, node)
}
}
val IF_EXPRESSION = object : LightTreePositioningStrategy() {
override fun mark(
node: LighterASTNode,
startOffset: Int,
endOffset: Int,
tree: FlyweightCapableTreeStructure<LighterASTNode>
): List<TextRange> {
return markElement(tree.ifKeyword(node) ?: node, startOffset, endOffset, tree, node)
}
}
}
fun FirSourceElement.hasValOrVar(): Boolean =
@@ -354,6 +376,12 @@ private fun FlyweightCapableTreeStructure<LighterASTNode>.dotOperator(node: Ligh
private fun FlyweightCapableTreeStructure<LighterASTNode>.initKeyword(node: LighterASTNode): LighterASTNode? =
findChildByType(node, KtTokens.INIT_KEYWORD)
private fun FlyweightCapableTreeStructure<LighterASTNode>.whenKeyword(node: LighterASTNode): LighterASTNode? =
findChildByType(node, KtTokens.WHEN_KEYWORD)
private fun FlyweightCapableTreeStructure<LighterASTNode>.ifKeyword(node: LighterASTNode): LighterASTNode? =
findChildByType(node, KtTokens.IF_KEYWORD)
private fun FlyweightCapableTreeStructure<LighterASTNode>.nameIdentifier(node: LighterASTNode): LighterASTNode? =
findChildByType(node, KtTokens.IDENTIFIER)
@@ -102,4 +102,14 @@ object SourceElementPositioningStrategies {
LightTreePositioningStrategies.DOT_BY_SELECTOR,
PositioningStrategies.DOT_BY_SELECTOR
)
}
val WHEN_EXPRESSION = SourceElementPositioningStrategy(
LightTreePositioningStrategies.WHEN_EXPRESSION,
PositioningStrategies.WHEN_EXPRESSION
)
val IF_EXPRESSION = SourceElementPositioningStrategy(
LightTreePositioningStrategies.IF_EXPRESSION,
PositioningStrategies.IF_EXPRESSION
)
}
@@ -28,4 +28,8 @@ object CommonExpressionCheckers : ExpressionCheckers() {
override val tryExpressionCheckers: Set<FirTryExpressionChecker> = setOf(
FirCatchParameterChecker
)
override val whenExpressionCheckers: Set<FirWhenExpressionChecker> = setOf(
FirExhaustiveWhenChecker
)
}
@@ -1105,7 +1105,10 @@ class ExpressionsConverter(
private val LighterASTNode.usedAsExpression: Boolean
get() {
val parent = getParent() ?: return true
var parent = getParent() ?: return true
if (parent.elementType == ANNOTATED_EXPRESSION) {
parent = parent.getParent() ?: return true
}
val parentTokenType = parent.tokenType
if (parentTokenType == BLOCK) return false
if (parentTokenType == ELSE || parentTokenType == WHEN_ENTRY) {
@@ -1625,6 +1625,10 @@ class RawFirBuilder(
private val KtExpression.usedAsExpression: Boolean
get() {
var parent = parent
if (parent.elementType == KtNodeTypes.ANNOTATED_EXPRESSION) {
parent = parent.parent
}
if (parent is KtBlockExpression) return false
when (parent.elementType) {
KtNodeTypes.ELSE, KtNodeTypes.WHEN_ENTRY -> {
@@ -507,6 +507,13 @@ object PositioningStrategies {
}
}
@JvmField
val IF_EXPRESSION: PositioningStrategy<KtIfExpression> = object : PositioningStrategy<KtIfExpression>() {
override fun mark(element: KtIfExpression): List<TextRange> {
return markElement(element.ifKeyword)
}
}
@JvmField
val WHEN_CONDITION_IN_RANGE: PositioningStrategy<KtWhenConditionInRange> = object : PositioningStrategy<KtWhenConditionInRange>() {
override fun mark(element: KtWhenConditionInRange): List<TextRange> {
@@ -72,7 +72,7 @@ fun main1() {
1."sdf"
1.{}
1.if (true) {}
1.<!INVALID_IF_AS_EXPRESSION!>if<!> (true) {}
}
fun test() {
@@ -70,7 +70,7 @@ fun blockReturnValueTypeMatch1() : Int {
return if (1 > 2) 1.0 else 2.0
}
fun blockReturnValueTypeMatch2() : Int {
return if (1 > 2) 1
return <!INVALID_IF_AS_EXPRESSION!>if<!> (1 > 2) 1
}
fun blockReturnValueTypeMatch3() : Int {
return if (1 > 2) else 1
@@ -106,7 +106,7 @@ fun blockReturnValueTypeMatch9() : Int {
1.0
}
fun blockReturnValueTypeMatch10() : Int {
return if (1 > 2)
return <!INVALID_IF_AS_EXPRESSION!>if<!> (1 > 2)
1
}
fun blockReturnValueTypeMatch11() : Int {
@@ -29,7 +29,7 @@ fun foo(d: Direction) = when(d) { //no 'else' should be requested
Direction.EAST -> 4
}
fun foo1(d: Direction) = when(d) {
fun foo1(d: Direction) = <!NO_ELSE_IN_WHEN!>when<!>(d) {
Direction.NORTH -> 1
Direction.SOUTH -> 2
Direction.WEST -> 3
@@ -41,7 +41,7 @@ fun bar(c: Color) = when (c) {
Color.BLUE -> 3
}
fun bar1(c: Color) = when (c) {
fun bar1(c: Color) = <!NO_ELSE_IN_WHEN!>when<!> (c) {
Color.RED -> 1
Color.GREEN -> 2
}
}
@@ -1,16 +0,0 @@
fun foo(x: Unit) = x
fun test() {
if (false);
if (true);
val x = if (false);
foo(x)
val y: Unit = if (false);
foo(y)
foo({if (1==1);}())
return if (true);
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
fun foo(x: Unit) = x
fun test() {
@@ -11,7 +11,7 @@ fun testMixedIfAndWhen() =
else 1
true -> if (true) 42
else println()
else -> if (true) println()
else -> <!INVALID_IF_AS_EXPRESSION!>if<!> (true) println()
}
else println()
@@ -28,4 +28,4 @@ fun testWrappedExpressions() =
}
else {
(((((42)) + 1)))
}
}
@@ -11,21 +11,21 @@ val mlist = MList()
fun work() {}
val xx1 = if (true) 42
val xx2: Unit = if (true) 42
val xx3 = idAny(if (true) 42)
val xx4 = id(if (true) 42)
val xx5 = idUnit(if (true) 42)
val xx6 = null ?: if (true) 42
val xx7 = "" + if (true) 42
val xx1 = <!INVALID_IF_AS_EXPRESSION!>if<!> (true) 42
val xx2: Unit = <!INVALID_IF_AS_EXPRESSION!>if<!> (true) 42
val xx3 = idAny(<!INVALID_IF_AS_EXPRESSION!>if<!> (true) 42)
val xx4 = id(<!INVALID_IF_AS_EXPRESSION!>if<!> (true) 42)
val xx5 = idUnit(<!INVALID_IF_AS_EXPRESSION!>if<!> (true) 42)
val xx6 = null ?: <!INVALID_IF_AS_EXPRESSION!>if<!> (true) 42
val xx7 = "" + <!INVALID_IF_AS_EXPRESSION!>if<!> (true) 42
val wxx1 = when { true -> 42 }
val wxx2: Unit = when { true -> 42 }
val wxx3 = idAny(when { true -> 42 })
val wxx4 = id(when { true -> 42 })
val wxx5 = idUnit(when { true -> 42 })
val wxx6 = null ?: when { true -> 42 }
val wxx7 = "" + when { true -> 42 }
val wxx1 = <!NO_ELSE_IN_WHEN!>when<!> { true -> 42 }
val wxx2: Unit = <!NO_ELSE_IN_WHEN!>when<!> { true -> 42 }
val wxx3 = idAny(<!NO_ELSE_IN_WHEN!>when<!> { true -> 42 })
val wxx4 = id(<!NO_ELSE_IN_WHEN!>when<!> { true -> 42 })
val wxx5 = idUnit(<!NO_ELSE_IN_WHEN!>when<!> { true -> 42 })
val wxx6 = null ?: <!NO_ELSE_IN_WHEN!>when<!> { true -> 42 }
val wxx7 = "" + <!NO_ELSE_IN_WHEN!>when<!> { true -> 42 }
val fn1 = { if (true) 42 }
val fn2 = { if (true) mlist.add() }
@@ -41,24 +41,24 @@ val ufn4: () -> Unit = { when { true -> 42 } }
val ufn5: () -> Unit = { when { true -> mlist.add() } }
val ufn6: () -> Unit = { when { true -> work() } }
fun f1() = if (true) work()
fun f2() = if (true) mlist.add()
fun f3() = if (true) 42
fun f4(): Unit = if (true) work()
fun f5(): Unit = if (true) mlist.add()
fun f6(): Unit = if (true) 42
fun g1() = when { true -> work() }
fun g2() = when { true -> mlist.add() }
fun g3() = when { true -> 42 }
fun g4(): Unit = when { true -> work() }
fun g5(): Unit = when { true -> mlist.add() }
fun g6(): Unit = when { true -> 42 }
fun f1() = <!INVALID_IF_AS_EXPRESSION!>if<!> (true) work()
fun f2() = <!INVALID_IF_AS_EXPRESSION!>if<!> (true) mlist.add()
fun f3() = <!INVALID_IF_AS_EXPRESSION!>if<!> (true) 42
fun f4(): Unit = <!INVALID_IF_AS_EXPRESSION!>if<!> (true) work()
fun f5(): Unit = <!INVALID_IF_AS_EXPRESSION!>if<!> (true) mlist.add()
fun f6(): Unit = <!INVALID_IF_AS_EXPRESSION!>if<!> (true) 42
fun g1() = <!NO_ELSE_IN_WHEN!>when<!> { true -> work() }
fun g2() = <!NO_ELSE_IN_WHEN!>when<!> { true -> mlist.add() }
fun g3() = <!NO_ELSE_IN_WHEN!>when<!> { true -> 42 }
fun g4(): Unit = <!NO_ELSE_IN_WHEN!>when<!> { true -> work() }
fun g5(): Unit = <!NO_ELSE_IN_WHEN!>when<!> { true -> mlist.add() }
fun g6(): Unit = <!NO_ELSE_IN_WHEN!>when<!> { true -> 42 }
fun foo1(x: String?) {
"" + if (true) 42
"" + <!INVALID_IF_AS_EXPRESSION!>if<!> (true) 42
w@while (true) {
x ?: if (true) break
x ?: when { true -> break@w }
x ?: <!INVALID_IF_AS_EXPRESSION!>if<!> (true) break
x ?: <!NO_ELSE_IN_WHEN!>when<!> { true -> break@w }
}
}
@@ -4,7 +4,7 @@
fun example() {
val a = if (true) true else false
val b = if (true) else false
val c = if (true) true
val c = <!INVALID_IF_AS_EXPRESSION!>if<!> (true) true
val d = if (true) true else;
val e = if (true) {} else false
val f = if (true) true else {}
@@ -27,7 +27,7 @@ fun example() {
}()
fun t(): Boolean {
return if (true) true
return <!INVALID_IF_AS_EXPRESSION!>if<!> (true) true
}
return if (true) true else {}
@@ -99,10 +99,10 @@ fun testImplicitCoercion() {
else -> z--
}
var iff = if (true) {
var iff = <!INVALID_IF_AS_EXPRESSION!>if<!> (true) {
z = 34
}
val g = if (true) 4
val g = <!INVALID_IF_AS_EXPRESSION!>if<!> (true) 4
val h = if (false) 4 else {}
<!INAPPLICABLE_CANDIDATE!>bar<!>(if (true) {
@@ -25,7 +25,7 @@ fun t1(x: Int) = when(x) {
else -> 1
}
fun t5(x: Int) = when (x) {
fun t5(x: Int) = <!NO_ELSE_IN_WHEN!>when<!> (x) {
is Int -> 1
2 -> 2
}
@@ -18,7 +18,7 @@ fun f3(s: Number?) {
}
fun f4(s: Int?) {
var u = if (s!! == 42);
if (u == Unit) u = if (s == 239);
var u = <!INVALID_IF_AS_EXPRESSION!>if<!> (s!! == 42);
if (u == Unit) u = <!INVALID_IF_AS_EXPRESSION!>if<!> (s == 239);
return u
}
}
@@ -142,7 +142,7 @@ fun getStringLength(obj : Any) : Char? {
}
fun toInt(i: Int?): Int = if (i != null) i else 0
fun illegalWhenBody(a: Any): Int = when(a) {
fun illegalWhenBody(a: Any): Int = <!NO_ELSE_IN_WHEN!>when<!>(a) {
is Int -> a
is String -> a
}
@@ -13,11 +13,11 @@ fun test(x: Stmt): String =
}
fun test2(x: Stmt): String =
when (x) {
<!NO_ELSE_IN_WHEN!>when<!> (x) {
is Expr -> "expr"
}
fun test3(x: Expr): String =
when (x) {
<!NO_ELSE_IN_WHEN!>when<!> (x) {
is Stmt -> "stmt"
}
}
@@ -15,7 +15,7 @@ sealed class Base {
// No else required
}
fun bar() = when (this) {
fun bar() = <!NO_ELSE_IN_WHEN!>when<!> (this) {
is A -> 1
is B.B1 -> 2
}
@@ -31,4 +31,4 @@ sealed class Base {
A.A1 -> 2
is A.A2 -> 3
}
}
}
@@ -1,24 +0,0 @@
sealed class A
sealed class B : A()
class C : B()
class D : B()
fun test(a: A): Any {
return when (a) {
is C -> ""
is D -> ""
}
}
fun test2(a: A): Any {
return when (a) {
is B -> ""
}
}
fun test3(a: A): Any {
return when (a) {
is D -> ""
}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
sealed class A
sealed class B : A()
@@ -1,47 +0,0 @@
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
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
sealed class Sealed() {
object First: Sealed()
open class NonFirst: Sealed() {
@@ -1,64 +0,0 @@
// ISSUE: KT-13495
// !DIAGNOSTICS: -UNUSED_VARIABLE
// !LANGUAGE: +AllowSealedInheritorsInDifferentFilesOfSamePackage
// FILE: Base.kt
sealed class Base {
class A : Base()
}
// FILE: B.kt
class B : Base()
// FILE: Container.kt
class Containter {
class C : Base()
inner class D : Base()
}
// FILE: main.kt
fun test_OK(base: Base) {
val x = when (base) {
is Base.A -> 1
is B -> 2
is Containter.C -> 3
is Containter.D -> 4
}
}
fun test_error_1(base: Base) {
val x = when (base) {
is B -> 2
is Containter.C -> 3
is Containter.D -> 4
}
}
fun test_error_2(base: Base) {
val x = when (base) {
is Base.A -> 1
is Containter.C -> 3
is Containter.D -> 4
}
}
fun test_error_3(base: Base) {
val x = when (base) {
is Base.A -> 1
is B -> 2
is Containter.D -> 4
}
}
fun test_error_4(base: Base) {
val x = when (base) {
is Base.A -> 1
is B -> 2
is Containter.C -> 3
}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// ISSUE: KT-13495
// !DIAGNOSTICS: -UNUSED_VARIABLE
// !LANGUAGE: +AllowSealedInheritorsInDifferentFilesOfSamePackage
@@ -1,14 +0,0 @@
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
}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
sealed class Sealed(val x: Int) {
object First: Sealed(12)
open class NonFirst(x: Int, val y: Int): Sealed(x) {
@@ -1,14 +0,0 @@
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
}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
sealed class Sealed(val x: Int) {
object First: Sealed(12)
open class NonFirst(x: Int, val y: Int): Sealed(x) {
@@ -1,22 +0,0 @@
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
}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
sealed class Sealed(val x: Int) {
interface ITuple {
val x: Int
@@ -7,7 +7,7 @@ sealed class Sealed {
}
fun foo(s: Sealed): Int {
return when(s) {
return <!NO_ELSE_IN_WHEN!>when<!>(s) {
is Sealed.First -> 1
!is Any -> 0
}
@@ -9,7 +9,7 @@ sealed class Tree {
is Node -> this.left.max()
}
fun maxIsClass(): Int = when(this) {
fun maxIsClass(): Int = <!NO_ELSE_IN_WHEN!>when<!>(this) {
Empty -> -1
Leaf -> 0
is Node -> this.left.max()
@@ -20,4 +20,4 @@ sealed class Tree {
is Node -> this.left.max()
else -> -1
}
}
}
@@ -1,11 +0,0 @@
// !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
}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_EXPRESSION
sealed class Sealed {
@@ -10,7 +10,7 @@ object CC : C()
fun foo(a: A) {
if (a is B) {
if (a is C) {
val t = when (a) {
val t = <!NO_ELSE_IN_WHEN!>when<!> (a) {
is CC -> "CC"
}
}
@@ -20,9 +20,9 @@ fun foo(a: A) {
fun foo2(a: A) {
if (a is C) {
if (a is B) {
val t = when (a) {
val t = <!NO_ELSE_IN_WHEN!>when<!> (a) {
is CC -> "CC"
}
}
}
}
}
@@ -0,0 +1,9 @@
public fun foo(x: String?, y: String?): Int {
while (true) {
x ?: <!INVALID_IF_AS_EXPRESSION!>if<!> (y == null) break
// y is nullable if x != null
y<!UNSAFE_CALL!>.<!>length
}
// y is null because of the break
return y<!UNSAFE_CALL!>.<!>length
}
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
public fun foo(x: String?, y: String?): Int {
while (true) {
x ?: if (y == null) break
@@ -7,4 +6,4 @@ public fun foo(x: String?, y: String?): Int {
}
// y is null because of the break
return y<!UNSAFE_CALL!>.<!>length
}
}
@@ -1,5 +0,0 @@
fun foo() {}
val x: Unit? = when ("A") {
"B" -> foo()
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
fun foo() {}
val x: Unit? = <!NO_ELSE_IN_WHEN!>when<!> ("A") {
@@ -12,7 +12,7 @@
enum class E { A, B }
fun test(e: E?) = when (e) {
fun test(e: E?) = <!NO_ELSE_IN_WHEN!>when<!> (e) {
E.A -> 1
E.B -> 2
}
@@ -1,18 +1,18 @@
/*
* KOTLIN DIAGNOSTICS SPEC TEST (POSITIVE)
* KOTLIN DIAGNOSTICS SPEC TEST (NEGATIVE)
*
* SPEC VERSION: 0.1-152
* SPEC VERSION: 0.1-313
* PRIMARY LINKS: expressions, when-expression -> paragraph 5 -> sentence 1
* expressions, when-expression, exhaustive-when-expressions -> paragraph 1 -> sentence 1
* expressions, when-expression, exhaustive-when-expressions -> paragraph 2 -> sentence 3
* expressions, when-expression, exhaustive-when-expressions -> paragraph 2 -> sentence 10
* expressions, when-expression, exhaustive-when-expressions -> paragraph 2 -> sentence 4
* expressions, when-expression, exhaustive-when-expressions -> paragraph 2 -> sentence 5
*/
// See also: KT-3743
fun foo(arg: Boolean?): String {
// Must be NOT exhaustive
return when(arg) {
true -> "truth"
false -> "falsehood"
fun foo(arg: Boolean): String {
// Must be exhaustive
return <!NO_ELSE_IN_WHEN!>when<!>(arg) {
2 == 2 -> "truth"
2 == 1 -> "falsehood"
}
}
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
/*
* KOTLIN DIAGNOSTICS SPEC TEST (NEGATIVE)
*
@@ -12,7 +12,7 @@ enum class MyEnum {
}
fun foo(x: MyEnum): Int {
return when (x) {
return <!NO_ELSE_IN_WHEN!>when<!> (x) {
is <!UNRESOLVED_REFERENCE!>MyEnum.A<!> -> 1
is <!UNRESOLVED_REFERENCE!>MyEnum.B<!> -> 2
is <!UNRESOLVED_REFERENCE!>MyEnum.C<!> -> 3
@@ -13,7 +13,7 @@ enum class MyEnum {
}
fun foo(x: MyEnum): Int {
return when (x) {
return <!NO_ELSE_IN_WHEN!>when<!> (x) {
MyEnum.A -> 1
is <!UNRESOLVED_REFERENCE!>MyEnum.B<!> -> 2
is <!UNRESOLVED_REFERENCE!>MyEnum.C<!> -> 3
@@ -1,16 +0,0 @@
/*
* KOTLIN DIAGNOSTICS SPEC TEST (NEGATIVE)
*
* SPEC VERSION: 0.1-152
* PRIMARY LINKS: expressions, when-expression -> paragraph 5 -> sentence 1
* expressions, when-expression -> paragraph 9 -> sentence 2
* expressions, when-expression, exhaustive-when-expressions -> paragraph 1 -> sentence 1
*/
fun foo(x: Int) {
val y: Unit = when (x) {
2 -> {}
3 -> {}
}
return y
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
/*
* KOTLIN DIAGNOSTICS SPEC TEST (NEGATIVE)
*
@@ -1,15 +0,0 @@
/*
* KOTLIN DIAGNOSTICS SPEC TEST (NEGATIVE)
*
* SPEC VERSION: 0.1-152
* PRIMARY LINKS: expressions, when-expression -> paragraph 5 -> sentence 1
* expressions, when-expression -> paragraph 9 -> sentence 2
* expressions, when-expression, exhaustive-when-expressions -> paragraph 1 -> sentence 1
*/
fun foo(x: Int): Any {
val v = when (x) {
2 -> 0
}
return v
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
/*
* KOTLIN DIAGNOSTICS SPEC TEST (NEGATIVE)
*
@@ -1,14 +0,0 @@
/*
* KOTLIN DIAGNOSTICS SPEC TEST (NEGATIVE)
*
* SPEC VERSION: 0.1-152
* PRIMARY LINKS: expressions, when-expression -> paragraph 5 -> sentence 1
* expressions, when-expression -> paragraph 9 -> sentence 2
* expressions, when-expression, exhaustive-when-expressions -> paragraph 1 -> sentence 1
*/
fun foo(x: Int): Any {
return when (x) {
2 -> 0
}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
/*
* KOTLIN DIAGNOSTICS SPEC TEST (NEGATIVE)
*
@@ -1,16 +0,0 @@
/*
* KOTLIN DIAGNOSTICS SPEC TEST (NEGATIVE)
*
* SPEC VERSION: 0.1-152
* PRIMARY LINKS: expressions, when-expression -> paragraph 5 -> sentence 1
* expressions, when-expression -> paragraph 9 -> sentence 2
* expressions, when-expression, exhaustive-when-expressions -> paragraph 1 -> sentence 1
*/
fun foo(x: Int) {
return when (x) {
2 -> {}
3 -> {}
}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
/*
* KOTLIN DIAGNOSTICS SPEC TEST (NEGATIVE)
*
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
/*
* KOTLIN DIAGNOSTICS SPEC TEST (POSITIVE)
*
@@ -1,29 +0,0 @@
/*
* KOTLIN DIAGNOSTICS SPEC TEST (NEGATIVE)
*
* SPEC VERSION: 0.1-152
* PRIMARY LINKS: expressions, when-expression -> paragraph 5 -> sentence 1
* expressions, when-expression, exhaustive-when-expressions -> paragraph 1 -> sentence 1
* expressions, when-expression, exhaustive-when-expressions -> paragraph 2 -> sentence 9
*/
// See KT-6399: exhaustive whens on platform enums
// FILE: J.java
public enum J {
A, B;
public static J create() {
return J.A;
}
}
// FILE: K.kt
fun foo(): Int {
// When is not-exhaustive
return when (J.create()) {
J.A -> 1
}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
/*
* KOTLIN DIAGNOSTICS SPEC TEST (NEGATIVE)
*
@@ -13,7 +13,7 @@
enum class X { A, B }
fun foo(arg: X?): Int {
if (arg != null) {
return when (arg) {
return <!NO_ELSE_IN_WHEN!>when<!> (arg) {
X.B -> 2
}
}
@@ -1,32 +0,0 @@
// !DIAGNOSTICS: -UNUSED_VARIABLE
/*
* KOTLIN DIAGNOSTICS SPEC TEST (NEGATIVE)
*
* SPEC VERSION: 0.1-152
* PRIMARY LINKS: expressions, when-expression -> paragraph 5 -> sentence 1
* expressions, when-expression -> paragraph 6 -> sentence 1
* expressions, when-expression, exhaustive-when-expressions -> paragraph 2 -> sentence 6
* expressions, when-expression, exhaustive-when-expressions -> paragraph 2 -> sentence 7
* expressions, when-expression, exhaustive-when-expressions -> paragraph 2 -> sentence 8
*/
sealed class A {
class B: A() {
class C: A()
}
}
class D: A()
fun test(a: A) {
val nonExhaustive = when (a) {
is A.B -> "B"
is A.B.C -> "C"
}
val exhaustive = when (a) {
is A.B -> "B"
is A.B.C -> "C"
is D -> "D"
}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_VARIABLE
/*
* KOTLIN DIAGNOSTICS SPEC TEST (NEGATIVE)
+1 -1
View File
@@ -27,7 +27,7 @@ fun foo(): Int {
fun bar(): Int {
val a = "a"
if (a.length > 0) {
return when (a) {
return <!NO_ELSE_IN_WHEN!>when<!> (a) {
"a" -> 1
}
}
@@ -38,4 +38,4 @@ fun test4() {
2 -> bar(x, y)
}
}
}
}
@@ -1,18 +0,0 @@
// !LANGUAGE: +VariableDeclarationInWhenSubject
// !DIAGNOSTICS: -UNUSED_VARIABLE
enum class E { FIRST, SECOND }
fun testSmartcastToEnumInSubjectInitializer1(e: E?) {
val x1 = when (val ne = e!!) {
E.FIRST -> "f"
E.SECOND -> "s"
}
}
fun testSmartcastToEnumInSubjectInitializer2(e: E?) {
val x2 = when (val ne: Any = e!!) { // NB explicit type annotation
E.FIRST -> "f"
E.SECOND -> "s"
}
}

Some files were not shown because too many files have changed in this diff Show More