FIR checker: differentiate UNSAFE_CALL from INAPPLICABLE_CANDIDATE

To do so, inside the root cause of inapplicable candidate errors,
we will record expected/actual type of receiver, if any.
That will help identifying inapplicable calls on nullable receiver.
This commit is contained in:
Jinseong Jeon
2021-01-26 16:03:27 -08:00
committed by Mikhail Glukhikh
parent 4b823eca21
commit e72ddbcbfe
275 changed files with 1426 additions and 1388 deletions
@@ -2,5 +2,5 @@ fun calc(x: List<String>?): Int {
// After KT-5840 fix !! assertion should become unnecessary here
x?.get(x!!.size - 1)
// x?. or x!! above should not provide smart cast here
return x.<!INAPPLICABLE_CANDIDATE!>size<!>
return x.<!UNSAFE_CALL!>size<!>
}
@@ -1,5 +1,5 @@
fun calc(x: List<String>?): Int {
// x is not-null only inside subList
x?.subList(0, x.size - 1).<!INAPPLICABLE_CANDIDATE!>get<!>(x.<!INAPPLICABLE_CANDIDATE!>size<!>)
x?.subList(0, x.size - 1).<!UNSAFE_CALL!>get<!>(x.<!UNSAFE_CALL!>size<!>)
return x!!.size
}
@@ -2,5 +2,5 @@ fun calc(x: List<String>?): Int {
// x should be non-null in arguments list, including inner call
x?.get(x.get(x.size - 1).length)
// but not also here!
return x.<!INAPPLICABLE_CANDIDATE!>size<!>
return x.<!UNSAFE_CALL!>size<!>
}
@@ -4,5 +4,5 @@ fun calc(x: String?, y: String?): Int {
// Smart cast because of y!! in receiver
x?.subSequence(y!!.subSequence(0, 1).length, y.length)
// No smart cast possible
return y.<!INAPPLICABLE_CANDIDATE!>length<!>
return y.<!UNSAFE_CALL!>length<!>
}
@@ -10,14 +10,14 @@ fun foo() {
} else {
s?.length
}
ss.<!INAPPLICABLE_CANDIDATE!>hashCode<!>() // Smart-cast to Int, should be unsafe call
ss.<!UNSAFE_CALL!>hashCode<!>() // Smart-cast to Int, should be unsafe call
val sss = if (true) {
s?.copy
}
else {
s?.copy
}
sss.<!INAPPLICABLE_CANDIDATE!>length<!>
sss.<!UNSAFE_CALL!>length<!>
}
class My {
@@ -31,13 +31,13 @@ class My {
} else {
s?.length
}
ss.<!INAPPLICABLE_CANDIDATE!>hashCode<!>()
ss.<!UNSAFE_CALL!>hashCode<!>()
val sss = if (true) {
s?.copy2
}
else {
s?.copy2
}
sss.<!INAPPLICABLE_CANDIDATE!>length<!>
sss.<!UNSAFE_CALL!>length<!>
}
}
@@ -4,9 +4,9 @@ class Foo(val bar: String)
fun test(foo: Foo?) {
foo?.bar.let {
// Error, foo?.bar is nullable
it.<!INAPPLICABLE_CANDIDATE!>length<!>
it.<!UNSAFE_CALL!>length<!>
// Error, foo is nullable
foo.<!INAPPLICABLE_CANDIDATE!>bar<!>.length
foo.<!UNSAFE_CALL!>bar<!>.length
// Correct
foo?.bar?.length
}
@@ -11,22 +11,22 @@ fun String.notNullLet(f: (String) -> Unit) = f(this)
fun test(foo: Foo?) {
foo?.bar?.gav.let {
// Error, foo?.bar?.gav is nullable
it.<!INAPPLICABLE_CANDIDATE!>length<!>
it.<!UNSAFE_CALL!>length<!>
// Error, foo is nullable
foo.<!INAPPLICABLE_CANDIDATE!>bar<!>.gav.length
foo.<!UNSAFE_CALL!>bar<!>.gav.length
// Correct
foo?.bar?.gav?.length
}
foo?.bar?.gav.call { it }?.notNullLet {
foo.<!INAPPLICABLE_CANDIDATE!>hashCode<!>()
foo.<!INAPPLICABLE_CANDIDATE!>bar<!>.hashCode()
foo.<!UNSAFE_CALL!>hashCode<!>()
foo.<!UNSAFE_CALL!>bar<!>.hashCode()
}
}
fun testNotNull(foo: Foo) {
val s: String? = ""
foo.baz(s!!)?.gav.let {
it.<!INAPPLICABLE_CANDIDATE!>length<!>
it.<!UNSAFE_CALL!>length<!>
// Ok because of foo.
s.length.hashCode()
}
@@ -35,7 +35,7 @@ fun testNotNull(foo: Foo) {
fun testNullable(foo: Foo?) {
val s: String? = ""
foo?.baz(s!!)?.gav.let {
it.<!INAPPLICABLE_CANDIDATE!>length<!>
it.<!UNSAFE_CALL!>length<!>
// Ok because of foo?.
s?.length?.hashCode()
}
@@ -16,7 +16,7 @@ fun test() {
val foo = Foo.create(null)
foo?.bar.let {
// Error, foo?.bar is nullable
it.<!INAPPLICABLE_CANDIDATE!>length<!>
it.<!UNSAFE_CALL!>length<!>
// Foo is nullable but flexible, so call is considered safe here
foo.bar.length
// Correct
@@ -18,7 +18,7 @@ fun kt6840_2(s: String?) {
fun kt1635(s: String?) {
s?.hashCode()!!
s.<!INAPPLICABLE_CANDIDATE!>hashCode<!>()
s.<!UNSAFE_CALL!>hashCode<!>()
}
fun kt2127() {
@@ -80,7 +80,7 @@ fun useA(a: A): Int = a.hashCode()
fun kt7491_2() {
val a = getA()
(a?.let { useA(a) } ?: a.<!INAPPLICABLE_CANDIDATE!>y<!> ).toString()
(a?.let { useA(a) } ?: a.<!UNSAFE_CALL!>y<!> ).toString()
}
fun String.correct() = true
@@ -88,7 +88,7 @@ fun String.correct() = true
fun kt8492(s: String?) {
if (s?.correct() ?: false) {
// To be supported
s.<!INAPPLICABLE_CANDIDATE!>length<!>
s.<!UNSAFE_CALL!>length<!>
}
}
@@ -118,7 +118,7 @@ class Wrapper {
fun falsePositive(w: Wrapper) {
if (w.unwrap() != null) {
// Here we should NOT have smart cast
w.unwrap().<!INAPPLICABLE_CANDIDATE!>length<!>
w.unwrap().<!UNSAFE_CALL!>length<!>
}
}
@@ -2,5 +2,5 @@ fun calc(x: List<String>?): Int {
// x should be non-null in arguments list
x?.get(x.size - 1)
// but not also here!
return x.<!INAPPLICABLE_CANDIDATE!>size<!>
return x.<!UNSAFE_CALL!>size<!>
}
@@ -2,5 +2,5 @@ fun calc(x: List<String>?): Int {
// x should be non-null in arguments list
x?.subList(x.size - 1, x.size)
// but not also here!
return x.<!INAPPLICABLE_CANDIDATE!>size<!>
return x.<!UNSAFE_CALL!>size<!>
}