Prohibit declaring multiple callsInPlace contract on same lambda.

#KT-26150 fixed
This commit is contained in:
Dmitriy Novozhilov
2019-02-28 13:58:52 +03:00
parent 4a7420d77d
commit 54e5cce9be
3 changed files with 19 additions and 10 deletions
@@ -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<Pair<EffectDeclaration?, KtExpression>>) {
val descriptorsWithCallsEffect = mutableSetOf<ParameterDescriptor>()
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)
@@ -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)
<!ERROR_IN_CONTRACT_DESCRIPTION!>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)
<!ERROR_IN_CONTRACT_DESCRIPTION!>callsInPlace(block, InvocationKind.AT_MOST_ONCE)<!>
}
return block()
}
@@ -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)