[NI] Reanalyze coroutine-block if there is inapplicable call

It's not clear how one should rollback _all_ resolution results if
 there is inapplicable call. Ideally, such calls should not be available
 in coroutine block but for now, to have backward compatibility, we'll
 just reanalyze coroutine block as a usual lambda if there is at least
 one such call.

 As a result, also remove diagnostic about non-applicable call as it
 become useless with current reanalysis

 #KT-37061 Fixed
 #KT-32097 Fixed
 #KT-32203 Fixed
 #KT-35306 Fixed
 #KT-36202 Fixed
 #KT-36220 Fixed
 #KT-32654 Fixed
This commit is contained in:
Mikhail Zarechenskiy
2020-02-27 19:06:14 +03:00
parent aab8555d65
commit 14f7529c1b
27 changed files with 641 additions and 127 deletions
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.expressions.DoubleColonExpressionResolver
import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices
import org.jetbrains.kotlin.types.typeUtil.contains
import org.jetbrains.kotlin.utils.addToStdlib.cast
class CoroutineInferenceSession(
@@ -52,6 +53,7 @@ class CoroutineInferenceSession(
) {
private val commonCalls = arrayListOf<PSICompletedCallInfo>()
private val diagnostics = arrayListOf<KotlinCallDiagnostic>()
private var hasInapplicableCall = false
override fun shouldRunCompletion(candidate: KotlinResolutionCandidate): Boolean {
val system = candidate.getSystem() as NewConstraintSystemImpl
@@ -78,26 +80,40 @@ class CoroutineInferenceSession(
commonCalls.add(callInfo)
val resultingDescriptor = callInfo.resolvedCall.resultingDescriptor
// This check is similar to one for old inference, see getCoroutineInferenceData() function
val checkCall = resultingDescriptor is LocalVariableDescriptor || anyReceiverContainStubType(resultingDescriptor)
if (!checkCall) return
val isApplicableCall =
callComponents.statelessCallbacks.isApplicableCallForBuilderInference(
callInfo.resolvedCall.resultingDescriptor,
resultingDescriptor,
callComponents.languageVersionSettings
)
if (!isApplicableCall) {
diagnostics.add(NonApplicableCallForBuilderInferenceDiagnostic(callInfo.callResolutionResult.resultCallAtom.atom))
hasInapplicableCall = true
}
}
private fun anyReceiverContainStubType(descriptor: CallableDescriptor): Boolean {
return descriptor.dispatchReceiverParameter?.type?.contains { it is StubType } == true ||
descriptor.extensionReceiverParameter?.type?.contains { it is StubType } == true
}
fun hasInapplicableCall(): Boolean = hasInapplicableCall
override fun writeOnlyStubs(callInfo: SingleCallResolutionResult): Boolean {
return !skipCall(callInfo)
}
private fun skipCall(callInfo: SingleCallResolutionResult): Boolean {
// FakeCallableDescriptorForObject and LocalVariableDescriptor can't introduce new information for inference,
// FakeCallableDescriptorForObject can't introduce new information for inference,
// so it's safe to complete it fully
val descriptor = callInfo.resultCallAtom.candidateDescriptor
return descriptor is FakeCallableDescriptorForObject || descriptor is LocalVariableDescriptor
return descriptor is FakeCallableDescriptorForObject
}
override fun currentConstraintSystem(): ConstraintStorage {
@@ -139,8 +139,6 @@ class KotlinResolutionCallbacksImpl(
if (expectedReturnType == null) ContextDependency.DEPENDENT else ContextDependency.INDEPENDENT
)
trace.record(BindingContext.NEW_INFERENCE_LAMBDA_INFO, psiCallArgument.ktFunction, lambdaInfo)
val builtIns = outerCallContext.scope.ownerDescriptor.builtIns
// We have to refine receiverType because resolve inside lambda needs proper scope from receiver,
@@ -179,8 +177,16 @@ class KotlinResolutionCallbacksImpl(
null
}
val temporaryTrace = if (coroutineSession != null)
TemporaryBindingTrace.create(trace, "Trace to resolve coroutine $lambdaArgument")
else
null
(temporaryTrace ?: trace).record(BindingContext.NEW_INFERENCE_LAMBDA_INFO, psiCallArgument.ktFunction, lambdaInfo)
val actualContext = outerCallContext
.replaceBindingTrace(trace)
.replaceBindingTrace(temporaryTrace ?: trace)
.replaceContextDependency(lambdaInfo.contextDependency)
.replaceExpectedType(approximatesExpectedType)
.replaceDataFlowInfo(psiCallArgument.dataFlowInfoBeforeThisArgument).let {
@@ -188,7 +194,13 @@ class KotlinResolutionCallbacksImpl(
}
val functionTypeInfo = expressionTypingServices.getTypeInfo(psiCallArgument.expression, actualContext)
trace.record(BindingContext.NEW_INFERENCE_LAMBDA_INFO, psiCallArgument.ktFunction, LambdaInfo.STUB_EMPTY)
(temporaryTrace ?: trace).record(BindingContext.NEW_INFERENCE_LAMBDA_INFO, psiCallArgument.ktFunction, LambdaInfo.STUB_EMPTY)
if (coroutineSession?.hasInapplicableCall() == true) {
return ReturnArgumentsAnalysisResult(ReturnArgumentsInfo.empty, coroutineSession, hasInapplicableCallForBuilderInference = true)
} else {
temporaryTrace?.commit()
}
var hasReturnWithoutExpression = false
var returnArgumentFound = false