Call chains: receiver data flow info is dropped for extensions with nullable receiver #KT-10056 Fixed
This commit is contained in:
+8
-2
@@ -413,9 +413,15 @@ public class CallExpressionResolver {
|
||||
|
||||
boolean lastStage = element.getQualified() == expression;
|
||||
// Drop NO_EXPECTED_TYPE / INDEPENDENT at last stage
|
||||
// But receiver data flow info changes should be always applied, while we are inside call chain
|
||||
ExpressionTypingContext baseContext = lastStage ? context : currentContext;
|
||||
contextForSelector = baseContext.replaceDataFlowInfo(receiverDataFlowInfo);
|
||||
if (TypeUtils.isNullableType(receiverType) && !element.getSafe()) {
|
||||
// Call with nullable receiver: take unconditional data flow info
|
||||
contextForSelector = baseContext.replaceDataFlowInfo(unconditionalDataFlowInfo);
|
||||
}
|
||||
else {
|
||||
// Take data flow info from the current receiver
|
||||
contextForSelector = baseContext.replaceDataFlowInfo(receiverDataFlowInfo);
|
||||
}
|
||||
|
||||
if (receiver.exists() && receiver instanceof ReceiverValue) {
|
||||
DataFlowValue receiverDataFlowValue = DataFlowValueFactory.createDataFlowValue((ReceiverValue) receiver, context);
|
||||
|
||||
+2
-3
@@ -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
|
||||
}
|
||||
}
|
||||
+12
@@ -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
|
||||
}
|
||||
+44
@@ -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()
|
||||
}
|
||||
}
|
||||
+26
@@ -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
|
||||
}
|
||||
+13
@@ -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
|
||||
}
|
||||
+12
@@ -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
|
||||
}
|
||||
+27
@@ -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
|
||||
}
|
||||
}
|
||||
+15
@@ -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!
|
||||
}
|
||||
@@ -15608,6 +15608,30 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("nullableReceiver.kt")
|
||||
public void testNullableReceiver() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/safecalls/nullableReceiver.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("nullableReceiverInLongChain.kt")
|
||||
public void testNullableReceiverInLongChain() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/safecalls/nullableReceiverInLongChain.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("nullableReceiverWithExclExcl.kt")
|
||||
public void testNullableReceiverWithExclExcl() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/safecalls/nullableReceiverWithExclExcl.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("nullableReceiverWithFlexible.kt")
|
||||
public void testNullableReceiverWithFlexible() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/safecalls/nullableReceiverWithFlexible.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("property.kt")
|
||||
public void testProperty() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/safecalls/property.kt");
|
||||
|
||||
Reference in New Issue
Block a user