More accurate safe call handling in GenericCandidateResolver / CallExpressionResolver

(real receiver nullability is taken into account)
This commit is contained in:
Mikhail Glukhikh
2016-02-12 13:21:10 +03:00
committed by Mikhail Glukhikh
parent 17593e4ef6
commit 0f7284f83a
3 changed files with 11 additions and 10 deletions
@@ -441,8 +441,9 @@ public class CallExpressionResolver {
}
DataFlowInfo initialDataFlowInfoForArguments = contextForSelector.dataFlowInfo;
DataFlowValue receiverDataFlowValue = null;
if (receiver instanceof ReceiverValue) {
DataFlowValue receiverDataFlowValue = DataFlowValueFactory.createDataFlowValue((ReceiverValue) receiver, context);
receiverDataFlowValue = DataFlowValueFactory.createDataFlowValue((ReceiverValue) receiver, context);
// Additional "receiver != null" information
// Should be applied if we consider a safe call
if (element.getSafe()) {
@@ -467,7 +468,8 @@ public class CallExpressionResolver {
checkNestedClassAccess(element.getQualified(), contextForSelector);
boolean safeCall = element.getSafe();
if (safeCall && selectorReturnType != null && TypeUtils.isNullableType(receiverType)) {
if (safeCall && selectorReturnType != null && receiverDataFlowValue != null &&
contextForSelector.dataFlowInfo.getPredictableNullability(receiverDataFlowValue).canBeNull()) {
selectorReturnType = TypeUtils.makeNullable(selectorReturnType);
selectorReturnTypeInfo = selectorReturnTypeInfo.replaceType(selectorReturnType);
}
@@ -36,7 +36,7 @@ import org.jetbrains.kotlin.resolve.calls.inference.*
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPosition
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.RECEIVER_POSITION
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.VALUE_PARAMETER_POSITION
import org.jetbrains.kotlin.resolve.calls.resolvedCallUtil.getExplicitReceiverValue
import org.jetbrains.kotlin.resolve.calls.resolvedCallUtil.makeNullableTypeIfSafeReceiver
import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus
import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus.INCOMPLETE_TYPE_INFERENCE
import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus.OTHER_ERROR
@@ -174,13 +174,8 @@ class GenericCandidateResolver(private val argumentTypeResolver: ArgumentTypeRes
val freshVariables = returnType.getNestedTypeParameters().mapNotNull { conversion[it] }
builder.registerTypeVariables(nestedCall.call.toHandle(), freshVariables, external = true)
// Looks not too nice, but safe call result must be nullable if receiver is nullable
val argumentExpressionType = candidateWithFreshVariables.returnType?.let {
if (nestedCall.isSafeCall && nestedCall.getExplicitReceiverValue()?.type?.let {TypeUtils.isNullableType(it) } ?: true ) {
TypeUtils.makeNullable(it)
}
else it
}
// Safe call result must be nullable if receiver is nullable
val argumentExpressionType = nestedCall.makeNullableTypeIfSafeReceiver(candidateWithFreshVariables.returnType, context)
builder.addSubtypeConstraint(
argumentExpressionType,
@@ -26,6 +26,10 @@ fun foo(l: A<String>?) {
// No errors should be here
foo(l?.bar()) checkType { _<String?>() }
foo(l?.gav()) checkType { _<String?>() }
if (l != null) {
foo(l<!UNNECESSARY_SAFE_CALL!>?.<!>bar()) checkType { _<String>() }
foo(l<!UNNECESSARY_SAFE_CALL!>?.<!>gav()) checkType { _<String>() }
}
}
fun fooNotNull(l: A<String>) {