From 54e5cce9be755c0566e3da38a641867bedbfac1d Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Thu, 28 Feb 2019 13:58:52 +0300 Subject: [PATCH] Prohibit declaring multiple `callsInPlace` contract on same lambda. #KT-26150 fixed --- .../parsing/PsiContractParserDispatcher.kt | 19 +++++++++++++++++-- .../effects/callsInPlace/neg/2.kt | 5 ++--- .../effects/callsInPlace/neg/2.txt | 5 ----- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/PsiContractParserDispatcher.kt b/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/PsiContractParserDispatcher.kt index a68babb5eff..f6ad47bb77a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/PsiContractParserDispatcher.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/PsiContractParserDispatcher.kt @@ -18,6 +18,7 @@ package org.jetbrains.kotlin.contracts.parsing import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.contracts.description.BooleanExpression +import org.jetbrains.kotlin.contracts.description.CallsEffectDeclaration import org.jetbrains.kotlin.contracts.description.ContractDescription import org.jetbrains.kotlin.contracts.description.EffectDeclaration import org.jetbrains.kotlin.contracts.description.expressions.BooleanVariableReference @@ -69,8 +70,9 @@ internal class PsiContractParserDispatcher( return null } - val effects = lambda.bodyExpression?.statements?.mapNotNull { parseEffect(it) } ?: return null - + val effectsWithExpression = lambda.bodyExpression?.statements?.map { parseEffect(it) to it } ?: return null + checkDuplicatedCallsEffectsAndReport(effectsWithExpression) + val effects = effectsWithExpression.mapNotNull { it.first } if (effects.isEmpty()) return null return ContractDescription(effects, callContext.functionDescriptor, storageManager) @@ -92,6 +94,19 @@ internal class PsiContractParserDispatcher( return parser.tryParseEffect(expression) } + private fun checkDuplicatedCallsEffectsAndReport(effects: List>) { + val descriptorsWithCallsEffect = mutableSetOf() + for ((effect, expression) in effects) { + if (effect !is CallsEffectDeclaration) continue + val descriptor = effect.variableReference.descriptor + if (descriptor in descriptorsWithCallsEffect) { + collector.badDescription("Duplicated contract for ${descriptor.name}. Only one `callsInPlace` contract per parameter is allowed.", expression) + } else { + descriptorsWithCallsEffect.add(descriptor) + } + } + } + private fun isValidEffectDeclaration(expression: KtExpression): Boolean { if (expression !is KtCallExpression && expression !is KtBinaryExpression) { collector.badDescription("unexpected construction in contract description", expression) diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractBuilder/effects/callsInPlace/neg/2.kt b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractBuilder/effects/callsInPlace/neg/2.kt index f17f6c41db4..e33f6ce8796 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractBuilder/effects/callsInPlace/neg/2.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractBuilder/effects/callsInPlace/neg/2.kt @@ -6,7 +6,6 @@ * SECTIONS: contracts, declarations, contractBuilder, effects, callsInPlace * NUMBER: 2 * DESCRIPTION: functions with contract and duplicate CallsInPlace. - * UNEXPECTED BEHAVIOUR * ISSUES: KT-26150 */ @@ -16,7 +15,7 @@ import kotlin.contracts.* inline fun case_1(block: () -> Unit) { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) - callsInPlace(block, InvocationKind.EXACTLY_ONCE) + callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return block() } @@ -25,7 +24,7 @@ inline fun case_1(block: () -> Unit) { inline fun case_2(block: () -> Unit) { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) - callsInPlace(block, InvocationKind.AT_MOST_ONCE) + callsInPlace(block, InvocationKind.AT_MOST_ONCE) } return block() } diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractBuilder/effects/callsInPlace/neg/2.txt b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractBuilder/effects/callsInPlace/neg/2.txt index aba002d3c4c..48508bb145a 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractBuilder/effects/callsInPlace/neg/2.txt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractBuilder/effects/callsInPlace/neg/2.txt @@ -1,9 +1,4 @@ package public inline fun case_1(/*0*/ block: () -> kotlin.Unit): kotlin.Unit - CallsInPlace(block, EXACTLY_ONCE) - CallsInPlace(block, EXACTLY_ONCE) - public inline fun case_2(/*0*/ block: () -> kotlin.Unit): kotlin.Unit - CallsInPlace(block, EXACTLY_ONCE) - CallsInPlace(block, AT_MOST_ONCE)