From 4b89c22ba778930cc7da947fac40a92826d8b86c Mon Sep 17 00:00:00 2001 From: Svetlana Isakova Date: Mon, 9 Dec 2013 14:17:36 +0400 Subject: [PATCH] Check separately auto casts for nullable types. It's necessary for functions with generics: constraint system ignores nullability divergence, the error checks are made afterwards, the auto cast check should be made similarly. --- .../lang/resolve/calls/CandidateResolver.java | 9 +++-- .../calls/autocasts/AutoCastUtils.java | 34 +++++++++++++++++++ .../smartCastedReceiverWithGenerics.kt | 7 ++++ .../checkers/JetDiagnosticsTestGenerated.java | 7 +++- 4 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/nullabilityAndAutoCasts/smartCastedReceiverWithGenerics.kt diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CandidateResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CandidateResolver.java index 2bed10eda77..1e65415e97b 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CandidateResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CandidateResolver.java @@ -892,9 +892,14 @@ public class CandidateResolver { BindingContext bindingContext = trace.getBindingContext(); boolean safeAccess = isExplicitReceiver && !implicitInvokeCheck && candidateCall.isSafeCall(); if (!safeAccess && !receiverParameter.getType().isNullable() && receiverArgument.getType().isNullable()) { + if (!AutoCastUtils.isNotNull(receiverArgument, bindingContext, context.dataFlowInfo)) { - context.tracing.unsafeCall(trace, receiverArgumentType, implicitInvokeCheck); - return UNSAFE_CALL_ERROR; + context.tracing.unsafeCall(trace, receiverArgumentType, implicitInvokeCheck); + return UNSAFE_CALL_ERROR; + } + if (isExplicitReceiver) { + AutoCastUtils.recordAutoCastToNotNullableType(receiverArgument, context.trace); + } } DataFlowValue receiverValue = DataFlowValueFactory.createDataFlowValue(receiverArgument, bindingContext); if (safeAccess && !context.dataFlowInfo.getNullability(receiverValue).canBeNull()) { diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/AutoCastUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/AutoCastUtils.java index e2d7e8a5779..64e4e6d7269 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/AutoCastUtils.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/AutoCastUtils.java @@ -18,6 +18,7 @@ package org.jetbrains.jet.lang.resolve.calls.autocasts; import com.google.common.collect.Lists; import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.JetExpression; import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.BindingTrace; import org.jetbrains.jet.lang.resolve.calls.context.ResolutionContext; @@ -25,6 +26,7 @@ import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver; import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue; import org.jetbrains.jet.lang.resolve.scopes.receivers.ThisReceiver; import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.lang.types.TypeUtils; import java.util.ArrayList; import java.util.Collections; @@ -117,4 +119,36 @@ public class AutoCastUtils { } } } + + public static void recordAutoCastToNotNullableType(@NotNull ReceiverValue receiver, @NotNull BindingTrace trace) { + if (!(receiver instanceof ExpressionReceiver)) return; + + JetType receiverType = receiver.getType(); + if (!receiverType.isNullable()) return; + JetType notNullableType = TypeUtils.makeNotNullable(receiverType); + + DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiver, trace.getBindingContext()); + JetExpression expression = ((ExpressionReceiver) receiver).getExpression(); + if (dataFlowValue.isStableIdentifier()) { + trace.record(AUTOCAST, expression, notNullableType); + trace.record(EXPRESSION_TYPE, expression, notNullableType); + } + else { + trace.report(AUTOCAST_IMPOSSIBLE.on(expression, notNullableType, expression.getText())); + } + } + + public static boolean isNotNull( + @NotNull ReceiverValue receiver, + @NotNull BindingContext bindingContext, + @NotNull DataFlowInfo dataFlowInfo + ) { + if (!receiver.getType().isNullable()) return true; + + List autoCastVariants = getAutoCastVariants(receiver, bindingContext, dataFlowInfo); + for (ReceiverValue autoCastVariant : autoCastVariants) { + if (!autoCastVariant.getType().isNullable()) return true; + } + return false; + } } diff --git a/compiler/testData/diagnostics/tests/nullabilityAndAutoCasts/smartCastedReceiverWithGenerics.kt b/compiler/testData/diagnostics/tests/nullabilityAndAutoCasts/smartCastedReceiverWithGenerics.kt new file mode 100644 index 00000000000..95e794aa8fe --- /dev/null +++ b/compiler/testData/diagnostics/tests/nullabilityAndAutoCasts/smartCastedReceiverWithGenerics.kt @@ -0,0 +1,7 @@ +fun test(a: Any?) { + if (a != null) { + a.foo(11) + } +} + +fun Any.foo(t: T) = t \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java index fe38410a9ca..570f36476d9 100644 --- a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java @@ -4702,6 +4702,11 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage doTest("compiler/testData/diagnostics/tests/nullabilityAndAutoCasts/senslessComparisonWithNullOnTypeParameters.kt"); } + @TestMetadata("smartCastedReceiverWithGenerics.kt") + public void testSmartCastedReceiverWithGenerics() throws Exception { + doTest("compiler/testData/diagnostics/tests/nullabilityAndAutoCasts/smartCastedReceiverWithGenerics.kt"); + } + } @TestMetadata("compiler/testData/diagnostics/tests/nullableTypes") @@ -6182,7 +6187,7 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage public void testPublicVal() throws Exception { doTest("compiler/testData/diagnostics/tests/smartCasts/publicVal.kt"); } - + @TestMetadata("thisWithLabel.kt") public void testThisWithLabel() throws Exception { doTest("compiler/testData/diagnostics/tests/smartCasts/thisWithLabel.kt");