diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java index 061e259f6b6..10f731a2000 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java @@ -17,7 +17,6 @@ package org.jetbrains.jet.lang.resolve.calls; import com.google.common.base.Function; -import com.google.common.base.Predicate; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; @@ -31,6 +30,8 @@ import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.*; import org.jetbrains.jet.lang.resolve.calls.autocasts.AutoCastServiceImpl; import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo; +import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValue; +import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValueFactory; import org.jetbrains.jet.lang.resolve.calls.inference.*; import org.jetbrains.jet.lang.resolve.name.Name; import org.jetbrains.jet.lang.resolve.scopes.JetScope; @@ -891,11 +892,13 @@ public class CallResolver { ReceiverDescriptor receiverParameter, ReceiverDescriptor receiverArgument, boolean isExplicitReceiver, boolean implicitInvokeCheck) { + BindingContext bindingContext = context.candidateCall.getTrace().getBindingContext(); + ResolutionStatus result = SUCCESS; if (receiverParameter.exists() && receiverArgument.exists()) { boolean safeAccess = isExplicitReceiver && !implicitInvokeCheck && candidateCall.isSafeCall(); JetType receiverArgumentType = receiverArgument.getType(); - AutoCastServiceImpl autoCastService = new AutoCastServiceImpl(context.dataFlowInfo, context.candidateCall.getTrace().getBindingContext()); + AutoCastServiceImpl autoCastService = new AutoCastServiceImpl(context.dataFlowInfo, bindingContext); if (!safeAccess && !receiverParameter.getType().isNullable() && !autoCastService.isNotNull(receiverArgument)) { context.tracing.unsafeCall(context.candidateCall.getTrace(), receiverArgumentType, implicitInvokeCheck); @@ -912,7 +915,8 @@ public class CallResolver { result = OTHER_ERROR; } } - if (safeAccess && !receiverArgumentType.isNullable()) { + DataFlowValue receiverValue = DataFlowValueFactory.INSTANCE.createDataFlowValue(receiverArgument, bindingContext); + if (safeAccess && !context.dataFlowInfo.getNullability(receiverValue).canBeNull()) { context.tracing.unnecessarySafeCall(context.candidateCall.getTrace(), receiverArgumentType); } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/DataFlowValueFactory.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/DataFlowValueFactory.java index 3941428a34d..841a42fbf93 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/DataFlowValueFactory.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/DataFlowValueFactory.java @@ -23,7 +23,7 @@ import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.JetModuleUtil; -import org.jetbrains.jet.lang.resolve.scopes.receivers.ThisReceiverDescriptor; +import org.jetbrains.jet.lang.resolve.scopes.receivers.*; import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lang.types.TypeUtils; import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; @@ -61,6 +61,48 @@ public class DataFlowValueFactory { return new DataFlowValue(variableDescriptor, type, isStableVariable(variableDescriptor), getImmanentNullability(type)); } + @NotNull + public DataFlowValue createDataFlowValue(@NotNull ReceiverDescriptor receiverDescriptor, @NotNull BindingContext bindingContext) { + return receiverDescriptor.accept(new ReceiverDescriptorVisitor() { + @Override + public DataFlowValue visitNoReceiver(ReceiverDescriptor noReceiver, BindingContext data) { + throw new IllegalArgumentException("No DataFlowValue exists for ReceiverDescriptor.NO_RECEIVER"); + } + + @Override + public DataFlowValue visitExtensionReceiver(ExtensionReceiver receiver, BindingContext data) { + return createDataFlowValue(receiver); + } + + @Override + public DataFlowValue visitExpressionReceiver(ExpressionReceiver receiver, BindingContext bindingContext) { + return createDataFlowValue(receiver.getExpression(), receiver.getType(), bindingContext); + } + + @Override + public DataFlowValue visitClassReceiver(ClassReceiver receiver, BindingContext data) { + return createDataFlowValue(receiver); + } + + @Override + public DataFlowValue visitTransientReceiver(TransientReceiver receiver, BindingContext data) { + return createTransientDataFlowValue(receiver); + } + + @Override + public DataFlowValue visitScriptReceiver(ScriptReceiver receiver, BindingContext data) { + return createTransientDataFlowValue(receiver); + } + + @NotNull + private DataFlowValue createTransientDataFlowValue(ReceiverDescriptor receiver) { + JetType type = receiver.getType(); + boolean nullable = type.isNullable() || TypeUtils.hasNullableSuperType(type); + return new DataFlowValue(receiver, type, nullable, Nullability.NOT_NULL); + } + }, bindingContext); + } + private Nullability getImmanentNullability(JetType type) { return type.isNullable() || TypeUtils.hasNullableSuperType(type) ? Nullability.UNKNOWN : Nullability.NOT_NULL; } diff --git a/compiler/testData/diagnostics/tests/extensions/ExtensionFunctions.kt b/compiler/testData/diagnostics/tests/extensions/ExtensionFunctions.kt index 2793a85541e..9d11eb8bbc9 100644 --- a/compiler/testData/diagnostics/tests/extensions/ExtensionFunctions.kt +++ b/compiler/testData/diagnostics/tests/extensions/ExtensionFunctions.kt @@ -4,7 +4,7 @@ package outer fun Int?.optint() : Unit {} val Int?.optval : Unit = Unit.VALUE -fun T.foo(x : E, y : A) : T { +fun T.foo(x : E, y : A) : T { y.plus(1) y plus 1 y + 1.0 diff --git a/compiler/testData/diagnostics/tests/nullableTypes/safeCallOnTypeWithNullableUpperBound.kt b/compiler/testData/diagnostics/tests/nullableTypes/safeCallOnTypeWithNullableUpperBound.kt new file mode 100644 index 00000000000..1ab014ea1a8 --- /dev/null +++ b/compiler/testData/diagnostics/tests/nullableTypes/safeCallOnTypeWithNullableUpperBound.kt @@ -0,0 +1,13 @@ +fun test(t: T): String { + if (t != null) { + return t?.toString() + } + return t?.toString() +} + +fun T.testThis(): String { + if (this != null) { + return this?.toString() + } + return this?.toString() +} \ 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 95779106db5..d96072e490b 100644 --- a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java @@ -2319,6 +2319,11 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage doTest("compiler/testData/diagnostics/tests/nullableTypes/redundantNullableInSupertype.kt"); } + @TestMetadata("safeCallOnTypeWithNullableUpperBound.kt") + public void testSafeCallOnTypeWithNullableUpperBound() throws Exception { + doTest("compiler/testData/diagnostics/tests/nullableTypes/safeCallOnTypeWithNullableUpperBound.kt"); + } + } @TestMetadata("compiler/testData/diagnostics/tests/objects") diff --git a/idea/testData/checker/ExtensionFunctions.jet b/idea/testData/checker/ExtensionFunctions.jet index 6b7b018e0ec..d52c79e886e 100644 --- a/idea/testData/checker/ExtensionFunctions.jet +++ b/idea/testData/checker/ExtensionFunctions.jet @@ -1,7 +1,7 @@ fun Int?.optint() : Unit {} val Int?.optval : Unit = Unit.VALUE -fun T.foo(x : E, y : A) : T { +fun T.foo(x : E, y : A) : T { y.plus(1) y plus 1 y + 1.0