Call chains: receiver data flow info is dropped for extensions with nullable receiver #KT-10056 Fixed

This commit is contained in:
Mikhail Glukhikh
2015-11-18 14:41:48 +03:00
parent 97cdaba8b0
commit 8788d8e2d5
11 changed files with 198 additions and 5 deletions
@@ -1,6 +1,5 @@
fun calc(x: List<String>?): Int {
// x should be non-null in arguments list, despite of a chain
x?.subList(0, 1)<!UNSAFE_CALL!>.<!>get(<!DEBUG_INFO_SMARTCAST!>x<!>.size)
// But not here!
// x is not-null only inside subList
x?.subList(0, <!DEBUG_INFO_SMARTCAST!>x<!>.size - 1)<!UNSAFE_CALL!>.<!>get(x<!UNSAFE_CALL!>.<!>size)
return x!!.size
}
@@ -0,0 +1,15 @@
// See KT-10056
class Foo(val bar: String)
public inline fun <T, R> T.let(f: (T) -> R): R = f(this)
fun test(foo: Foo?) {
foo?.bar.let {
// Error, foo?.bar is nullable
it<!UNSAFE_CALL!>.<!>length
// Error, foo is nullable
foo<!UNSAFE_CALL!>.<!>bar.length
// Correct
foo?.bar?.length
}
}
@@ -0,0 +1,12 @@
package
public fun test(/*0*/ foo: Foo?): kotlin.Unit
public inline fun </*0*/ T, /*1*/ R> T.let(/*0*/ f: (T) -> R): R
public final class Foo {
public constructor Foo(/*0*/ bar: kotlin.String)
public final val bar: kotlin.String
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 open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,44 @@
class Bar(val gav: String)
class Foo(val bar: Bar, val nbar: Bar?) {
fun baz(s: String) = if (s != "") Bar(s) else null
}
public inline fun <T, R> T.let(f: (T) -> R): R = f(this)
fun String?.call(f: (String?) -> String?) = f(this)
fun String.notNullLet(f: (String) -> Unit) = f(this)
fun test(foo: Foo?) {
foo?.bar?.gav.let {
// Error, foo?.bar?.gav is nullable
it<!UNSAFE_CALL!>.<!>length
// Error, foo is nullable
foo<!UNSAFE_CALL!>.<!>bar.gav.length
// Correct
foo?.bar?.gav?.length
}
foo?.bar?.gav.call { it }?.notNullLet {
foo<!UNSAFE_CALL!>.<!>hashCode()
foo<!UNSAFE_CALL!>.<!>bar.hashCode()
}
}
fun testNotNull(foo: Foo) {
val s: String? = ""
foo.baz(s!!)?.gav.let {
it<!UNSAFE_CALL!>.<!>length
// Ok because of foo.
<!DEBUG_INFO_SMARTCAST!>s<!>.length.hashCode()
}
}
fun testNullable(foo: Foo?) {
val s: String? = ""
foo?.baz(s!!)?.gav.let {
it<!UNSAFE_CALL!>.<!>length
// Ok because of foo?.
s?.length?.hashCode()
}
}
@@ -0,0 +1,26 @@
package
public fun test(/*0*/ foo: Foo?): kotlin.Unit
public fun testNotNull(/*0*/ foo: Foo): kotlin.Unit
public fun testNullable(/*0*/ foo: Foo?): kotlin.Unit
public fun kotlin.String?.call(/*0*/ f: (kotlin.String?) -> kotlin.String?): kotlin.String?
public inline fun </*0*/ T, /*1*/ R> T.let(/*0*/ f: (T) -> R): R
public fun kotlin.String.notNullLet(/*0*/ f: (kotlin.String) -> kotlin.Unit): kotlin.Unit
public final class Bar {
public constructor Bar(/*0*/ gav: kotlin.String)
public final val gav: kotlin.String
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 open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final class Foo {
public constructor Foo(/*0*/ bar: Bar, /*1*/ nbar: Bar?)
public final val bar: Bar
public final val nbar: Bar?
public final fun baz(/*0*/ s: kotlin.String): Bar?
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 open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,13 @@
class Foo(val bar: String?)
public inline fun <T, R> T.let(f: (T) -> R): R = f(this)
fun test(foo: Foo?) {
foo!!.bar.let {
// Correct
<!DEBUG_INFO_SMARTCAST!>foo<!>.bar?.length
// Unnecessary
foo<!UNNECESSARY_SAFE_CALL!>?.<!>bar?.length
}
<!DEBUG_INFO_SMARTCAST!>foo<!>.bar?.length
}
@@ -0,0 +1,12 @@
package
public fun test(/*0*/ foo: Foo?): kotlin.Unit
public inline fun </*0*/ T, /*1*/ R> T.let(/*0*/ f: (T) -> R): R
public final class Foo {
public constructor Foo(/*0*/ bar: kotlin.String?)
public final val bar: kotlin.String?
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 open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,27 @@
// FILE: Foo.java
public class Foo {
public String bar;
private Foo(String bar) {
this.bar = bar;
}
public static Foo create(String bar) {
return new Foo(bar);
}
}
// FILE: Test.kt
public inline fun <T, R> T.let(f: (T) -> R): R = f(this)
fun test() {
val foo = Foo.create(null)
foo?.bar.let {
// Error, foo?.bar is nullable
it<!UNSAFE_CALL!>.<!>length
// Foo is nullable but flexible, so call is considered safe here
foo.bar.length
// Correct
foo?.bar?.length
}
}
@@ -0,0 +1,15 @@
package
public fun test(): kotlin.Unit
public inline fun </*0*/ T, /*1*/ R> T.let(/*0*/ f: (T) -> R): R
public open class Foo {
private constructor Foo(/*0*/ bar: kotlin.String!)
public final var bar: kotlin.String!
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 open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
// Static members
public open fun create(/*0*/ bar: kotlin.String!): Foo!
}