[FIR] Properly track problems in contract description during effect extraction

This commit is contained in:
Dmitriy Novozhilov
2023-04-12 17:31:16 +03:00
committed by Space Team
parent 79d378f2bd
commit 5a92eb2c67
34 changed files with 563 additions and 102 deletions
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.fir.analysis.checkers.declaration
import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.KtRealSourceElementKind
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
@@ -13,15 +12,16 @@ import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.contracts.FirResolvedContractDescription
import org.jetbrains.kotlin.fir.contracts.description.*
import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction
import org.jetbrains.kotlin.fir.declarations.FirContractDescriptionOwner
import org.jetbrains.kotlin.fir.declarations.FirFunction
import org.jetbrains.kotlin.fir.declarations.FirPropertyAccessor
import org.jetbrains.kotlin.fir.declarations.utils.*
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
object FirContractChecker : FirFunctionChecker() {
// TODO: The message should vary. Migrate this to [ConeEffectExtractor] when creating fine-grained errors.
private const val UNEXPECTED_CONSTRUCTION = "unexpected construction in contract description"
private val EMPTY_CONTRACT_MESSAGE = "Empty contract block is not allowed"
override fun check(declaration: FirFunction, context: CheckerContext, reporter: DiagnosticReporter) {
if (declaration !is FirContractDescriptionOwner) return
@@ -29,6 +29,9 @@ object FirContractChecker : FirFunctionChecker() {
checkUnresolvedEffects(contractDescription, context, reporter)
checkContractNotAllowed(declaration, contractDescription, context, reporter)
if (contractDescription.effects.isEmpty() && contractDescription.unresolvedEffects.isEmpty()) {
reporter.reportOn(contractDescription.source, FirErrors.ERROR_IN_CONTRACT_DESCRIPTION, EMPTY_CONTRACT_MESSAGE, context)
}
}
private fun checkUnresolvedEffects(
@@ -38,12 +41,11 @@ object FirContractChecker : FirFunctionChecker() {
) {
// Any statements that [ConeEffectExtractor] cannot extract effects will be in `unresolvedEffects`.
for (unresolvedEffect in contractDescription.unresolvedEffects) {
val statement = unresolvedEffect.statement
if (statement.source == null || statement.source!!.kind is KtFakeSourceElementKind) continue
val diagnostic = unresolvedEffect.effect.accept(DiagnosticExtractor, null) ?: continue
// TODO: report on fine-grained locations, e.g., ... implies unresolved => report on unresolved, not the entire statement.
// but, sometimes, it's just reported on `contract`...
reporter.reportOn(statement.source, FirErrors.ERROR_IN_CONTRACT_DESCRIPTION, UNEXPECTED_CONSTRUCTION, context)
reporter.reportOn(unresolvedEffect.source, FirErrors.ERROR_IN_CONTRACT_DESCRIPTION, diagnostic.reason, context)
}
}
@@ -64,4 +66,78 @@ object FirContractChecker : FirFunctionChecker() {
else if (declaration.symbol.callableId.isLocal || declaration.visibility == Visibilities.Local) contractNotAllowed("Contracts are not allowed for local functions")
}
private object DiagnosticExtractor : ConeContractDescriptionVisitor<ConeDiagnostic?, Nothing?>() {
override fun visitContractDescriptionElement(
contractDescriptionElement: ConeContractDescriptionElement,
data: Nothing?
): ConeDiagnostic? {
return null
}
override fun visitConditionalEffectDeclaration(
conditionalEffect: ConeConditionalEffectDeclaration,
data: Nothing?
): ConeDiagnostic? {
return conditionalEffect.effect.accept(this, null) ?: conditionalEffect.condition.accept(this, null)
}
override fun visitReturnsEffectDeclaration(returnsEffect: ConeReturnsEffectDeclaration, data: Nothing?): ConeDiagnostic? {
return returnsEffect.value.accept(this, null)
}
override fun visitCallsEffectDeclaration(callsEffect: ConeCallsEffectDeclaration, data: Nothing?): ConeDiagnostic? {
return callsEffect.valueParameterReference.accept(this, data)
}
override fun visitErroneousCallsEffectDeclaration(
callsEffect: ConeErroneousCallsEffectDeclaration,
data: Nothing?
): ConeDiagnostic {
return callsEffect.diagnostic
}
override fun visitLogicalBinaryOperationContractExpression(
binaryLogicExpression: ConeBinaryLogicExpression,
data: Nothing?
): ConeDiagnostic? {
return binaryLogicExpression.left.accept(this, null) ?: binaryLogicExpression.right.accept(this, null)
}
override fun visitLogicalNot(logicalNot: ConeLogicalNot, data: Nothing?): ConeDiagnostic? {
return logicalNot.arg.accept(this, null)
}
override fun visitIsInstancePredicate(isInstancePredicate: ConeIsInstancePredicate, data: Nothing?): ConeDiagnostic? {
return isInstancePredicate.arg.accept(this, data)
}
override fun visitErroneousIsInstancePredicate(
isInstancePredicate: ConeErroneousIsInstancePredicate,
data: Nothing?
): ConeDiagnostic {
return isInstancePredicate.diagnostic
}
override fun visitIsNullPredicate(isNullPredicate: ConeIsNullPredicate, data: Nothing?): ConeDiagnostic? {
return isNullPredicate.arg.accept(this, data)
}
override fun visitErroneousConstantReference(
erroneousConstantReference: ConeErroneousConstantReference,
data: Nothing?
): ConeDiagnostic {
return erroneousConstantReference.diagnostic
}
override fun visitErroneousValueParameterReference(
valueParameterReference: ConeErroneousValueParameterReference,
data: Nothing?
): ConeDiagnostic {
return valueParameterReference.diagnostic
}
override fun visitErroneousElement(element: ConeErroneousContractElement, data: Nothing?): ConeDiagnostic {
return element.diagnostic
}
}
}
@@ -9,7 +9,7 @@ import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange
import org.jetbrains.kotlin.fir.contracts.FirContractDescription
import org.jetbrains.kotlin.fir.contracts.builder.buildResolvedContractDescription
import org.jetbrains.kotlin.fir.contracts.description.*
import org.jetbrains.kotlin.fir.contracts.toFirEffectDeclaration
import org.jetbrains.kotlin.fir.contracts.toFirElement
import org.jetbrains.kotlin.fir.declarations.FirContractDescriptionOwner
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.expressions.LogicOperationKind
@@ -24,7 +24,7 @@ class FirContractDeserializer(private val c: FirDeserializationContext) {
fun loadContract(proto: ProtoBuf.Contract, owner: FirContractDescriptionOwner): FirContractDescription? {
val effects = proto.effectList.map { loadPossiblyConditionalEffect(it, owner) ?: return null }
return buildResolvedContractDescription {
this.effects += effects.map { it.toFirEffectDeclaration() }
this.effects += effects.map { it.toFirElement() }
}
}
@@ -11,39 +11,49 @@ import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.contracts.description.*
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.references.FirNamedReference
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeContractDescriptionError
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.resolve.getContainingClass
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitor
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.ConstantValueKind
import org.jetbrains.kotlin.utils.addToStdlib.shouldNotBeCalled
class ConeEffectExtractor(
private val session: FirSession,
private val owner: FirContractDescriptionOwner,
private val valueParameters: List<FirValueParameter>
) : FirDefaultVisitor<ConeContractDescriptionElement?, Nothing?>() {
) : FirDefaultVisitor<ConeContractDescriptionElement, Nothing?>() {
companion object {
private val BOOLEAN_AND = FirContractsDslNames.id("kotlin", "Boolean", "and")
private val BOOLEAN_OR = FirContractsDslNames.id("kotlin", "Boolean", "or")
private val BOOLEAN_NOT = FirContractsDslNames.id("kotlin", "Boolean", "not")
}
override fun visitElement(element: FirElement, data: Nothing?): ConeContractDescriptionElement? {
return null
private fun ConeContractDescriptionError.asElement(): ConeErroneousContractElement {
return ConeErroneousContractElement(this)
}
override fun visitReturnExpression(returnExpression: FirReturnExpression, data: Nothing?): ConeContractDescriptionElement? {
override fun visitElement(element: FirElement, data: Nothing?): ConeContractDescriptionElement {
return ConeContractDescriptionError.IllegalElement(element).asElement()
}
override fun visitReturnExpression(returnExpression: FirReturnExpression, data: Nothing?): ConeContractDescriptionElement {
return returnExpression.result.accept(this, data)
}
override fun visitFunctionCall(functionCall: FirFunctionCall, data: Nothing?): ConeContractDescriptionElement? {
val resolvedId = functionCall.toResolvedCallableSymbol()?.callableId ?: return null
override fun visitFunctionCall(functionCall: FirFunctionCall, data: Nothing?): ConeContractDescriptionElement {
val resolvedId = functionCall.toResolvedCallableSymbol()?.callableId
?: return ConeContractDescriptionError.UnresolvedCall(functionCall.calleeReference.name).asElement()
return when (resolvedId) {
FirContractsDslNames.IMPLIES -> {
val effect = functionCall.explicitReceiver?.accept(this, null) as? ConeEffectDeclaration
?: return null
val condition = functionCall.argument.accept(this, null) as? ConeBooleanExpression
?: return null
val effect = functionCall.explicitReceiver?.asContractElement() as? ConeEffectDeclaration ?: noReceiver(resolvedId)
val condition = functionCall.argument.asContractElement() as? ConeBooleanExpression ?: noArgument(resolvedId)
ConeConditionalEffectDeclaration(effect, condition)
}
@@ -52,8 +62,10 @@ class ConeEffectExtractor(
val value = if (argument == null) {
ConeConstantReference.WILDCARD
} else {
argument.accept(this, null) as? ConeConstantReference
?: return null
when (val value = argument.asContractElement()) {
is ConeConstantReference -> value
else -> ConeErroneousConstantReference(ConeContractDescriptionError.NotAConstant(value))
}
}
ConeReturnsEffectDeclaration(value)
}
@@ -63,63 +75,80 @@ class ConeEffectExtractor(
}
FirContractsDslNames.CALLS_IN_PLACE -> {
val reference = functionCall.arguments[0].accept(this, null) as? ConeValueParameterReference
?: return null
val kind = functionCall.arguments.getOrNull(1)?.parseInvocationKind() ?: EventOccurrencesRange.UNKNOWN
ConeCallsEffectDeclaration(reference, kind)
val reference = functionCall.arguments[0].asContractValueExpression()
when (val argument = functionCall.arguments.getOrNull(1)) {
null -> ConeCallsEffectDeclaration(reference, EventOccurrencesRange.UNKNOWN)
else -> when (val kind = argument.parseInvocationKind()) {
null -> ConeErroneousCallsEffectDeclaration(reference, ConeContractDescriptionError.UnresolvedInvocationKind(argument))
else -> ConeCallsEffectDeclaration(reference, kind)
}
}
}
BOOLEAN_AND, BOOLEAN_OR -> {
val left = functionCall.explicitReceiver?.accept(this, null) as? ConeBooleanExpression ?: return null
val right = functionCall.argument.accept(this, null) as? ConeBooleanExpression ?: return null
val left = functionCall.explicitReceiver?.asContractBooleanExpression() ?: noReceiver(resolvedId)
val right = functionCall.arguments.firstOrNull()?.asContractBooleanExpression() ?: noArgument(resolvedId)
val kind = when (resolvedId) {
BOOLEAN_AND -> LogicOperationKind.AND
BOOLEAN_OR -> LogicOperationKind.OR
else -> throw IllegalStateException()
else -> shouldNotBeCalled()
}
ConeBinaryLogicExpression(left, right, kind)
}
BOOLEAN_NOT -> {
val arg = functionCall.explicitReceiver?.accept(this, null) as? ConeBooleanExpression ?: return null
val arg = functionCall.explicitReceiver?.asContractBooleanExpression() ?: noReceiver(resolvedId)
ConeLogicalNot(arg)
}
else -> null
else -> ConeContractDescriptionError.NotContractDsl(resolvedId).asElement()
}
}
override fun visitBinaryLogicExpression(
binaryLogicExpression: FirBinaryLogicExpression,
data: Nothing?
): ConeContractDescriptionElement? {
val left = binaryLogicExpression.leftOperand.accept(this, null) as? ConeBooleanExpression ?: return null
val right = binaryLogicExpression.rightOperand.accept(this, null) as? ConeBooleanExpression ?: return null
): ConeContractDescriptionElement {
val left = binaryLogicExpression.leftOperand.asContractBooleanExpression()
val right = binaryLogicExpression.rightOperand.asContractBooleanExpression()
return ConeBinaryLogicExpression(left, right, binaryLogicExpression.kind)
}
override fun visitEqualityOperatorCall(equalityOperatorCall: FirEqualityOperatorCall, data: Nothing?): ConeContractDescriptionElement? {
val isNegated = when (equalityOperatorCall.operation) {
override fun visitEqualityOperatorCall(equalityOperatorCall: FirEqualityOperatorCall, data: Nothing?): ConeContractDescriptionElement {
val isNegated = when (val operation = equalityOperatorCall.operation) {
FirOperation.EQ -> false
FirOperation.NOT_EQ -> true
else -> return null
else -> return ConeContractDescriptionError.IllegalEqualityOperator(operation).asElement()
}
val const = equalityOperatorCall.arguments[1] as? FirConstExpression<*> ?: return null
if (const.kind != ConstantValueKind.Null) return null
val arg = equalityOperatorCall.arguments[0].accept(this, null) as? ConeValueParameterReference ?: return null
val argument = equalityOperatorCall.arguments[1]
val const = argument as? FirConstExpression<*> ?: return ConeContractDescriptionError.NotAConstant(argument).asElement()
if (const.kind != ConstantValueKind.Null) return ConeContractDescriptionError.IllegalConst(const, onlyNullAllowed = true).asElement()
val arg = equalityOperatorCall.arguments[0].asContractValueExpression()
return ConeIsNullPredicate(arg, isNegated)
}
override fun visitSmartCastExpression(smartCastExpression: FirSmartCastExpression, data: Nothing?): ConeContractDescriptionElement? {
override fun visitSmartCastExpression(smartCastExpression: FirSmartCastExpression, data: Nothing?): ConeContractDescriptionElement {
return smartCastExpression.originalExpression.accept(this, data)
}
override fun visitQualifiedAccessExpression(
qualifiedAccessExpression: FirQualifiedAccessExpression,
data: Nothing?
): ConeContractDescriptionElement? {
val symbol = qualifiedAccessExpression.toResolvedCallableSymbol() ?: return null
val parameter = symbol.fir as? FirValueParameter ?: return null
val index = valueParameters.indexOf(parameter).takeUnless { it < 0 } ?: return null
): ConeContractDescriptionElement {
val symbol = qualifiedAccessExpression.toResolvedCallableSymbol()
?: run {
val name = (qualifiedAccessExpression.calleeReference as? FirNamedReference)?.name ?: Name.special("unresolved")
return ConeContractDescriptionError.UnresolvedCall(name).asElement()
}
val parameter = symbol.fir as? FirValueParameter
?: return ConeErroneousValueParameterReference(
ConeContractDescriptionError.IllegalParameter(symbol, "$symbol is not a value parameter")
)
val index = valueParameters.indexOf(parameter).takeUnless { it < 0 } ?: return ConeErroneousValueParameterReference(
ConeContractDescriptionError.IllegalParameter(symbol, "Value paramter $symbol is not found in parameters of outer function")
)
val type = parameter.returnTypeRef.coneType
val name = parameter.name.asString()
@@ -129,7 +158,7 @@ class ConeEffectExtractor(
override fun visitPropertyAccessExpression(
propertyAccessExpression: FirPropertyAccessExpression,
data: Nothing?
): ConeContractDescriptionElement? {
): ConeContractDescriptionElement {
return visitQualifiedAccessExpression(propertyAccessExpression, data)
}
@@ -152,8 +181,9 @@ class ConeEffectExtractor(
override fun visitThisReceiverExpression(
thisReceiverExpression: FirThisReceiverExpression,
data: Nothing?
): ConeContractDescriptionElement? {
val declaration = thisReceiverExpression.calleeReference.boundSymbol?.fir ?: return null
): ConeContractDescriptionElement {
val declaration = thisReceiverExpression.calleeReference.boundSymbol?.fir
?: return ConeContractDescriptionError.UnresolvedThis(thisReceiverExpression).asElement()
val callableOwner = owner as? FirCallableDeclaration
val ownerHasReceiver = callableOwner?.receiverParameter != null
val ownerIsMemberOfDeclaration = callableOwner?.getContainingClass(session) == declaration
@@ -161,24 +191,24 @@ class ConeEffectExtractor(
val type = thisReceiverExpression.typeRef.coneType
toValueParameterReference(type, -1, "this")
} else {
null
ConeContractDescriptionError.IllegalThis(thisReceiverExpression).asElement()
}
}
override fun <T> visitConstExpression(constExpression: FirConstExpression<T>, data: Nothing?): ConeContractDescriptionElement? {
override fun <T> visitConstExpression(constExpression: FirConstExpression<T>, data: Nothing?): ConeContractDescriptionElement {
return when (constExpression.kind) {
ConstantValueKind.Null -> ConeConstantReference.NULL
ConstantValueKind.Boolean -> when (constExpression.value as Boolean) {
true -> ConeBooleanConstantReference.TRUE
false -> ConeBooleanConstantReference.FALSE
}
else -> null
else -> ConeContractDescriptionError.IllegalConst(constExpression, onlyNullAllowed = false).asElement()
}
}
override fun visitTypeOperatorCall(typeOperatorCall: FirTypeOperatorCall, data: Nothing?): ConeContractDescriptionElement? {
val arg = typeOperatorCall.argument.accept(this, data) as? ConeValueParameterReference ?: return null
val type = typeOperatorCall.conversionTypeRef.coneType
override fun visitTypeOperatorCall(typeOperatorCall: FirTypeOperatorCall, data: Nothing?): ConeContractDescriptionElement {
val arg = typeOperatorCall.argument.asContractValueExpression()
val type = typeOperatorCall.conversionTypeRef.coneType.fullyExpandedType(session)
val isNegated = typeOperatorCall.operation == FirOperation.NOT_IS
return ConeIsInstancePredicate(arg, type, isNegated)
}
@@ -194,4 +224,30 @@ class ConeEffectExtractor(
else -> null
}
}
private fun noReceiver(callableId: CallableId): ConeErroneousContractElement {
return ConeContractDescriptionError.NoReceiver(callableId.callableName).asElement()
}
private fun noArgument(callableId: CallableId): ConeErroneousContractElement {
return ConeContractDescriptionError.NoArgument(callableId.callableName).asElement()
}
private fun FirElement.asContractElement(): ConeContractDescriptionElement {
return accept(this@ConeEffectExtractor, null)
}
private fun FirExpression.asContractBooleanExpression(): ConeBooleanExpression {
return when (val element = asContractElement()) {
is ConeBooleanExpression -> element
else -> ConeContractDescriptionError.NotABooleanExpression(element).asElement()
}
}
private fun FirExpression.asContractValueExpression(): ConeValueParameterReference {
return when (val element = asContractElement()) {
is ConeValueParameterReference -> element
else -> ConeErroneousValueParameterReference(ConeContractDescriptionError.NotAParameterReference(element))
}
}
}
@@ -5,15 +5,15 @@
package org.jetbrains.kotlin.fir.resolve.transformers.contracts
import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.contracts.FirLegacyRawContractDescription
import org.jetbrains.kotlin.fir.contracts.FirRawContractDescription
import org.jetbrains.kotlin.fir.contracts.builder.buildLegacyRawContractDescription
import org.jetbrains.kotlin.fir.contracts.builder.buildResolvedContractDescription
import org.jetbrains.kotlin.fir.contracts.description.ConeEffectDeclaration
import org.jetbrains.kotlin.fir.contracts.description.ConeUnresolvedEffect
import org.jetbrains.kotlin.fir.contracts.impl.FirEmptyContractDescription
import org.jetbrains.kotlin.fir.contracts.toFirEffectDeclaration
import org.jetbrains.kotlin.fir.contracts.toFirElement
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.builder.buildAnonymousFunction
import org.jetbrains.kotlin.fir.declarations.builder.buildReceiverParameter
@@ -179,11 +179,13 @@ abstract class FirAbstractContractResolveTransformerDispatcher(
val resolvedContractDescription = buildResolvedContractDescription {
val effectExtractor = ConeEffectExtractor(session, owner, valueParameters)
for (statement in lambdaBody.statements) {
val effect = statement.accept(effectExtractor, null) as? ConeEffectDeclaration
if (effect == null) {
unresolvedEffects += ConeUnresolvedEffect(statement)
} else {
effects += effect.toFirEffectDeclaration(statement.source)
if (statement.source?.kind is KtFakeSourceElementKind.ImplicitReturn) continue
when (val effect = statement.accept(effectExtractor, null)) {
is ConeEffectDeclaration -> when (effect.erroneous) {
false -> effects += effect.toFirElement(statement.source)
true -> unresolvedEffects += effect.toFirElement(statement.source)
}
else -> unresolvedEffects += effect.toFirElement(statement.source)
}
}
this.source = contractDescription.source
@@ -7,9 +7,14 @@ package org.jetbrains.kotlin.fir.resolve.diagnostics
import kotlinx.collections.immutable.ImmutableList
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.contracts.description.ConeContractDescriptionElement
import org.jetbrains.kotlin.fir.declarations.FirVariable
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnosticWithSource
import org.jetbrains.kotlin.fir.expressions.FirConstExpression
import org.jetbrains.kotlin.fir.expressions.FirOperation
import org.jetbrains.kotlin.fir.expressions.FirThisReceiverExpression
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.resolve.calls.AbstractCandidate
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
@@ -17,6 +22,7 @@ import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.FirQualifierPart
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
@@ -138,7 +144,83 @@ object ConeVariableExpectedError : ConeDiagnostic {
override val reason: String get() = "Variable expected"
}
class ConeContractDescriptionError(override val reason: String) : ConeDiagnostic
sealed class ConeContractDescriptionError : ConeDiagnostic {
class IllegalElement(val element: FirElement) : ConeContractDescriptionError() {
override val reason: String
get() = "Illegal element in contract description"
}
class UnresolvedCall(val name: Name) : ConeContractDescriptionError() {
override val reason: String
get() = "Unresolved call in contract description: ${name.asString()}"
}
class NoReceiver(val name: Name) : ConeContractDescriptionError() {
override val reason: String
get() = "No receiver for call ${name.asString()} found"
}
class NoArgument(val name: Name) : ConeContractDescriptionError() {
override val reason: String
get() = "No argument for call ${name.asString()} found"
}
class NotAConstant(val element: Any) : ConeContractDescriptionError() {
override val reason: String
get() = "$element is not a constant reference"
}
class IllegalConst(
val element: FirConstExpression<*>,
val onlyNullAllowed: Boolean
) : ConeContractDescriptionError() {
override val reason: String
get() = buildString {
append(element.render())
append("is not a null")
if (!onlyNullAllowed) {
append(", true or false")
}
}
}
class NotAParameterReference(val element: ConeContractDescriptionElement) : ConeContractDescriptionError() {
override val reason: String
get() = "$element is not a parameter or receiver reference"
}
class IllegalParameter(val symbol: FirCallableSymbol<*>, override val reason: String) : ConeContractDescriptionError()
class UnresolvedThis(val expression: FirThisReceiverExpression) : ConeContractDescriptionError() {
override val reason: String
get() = "Can't resolve this reference"
}
class IllegalThis(val expression: FirThisReceiverExpression) : ConeContractDescriptionError() {
override val reason: String
get() = "Only this reference to extension receiver of a function is allowed"
}
class UnresolvedInvocationKind(val element: FirElement) : ConeContractDescriptionError() {
override val reason: String
get() = "${element.render()} is not a valid invocation kind"
}
class NotABooleanExpression(val element: ConeContractDescriptionElement) : ConeContractDescriptionError() {
override val reason: String
get() = "$element is not a boolean expression"
}
class NotContractDsl(val callableId: CallableId) : ConeContractDescriptionError() {
override val reason: String
get() = "$callableId is not a part of contracts DSL"
}
class IllegalEqualityOperator(val operation: FirOperation) : ConeContractDescriptionError() {
override val reason: String
get() = "$operation operator call is illegal in contract description"
}
}
class ConeIllegalAnnotationError(val name: Name) : ConeDiagnostic {
override val reason: String get() = "Not a legal annotation: $name"
@@ -0,0 +1,28 @@
/*
* Copyright 2010-2023 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.fir.contracts
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirPureAbstractElement
import org.jetbrains.kotlin.fir.contracts.description.ConeContractDescriptionElement
import org.jetbrains.kotlin.fir.visitors.*
/*
* This file was generated automatically
* DO NOT MODIFY IT MANUALLY
*/
abstract class FirContractElementDeclaration : FirPureAbstractElement(), FirElement {
abstract override val source: KtSourceElement?
abstract val effect: ConeContractDescriptionElement
override fun <R, D> accept(visitor: FirVisitor<R, D>, data: D): R = visitor.visitContractElementDeclaration(this, data)
@Suppress("UNCHECKED_CAST")
override fun <E : FirElement, D> transform(transformer: FirTransformer<D>, data: D): E =
transformer.transformContractElementDeclaration(this, data) as E
}
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.fir.contracts
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirPureAbstractElement
import org.jetbrains.kotlin.fir.contracts.description.ConeEffectDeclaration
import org.jetbrains.kotlin.fir.visitors.*
@@ -16,9 +15,9 @@ import org.jetbrains.kotlin.fir.visitors.*
* DO NOT MODIFY IT MANUALLY
*/
abstract class FirEffectDeclaration : FirPureAbstractElement(), FirElement {
abstract class FirEffectDeclaration : FirContractElementDeclaration() {
abstract override val source: KtSourceElement?
abstract val effect: ConeEffectDeclaration
abstract override val effect: ConeEffectDeclaration
override fun <R, D> accept(visitor: FirVisitor<R, D>, data: D): R = visitor.visitEffectDeclaration(this, data)
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.fir.contracts
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.contracts.description.ConeUnresolvedEffect
import org.jetbrains.kotlin.fir.visitors.*
/*
@@ -18,7 +17,7 @@ import org.jetbrains.kotlin.fir.visitors.*
abstract class FirResolvedContractDescription : FirContractDescription() {
abstract override val source: KtSourceElement?
abstract val effects: List<FirEffectDeclaration>
abstract val unresolvedEffects: List<ConeUnresolvedEffect>
abstract val unresolvedEffects: List<FirContractElementDeclaration>
override fun <R, D> accept(visitor: FirVisitor<R, D>, data: D): R = visitor.visitResolvedContractDescription(this, data)
@@ -0,0 +1,43 @@
/*
* Copyright 2010-2023 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.
*/
@file:Suppress("DuplicatedCode")
package org.jetbrains.kotlin.fir.contracts.builder
import kotlin.contracts.*
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.fir.builder.FirBuilderDsl
import org.jetbrains.kotlin.fir.contracts.FirContractElementDeclaration
import org.jetbrains.kotlin.fir.contracts.description.ConeContractDescriptionElement
import org.jetbrains.kotlin.fir.contracts.impl.FirContractElementDeclarationImpl
import org.jetbrains.kotlin.fir.visitors.*
/*
* This file was generated automatically
* DO NOT MODIFY IT MANUALLY
*/
@FirBuilderDsl
class FirContractElementDeclarationBuilder {
var source: KtSourceElement? = null
lateinit var effect: ConeContractDescriptionElement
fun build(): FirContractElementDeclaration {
return FirContractElementDeclarationImpl(
source,
effect,
)
}
}
@OptIn(ExperimentalContracts::class)
inline fun buildContractElementDeclaration(init: FirContractElementDeclarationBuilder.() -> Unit): FirContractElementDeclaration {
contract {
callsInPlace(init, kotlin.contracts.InvocationKind.EXACTLY_ONCE)
}
return FirContractElementDeclarationBuilder().apply(init).build()
}
@@ -10,9 +10,9 @@ package org.jetbrains.kotlin.fir.contracts.builder
import kotlin.contracts.*
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.fir.builder.FirBuilderDsl
import org.jetbrains.kotlin.fir.contracts.FirContractElementDeclaration
import org.jetbrains.kotlin.fir.contracts.FirEffectDeclaration
import org.jetbrains.kotlin.fir.contracts.FirResolvedContractDescription
import org.jetbrains.kotlin.fir.contracts.description.ConeUnresolvedEffect
import org.jetbrains.kotlin.fir.contracts.impl.FirResolvedContractDescriptionImpl
import org.jetbrains.kotlin.fir.visitors.*
@@ -25,7 +25,7 @@ import org.jetbrains.kotlin.fir.visitors.*
class FirResolvedContractDescriptionBuilder {
var source: KtSourceElement? = null
val effects: MutableList<FirEffectDeclaration> = mutableListOf()
val unresolvedEffects: MutableList<ConeUnresolvedEffect> = mutableListOf()
val unresolvedEffects: MutableList<FirContractElementDeclaration> = mutableListOf()
fun build(): FirResolvedContractDescription {
return FirResolvedContractDescriptionImpl(
@@ -0,0 +1,29 @@
/*
* Copyright 2010-2023 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.
*/
@file:Suppress("DuplicatedCode")
package org.jetbrains.kotlin.fir.contracts.impl
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.fir.contracts.FirContractElementDeclaration
import org.jetbrains.kotlin.fir.contracts.description.ConeContractDescriptionElement
import org.jetbrains.kotlin.fir.visitors.*
/*
* This file was generated automatically
* DO NOT MODIFY IT MANUALLY
*/
internal class FirContractElementDeclarationImpl(
override val source: KtSourceElement?,
override val effect: ConeContractDescriptionElement,
) : FirContractElementDeclaration() {
override fun <R, D> acceptChildren(visitor: FirVisitor<R, D>, data: D) {}
override fun <D> transformChildren(transformer: FirTransformer<D>, data: D): FirContractElementDeclarationImpl {
return this
}
}
@@ -8,9 +8,9 @@
package org.jetbrains.kotlin.fir.contracts.impl
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.fir.contracts.FirContractElementDeclaration
import org.jetbrains.kotlin.fir.contracts.FirEffectDeclaration
import org.jetbrains.kotlin.fir.contracts.FirResolvedContractDescription
import org.jetbrains.kotlin.fir.contracts.description.ConeUnresolvedEffect
import org.jetbrains.kotlin.fir.visitors.*
/*
@@ -21,14 +21,16 @@ import org.jetbrains.kotlin.fir.visitors.*
internal class FirResolvedContractDescriptionImpl(
override val source: KtSourceElement?,
override val effects: MutableList<FirEffectDeclaration>,
override val unresolvedEffects: MutableList<ConeUnresolvedEffect>,
override val unresolvedEffects: MutableList<FirContractElementDeclaration>,
) : FirResolvedContractDescription() {
override fun <R, D> acceptChildren(visitor: FirVisitor<R, D>, data: D) {
effects.forEach { it.accept(visitor, data) }
unresolvedEffects.forEach { it.accept(visitor, data) }
}
override fun <D> transformChildren(transformer: FirTransformer<D>, data: D): FirResolvedContractDescriptionImpl {
effects.transformInplace(transformer, data)
unresolvedEffects.transformInplace(transformer, data)
return this
}
}
@@ -148,6 +148,7 @@ import org.jetbrains.kotlin.fir.types.FirDynamicTypeRef
import org.jetbrains.kotlin.fir.types.FirFunctionTypeRef
import org.jetbrains.kotlin.fir.types.FirIntersectionTypeRef
import org.jetbrains.kotlin.fir.types.FirImplicitTypeRef
import org.jetbrains.kotlin.fir.contracts.FirContractElementDeclaration
import org.jetbrains.kotlin.fir.contracts.FirEffectDeclaration
import org.jetbrains.kotlin.fir.contracts.FirContractDescription
import org.jetbrains.kotlin.fir.contracts.FirLegacyRawContractDescription
@@ -314,6 +315,8 @@ abstract class FirDefaultVisitor<out R, in D> : FirVisitor<R, D>() {
override fun visitImplicitTypeRef(implicitTypeRef: FirImplicitTypeRef, data: D): R = visitTypeRef(implicitTypeRef, data)
override fun visitEffectDeclaration(effectDeclaration: FirEffectDeclaration, data: D): R = visitContractElementDeclaration(effectDeclaration, data)
override fun visitLegacyRawContractDescription(legacyRawContractDescription: FirLegacyRawContractDescription, data: D): R = visitContractDescription(legacyRawContractDescription, data)
override fun visitRawContractDescription(rawContractDescription: FirRawContractDescription, data: D): R = visitContractDescription(rawContractDescription, data)
@@ -148,6 +148,7 @@ import org.jetbrains.kotlin.fir.types.FirDynamicTypeRef
import org.jetbrains.kotlin.fir.types.FirFunctionTypeRef
import org.jetbrains.kotlin.fir.types.FirIntersectionTypeRef
import org.jetbrains.kotlin.fir.types.FirImplicitTypeRef
import org.jetbrains.kotlin.fir.contracts.FirContractElementDeclaration
import org.jetbrains.kotlin.fir.contracts.FirEffectDeclaration
import org.jetbrains.kotlin.fir.contracts.FirContractDescription
import org.jetbrains.kotlin.fir.contracts.FirLegacyRawContractDescription
@@ -314,6 +315,8 @@ abstract class FirDefaultVisitorVoid : FirVisitorVoid() {
override fun visitImplicitTypeRef(implicitTypeRef: FirImplicitTypeRef) = visitTypeRef(implicitTypeRef)
override fun visitEffectDeclaration(effectDeclaration: FirEffectDeclaration) = visitContractElementDeclaration(effectDeclaration)
override fun visitLegacyRawContractDescription(legacyRawContractDescription: FirLegacyRawContractDescription) = visitContractDescription(legacyRawContractDescription)
override fun visitRawContractDescription(rawContractDescription: FirRawContractDescription) = visitContractDescription(rawContractDescription)
@@ -148,6 +148,7 @@ import org.jetbrains.kotlin.fir.types.FirDynamicTypeRef
import org.jetbrains.kotlin.fir.types.FirFunctionTypeRef
import org.jetbrains.kotlin.fir.types.FirIntersectionTypeRef
import org.jetbrains.kotlin.fir.types.FirImplicitTypeRef
import org.jetbrains.kotlin.fir.contracts.FirContractElementDeclaration
import org.jetbrains.kotlin.fir.contracts.FirEffectDeclaration
import org.jetbrains.kotlin.fir.contracts.FirContractDescription
import org.jetbrains.kotlin.fir.contracts.FirLegacyRawContractDescription
@@ -731,7 +732,11 @@ abstract class FirTransformer<in D> : FirVisitor<FirElement, D>() {
return transformElement(implicitTypeRef, data)
}
open fun transformEffectDeclaration(effectDeclaration: FirEffectDeclaration, data: D): FirEffectDeclaration {
open fun transformContractElementDeclaration(contractElementDeclaration: FirContractElementDeclaration, data: D): FirContractElementDeclaration {
return transformElement(contractElementDeclaration, data)
}
open fun transformEffectDeclaration(effectDeclaration: FirEffectDeclaration, data: D): FirContractElementDeclaration {
return transformElement(effectDeclaration, data)
}
@@ -1323,7 +1328,11 @@ abstract class FirTransformer<in D> : FirVisitor<FirElement, D>() {
return transformImplicitTypeRef(implicitTypeRef, data)
}
final override fun visitEffectDeclaration(effectDeclaration: FirEffectDeclaration, data: D): FirEffectDeclaration {
final override fun visitContractElementDeclaration(contractElementDeclaration: FirContractElementDeclaration, data: D): FirContractElementDeclaration {
return transformContractElementDeclaration(contractElementDeclaration, data)
}
final override fun visitEffectDeclaration(effectDeclaration: FirEffectDeclaration, data: D): FirContractElementDeclaration {
return transformEffectDeclaration(effectDeclaration, data)
}
@@ -148,6 +148,7 @@ import org.jetbrains.kotlin.fir.types.FirDynamicTypeRef
import org.jetbrains.kotlin.fir.types.FirFunctionTypeRef
import org.jetbrains.kotlin.fir.types.FirIntersectionTypeRef
import org.jetbrains.kotlin.fir.types.FirImplicitTypeRef
import org.jetbrains.kotlin.fir.contracts.FirContractElementDeclaration
import org.jetbrains.kotlin.fir.contracts.FirEffectDeclaration
import org.jetbrains.kotlin.fir.contracts.FirContractDescription
import org.jetbrains.kotlin.fir.contracts.FirLegacyRawContractDescription
@@ -446,6 +447,8 @@ abstract class FirVisitor<out R, in D> {
open fun visitImplicitTypeRef(implicitTypeRef: FirImplicitTypeRef, data: D): R = visitElement(implicitTypeRef, data)
open fun visitContractElementDeclaration(contractElementDeclaration: FirContractElementDeclaration, data: D): R = visitElement(contractElementDeclaration, data)
open fun visitEffectDeclaration(effectDeclaration: FirEffectDeclaration, data: D): R = visitElement(effectDeclaration, data)
open fun visitContractDescription(contractDescription: FirContractDescription, data: D): R = visitElement(contractDescription, data)
@@ -148,6 +148,7 @@ import org.jetbrains.kotlin.fir.types.FirDynamicTypeRef
import org.jetbrains.kotlin.fir.types.FirFunctionTypeRef
import org.jetbrains.kotlin.fir.types.FirIntersectionTypeRef
import org.jetbrains.kotlin.fir.types.FirImplicitTypeRef
import org.jetbrains.kotlin.fir.contracts.FirContractElementDeclaration
import org.jetbrains.kotlin.fir.contracts.FirEffectDeclaration
import org.jetbrains.kotlin.fir.contracts.FirContractDescription
import org.jetbrains.kotlin.fir.contracts.FirLegacyRawContractDescription
@@ -730,6 +731,10 @@ abstract class FirVisitorVoid : FirVisitor<Unit, Nothing?>() {
visitElement(implicitTypeRef)
}
open fun visitContractElementDeclaration(contractElementDeclaration: FirContractElementDeclaration) {
visitElement(contractElementDeclaration)
}
open fun visitEffectDeclaration(effectDeclaration: FirEffectDeclaration) {
visitElement(effectDeclaration)
}
@@ -1322,6 +1327,10 @@ abstract class FirVisitorVoid : FirVisitor<Unit, Nothing?>() {
visitImplicitTypeRef(implicitTypeRef)
}
final override fun visitContractElementDeclaration(contractElementDeclaration: FirContractElementDeclaration, data: Nothing?) {
visitContractElementDeclaration(contractElementDeclaration)
}
final override fun visitEffectDeclaration(effectDeclaration: FirEffectDeclaration, data: Nothing?) {
visitEffectDeclaration(effectDeclaration)
}
@@ -6,16 +6,27 @@
package org.jetbrains.kotlin.fir.contracts
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.fir.contracts.builder.buildContractElementDeclaration
import org.jetbrains.kotlin.fir.contracts.builder.buildEffectDeclaration
import org.jetbrains.kotlin.fir.contracts.description.ConeContractDescriptionElement
import org.jetbrains.kotlin.fir.contracts.description.ConeEffectDeclaration
val FirContractDescription.effects: List<FirEffectDeclaration>?
get() = (this as? FirResolvedContractDescription)?.effects
fun ConeEffectDeclaration.toFirEffectDeclaration(source: KtSourceElement? = null): FirEffectDeclaration =
fun ConeEffectDeclaration.toFirElement(source: KtSourceElement? = null): FirEffectDeclaration =
buildEffectDeclaration {
if (source != null) {
this.source = source
}
effect = this@toFirEffectDeclaration
effect = this@toFirElement
}
fun ConeContractDescriptionElement.toFirElement(source: KtSourceElement? = null): FirContractElementDeclaration =
buildContractElementDeclaration {
if (source != null) {
this.source = source
}
effect = this@toFirElement
}
@@ -7,6 +7,8 @@ package org.jetbrains.kotlin.fir.contracts.description
interface ConeContractDescriptionElement {
fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R
val erroneous: Boolean
}
abstract class ConeEffectDeclaration : ConeContractDescriptionElement {
@@ -23,6 +23,9 @@ abstract class ConeContractDescriptionVisitor<out R, in D> {
open fun visitCallsEffectDeclaration(callsEffect: ConeCallsEffectDeclaration, data: D): R =
visitEffectDeclaration(callsEffect, data)
open fun visitErroneousCallsEffectDeclaration(callsEffect: ConeErroneousCallsEffectDeclaration, data: D): R =
visitCallsEffectDeclaration(callsEffect, data)
// Expressions
open fun visitBooleanExpression(booleanExpression: ConeBooleanExpression, data: D): R =
visitContractDescriptionElement(booleanExpression, data)
@@ -35,6 +38,9 @@ abstract class ConeContractDescriptionVisitor<out R, in D> {
open fun visitIsInstancePredicate(isInstancePredicate: ConeIsInstancePredicate, data: D): R =
visitBooleanExpression(isInstancePredicate, data)
open fun visitErroneousIsInstancePredicate(isInstancePredicate: ConeErroneousIsInstancePredicate, data: D): R =
visitIsInstancePredicate(isInstancePredicate, data)
open fun visitIsNullPredicate(isNullPredicate: ConeIsNullPredicate, data: D): R = visitBooleanExpression(isNullPredicate, data)
// Values
@@ -45,9 +51,19 @@ abstract class ConeContractDescriptionVisitor<out R, in D> {
open fun visitBooleanConstantDescriptor(booleanConstantDescriptor: ConeBooleanConstantReference, data: D): R =
visitConstantDescriptor(booleanConstantDescriptor, data)
open fun visitErroneousConstantReference(erroneousConstantReference: ConeErroneousConstantReference, data: D): R =
visitConstantDescriptor(erroneousConstantReference, data)
open fun visitValueParameterReference(valueParameterReference: ConeValueParameterReference, data: D): R =
visitValue(valueParameterReference, data)
open fun visitBooleanValueParameterReference(booleanValueParameterReference: ConeBooleanValueParameterReference, data: D): R =
visitValueParameterReference(booleanValueParameterReference, data)
}
open fun visitErroneousValueParameterReference(valueParameterReference: ConeErroneousValueParameterReference, data: D): R =
visitValueParameterReference(valueParameterReference, data)
// Error
open fun visitErroneousElement(element: ConeErroneousContractElement, data: D): R =
visitContractDescriptionElement(element, data)
}
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.fir.contracts.description
import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
/**
* Effect with condition attached to it.
@@ -22,6 +23,9 @@ import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange
* - if [condition] is true, we *can't* reason that [effect] will be observed.
*/
class ConeConditionalEffectDeclaration(val effect: ConeEffectDeclaration, val condition: ConeBooleanExpression) : ConeEffectDeclaration() {
override val erroneous: Boolean
get() = effect.erroneous || condition.erroneous
override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitConditionalEffectDeclaration(this, data)
}
@@ -31,9 +35,11 @@ class ConeConditionalEffectDeclaration(val effect: ConeEffectDeclaration, val co
* Effect which specifies that subroutine returns some particular value
*/
class ConeReturnsEffectDeclaration(val value: ConeConstantReference) : ConeEffectDeclaration() {
override val erroneous: Boolean
get() = value.erroneous
override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitReturnsEffectDeclaration(this, data)
}
@@ -41,7 +47,24 @@ class ConeReturnsEffectDeclaration(val value: ConeConstantReference) : ConeEffec
* Effect which specifies, that during execution of subroutine, callable [valueParameterReference] will be invoked
* [kind] amount of times, and will never be invoked after subroutine call is finished.
*/
class ConeCallsEffectDeclaration(val valueParameterReference: ConeValueParameterReference, val kind: EventOccurrencesRange) : ConeEffectDeclaration() {
open class ConeCallsEffectDeclaration(
val valueParameterReference: ConeValueParameterReference,
val kind: EventOccurrencesRange
) : ConeEffectDeclaration() {
override val erroneous: Boolean
get() = valueParameterReference.erroneous
override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitCallsEffectDeclaration(this, data)
}
}
class ConeErroneousCallsEffectDeclaration(
valueParameterReference: ConeValueParameterReference,
val diagnostic: ConeDiagnostic
) : ConeCallsEffectDeclaration(valueParameterReference, EventOccurrencesRange.UNKNOWN) {
override val erroneous: Boolean
get() = true
override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitErroneousCallsEffectDeclaration(this, data)
}
@@ -0,0 +1,19 @@
/*
* Copyright 2010-2022 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.fir.contracts.description
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
class ConeErroneousContractElement(
val diagnostic: ConeDiagnostic
) : ConeEffectDeclaration(), ConeBooleanExpression, ConeContractDescriptionValue {
override val erroneous: Boolean
get() = true
override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R {
return contractDescriptionVisitor.visitErroneousElement(this, data)
}
}
@@ -8,12 +8,18 @@ package org.jetbrains.kotlin.fir.contracts.description
import org.jetbrains.kotlin.fir.expressions.LogicOperationKind
class ConeBinaryLogicExpression(val left: ConeBooleanExpression, val right: ConeBooleanExpression, val kind: LogicOperationKind) : ConeBooleanExpression {
override val erroneous: Boolean
get() = left.erroneous || right.erroneous
override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R {
return contractDescriptionVisitor.visitLogicalBinaryOperationContractExpression(this, data)
}
}
class ConeLogicalNot(val arg: ConeBooleanExpression) : ConeBooleanExpression {
override val erroneous: Boolean
get() = arg.erroneous
override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitLogicalNot(this, data)
}
@@ -5,9 +5,13 @@
package org.jetbrains.kotlin.fir.contracts.description
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
import org.jetbrains.kotlin.fir.types.ConeKotlinType
class ConeIsInstancePredicate(val arg: ConeValueParameterReference, val type: ConeKotlinType, val isNegated: Boolean) : ConeBooleanExpression {
open class ConeIsInstancePredicate(val arg: ConeValueParameterReference, val type: ConeKotlinType, val isNegated: Boolean) : ConeBooleanExpression {
override val erroneous: Boolean
get() = arg.erroneous
override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitIsInstancePredicate(this, data)
@@ -15,10 +19,26 @@ class ConeIsInstancePredicate(val arg: ConeValueParameterReference, val type: Co
ConeIsInstancePredicate(arg, type, isNegated.not())
}
class ConeErroneousIsInstancePredicate(
arg: ConeValueParameterReference,
type: ConeKotlinType,
isNegated: Boolean,
val diagnostic: ConeDiagnostic
) : ConeIsInstancePredicate(arg, type, isNegated) {
override val erroneous: Boolean
get() = true
override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitErroneousIsInstancePredicate(this, data)
}
class ConeIsNullPredicate(val arg: ConeValueParameterReference, val isNegated: Boolean) : ConeBooleanExpression {
override val erroneous: Boolean
get() = arg.erroneous
override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitIsNullPredicate(this, data)
fun negated(): ConeIsNullPredicate =
ConeIsNullPredicate(arg, isNegated.not())
}
}
@@ -1,10 +0,0 @@
/*
* Copyright 2010-2022 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.fir.contracts.description
import org.jetbrains.kotlin.fir.expressions.FirStatement
class ConeUnresolvedEffect(val statement: FirStatement)
@@ -5,12 +5,17 @@
package org.jetbrains.kotlin.fir.contracts.description
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
interface ConeContractDescriptionValue : ConeContractDescriptionElement {
override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitValue(this, data)
}
open class ConeConstantReference protected constructor(val name: String) : ConeContractDescriptionValue {
override val erroneous: Boolean
get() = false
override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitConstantDescriptor(this, data)
@@ -31,6 +36,14 @@ class ConeBooleanConstantReference private constructor(name: String) : ConeConst
}
}
class ConeErroneousConstantReference(val diagnostic: ConeDiagnostic) : ConeConstantReference("ERROR") {
override val erroneous: Boolean
get() = true
override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitErroneousConstantReference(this, data)
}
/*
* Index of value parameter of function
* -1 means that it is reference to extension receiver
@@ -40,6 +53,9 @@ open class ConeValueParameterReference(val parameterIndex: Int, val name: String
assert(parameterIndex >= -1)
}
override val erroneous: Boolean
get() = false
override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitValueParameterReference(this, data)
}
@@ -47,4 +63,12 @@ open class ConeValueParameterReference(val parameterIndex: Int, val name: String
class ConeBooleanValueParameterReference(parameterIndex: Int, name: String) : ConeValueParameterReference(parameterIndex, name), ConeBooleanExpression {
override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitBooleanValueParameterReference(this, data)
}
}
class ConeErroneousValueParameterReference(val diagnostic: ConeDiagnostic) : ConeValueParameterReference(Int.MAX_VALUE, "ERROR") {
override val erroneous: Boolean
get() = true
override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitErroneousValueParameterReference(this, data)
}
@@ -180,7 +180,8 @@ object FirTreeBuilder : AbstractFirTreeBuilder() {
val intersectionTypeRef by element(TypeRef, typeRefWithNullability)
val implicitTypeRef by element(TypeRef, typeRef)
val effectDeclaration by element(Contracts)
val contractElementDeclaration by element(Contracts)
val effectDeclaration by element(Contracts, contractElementDeclaration)
val contractDescription by element(Contracts)
val legacyRawContractDescription by element(Contracts, contractDescription)
@@ -562,6 +562,8 @@ object ImplementationConfigurator : AbstractFirTreeImplementationConfigurator()
noImpl(argumentList)
noImpl(annotationArgumentMapping)
impl(contractElementDeclaration)
val implementationsWithoutStatusAndTypeParameters = listOf(
"FirAnonymousFunctionImpl",
"FirValueParameterImpl",
@@ -732,6 +732,10 @@ object NodeConfigurator : AbstractFieldConfigurator<FirTreeBuilder>(FirTreeBuild
+field(varianceType)
}
contractElementDeclaration.configure {
+field("effect", coneContractElementType)
}
effectDeclaration.configure {
+field("effect", coneEffectDeclarationType)
}
@@ -742,7 +746,7 @@ object NodeConfigurator : AbstractFieldConfigurator<FirTreeBuilder>(FirTreeBuild
resolvedContractDescription.configure {
+fieldList("effects", effectDeclaration)
+fieldList("unresolvedEffects", coneUnresolvedEffect)
+fieldList("unresolvedEffects", contractElementDeclaration)
}
legacyRawContractDescription.configure {
@@ -79,11 +79,11 @@ val emptyArgumentListType = type("fir.expressions", "FirEmptyArgumentList")
val firScopeProviderType = type("fir.scopes", "FirScopeProvider")
val pureAbstractElementType = generatedType("FirPureAbstractElement")
val coneContractElementType = type("fir.contracts.description", "ConeContractDescriptionElement")
val coneEffectDeclarationType = type("fir.contracts.description", "ConeEffectDeclaration")
val emptyContractDescriptionType = generatedType("contracts.impl", "FirEmptyContractDescription")
val coneDiagnosticType = generatedType("diagnostics", "ConeDiagnostic")
val coneStubDiagnosticType = generatedType("diagnostics", "ConeStubDiagnostic")
val coneUnresolvedEffect = type("fir.contracts.description", "ConeUnresolvedEffect")
val dslBuilderAnnotationType = generatedType("builder", "FirBuilderDsl")
val firImplementationDetailType = generatedType("FirImplementationDetail")
@@ -5,5 +5,5 @@
import kotlin.contracts.*
fun emptyContract() {
contract { }
}
<!ERROR_IN_CONTRACT_DESCRIPTION!>contract { }<!>
}
@@ -25,9 +25,9 @@ fun whenInContract(x: Any?, boolean: Boolean) {
fun forInContract(x: Any?) {
contract {
for (i in 0..1) {
<!ERROR_IN_CONTRACT_DESCRIPTION!>for (i in 0..1) {
returns() implies (x is String)
}
}<!>
}
}
@@ -5,7 +5,7 @@ import kotlin.contracts.*
// TESTCASE NUMBER: 1
inline fun case_1(block: () -> Unit) {
contract { }
<!ERROR_IN_CONTRACT_DESCRIPTION!>contract { }<!>
return block()
}
@@ -11,24 +11,24 @@ object SampleObject {
// TESTCASE NUMBER: 1
inline fun case_1(invocationKind: InvocationKind, block: () -> Unit) {
contract { callsInPlace(block, invocationKind) }
contract { <!ERROR_IN_CONTRACT_DESCRIPTION!>callsInPlace(block, invocationKind)<!> }
return block()
}
// TESTCASE NUMBER: 2
inline fun <T : InvocationKind> case_2(invocationKind: T, block: () -> Unit) {
contract { callsInPlace(block, invocationKind) }
contract { <!ERROR_IN_CONTRACT_DESCRIPTION!>callsInPlace(block, invocationKind)<!> }
return block()
}
// TESTCASE NUMBER: 3
inline fun case_3(block: () -> Unit) {
contract { callsInPlace(block, invocationKind) }
contract { <!ERROR_IN_CONTRACT_DESCRIPTION!>callsInPlace(block, invocationKind)<!> }
return block()
}
// TESTCASE NUMBER: 4
inline fun case_4(block: () -> Unit) {
contract { callsInPlace(block, SampleObject.invocationKind) }
contract { <!ERROR_IN_CONTRACT_DESCRIPTION!>callsInPlace(block, SampleObject.invocationKind)<!> }
return block()
}