Nullable function-like property call is prohibited now #KT-8252 Fixed
This commit is contained in:
@@ -34,6 +34,7 @@ import org.jetbrains.kotlin.resolve.calls.callResolverUtil.getEffectiveExpectedT
|
||||
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.getErasedReceiverType
|
||||
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.isInvokeCallOnExpressionWithBothReceivers
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.isExplicitSafeCall
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.AdditionalTypeChecker
|
||||
import org.jetbrains.kotlin.resolve.calls.context.*
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.SubstitutionFilteringInternalResolveAnnotations
|
||||
@@ -454,13 +455,25 @@ class CandidateResolver(
|
||||
|
||||
// Here we know that receiver is OK ignoring nullability and check that nullability is OK too
|
||||
// Doing it simply as full subtyping check (receiverValueType <: receiverParameterType)
|
||||
val safeAccess = isExplicitReceiver && !implicitInvokeCheck && candidateCall.call.isExplicitSafeCall()
|
||||
val call = candidateCall.call
|
||||
val safeAccess = isExplicitReceiver && !implicitInvokeCheck && call.isExplicitSafeCall()
|
||||
val expectedReceiverParameterType = if (safeAccess) TypeUtils.makeNullable(receiverParameter.type) else receiverParameter.type
|
||||
val smartCastNeeded = !ArgumentTypeResolver.isSubtypeOfForArgumentType(receiverArgument.type, expectedReceiverParameterType)
|
||||
var reportUnsafeCall = false
|
||||
|
||||
val dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiverArgument, this)
|
||||
val nullability = dataFlowInfo.getPredictableNullability(dataFlowValue)
|
||||
var nullableImplicitInvokeReceiver = false
|
||||
if (implicitInvokeCheck && call is CallForImplicitInvoke && call.isSafeCall()) {
|
||||
val outerCallReceiver = call.outerCall.explicitReceiver
|
||||
if (outerCallReceiver != call.explicitReceiver && outerCallReceiver is ReceiverValue) {
|
||||
val outerReceiverDataFlowValue = DataFlowValueFactory.createDataFlowValue(outerCallReceiver, this)
|
||||
val outerReceiverNullability = dataFlowInfo.getPredictableNullability(outerReceiverDataFlowValue)
|
||||
if (outerReceiverNullability.canBeNull() && !TypeUtils.isNullableType(expectedReceiverParameterType)) {
|
||||
nullableImplicitInvokeReceiver = true
|
||||
}
|
||||
}
|
||||
}
|
||||
val expression = (receiverArgument as? ExpressionReceiver)?.expression
|
||||
if (nullability.canBeNull() && !nullability.canBeNonNull()) {
|
||||
if (!TypeUtils.isNullableType(expectedReceiverParameterType)) {
|
||||
@@ -470,7 +483,7 @@ class CandidateResolver(
|
||||
expression?.let { trace.record(BindingContext.SMARTCAST_NULL, it) }
|
||||
}
|
||||
}
|
||||
else if (smartCastNeeded) {
|
||||
else if (!nullableImplicitInvokeReceiver && smartCastNeeded) {
|
||||
// Look if smart cast has some useful nullability info
|
||||
|
||||
val smartCastResult = SmartCastManager.checkAndRecordPossibleCast(
|
||||
@@ -488,7 +501,7 @@ class CandidateResolver(
|
||||
|
||||
val receiverArgumentType = receiverArgument.type
|
||||
|
||||
if (reportUnsafeCall) {
|
||||
if (reportUnsafeCall || nullableImplicitInvokeReceiver) {
|
||||
tracing.unsafeCall(trace, receiverArgumentType, implicitInvokeCheck)
|
||||
return UNSAFE_CALL_ERROR
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
fun bar(doIt: Int.() -> Int) {
|
||||
1.doIt()
|
||||
1<!UNNECESSARY_SAFE_CALL!>?.<!>doIt()
|
||||
val i: Int? = 1
|
||||
i<!UNSAFE_CALL!>.<!>doIt()
|
||||
i?.doIt()
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package
|
||||
|
||||
public fun bar(/*0*/ doIt: kotlin.Int.() -> kotlin.Int): kotlin.Unit
|
||||
@@ -0,0 +1,23 @@
|
||||
class Rule(val apply:() -> Unit)
|
||||
|
||||
fun bar() {}
|
||||
|
||||
fun foo() {
|
||||
val rule: Rule? = Rule { bar() }
|
||||
|
||||
// this compiles and works
|
||||
val apply = rule?.apply
|
||||
if (apply != null) <!DEBUG_INFO_SMARTCAST!>apply<!>()
|
||||
|
||||
// this compiles and works
|
||||
rule?.apply?.invoke()
|
||||
|
||||
// this should be an error
|
||||
rule?.<!UNSAFE_CALL!>apply<!>()
|
||||
|
||||
// these both also ok (with smart cast / unnecessary safe call)
|
||||
if (rule != null) {
|
||||
<!DEBUG_INFO_SMARTCAST!>rule<!>.apply()
|
||||
rule<!UNNECESSARY_SAFE_CALL!>?.<!>apply()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package
|
||||
|
||||
public fun bar(): kotlin.Unit
|
||||
public fun foo(): kotlin.Unit
|
||||
|
||||
public final class Rule {
|
||||
public constructor Rule(/*0*/ apply: () -> kotlin.Unit)
|
||||
public final val apply: () -> 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 open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
@@ -241,6 +241,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("ExtensionCallInvoke.kt")
|
||||
public void testExtensionCallInvoke() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/ExtensionCallInvoke.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("fileDependencyRecursion.kt")
|
||||
public void testFileDependencyRecursion() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/fileDependencyRecursion.kt");
|
||||
@@ -577,6 +583,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("SafeCallInvoke.kt")
|
||||
public void testSafeCallInvoke() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/SafeCallInvoke.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("SafeCallNonNullReceiver.kt")
|
||||
public void testSafeCallNonNullReceiver() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/SafeCallNonNullReceiver.kt");
|
||||
|
||||
@@ -50,7 +50,7 @@ fun box(): String {
|
||||
return "Bad call getNullA()?.someFun(). result: $n1, counters: ${toStr()}"
|
||||
}
|
||||
|
||||
val n2 = getNullA()?.b()
|
||||
val n2 = getNullA()?.b?.invoke()
|
||||
if (n2 != null || toStr() != "20000") {
|
||||
return "Bad call getNullA()?.b(). result: $n2, counters: ${toStr()}"
|
||||
}
|
||||
@@ -65,7 +65,7 @@ fun box(): String {
|
||||
return "Bad call getA()?.someFun(). result: $i1, counters: ${toStr()}"
|
||||
}
|
||||
|
||||
val i2 = getA()?.b()
|
||||
val i2 = getA()?.b?.invoke()
|
||||
if (i2 != 2 || toStr() != "51101") {
|
||||
return "Bad call getA()?.b(). result: $i2, counters: ${toStr()}"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user