FIR: report UNSAFE_CALL on dot when possible

This commit is contained in:
Mikhail Glukhikh
2021-01-29 10:25:59 +03:00
parent 0ee4f1f393
commit 7d4eaefd36
249 changed files with 1208 additions and 1649 deletions
@@ -9,5 +9,5 @@ fun checkJump(x: Int?, y: Int?) {
y.hashCode()
}
// Smart cast here is erroneous: y is nullable
y.<!UNSAFE_CALL!>hashCode<!>()
y<!UNSAFE_CALL!>.<!>hashCode()
}
@@ -4,8 +4,8 @@ public fun foo(x: String?, y: String?): Int {
// z is not null in both branches
z.length
// y is nullable if x != null
y.<!UNSAFE_CALL!>length<!>
y<!UNSAFE_CALL!>.<!>length
}
// y is null because of the break
return y.<!UNSAFE_CALL!>length<!>
return y<!UNSAFE_CALL!>.<!>length
}
@@ -13,5 +13,5 @@ public fun foo(x: String?): Int {
checkSubtype<Int>(y)
}
// x is null because of the break
return x.<!UNSAFE_CALL!>length<!>
return x<!UNSAFE_CALL!>.<!>length
}
@@ -10,7 +10,7 @@ public fun foo(x: String?, z: String?, w: String?): Int {
// w is not null because of w!!
w.length
// z is nullable despite of z!!
z.<!UNSAFE_CALL!>length<!>
z<!UNSAFE_CALL!>.<!>length
// x is null because of the break
return x.<!UNSAFE_CALL!>length<!>
return x<!UNSAFE_CALL!>.<!>length
}
@@ -7,7 +7,7 @@ public fun foo(x: String?, z: String?): Int {
gav(if (x == null) break else x, z!!)
} while (bar())
// z is nullable despite of z!!
z.<!UNSAFE_CALL!>length<!>
z<!UNSAFE_CALL!>.<!>length
// x is null because of the break
return x.<!UNSAFE_CALL!>length<!>
return x<!UNSAFE_CALL!>.<!>length
}
@@ -10,5 +10,5 @@ public fun foo(x: String?): Int {
} while (bar())
y.hashCode()
// x is null because of the break
return x.<!UNSAFE_CALL!>length<!>
return x<!UNSAFE_CALL!>.<!>length
}
@@ -9,5 +9,5 @@ public fun foo(x: String?, z: String?): Int {
// z is not null because of z!!
z.length
// x is null because of the break
return x.<!UNSAFE_CALL!>length<!>
return x<!UNSAFE_CALL!>.<!>length
}
@@ -12,5 +12,5 @@ public fun foo(x: String?, z: String?, w: String?): Int {
// z is not null because of z!!
z.length
// x is null because of the break
return x.<!UNSAFE_CALL!>length<!>
return x<!UNSAFE_CALL!>.<!>length
}
@@ -1,13 +0,0 @@
fun x(): Boolean { return true }
fun y(): Boolean { return false }
public fun foo(p: String?): Int {
do {
if (y()) break
// We do not always reach this statement
p!!.length
} while (!x())
// Here we have do while loop but p is still nullable due to break before
return p.<!UNSAFE_CALL!>length<!>
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
fun x(): Boolean { return true }
fun y(): Boolean { return false }
@@ -1,9 +0,0 @@
fun bar(): Boolean { return true }
fun foo(s: String?): Int {
do {
if (bar()) break
} while (s!!.length > 0)
// This call is unsafe due to break
return s.<!UNSAFE_CALL!>length<!>
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
fun bar(): Boolean { return true }
fun foo(s: String?): Int {
@@ -1,10 +0,0 @@
fun x(): Boolean { return true }
public fun foo(p: String?): Int {
// See KT-6283
do {
if (p != null) break
} while (!x())
// p can be null despite of the break
return p.<!UNSAFE_CALL!>length<!>
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
fun x(): Boolean { return true }
public fun foo(p: String?): Int {
@@ -1,9 +0,0 @@
fun bar(): Boolean { return true }
fun foo(s: String?): Int {
do {
if (bar()) break
} while (s==null)
// This call is unsafe due to break
return s.<!UNSAFE_CALL!>length<!>
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
fun bar(): Boolean { return true }
fun foo(s: String?): Int {
@@ -6,5 +6,5 @@ public fun foo(x: String?): Int {
x.length
} while (true)
// x is null because of the break
return x.<!UNSAFE_CALL!>length<!>
return x<!UNSAFE_CALL!>.<!>length
}
@@ -1,9 +0,0 @@
public fun foo(x: String?, y: String?): Int {
while (true) {
x ?: 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,3 +1,4 @@
// FIR_IDENTICAL
public fun foo(x: String?, y: String?): Int {
while (true) {
x ?: if (y == null) break
@@ -1,9 +0,0 @@
public fun foo(x: String?, y: String?): Int {
while (true) {
(if (x != null) break else y) ?: y!!
// y is not null in both branches but it's hard to determine
y.<!UNSAFE_CALL!>length<!>
}
// y can be null because of the break
return y.<!UNSAFE_CALL!>length<!>
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
public fun foo(x: String?, y: String?): Int {
while (true) {
(if (x != null) break else y) ?: y!!
@@ -10,5 +10,5 @@ public fun foo(x: String?): Int {
} while (bar())
y.hashCode()
// x is null because of the break
return x.<!UNSAFE_CALL!>length<!>
return x<!UNSAFE_CALL!>.<!>length
}
@@ -6,5 +6,5 @@ public fun foo(x: String?): Int {
// In future we can infer this initialization
<!UNINITIALIZED_VARIABLE!>y<!>.hashCode()
// x is null because of the break
return x.<!UNSAFE_CALL!>length<!>
return x<!UNSAFE_CALL!>.<!>length
}
@@ -6,5 +6,5 @@ public fun foo(x: String?): Int {
y.length
}
// x is null because of the break
return x.<!UNSAFE_CALL!>length<!>
return x<!UNSAFE_CALL!>.<!>length
}
@@ -7,5 +7,5 @@ public fun foo(x: String?, y: String?): Int {
y.length
}
// y is null because of the break
return y.<!UNSAFE_CALL!>length<!>
return y<!UNSAFE_CALL!>.<!>length
}
@@ -13,7 +13,7 @@ public fun foo(p: String?, r: String?, q: String?): Int {
if (!x(p)) break
}
// Long break allows r == null
r.<!UNSAFE_CALL!>length<!>
r<!UNSAFE_CALL!>.<!>length
// Smart cast is possible
q.length
return p.length
@@ -18,5 +18,5 @@ public fun foo(x: String?): Int {
// y is always Int even here
checkSubtype<Int>(y)
// x is null because of the break
return x.<!UNSAFE_CALL!>length<!>
return x<!UNSAFE_CALL!>.<!>length
}
@@ -8,5 +8,5 @@ public fun foo(x: String?): Int {
}
}
// x is null because of the break
return x.<!UNSAFE_CALL!>length<!>
return x<!UNSAFE_CALL!>.<!>length
}
@@ -1,9 +0,0 @@
fun bar(): Boolean { return true }
fun foo(s: String?): Int {
while (s==null) {
if (bar()) break
}
// Call is unsafe due to break
return s.<!UNSAFE_CALL!>length<!>
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
fun bar(): Boolean { return true }
fun foo(s: String?): Int {
@@ -1,10 +0,0 @@
fun x(): Boolean { return true }
public fun foo(p: String?): Int {
while(x()) {
p!!.length
if (x()) break
}
// p is nullable because it's possible loop body is not executed at all
return p.<!UNSAFE_CALL!>length<!>
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
fun x(): Boolean { return true }
public fun foo(p: String?): Int {
@@ -1,11 +0,0 @@
fun x(): Boolean { return true }
public fun foo(p: String?): Int {
// Like whileTrue but 2 == 2 is in use
while(2 == 2) {
p!!.length
if (x()) break
}
// Smart cast should not work in this case, see KT-6284
return p.<!UNSAFE_CALL!>length<!>
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
fun x(): Boolean { return true }
public fun foo(p: String?): Int {
@@ -8,5 +8,5 @@ public fun foo(p: String?): Int {
p.length
}
// p can be null because break is earlier than return
return p.<!UNSAFE_CALL!>length<!>
return p<!UNSAFE_CALL!>.<!>length
}
@@ -1,11 +0,0 @@
fun x(): Boolean { return true }
public fun foo(p: String?): Int {
while(true) {
if (x()) break
// We do not always reach this statement
p!!.length
}
// Here we have while (true) loop but p is nullable due to break before
return p.<!UNSAFE_CALL!>length<!>
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
fun x(): Boolean { return true }
public fun foo(p: String?): Int {
@@ -8,7 +8,7 @@ fun foo() {
}
// TODO: this testdata fixates undesired behavior (it should be an unsafe call)
x.<!UNSAFE_CALL!>length<!> // 'x' is unsoundly smartcasted here
x<!UNSAFE_CALL!>.<!>length // 'x' is unsoundly smartcasted here
}
fun bar() {
@@ -19,5 +19,5 @@ fun bar() {
}
// TODO: this testdata fixates undesired behavior (it should be an unsafe call)
x.<!UNSAFE_CALL!>size<!> // 'x' is unsoundly smartcasted here
x<!UNSAFE_CALL!>.<!>size // 'x' is unsoundly smartcasted here
}
@@ -7,7 +7,7 @@ fun foo() {
break
}
x.<!UNSAFE_CALL!>length<!> // 'x' is unsoundly smartcasted here
x<!UNSAFE_CALL!>.<!>length // 'x' is unsoundly smartcasted here
}
fun bar() {
@@ -17,5 +17,5 @@ fun bar() {
break
}
x.<!UNSAFE_CALL!>size<!> // 'x' is unsoundly smartcasted here
x<!UNSAFE_CALL!>.<!>size // 'x' is unsoundly smartcasted here
}