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; DataFlowInfo initialDataFlowInfoForArguments = contextForSelector.dataFlowInfo;
DataFlowValue receiverDataFlowValue = null;
if (receiver instanceof ReceiverValue) { if (receiver instanceof ReceiverValue) {
DataFlowValue receiverDataFlowValue = DataFlowValueFactory.createDataFlowValue((ReceiverValue) receiver, context); receiverDataFlowValue = DataFlowValueFactory.createDataFlowValue((ReceiverValue) receiver, context);
// Additional "receiver != null" information // Additional "receiver != null" information
// Should be applied if we consider a safe call // Should be applied if we consider a safe call
if (element.getSafe()) { if (element.getSafe()) {
@@ -467,7 +468,8 @@ public class CallExpressionResolver {
checkNestedClassAccess(element.getQualified(), contextForSelector); checkNestedClassAccess(element.getQualified(), contextForSelector);
boolean safeCall = element.getSafe(); 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); selectorReturnType = TypeUtils.makeNullable(selectorReturnType);
selectorReturnTypeInfo = selectorReturnTypeInfo.replaceType(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.ConstraintPosition
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.RECEIVER_POSITION 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.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
import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus.INCOMPLETE_TYPE_INFERENCE import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus.INCOMPLETE_TYPE_INFERENCE
import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus.OTHER_ERROR 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] } val freshVariables = returnType.getNestedTypeParameters().mapNotNull { conversion[it] }
builder.registerTypeVariables(nestedCall.call.toHandle(), freshVariables, external = true) builder.registerTypeVariables(nestedCall.call.toHandle(), freshVariables, external = true)
// Looks not too nice, but safe call result must be nullable if receiver is nullable // Safe call result must be nullable if receiver is nullable
val argumentExpressionType = candidateWithFreshVariables.returnType?.let { val argumentExpressionType = nestedCall.makeNullableTypeIfSafeReceiver(candidateWithFreshVariables.returnType, context)
if (nestedCall.isSafeCall && nestedCall.getExplicitReceiverValue()?.type?.let {TypeUtils.isNullableType(it) } ?: true ) {
TypeUtils.makeNullable(it)
}
else it
}
builder.addSubtypeConstraint( builder.addSubtypeConstraint(
argumentExpressionType, argumentExpressionType,
@@ -26,6 +26,10 @@ fun foo(l: A<String>?) {
// No errors should be here // No errors should be here
foo(l?.bar()) checkType { _<String?>() } foo(l?.bar()) checkType { _<String?>() }
foo(l?.gav()) 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>) { fun fooNotNull(l: A<String>) {