Fix recursion in contract declaration analysis. KT-26386 Fixed

Issue fixed only in old inference
This commit is contained in:
Dmitriy Novozhilov
2019-03-07 14:13:28 +03:00
parent d0462859bf
commit 77c98bef4d
13 changed files with 137 additions and 14 deletions
@@ -33,6 +33,7 @@ import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
@@ -127,6 +128,10 @@ class EffectSystem(
bindingTrace: BindingTrace,
moduleDescriptor: ModuleDescriptor
): MutableContextInfo {
val isInContractBlock = expression.parentsWithSelf.filterIsInstance<KtExpression>().any {
bindingTrace.bindingContext[BindingContext.IS_CONTRACT_DECLARATION_BLOCK, it] == true
}
if (isInContractBlock) return MutableContextInfo.EMPTY
val computation = getNonTrivialComputation(expression, bindingTrace, moduleDescriptor) ?: return MutableContextInfo.EMPTY
return InfoCollector(observedEffect, constants).collectFromSchema(computation.effects)
}
@@ -99,6 +99,7 @@ public interface BindingContext {
WritableSlice<KtExpression, KotlinType> EXPECTED_EXPRESSION_TYPE = new BasicWritableSlice<>(DO_NOTHING);
WritableSlice<KtElement, Computation> EXPRESSION_EFFECTS = Slices.createSimpleSlice();
WritableSlice<KtElement, Boolean> CONTRACT_NOT_ALLOWED = Slices.createSimpleSlice();
WritableSlice<KtElement, Boolean> IS_CONTRACT_DECLARATION_BLOCK = Slices.createSimpleSlice();
WritableSlice<KtFunction, KotlinType> EXPECTED_RETURN_TYPE = new BasicWritableSlice<>(DO_NOTHING);
WritableSlice<KtExpression, DataFlowInfo> DATAFLOW_INFO_AFTER_CONDITION = Slices.createSimpleSlice();
WritableSlice<VariableDescriptor, DataFlowValue> BOUND_INITIALIZER_VALUE = Slices.createSimpleSlice();
@@ -9,12 +9,18 @@ import org.jetbrains.kotlin.builtins.getReturnTypeFromFunctionType
import org.jetbrains.kotlin.builtins.getValueParameterTypesFromFunctionType
import org.jetbrains.kotlin.builtins.isFunctionOrSuspendFunctionType
import org.jetbrains.kotlin.contracts.EffectSystem
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.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.isContractDescriptionCallPsiCheck
import org.jetbrains.kotlin.psi.psiUtil.isFirstStatement
import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.BindingContext.CONSTRAINT_SYSTEM_COMPLETER
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.ResolveArgumentsMode.RESOLVE_FUNCTION_ARGUMENTS
@@ -129,6 +135,20 @@ 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)
}
}
}
if (resolvedCall == null || resolvedCall.isCompleted || resolvedCall.constraintSystem == null) {
completeArguments(context, results)
resolvedCall?.updateResultDataFlowInfoUsingEffects(context.trace)
@@ -12,9 +12,8 @@ import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.descriptors.PropertyAccessorDescriptor
import org.jetbrains.kotlin.descriptors.isOverridable
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.psi.KtBlockExpression
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.psiUtil.isFirstStatement
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
@@ -57,13 +56,7 @@ object ContractNotAllowedCallChecker : CallChecker {
if (functionDescriptor?.isOverridable == true) contractNotAllowed("Contracts are not allowed for open functions")
var parent = callElement.parent
var element = callElement
if (parent is KtDotQualifiedExpression) {
element = parent
parent = parent.parent
}
if ((parent !is KtBlockExpression || parent.children.first { it is KtElement } != element) && inFunctionBodyBlock) {
if (!callElement.isFirstStatement() && inFunctionBodyBlock) {
contractNotAllowed("Contract should be the first statement")
}