[NI] Fix recursion in contract declaration analysis.

#KT-30410 Fixed
This commit is contained in:
Dmitriy Novozhilov
2019-05-21 10:58:29 +03:00
parent 8d5c61f3ab
commit fb77e1f0bd
5 changed files with 50 additions and 14 deletions
@@ -0,0 +1,37 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.resolve
import org.jetbrains.kotlin.contracts.description.ContractProviderKey
import org.jetbrains.kotlin.contracts.description.LazyContractProvider
import org.jetbrains.kotlin.contracts.parsing.isContractCallDescriptor
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.psi.Call
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.psiUtil.isContractDescriptionCallPsiCheck
import org.jetbrains.kotlin.psi.psiUtil.isFirstStatement
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
/*
* See KT-26386 and KT-30410
*/
fun disableContractsInsideContractsBlock(call: Call, descriptor: CallableDescriptor?, scope: LexicalScope, trace: BindingTrace) {
call.callElement.safeAs<KtExpression>()?.let { callExpression ->
if (callExpression.isFirstStatement() && callExpression.isContractDescriptionCallPsiCheck()) {
if (descriptor?.isContractCallDescriptor() != true) {
scope.ownerDescriptor
.safeAs<FunctionDescriptor>()
?.getUserData(ContractProviderKey)
?.safeAs<LazyContractProvider>()
?.setContractDescription(null)
} else {
trace.record(BindingContext.IS_CONTRACT_DECLARATION_BLOCK, callExpression, true)
}
}
}
}
@@ -135,19 +135,7 @@ class CallCompleter(
context: BasicCallResolutionContext,
tracing: TracingStrategy
) {
context.call.callElement.safeAs<KtExpression>()?.let { callExpression ->
if (callExpression.isFirstStatement() && callExpression.isContractDescriptionCallPsiCheck()) {
if (resolvedCall?.resultingDescriptor?.isContractCallDescriptor() != true) {
context.scope.ownerDescriptor
.safeAs<FunctionDescriptor>()
?.getUserData(ContractProviderKey)
?.safeAs<LazyContractProvider>()
?.setContractDescription(null)
} else {
context.trace.record(BindingContext.IS_CONTRACT_DECLARATION_BLOCK, callExpression, true)
}
}
}
disableContractsInsideContractsBlock(context.call, resolvedCall?.resultingDescriptor, context.scope, context.trace)
if (resolvedCall == null || resolvedCall.isCompleted || resolvedCall.constraintSystem == null) {
completeArguments(context, results)
@@ -265,4 +265,10 @@ class KotlinResolutionCallbacksImpl(
trace.record(BindingContext.CAST_TYPE_USED_AS_EXPECTED_TYPE, binaryParent)
return resultType
}
override fun disableContractsIfNecessary(resolvedAtom: ResolvedCallAtom) {
val atom = resolvedAtom.atom as? PSIKotlinCall ?: return
val context = topLevelCallContext ?: return
disableContractsInsideContractsBlock(atom.psiCall, resolvedAtom.candidateDescriptor, context.scope, trace)
}
}
@@ -55,6 +55,8 @@ interface KotlinResolutionCallbacks {
val inferenceSession: InferenceSession
fun getExpectedTypeFromAsExpressionAndRecordItInTrace(resolvedAtom: ResolvedCallAtom): UnwrappedType?
fun disableContractsIfNecessary(resolvedAtom: ResolvedCallAtom)
}
interface SamConversionTransformer {
@@ -133,7 +133,10 @@ class KotlinCallCompleter(
val candidate = candidates.singleOrNull()
// this is needed at least for non-local return checker, because when we analyze lambda we should already bind descriptor for outer call
candidate?.resolvedCall?.let { resolutionCallbacks.bindStubResolvedCallForCandidate(it) }
candidate?.resolvedCall?.let {
resolutionCallbacks.bindStubResolvedCallForCandidate(it)
resolutionCallbacks.disableContractsIfNecessary(it)
}
return candidate ?: factory.createErrorCandidate().forceResolution()
}