Nullable function-like property call is prohibited now #KT-8252 Fixed

This commit is contained in:
Mikhail Glukhikh
2016-01-12 12:13:12 +03:00
parent 0f80df7b2e
commit 1049d4c53a
7 changed files with 75 additions and 5 deletions
@@ -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
+23
View File
@@ -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()
}
}
+12
View File
@@ -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()}"
}