[FE 1.0] Prohibit confusing syntax inside when branches

^KT-48385 Fixed
This commit is contained in:
Dmitriy Novozhilov
2021-10-20 16:40:13 +03:00
committed by teamcityserver
parent 653fc85461
commit 1513e739c6
28 changed files with 1323 additions and 28 deletions
@@ -14,7 +14,7 @@
fun foo(arg: Boolean): String {
// Must be exhaustive
return <!NO_ELSE_IN_WHEN!>when<!>(arg) {
2 == 2 -> "truth"
2 == 1 -> "falsehood"
(2 == 2) -> "truth"
(2 == 1) -> "falsehood"
}
}
@@ -0,0 +1,61 @@
// LANGUAGE: +ProhibitConfusingSyntaxInWhenBranches
// DIAGNOSTICS: -INCOMPATIBLE_TYPES, -NON_EXHAUSTIVE_WHEN_STATEMENT, -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE
// ISSUE: KT-48385
operator fun Boolean.plus(other: Boolean): Boolean = true
operator fun Boolean.minus(other: Boolean): Boolean = true
operator fun Boolean.times(other: Boolean): Boolean = true
operator fun Boolean.div(other: Boolean): Boolean = true
operator fun Boolean.rem(other: Boolean): Boolean = true
operator fun Boolean.rangeTo(other: Boolean): Boolean = true
fun Boolean.id(): Boolean = true
operator fun Boolean.inc(): Boolean = true
operator fun Boolean.dec(): Boolean = true
operator fun Boolean.plusAssign(other: Boolean) {}
operator fun Boolean.minusAssign(other: Boolean) {}
operator fun Boolean.timesAssign(other: Boolean) {}
operator fun Boolean.divAssign(other: Boolean) {}
operator fun Boolean.remAssign(other: Boolean) {}
operator fun Any?.contains(other: Any): Boolean = false
fun testWithSubject_ok(x: Boolean, y: Boolean?, any: Any, z: Boolean) {
when {
x.id() -> {}
<!CONDITION_TYPE_MISMATCH!>y?.id()<!> -> {}
<!CONDITION_TYPE_MISMATCH!>any as? Boolean<!> -> {}
any as Boolean -> {}
x * x -> {}
x / x -> {}
x % x -> {}
x + x -> {}
x - x -> {}
x..x -> {}
<!CONDITION_TYPE_MISMATCH!>y<!> -> {}
y ?: x -> {}
x in x -> {}
x !in x -> {}
x is String -> {}
x !is String -> {}
x <!OVERLOAD_RESOLUTION_AMBIGUITY!><<!> x -> {}
x <!OVERLOAD_RESOLUTION_AMBIGUITY!>><!> x -> {}
x <!OVERLOAD_RESOLUTION_AMBIGUITY!><=<!> x -> {}
x <!OVERLOAD_RESOLUTION_AMBIGUITY!>>=<!> x -> {}
x == x -> {}
x != x -> {}
x === x -> {}
x !== x -> {}
x && x -> {}
x || x -> {}
}
var b = z
when (z) {
b++ -> {}
b-- -> {}
}
}
@@ -0,0 +1,61 @@
// LANGUAGE: +ProhibitConfusingSyntaxInWhenBranches
// DIAGNOSTICS: -INCOMPATIBLE_TYPES, -NON_EXHAUSTIVE_WHEN_STATEMENT, -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE
// ISSUE: KT-48385
operator fun Boolean.plus(other: Boolean): Boolean = true
operator fun Boolean.minus(other: Boolean): Boolean = true
operator fun Boolean.times(other: Boolean): Boolean = true
operator fun Boolean.div(other: Boolean): Boolean = true
operator fun Boolean.rem(other: Boolean): Boolean = true
operator fun Boolean.rangeTo(other: Boolean): Boolean = true
fun Boolean.id(): Boolean = true
operator fun Boolean.inc(): Boolean = true
operator fun Boolean.dec(): Boolean = true
operator fun Boolean.plusAssign(other: Boolean) {}
operator fun Boolean.minusAssign(other: Boolean) {}
operator fun Boolean.timesAssign(other: Boolean) {}
operator fun Boolean.divAssign(other: Boolean) {}
operator fun Boolean.remAssign(other: Boolean) {}
operator fun Any?.contains(other: Any): Boolean = false
fun testWithSubject_ok(x: Boolean, y: Boolean?, any: Any, z: Boolean) {
when {
x.id() -> {}
<!TYPE_MISMATCH!>y?.id()<!> -> {}
<!TYPE_MISMATCH!>any as? Boolean<!> -> {}
any as Boolean -> {}
x * x -> {}
x / x -> {}
x % x -> {}
x + x -> {}
x - x -> {}
x..x -> {}
<!TYPE_MISMATCH!>y<!> -> {}
y ?: x -> {}
x in x -> {}
x !in x -> {}
x is String -> {}
x !is String -> {}
x <!OVERLOAD_RESOLUTION_AMBIGUITY!><<!> x -> {}
x <!OVERLOAD_RESOLUTION_AMBIGUITY!>><!> x -> {}
x <!OVERLOAD_RESOLUTION_AMBIGUITY!><=<!> x -> {}
x <!OVERLOAD_RESOLUTION_AMBIGUITY!>>=<!> x -> {}
x == x -> {}
x != x -> {}
<!DEPRECATED_IDENTITY_EQUALS!>x === x<!> -> {}
<!DEPRECATED_IDENTITY_EQUALS!>x !== x<!> -> {}
x && x -> {}
x || x -> {}
}
var b = z
when (z) {
b++ -> {}
b-- -> {}
}
}
@@ -0,0 +1,19 @@
package
public fun testWithSubject_ok(/*0*/ x: kotlin.Boolean, /*1*/ y: kotlin.Boolean?, /*2*/ any: kotlin.Any, /*3*/ z: kotlin.Boolean): kotlin.Unit
public operator fun kotlin.Any?.contains(/*0*/ other: kotlin.Any): kotlin.Boolean
public operator fun kotlin.Boolean.dec(): kotlin.Boolean
public operator fun kotlin.Boolean.div(/*0*/ other: kotlin.Boolean): kotlin.Boolean
public operator fun kotlin.Boolean.divAssign(/*0*/ other: kotlin.Boolean): kotlin.Unit
public fun kotlin.Boolean.id(): kotlin.Boolean
public operator fun kotlin.Boolean.inc(): kotlin.Boolean
public operator fun kotlin.Boolean.minus(/*0*/ other: kotlin.Boolean): kotlin.Boolean
public operator fun kotlin.Boolean.minusAssign(/*0*/ other: kotlin.Boolean): kotlin.Unit
public operator fun kotlin.Boolean.plus(/*0*/ other: kotlin.Boolean): kotlin.Boolean
public operator fun kotlin.Boolean.plusAssign(/*0*/ other: kotlin.Boolean): kotlin.Unit
public operator fun kotlin.Boolean.rangeTo(/*0*/ other: kotlin.Boolean): kotlin.Boolean
public operator fun kotlin.Boolean.rem(/*0*/ other: kotlin.Boolean): kotlin.Boolean
public operator fun kotlin.Boolean.remAssign(/*0*/ other: kotlin.Boolean): kotlin.Unit
public operator fun kotlin.Boolean.times(/*0*/ other: kotlin.Boolean): kotlin.Boolean
public operator fun kotlin.Boolean.timesAssign(/*0*/ other: kotlin.Boolean): kotlin.Unit
@@ -0,0 +1,236 @@
// LANGUAGE: +ProhibitConfusingSyntaxInWhenBranches
// DIAGNOSTICS: -INCOMPATIBLE_TYPES, -NON_EXHAUSTIVE_WHEN_STATEMENT, -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE
// ISSUE: KT-48385
interface A {
operator fun plus(other: A): A
operator fun minus(other: A): A
operator fun times(other: A): A
operator fun div(other: A): A
operator fun rem(other: A): A
operator fun rangeTo(other: A): A
operator fun compareTo(other: A): Int
fun id(): A
}
interface B {
operator fun inc(): B
operator fun dec(): B
operator fun plusAssign(other: B)
operator fun minusAssign(other: B)
operator fun timesAssign(other: B)
operator fun divAssign(other: B)
operator fun remAssign(other: B)
}
operator fun Any?.contains(other: Any): Boolean = false
fun testWithSubject_ok(x: A, y: A?, any: Any, z: B) {
when (x) {
x.id() -> {}
y?.id() -> {}
any as? A -> {}
any as A -> {}
x * x -> {}
x / x -> {}
x % x -> {}
x + x -> {}
x - x -> {}
x..x -> {}
y -> {}
y ?: x -> {}
}
var b = z
when (z) {
b++ -> {}
b-- -> {}
}
}
fun testWithSubject_bad_1(x: A) {
// bad
when (x) {
x in x -> {}
x !in x -> {}
x is String -> {}
x !is String -> {}
x < x -> {}
x > x -> {}
x <= x -> {}
x >= x -> {}
x == x -> {}
x != x -> {}
x === x -> {}
x !== x -> {}
}
// ok
when (x) {
(x in x) -> {}
(x !in x) -> {}
(x is String) -> {}
(x !is String) -> {}
(x < x) -> {}
(x > x) -> {}
(x <= x) -> {}
(x >= x) -> {}
(x == x) -> {}
(x != x) -> {}
(x === x) -> {}
(x !== x) -> {}
}
}
fun testWithSubject_bad_2(b: Boolean) {
// bad
when (b) {
b && b -> {}
b || b -> {}
}
// ok
when (b) {
(b && b) -> {}
(b || b) -> {}
}
}
fun testWithSubject_bad_3(a: Array<Any>) {
// bad
when (a) {
<!SYNTAX!><!SYNTAX!><!>*<!>a <!SYNTAX!><!>-> {}
}
// also bad
when (a) {
(<!UNRESOLVED_REFERENCE!><!SYNTAX!><!>*<!>a<!SYNTAX!>)<!><!SYNTAX!><!> <!SYNTAX!><!>-> {}
}
}
fun testWithSubject_bad_4(b: B) {
var x = b
// bad
when (b) {
<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>x = b<!> -> {}
<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b += b<!> -> {}
<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b -= b<!> -> {}
<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b *= b<!> -> {}
<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b /= b<!> -> {}
<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b %= b<!> -> {}
}
// also bad
when (b) {
<!EXPRESSION_EXPECTED!>(x = b)<!> -> {}
<!EXPRESSION_EXPECTED!>(b += b)<!> -> {}
<!EXPRESSION_EXPECTED!>(b -= b)<!> -> {}
<!EXPRESSION_EXPECTED!>(b *= b)<!> -> {}
<!EXPRESSION_EXPECTED!>(b /= b)<!> -> {}
<!EXPRESSION_EXPECTED!>(b %= b)<!> -> {}
}
}
// range
fun testWithRange_ok(x: A, y: A?, any: Any, z: B) {
when (x) {
in x.id() -> {}
in y?.id() -> {}
in any as? A -> {}
in any as A -> {}
in x * x -> {}
in x / x -> {}
in x % x -> {}
in x + x -> {}
in x - x -> {}
in x..x -> {}
in y -> {}
in y ?: x -> {}
}
var b = z
when (z) {
in b++ -> {}
in b-- -> {}
}
}
fun testWithRange_bad_1(x: A) {
// bad
when (x) {
in x in x -> {}
in x !in x -> {}
in x is String -> {}
in x !is String -> {}
in x < x -> {}
in x > x -> {}
in x <= x -> {}
in x >= x -> {}
in x == x -> {}
in x != x -> {}
in x === x -> {}
in x !== x -> {}
}
// ok
when (x) {
in (x in x) -> {}
in (x !in x) -> {}
in (x is String) -> {}
in (x !is String) -> {}
in (x < x) -> {}
in (x > x) -> {}
in (x <= x) -> {}
in (x >= x) -> {}
in (x == x) -> {}
in (x != x) -> {}
in (x === x) -> {}
in (x !== x) -> {}
}
}
fun testWithRange_bad_2(b: Boolean) {
// bad
when (b) {
in b && b -> {}
in b || b -> {}
}
// ok
when (b) {
in (b && b) -> {}
in (b || b) -> {}
}
}
fun testWithRange_bad_3(a: Array<Any>) {
// bad
when (a) {
in<!SYNTAX!><!> <!SYNTAX!>*<!>a <!SYNTAX!><!>-> {}
}
// also bad
when (a) {
in (<!UNRESOLVED_REFERENCE!><!SYNTAX!><!>*<!>a<!SYNTAX!>)<!><!SYNTAX!><!> <!SYNTAX!><!>-> {}
}
}
fun testWithRange_bad_4(b: B) {
var x = b
// bad
when (b) {
in <!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>x = b<!> -> {}
in <!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b += b<!> -> {}
in <!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b -= b<!> -> {}
in <!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b *= b<!> -> {}
in <!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b /= b<!> -> {}
in <!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b %= b<!> -> {}
}
// also bad
when (b) {
in <!EXPRESSION_EXPECTED!>(x = b)<!> -> {}
in <!EXPRESSION_EXPECTED!>(b += b)<!> -> {}
in <!EXPRESSION_EXPECTED!>(b -= b)<!> -> {}
in <!EXPRESSION_EXPECTED!>(b *= b)<!> -> {}
in <!EXPRESSION_EXPECTED!>(b /= b)<!> -> {}
in <!EXPRESSION_EXPECTED!>(b %= b)<!> -> {}
}
}
@@ -0,0 +1,236 @@
// LANGUAGE: +ProhibitConfusingSyntaxInWhenBranches
// DIAGNOSTICS: -INCOMPATIBLE_TYPES, -NON_EXHAUSTIVE_WHEN_STATEMENT, -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE
// ISSUE: KT-48385
interface A {
operator fun plus(other: A): A
operator fun minus(other: A): A
operator fun times(other: A): A
operator fun div(other: A): A
operator fun rem(other: A): A
operator fun rangeTo(other: A): A
operator fun compareTo(other: A): Int
fun id(): A
}
interface B {
operator fun inc(): B
operator fun dec(): B
operator fun plusAssign(other: B)
operator fun minusAssign(other: B)
operator fun timesAssign(other: B)
operator fun divAssign(other: B)
operator fun remAssign(other: B)
}
operator fun Any?.contains(other: Any): Boolean = false
fun testWithSubject_ok(x: A, y: A?, any: Any, z: B) {
when (x) {
x.id() -> {}
y?.id() -> {}
any as? A -> {}
any as A -> {}
x * x -> {}
x / x -> {}
x % x -> {}
x + x -> {}
x - x -> {}
x..x -> {}
y -> {}
y ?: x -> {}
}
var b = z
when (z) {
b++ -> {}
b-- -> {}
}
}
fun testWithSubject_bad_1(x: A) {
// bad
when (x) {
<!CONFUSING_BRANCH_CONDITION_ERROR!>x in x<!> -> {}
<!CONFUSING_BRANCH_CONDITION_ERROR!>x !in x<!> -> {}
<!CONFUSING_BRANCH_CONDITION_ERROR!>x is String<!> -> {}
<!CONFUSING_BRANCH_CONDITION_ERROR!>x !is String<!> -> {}
<!CONFUSING_BRANCH_CONDITION_ERROR!>x < x<!> -> {}
<!CONFUSING_BRANCH_CONDITION_ERROR!>x > x<!> -> {}
<!CONFUSING_BRANCH_CONDITION_ERROR!>x <= x<!> -> {}
<!CONFUSING_BRANCH_CONDITION_ERROR!>x >= x<!> -> {}
<!CONFUSING_BRANCH_CONDITION_ERROR!>x == x<!> -> {}
<!CONFUSING_BRANCH_CONDITION_ERROR!>x != x<!> -> {}
<!CONFUSING_BRANCH_CONDITION_ERROR!>x === x<!> -> {}
<!CONFUSING_BRANCH_CONDITION_ERROR!>x !== x<!> -> {}
}
// ok
when (x) {
(x in x) -> {}
(x !in x) -> {}
(x is String) -> {}
(x !is String) -> {}
(x < x) -> {}
(x > x) -> {}
(x <= x) -> {}
(x >= x) -> {}
(x == x) -> {}
(x != x) -> {}
(x === x) -> {}
(x !== x) -> {}
}
}
fun testWithSubject_bad_2(b: Boolean) {
// bad
when (b) {
<!CONFUSING_BRANCH_CONDITION_ERROR!>b && b<!> -> {}
<!CONFUSING_BRANCH_CONDITION_ERROR!>b || b<!> -> {}
}
// ok
when (b) {
(b && b) -> {}
(b || b) -> {}
}
}
fun testWithSubject_bad_3(a: Array<Any>) {
// bad
when (a) {
<!SYNTAX!><!SYNTAX!><!>*<!>a <!SYNTAX!><!>-> {}
}
// also bad
when (a) {
(<!DEBUG_INFO_MISSING_UNRESOLVED!><!SYNTAX!><!>*<!>a<!SYNTAX!>)<!><!SYNTAX!><!> <!SYNTAX!><!>-> {}
}
}
fun testWithSubject_bad_4(b: B) {
var x = b
// bad
when (b) {
<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>x = b<!> -> {}
<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b += b<!> -> {}
<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b -= b<!> -> {}
<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b *= b<!> -> {}
<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b /= b<!> -> {}
<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b %= b<!> -> {}
}
// also bad
when (b) {
(<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>x = b<!>) -> {}
(<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b += b<!>) -> {}
(<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b -= b<!>) -> {}
(<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b *= b<!>) -> {}
(<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b /= b<!>) -> {}
(<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b %= b<!>) -> {}
}
}
// range
fun testWithRange_ok(x: A, y: A?, any: Any, z: B) {
when (x) {
in x.id() -> {}
in y?.id() -> {}
in any as? A -> {}
in any as A -> {}
in x * x -> {}
in x / x -> {}
in x % x -> {}
in x + x -> {}
in x - x -> {}
in x..x -> {}
in y -> {}
in y ?: x -> {}
}
var b = z
when (z) {
in b++ -> {}
in b-- -> {}
}
}
fun testWithRange_bad_1(x: A) {
// bad
when (x) {
in <!CONFUSING_BRANCH_CONDITION_ERROR!>x in x<!> -> {}
in <!CONFUSING_BRANCH_CONDITION_ERROR!>x !in x<!> -> {}
in <!CONFUSING_BRANCH_CONDITION_ERROR!>x is String<!> -> {}
in <!CONFUSING_BRANCH_CONDITION_ERROR!>x !is String<!> -> {}
in <!CONFUSING_BRANCH_CONDITION_ERROR!>x < x<!> -> {}
in <!CONFUSING_BRANCH_CONDITION_ERROR!>x > x<!> -> {}
in <!CONFUSING_BRANCH_CONDITION_ERROR!>x <= x<!> -> {}
in <!CONFUSING_BRANCH_CONDITION_ERROR!>x >= x<!> -> {}
in <!CONFUSING_BRANCH_CONDITION_ERROR!>x == x<!> -> {}
in <!CONFUSING_BRANCH_CONDITION_ERROR!>x != x<!> -> {}
in <!CONFUSING_BRANCH_CONDITION_ERROR!>x === x<!> -> {}
in <!CONFUSING_BRANCH_CONDITION_ERROR!>x !== x<!> -> {}
}
// ok
when (x) {
in (x in x) -> {}
in (x !in x) -> {}
in (x is String) -> {}
in (x !is String) -> {}
in (x < x) -> {}
in (x > x) -> {}
in (x <= x) -> {}
in (x >= x) -> {}
in (x == x) -> {}
in (x != x) -> {}
in (x === x) -> {}
in (x !== x) -> {}
}
}
fun testWithRange_bad_2(b: Boolean) {
// bad
when (b) {
in <!CONFUSING_BRANCH_CONDITION_ERROR!>b && b<!> -> {}
in <!CONFUSING_BRANCH_CONDITION_ERROR!>b || b<!> -> {}
}
// ok
when (b) {
in (b && b) -> {}
in (b || b) -> {}
}
}
fun testWithRange_bad_3(a: Array<Any>) {
// bad
when (a) {
in<!SYNTAX!><!> <!SYNTAX!>*<!>a <!SYNTAX!><!>-> {}
}
// also bad
when (a) {
in (<!DEBUG_INFO_MISSING_UNRESOLVED!><!SYNTAX!><!>*<!>a<!SYNTAX!>)<!><!SYNTAX!><!> <!SYNTAX!><!>-> {}
}
}
fun testWithRange_bad_4(b: B) {
var x = b
// bad
when (b) {
in <!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>x = b<!> -> {}
in <!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b += b<!> -> {}
in <!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b -= b<!> -> {}
in <!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b *= b<!> -> {}
in <!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b /= b<!> -> {}
in <!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b %= b<!> -> {}
}
// also bad
when (b) {
in (<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>x = b<!>) -> {}
in (<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b += b<!>) -> {}
in (<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b -= b<!>) -> {}
in (<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b *= b<!>) -> {}
in (<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b /= b<!>) -> {}
in (<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b %= b<!>) -> {}
}
}
@@ -0,0 +1,40 @@
package
public fun testWithRange_bad_1(/*0*/ x: A): kotlin.Unit
public fun testWithRange_bad_2(/*0*/ b: kotlin.Boolean): kotlin.Unit
public fun testWithRange_bad_3(/*0*/ a: kotlin.Array<kotlin.Any>): kotlin.Unit
public fun testWithRange_bad_4(/*0*/ b: B): kotlin.Unit
public fun testWithRange_ok(/*0*/ x: A, /*1*/ y: A?, /*2*/ any: kotlin.Any, /*3*/ z: B): kotlin.Unit
public fun testWithSubject_bad_1(/*0*/ x: A): kotlin.Unit
public fun testWithSubject_bad_2(/*0*/ b: kotlin.Boolean): kotlin.Unit
public fun testWithSubject_bad_3(/*0*/ a: kotlin.Array<kotlin.Any>): kotlin.Unit
public fun testWithSubject_bad_4(/*0*/ b: B): kotlin.Unit
public fun testWithSubject_ok(/*0*/ x: A, /*1*/ y: A?, /*2*/ any: kotlin.Any, /*3*/ z: B): kotlin.Unit
public operator fun kotlin.Any?.contains(/*0*/ other: kotlin.Any): kotlin.Boolean
public interface A {
public abstract operator fun compareTo(/*0*/ other: A): kotlin.Int
public abstract operator fun div(/*0*/ other: A): 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 abstract fun id(): A
public abstract operator fun minus(/*0*/ other: A): A
public abstract operator fun plus(/*0*/ other: A): A
public abstract operator fun rangeTo(/*0*/ other: A): A
public abstract operator fun rem(/*0*/ other: A): A
public abstract operator fun times(/*0*/ other: A): A
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface B {
public abstract operator fun dec(): B
public abstract operator fun divAssign(/*0*/ other: B): kotlin.Unit
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 abstract operator fun inc(): B
public abstract operator fun minusAssign(/*0*/ other: B): kotlin.Unit
public abstract operator fun plusAssign(/*0*/ other: B): kotlin.Unit
public abstract operator fun remAssign(/*0*/ other: B): kotlin.Unit
public abstract operator fun timesAssign(/*0*/ other: B): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,236 @@
// LANGUAGE: -ProhibitConfusingSyntaxInWhenBranches
// DIAGNOSTICS: -INCOMPATIBLE_TYPES, -NON_EXHAUSTIVE_WHEN_STATEMENT, -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE
// ISSUE: KT-48385
interface A {
operator fun plus(other: A): A
operator fun minus(other: A): A
operator fun times(other: A): A
operator fun div(other: A): A
operator fun rem(other: A): A
operator fun rangeTo(other: A): A
operator fun compareTo(other: A): Int
fun id(): A
}
interface B {
operator fun inc(): B
operator fun dec(): B
operator fun plusAssign(other: B)
operator fun minusAssign(other: B)
operator fun timesAssign(other: B)
operator fun divAssign(other: B)
operator fun remAssign(other: B)
}
operator fun Any?.contains(other: Any): Boolean = false
fun testWithSubject_ok(x: A, y: A?, any: Any, z: B) {
when (x) {
x.id() -> {}
y?.id() -> {}
any as? A -> {}
any as A -> {}
x * x -> {}
x / x -> {}
x % x -> {}
x + x -> {}
x - x -> {}
x..x -> {}
y -> {}
y ?: x -> {}
}
var b = z
when (z) {
b++ -> {}
b-- -> {}
}
}
fun testWithSubject_bad_1(x: A) {
// bad
when (x) {
x in x -> {}
x !in x -> {}
x is String -> {}
x !is String -> {}
x < x -> {}
x > x -> {}
x <= x -> {}
x >= x -> {}
x == x -> {}
x != x -> {}
x === x -> {}
x !== x -> {}
}
// ok
when (x) {
(x in x) -> {}
(x !in x) -> {}
(x is String) -> {}
(x !is String) -> {}
(x < x) -> {}
(x > x) -> {}
(x <= x) -> {}
(x >= x) -> {}
(x == x) -> {}
(x != x) -> {}
(x === x) -> {}
(x !== x) -> {}
}
}
fun testWithSubject_bad_2(b: Boolean) {
// bad
when (b) {
b && b -> {}
b || b -> {}
}
// ok
when (b) {
(b && b) -> {}
(b || b) -> {}
}
}
fun testWithSubject_bad_3(a: Array<Any>) {
// bad
when (a) {
<!SYNTAX!><!SYNTAX!><!>*<!>a <!SYNTAX!><!>-> {}
}
// also bad
when (a) {
(<!UNRESOLVED_REFERENCE!><!SYNTAX!><!>*<!>a<!SYNTAX!>)<!><!SYNTAX!><!> <!SYNTAX!><!>-> {}
}
}
fun testWithSubject_bad_4(b: B) {
var x = b
// bad
when (b) {
<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>x = b<!> -> {}
<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b += b<!> -> {}
<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b -= b<!> -> {}
<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b *= b<!> -> {}
<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b /= b<!> -> {}
<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b %= b<!> -> {}
}
// also bad
when (b) {
<!EXPRESSION_EXPECTED!>(x = b)<!> -> {}
<!EXPRESSION_EXPECTED!>(b += b)<!> -> {}
<!EXPRESSION_EXPECTED!>(b -= b)<!> -> {}
<!EXPRESSION_EXPECTED!>(b *= b)<!> -> {}
<!EXPRESSION_EXPECTED!>(b /= b)<!> -> {}
<!EXPRESSION_EXPECTED!>(b %= b)<!> -> {}
}
}
// range
fun testWithRange_ok(x: A, y: A?, any: Any, z: B) {
when (x) {
in x.id() -> {}
in y?.id() -> {}
in any as? A -> {}
in any as A -> {}
in x * x -> {}
in x / x -> {}
in x % x -> {}
in x + x -> {}
in x - x -> {}
in x..x -> {}
in y -> {}
in y ?: x -> {}
}
var b = z
when (z) {
in b++ -> {}
in b-- -> {}
}
}
fun testWithRange_bad_1(x: A) {
// bad
when (x) {
in x in x -> {}
in x !in x -> {}
in x is String -> {}
in x !is String -> {}
in x < x -> {}
in x > x -> {}
in x <= x -> {}
in x >= x -> {}
in x == x -> {}
in x != x -> {}
in x === x -> {}
in x !== x -> {}
}
// ok
when (x) {
in (x in x) -> {}
in (x !in x) -> {}
in (x is String) -> {}
in (x !is String) -> {}
in (x < x) -> {}
in (x > x) -> {}
in (x <= x) -> {}
in (x >= x) -> {}
in (x == x) -> {}
in (x != x) -> {}
in (x === x) -> {}
in (x !== x) -> {}
}
}
fun testWithRange_bad_2(b: Boolean) {
// bad
when (b) {
in b && b -> {}
in b || b -> {}
}
// ok
when (b) {
in (b && b) -> {}
in (b || b) -> {}
}
}
fun testWithRange_bad_3(a: Array<Any>) {
// bad
when (a) {
in<!SYNTAX!><!> <!SYNTAX!>*<!>a <!SYNTAX!><!>-> {}
}
// also bad
when (a) {
in (<!UNRESOLVED_REFERENCE!><!SYNTAX!><!>*<!>a<!SYNTAX!>)<!><!SYNTAX!><!> <!SYNTAX!><!>-> {}
}
}
fun testWithRange_bad_4(b: B) {
var x = b
// bad
when (b) {
in <!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>x = b<!> -> {}
in <!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b += b<!> -> {}
in <!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b -= b<!> -> {}
in <!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b *= b<!> -> {}
in <!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b /= b<!> -> {}
in <!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b %= b<!> -> {}
}
// also bad
when (b) {
in <!EXPRESSION_EXPECTED!>(x = b)<!> -> {}
in <!EXPRESSION_EXPECTED!>(b += b)<!> -> {}
in <!EXPRESSION_EXPECTED!>(b -= b)<!> -> {}
in <!EXPRESSION_EXPECTED!>(b *= b)<!> -> {}
in <!EXPRESSION_EXPECTED!>(b /= b)<!> -> {}
in <!EXPRESSION_EXPECTED!>(b %= b)<!> -> {}
}
}
@@ -0,0 +1,236 @@
// LANGUAGE: -ProhibitConfusingSyntaxInWhenBranches
// DIAGNOSTICS: -INCOMPATIBLE_TYPES, -NON_EXHAUSTIVE_WHEN_STATEMENT, -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE
// ISSUE: KT-48385
interface A {
operator fun plus(other: A): A
operator fun minus(other: A): A
operator fun times(other: A): A
operator fun div(other: A): A
operator fun rem(other: A): A
operator fun rangeTo(other: A): A
operator fun compareTo(other: A): Int
fun id(): A
}
interface B {
operator fun inc(): B
operator fun dec(): B
operator fun plusAssign(other: B)
operator fun minusAssign(other: B)
operator fun timesAssign(other: B)
operator fun divAssign(other: B)
operator fun remAssign(other: B)
}
operator fun Any?.contains(other: Any): Boolean = false
fun testWithSubject_ok(x: A, y: A?, any: Any, z: B) {
when (x) {
x.id() -> {}
y?.id() -> {}
any as? A -> {}
any as A -> {}
x * x -> {}
x / x -> {}
x % x -> {}
x + x -> {}
x - x -> {}
x..x -> {}
y -> {}
y ?: x -> {}
}
var b = z
when (z) {
b++ -> {}
b-- -> {}
}
}
fun testWithSubject_bad_1(x: A) {
// bad
when (x) {
<!CONFUSING_BRANCH_CONDITION_WARNING!>x in x<!> -> {}
<!CONFUSING_BRANCH_CONDITION_WARNING!>x !in x<!> -> {}
<!CONFUSING_BRANCH_CONDITION_WARNING!>x is String<!> -> {}
<!CONFUSING_BRANCH_CONDITION_WARNING!>x !is String<!> -> {}
<!CONFUSING_BRANCH_CONDITION_WARNING!>x < x<!> -> {}
<!CONFUSING_BRANCH_CONDITION_WARNING!>x > x<!> -> {}
<!CONFUSING_BRANCH_CONDITION_WARNING!>x <= x<!> -> {}
<!CONFUSING_BRANCH_CONDITION_WARNING!>x >= x<!> -> {}
<!CONFUSING_BRANCH_CONDITION_WARNING!>x == x<!> -> {}
<!CONFUSING_BRANCH_CONDITION_WARNING!>x != x<!> -> {}
<!CONFUSING_BRANCH_CONDITION_WARNING!>x === x<!> -> {}
<!CONFUSING_BRANCH_CONDITION_WARNING!>x !== x<!> -> {}
}
// ok
when (x) {
(x in x) -> {}
(x !in x) -> {}
(x is String) -> {}
(x !is String) -> {}
(x < x) -> {}
(x > x) -> {}
(x <= x) -> {}
(x >= x) -> {}
(x == x) -> {}
(x != x) -> {}
(x === x) -> {}
(x !== x) -> {}
}
}
fun testWithSubject_bad_2(b: Boolean) {
// bad
when (b) {
<!CONFUSING_BRANCH_CONDITION_WARNING!>b && b<!> -> {}
<!CONFUSING_BRANCH_CONDITION_WARNING!>b || b<!> -> {}
}
// ok
when (b) {
(b && b) -> {}
(b || b) -> {}
}
}
fun testWithSubject_bad_3(a: Array<Any>) {
// bad
when (a) {
<!SYNTAX!><!SYNTAX!><!>*<!>a <!SYNTAX!><!>-> {}
}
// also bad
when (a) {
(<!DEBUG_INFO_MISSING_UNRESOLVED!><!SYNTAX!><!>*<!>a<!SYNTAX!>)<!><!SYNTAX!><!> <!SYNTAX!><!>-> {}
}
}
fun testWithSubject_bad_4(b: B) {
var x = b
// bad
when (b) {
<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>x = b<!> -> {}
<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b += b<!> -> {}
<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b -= b<!> -> {}
<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b *= b<!> -> {}
<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b /= b<!> -> {}
<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b %= b<!> -> {}
}
// also bad
when (b) {
(<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>x = b<!>) -> {}
(<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b += b<!>) -> {}
(<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b -= b<!>) -> {}
(<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b *= b<!>) -> {}
(<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b /= b<!>) -> {}
(<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b %= b<!>) -> {}
}
}
// range
fun testWithRange_ok(x: A, y: A?, any: Any, z: B) {
when (x) {
in x.id() -> {}
in y?.id() -> {}
in any as? A -> {}
in any as A -> {}
in x * x -> {}
in x / x -> {}
in x % x -> {}
in x + x -> {}
in x - x -> {}
in x..x -> {}
in y -> {}
in y ?: x -> {}
}
var b = z
when (z) {
in b++ -> {}
in b-- -> {}
}
}
fun testWithRange_bad_1(x: A) {
// bad
when (x) {
in <!CONFUSING_BRANCH_CONDITION_WARNING!>x in x<!> -> {}
in <!CONFUSING_BRANCH_CONDITION_WARNING!>x !in x<!> -> {}
in <!CONFUSING_BRANCH_CONDITION_WARNING!>x is String<!> -> {}
in <!CONFUSING_BRANCH_CONDITION_WARNING!>x !is String<!> -> {}
in <!CONFUSING_BRANCH_CONDITION_WARNING!>x < x<!> -> {}
in <!CONFUSING_BRANCH_CONDITION_WARNING!>x > x<!> -> {}
in <!CONFUSING_BRANCH_CONDITION_WARNING!>x <= x<!> -> {}
in <!CONFUSING_BRANCH_CONDITION_WARNING!>x >= x<!> -> {}
in <!CONFUSING_BRANCH_CONDITION_WARNING!>x == x<!> -> {}
in <!CONFUSING_BRANCH_CONDITION_WARNING!>x != x<!> -> {}
in <!CONFUSING_BRANCH_CONDITION_WARNING!>x === x<!> -> {}
in <!CONFUSING_BRANCH_CONDITION_WARNING!>x !== x<!> -> {}
}
// ok
when (x) {
in (x in x) -> {}
in (x !in x) -> {}
in (x is String) -> {}
in (x !is String) -> {}
in (x < x) -> {}
in (x > x) -> {}
in (x <= x) -> {}
in (x >= x) -> {}
in (x == x) -> {}
in (x != x) -> {}
in (x === x) -> {}
in (x !== x) -> {}
}
}
fun testWithRange_bad_2(b: Boolean) {
// bad
when (b) {
in <!CONFUSING_BRANCH_CONDITION_WARNING!>b && b<!> -> {}
in <!CONFUSING_BRANCH_CONDITION_WARNING!>b || b<!> -> {}
}
// ok
when (b) {
in (b && b) -> {}
in (b || b) -> {}
}
}
fun testWithRange_bad_3(a: Array<Any>) {
// bad
when (a) {
in<!SYNTAX!><!> <!SYNTAX!>*<!>a <!SYNTAX!><!>-> {}
}
// also bad
when (a) {
in (<!DEBUG_INFO_MISSING_UNRESOLVED!><!SYNTAX!><!>*<!>a<!SYNTAX!>)<!><!SYNTAX!><!> <!SYNTAX!><!>-> {}
}
}
fun testWithRange_bad_4(b: B) {
var x = b
// bad
when (b) {
in <!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>x = b<!> -> {}
in <!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b += b<!> -> {}
in <!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b -= b<!> -> {}
in <!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b *= b<!> -> {}
in <!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b /= b<!> -> {}
in <!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b %= b<!> -> {}
}
// also bad
when (b) {
in (<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>x = b<!>) -> {}
in (<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b += b<!>) -> {}
in (<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b -= b<!>) -> {}
in (<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b *= b<!>) -> {}
in (<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b /= b<!>) -> {}
in (<!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>b %= b<!>) -> {}
}
}
@@ -0,0 +1,41 @@
package
public fun testWithRange_bad_1(/*0*/ x: A): kotlin.Unit
public fun testWithRange_bad_2(/*0*/ b: kotlin.Boolean): kotlin.Unit
public fun testWithRange_bad_3(/*0*/ a: kotlin.Array<kotlin.Any>): kotlin.Unit
public fun testWithRange_bad_4(/*0*/ b: B): kotlin.Unit
public fun testWithRange_ok(/*0*/ x: A, /*1*/ y: A?, /*2*/ any: kotlin.Any, /*3*/ z: B): kotlin.Unit
public fun testWithSubject_bad_1(/*0*/ x: A): kotlin.Unit
public fun testWithSubject_bad_2(/*0*/ b: kotlin.Boolean): kotlin.Unit
public fun testWithSubject_bad_3(/*0*/ a: kotlin.Array<kotlin.Any>): kotlin.Unit
public fun testWithSubject_bad_4(/*0*/ b: B): kotlin.Unit
public fun testWithSubject_ok(/*0*/ x: A, /*1*/ y: A?, /*2*/ any: kotlin.Any, /*3*/ z: B): kotlin.Unit
public operator fun kotlin.Any?.contains(/*0*/ other: kotlin.Any): kotlin.Boolean
public interface A {
public abstract operator fun compareTo(/*0*/ other: A): kotlin.Int
public abstract operator fun div(/*0*/ other: A): 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 abstract fun id(): A
public abstract operator fun minus(/*0*/ other: A): A
public abstract operator fun plus(/*0*/ other: A): A
public abstract operator fun rangeTo(/*0*/ other: A): A
public abstract operator fun rem(/*0*/ other: A): A
public abstract operator fun times(/*0*/ other: A): A
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface B {
public abstract operator fun dec(): B
public abstract operator fun divAssign(/*0*/ other: B): kotlin.Unit
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 abstract operator fun inc(): B
public abstract operator fun minusAssign(/*0*/ other: B): kotlin.Unit
public abstract operator fun plusAssign(/*0*/ other: B): kotlin.Unit
public abstract operator fun remAssign(/*0*/ other: B): kotlin.Unit
public abstract operator fun timesAssign(/*0*/ other: B): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -10,8 +10,8 @@ fun test_0(b: Boolean): String = when (b) {
// Deprecated
fun test_1(b: Boolean): String = <!NO_ELSE_IN_WHEN!>when<!> (b) {
1 == 1 -> "true"
"" != "" -> "false"
(1 == 1) -> "true"
("" != "") -> "false"
}
const val TRUE = true
@@ -28,5 +28,5 @@ const val s2 = "s2"
// Already not working
fun test_3(b: Boolean): String = <!NO_ELSE_IN_WHEN!>when<!>(b) {
true -> "true"
s1 == s2 -> "false"
(s1 == s2) -> "false"
}
@@ -9,8 +9,8 @@ fun test_0(b: Boolean): String = when (b) {
// Deprecated
fun test_1(b: Boolean): String = when (b) {
<!NON_TRIVIAL_BOOLEAN_CONSTANT!>1 == 1<!> -> "true"
<!NON_TRIVIAL_BOOLEAN_CONSTANT!>"" != ""<!> -> "false"
(<!NON_TRIVIAL_BOOLEAN_CONSTANT!>1 == 1<!>) -> "true"
(<!NON_TRIVIAL_BOOLEAN_CONSTANT!>"" != ""<!>) -> "false"
}
const val TRUE = true
@@ -27,5 +27,5 @@ const val s2 = "s2"
// Already not working
fun test_3(b: Boolean): String = <!NO_ELSE_IN_WHEN!>when<!>(b) {
true -> "true"
s1 == s2 -> "false"
(s1 == s2) -> "false"
}