[NI] Fix smartcasts for conventional contains in when

Call argument for conventional `contains` after expanding `in` may come from a `when` subject during its branch analysis.
In this case data flow info from a previous when branch was not considered,
because data flow info for subject had been used instead of data flow before argument.
Use of the latter one for the conventional `contains` solves the issue.

The old FE uses `isExternal` property of value arguments to skip smartcast reporting on `when` subject,
if they come from branches. To prevent undesired smartcasts on `when` subject after branch analysis in the new FE,
`isExternal` arguments are skipped in diagnostic reporter and during recorded type update.

Also, the new FE interprets `isExternal` completely differently from the old FE.
In the old FE this property is used exclusively by `when` with subject.
In the new FE it is also used for parially resolved calls, lambda return arguments and receivers.
This may be preventing the use of data flow info before argument in the first place, but this assumption requires additional investigation.

^KT-36818 Fixed
This commit is contained in:
Pavel Kirpichenkov
2020-02-19 20:22:57 +03:00
parent afceec71a4
commit 07ca355af8
17 changed files with 183 additions and 22 deletions
@@ -251,10 +251,12 @@ class DiagnosticReporterByTrackingStrategy(
)
val dataFlowValue = dataFlowValueFactory.createDataFlowValue(expressionArgument.receiver.receiverValue, context)
val call = if (call.callElement is KtBinaryExpression) null else call
smartCastManager.checkAndRecordPossibleCast(
dataFlowValue, smartCastDiagnostic.smartCastType, argumentExpression, context, call,
recordExpressionType = false
)
if (!expressionArgument.valueArgument.isExternal()) {
smartCastManager.checkAndRecordPossibleCast(
dataFlowValue, smartCastDiagnostic.smartCastType, argumentExpression, context, call,
recordExpressionType = false
)
} else null
}
is ReceiverExpressionKotlinCallArgument -> {
trace.markAsReported()
@@ -130,7 +130,7 @@ class KotlinResolutionCallbacksImpl(
return createSimplePSICallArgument(
trace.bindingContext, outerCallContext.statementFilter, outerCallContext.scope.ownerDescriptor,
CallMaker.makeExternalValueArgument(ktExpression), DataFlowInfo.EMPTY, typeInfo, languageVersionSettings,
dataFlowValueFactory
dataFlowValueFactory, outerCallContext.call
)
}
@@ -318,13 +318,15 @@ class KotlinToResolvedCallTransformer(
)
}
updateRecordedType(
argumentExpression,
parameter,
newContext,
constantConvertedArgument?.unknownIntegerType?.unwrap(),
resolvedCall.isReallySuccess()
)
if (!valueArgument.isExternal()) {
updateRecordedType(
argumentExpression,
parameter,
newContext,
constantConvertedArgument?.unknownIntegerType?.unwrap(),
resolvedCall.isReallySuccess()
)
}
}
}
@@ -303,7 +303,8 @@ internal fun createSimplePSICallArgument(
contextForArgument.scope.ownerDescriptor, valueArgument,
contextForArgument.dataFlowInfo, typeInfoForArgument,
contextForArgument.languageVersionSettings,
contextForArgument.dataFlowValueFactory
contextForArgument.dataFlowValueFactory,
contextForArgument.call,
)
internal fun createSimplePSICallArgument(
@@ -314,7 +315,8 @@ internal fun createSimplePSICallArgument(
dataFlowInfoBeforeThisArgument: DataFlowInfo,
typeInfoForArgument: KotlinTypeInfo,
languageVersionSettings: LanguageVersionSettings,
dataFlowValueFactory: DataFlowValueFactory
dataFlowValueFactory: DataFlowValueFactory,
call: Call
): SimplePSIKotlinCallArgument? {
val ktExpression = KtPsiUtil.getLastElementDeparenthesized(valueArgument.getArgumentExpression(), statementFilter) ?: return null
@@ -331,10 +333,10 @@ internal fun createSimplePSICallArgument(
// so we use a fast-path here to avoid calling transformToReceiverWithSmartCastInfo function
ReceiverValueWithSmartCastInfo(expressionReceiver, emptySet(), isStable = true)
} else {
val useDataFlowInfoBeforeArgument = call.callType == Call.CallType.CONTAINS
transformToReceiverWithSmartCastInfo(
ownerDescriptor, bindingContext,
// dataFlowInfoBeforeThisArgument cannot be used here, because of if() { if (x != null) return; x }
typeInfoForArgument.dataFlowInfo,
if (useDataFlowInfoBeforeArgument) dataFlowInfoBeforeThisArgument else typeInfoForArgument.dataFlowInfo,
expressionReceiver,
languageVersionSettings,
dataFlowValueFactory
@@ -1402,9 +1402,14 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
ExpressionReceiver receiver = safeGetExpressionReceiver(facade, right, contextWithNoExpectedType);
ExpressionTypingContext contextWithDataFlow = context.replaceDataFlowInfo(dataFlowInfo);
Call containsCall = CallMaker.makeCall(
callElement, receiver, null, operationSign,
Collections.singletonList(leftArgument), Call.CallType.CONTAINS
);
OverloadResolutionResults<FunctionDescriptor> resolutionResult = components.callResolver.resolveCallWithGivenName(
contextWithDataFlow,
CallMaker.makeCall(callElement, receiver, null, operationSign, Collections.singletonList(leftArgument)),
containsCall,
operationSign,
OperatorNameConventions.CONTAINS);
KotlinType containsType = OverloadResolutionResultsUtil.getResultingType(resolutionResult, context);