[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 package org.jetbrains.kotlin.fir.analysis.checkers.declaration
import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.KtRealSourceElementKind import org.jetbrains.kotlin.KtRealSourceElementKind
import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter 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.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.contracts.FirResolvedContractDescription 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.FirAnonymousFunction
import org.jetbrains.kotlin.fir.declarations.FirContractDescriptionOwner import org.jetbrains.kotlin.fir.declarations.FirContractDescriptionOwner
import org.jetbrains.kotlin.fir.declarations.FirFunction import org.jetbrains.kotlin.fir.declarations.FirFunction
import org.jetbrains.kotlin.fir.declarations.FirPropertyAccessor import org.jetbrains.kotlin.fir.declarations.FirPropertyAccessor
import org.jetbrains.kotlin.fir.declarations.utils.* import org.jetbrains.kotlin.fir.declarations.utils.*
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
object FirContractChecker : FirFunctionChecker() { object FirContractChecker : FirFunctionChecker() {
// TODO: The message should vary. Migrate this to [ConeEffectExtractor] when creating fine-grained errors. private val EMPTY_CONTRACT_MESSAGE = "Empty contract block is not allowed"
private const val UNEXPECTED_CONSTRUCTION = "unexpected construction in contract description"
override fun check(declaration: FirFunction, context: CheckerContext, reporter: DiagnosticReporter) { override fun check(declaration: FirFunction, context: CheckerContext, reporter: DiagnosticReporter) {
if (declaration !is FirContractDescriptionOwner) return if (declaration !is FirContractDescriptionOwner) return
@@ -29,6 +29,9 @@ object FirContractChecker : FirFunctionChecker() {
checkUnresolvedEffects(contractDescription, context, reporter) checkUnresolvedEffects(contractDescription, context, reporter)
checkContractNotAllowed(declaration, 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( private fun checkUnresolvedEffects(
@@ -38,12 +41,11 @@ object FirContractChecker : FirFunctionChecker() {
) { ) {
// Any statements that [ConeEffectExtractor] cannot extract effects will be in `unresolvedEffects`. // Any statements that [ConeEffectExtractor] cannot extract effects will be in `unresolvedEffects`.
for (unresolvedEffect in contractDescription.unresolvedEffects) { for (unresolvedEffect in contractDescription.unresolvedEffects) {
val statement = unresolvedEffect.statement val diagnostic = unresolvedEffect.effect.accept(DiagnosticExtractor, null) ?: continue
if (statement.source == null || statement.source!!.kind is KtFakeSourceElementKind) continue
// TODO: report on fine-grained locations, e.g., ... implies unresolved => report on unresolved, not the entire statement. // 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`... // 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") 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.FirContractDescription
import org.jetbrains.kotlin.fir.contracts.builder.buildResolvedContractDescription import org.jetbrains.kotlin.fir.contracts.builder.buildResolvedContractDescription
import org.jetbrains.kotlin.fir.contracts.description.* 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.FirContractDescriptionOwner
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.expressions.LogicOperationKind 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? { fun loadContract(proto: ProtoBuf.Contract, owner: FirContractDescriptionOwner): FirContractDescription? {
val effects = proto.effectList.map { loadPossiblyConditionalEffect(it, owner) ?: return null } val effects = proto.effectList.map { loadPossiblyConditionalEffect(it, owner) ?: return null }
return buildResolvedContractDescription { 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.contracts.description.*
import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.* 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.resolve.getContainingClass
import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.coneType import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitor 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.types.ConstantValueKind
import org.jetbrains.kotlin.utils.addToStdlib.shouldNotBeCalled
class ConeEffectExtractor( class ConeEffectExtractor(
private val session: FirSession, private val session: FirSession,
private val owner: FirContractDescriptionOwner, private val owner: FirContractDescriptionOwner,
private val valueParameters: List<FirValueParameter> private val valueParameters: List<FirValueParameter>
) : FirDefaultVisitor<ConeContractDescriptionElement?, Nothing?>() { ) : FirDefaultVisitor<ConeContractDescriptionElement, Nothing?>() {
companion object { companion object {
private val BOOLEAN_AND = FirContractsDslNames.id("kotlin", "Boolean", "and") private val BOOLEAN_AND = FirContractsDslNames.id("kotlin", "Boolean", "and")
private val BOOLEAN_OR = FirContractsDslNames.id("kotlin", "Boolean", "or") private val BOOLEAN_OR = FirContractsDslNames.id("kotlin", "Boolean", "or")
private val BOOLEAN_NOT = FirContractsDslNames.id("kotlin", "Boolean", "not") private val BOOLEAN_NOT = FirContractsDslNames.id("kotlin", "Boolean", "not")
} }
override fun visitElement(element: FirElement, data: Nothing?): ConeContractDescriptionElement? { private fun ConeContractDescriptionError.asElement(): ConeErroneousContractElement {
return null 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) return returnExpression.result.accept(this, data)
} }
override fun visitFunctionCall(functionCall: FirFunctionCall, data: Nothing?): ConeContractDescriptionElement? { override fun visitFunctionCall(functionCall: FirFunctionCall, data: Nothing?): ConeContractDescriptionElement {
val resolvedId = functionCall.toResolvedCallableSymbol()?.callableId ?: return null val resolvedId = functionCall.toResolvedCallableSymbol()?.callableId
?: return ConeContractDescriptionError.UnresolvedCall(functionCall.calleeReference.name).asElement()
return when (resolvedId) { return when (resolvedId) {
FirContractsDslNames.IMPLIES -> { FirContractsDslNames.IMPLIES -> {
val effect = functionCall.explicitReceiver?.accept(this, null) as? ConeEffectDeclaration val effect = functionCall.explicitReceiver?.asContractElement() as? ConeEffectDeclaration ?: noReceiver(resolvedId)
?: return null val condition = functionCall.argument.asContractElement() as? ConeBooleanExpression ?: noArgument(resolvedId)
val condition = functionCall.argument.accept(this, null) as? ConeBooleanExpression
?: return null
ConeConditionalEffectDeclaration(effect, condition) ConeConditionalEffectDeclaration(effect, condition)
} }
@@ -52,8 +62,10 @@ class ConeEffectExtractor(
val value = if (argument == null) { val value = if (argument == null) {
ConeConstantReference.WILDCARD ConeConstantReference.WILDCARD
} else { } else {
argument.accept(this, null) as? ConeConstantReference when (val value = argument.asContractElement()) {
?: return null is ConeConstantReference -> value
else -> ConeErroneousConstantReference(ConeContractDescriptionError.NotAConstant(value))
}
} }
ConeReturnsEffectDeclaration(value) ConeReturnsEffectDeclaration(value)
} }
@@ -63,63 +75,80 @@ class ConeEffectExtractor(
} }
FirContractsDslNames.CALLS_IN_PLACE -> { FirContractsDslNames.CALLS_IN_PLACE -> {
val reference = functionCall.arguments[0].accept(this, null) as? ConeValueParameterReference val reference = functionCall.arguments[0].asContractValueExpression()
?: return null when (val argument = functionCall.arguments.getOrNull(1)) {
val kind = functionCall.arguments.getOrNull(1)?.parseInvocationKind() ?: EventOccurrencesRange.UNKNOWN null -> ConeCallsEffectDeclaration(reference, EventOccurrencesRange.UNKNOWN)
ConeCallsEffectDeclaration(reference, kind) else -> when (val kind = argument.parseInvocationKind()) {
null -> ConeErroneousCallsEffectDeclaration(reference, ConeContractDescriptionError.UnresolvedInvocationKind(argument))
else -> ConeCallsEffectDeclaration(reference, kind)
}
}
} }
BOOLEAN_AND, BOOLEAN_OR -> { BOOLEAN_AND, BOOLEAN_OR -> {
val left = functionCall.explicitReceiver?.accept(this, null) as? ConeBooleanExpression ?: return null val left = functionCall.explicitReceiver?.asContractBooleanExpression() ?: noReceiver(resolvedId)
val right = functionCall.argument.accept(this, null) as? ConeBooleanExpression ?: return null val right = functionCall.arguments.firstOrNull()?.asContractBooleanExpression() ?: noArgument(resolvedId)
val kind = when (resolvedId) { val kind = when (resolvedId) {
BOOLEAN_AND -> LogicOperationKind.AND BOOLEAN_AND -> LogicOperationKind.AND
BOOLEAN_OR -> LogicOperationKind.OR BOOLEAN_OR -> LogicOperationKind.OR
else -> throw IllegalStateException() else -> shouldNotBeCalled()
} }
ConeBinaryLogicExpression(left, right, kind) ConeBinaryLogicExpression(left, right, kind)
} }
BOOLEAN_NOT -> { BOOLEAN_NOT -> {
val arg = functionCall.explicitReceiver?.accept(this, null) as? ConeBooleanExpression ?: return null val arg = functionCall.explicitReceiver?.asContractBooleanExpression() ?: noReceiver(resolvedId)
ConeLogicalNot(arg) ConeLogicalNot(arg)
} }
else -> null
else -> ConeContractDescriptionError.NotContractDsl(resolvedId).asElement()
} }
} }
override fun visitBinaryLogicExpression( override fun visitBinaryLogicExpression(
binaryLogicExpression: FirBinaryLogicExpression, binaryLogicExpression: FirBinaryLogicExpression,
data: Nothing? data: Nothing?
): ConeContractDescriptionElement? { ): ConeContractDescriptionElement {
val left = binaryLogicExpression.leftOperand.accept(this, null) as? ConeBooleanExpression ?: return null val left = binaryLogicExpression.leftOperand.asContractBooleanExpression()
val right = binaryLogicExpression.rightOperand.accept(this, null) as? ConeBooleanExpression ?: return null val right = binaryLogicExpression.rightOperand.asContractBooleanExpression()
return ConeBinaryLogicExpression(left, right, binaryLogicExpression.kind) return ConeBinaryLogicExpression(left, right, binaryLogicExpression.kind)
} }
override fun visitEqualityOperatorCall(equalityOperatorCall: FirEqualityOperatorCall, data: Nothing?): ConeContractDescriptionElement? { override fun visitEqualityOperatorCall(equalityOperatorCall: FirEqualityOperatorCall, data: Nothing?): ConeContractDescriptionElement {
val isNegated = when (equalityOperatorCall.operation) { val isNegated = when (val operation = equalityOperatorCall.operation) {
FirOperation.EQ -> false FirOperation.EQ -> false
FirOperation.NOT_EQ -> true 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 argument = equalityOperatorCall.arguments[1]
val arg = equalityOperatorCall.arguments[0].accept(this, null) as? ConeValueParameterReference ?: return null 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) 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) return smartCastExpression.originalExpression.accept(this, data)
} }
override fun visitQualifiedAccessExpression( override fun visitQualifiedAccessExpression(
qualifiedAccessExpression: FirQualifiedAccessExpression, qualifiedAccessExpression: FirQualifiedAccessExpression,
data: Nothing? data: Nothing?
): ConeContractDescriptionElement? { ): ConeContractDescriptionElement {
val symbol = qualifiedAccessExpression.toResolvedCallableSymbol() ?: return null val symbol = qualifiedAccessExpression.toResolvedCallableSymbol()
val parameter = symbol.fir as? FirValueParameter ?: return null ?: run {
val index = valueParameters.indexOf(parameter).takeUnless { it < 0 } ?: return null 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 type = parameter.returnTypeRef.coneType
val name = parameter.name.asString() val name = parameter.name.asString()
@@ -129,7 +158,7 @@ class ConeEffectExtractor(
override fun visitPropertyAccessExpression( override fun visitPropertyAccessExpression(
propertyAccessExpression: FirPropertyAccessExpression, propertyAccessExpression: FirPropertyAccessExpression,
data: Nothing? data: Nothing?
): ConeContractDescriptionElement? { ): ConeContractDescriptionElement {
return visitQualifiedAccessExpression(propertyAccessExpression, data) return visitQualifiedAccessExpression(propertyAccessExpression, data)
} }
@@ -152,8 +181,9 @@ class ConeEffectExtractor(
override fun visitThisReceiverExpression( override fun visitThisReceiverExpression(
thisReceiverExpression: FirThisReceiverExpression, thisReceiverExpression: FirThisReceiverExpression,
data: Nothing? data: Nothing?
): ConeContractDescriptionElement? { ): ConeContractDescriptionElement {
val declaration = thisReceiverExpression.calleeReference.boundSymbol?.fir ?: return null val declaration = thisReceiverExpression.calleeReference.boundSymbol?.fir
?: return ConeContractDescriptionError.UnresolvedThis(thisReceiverExpression).asElement()
val callableOwner = owner as? FirCallableDeclaration val callableOwner = owner as? FirCallableDeclaration
val ownerHasReceiver = callableOwner?.receiverParameter != null val ownerHasReceiver = callableOwner?.receiverParameter != null
val ownerIsMemberOfDeclaration = callableOwner?.getContainingClass(session) == declaration val ownerIsMemberOfDeclaration = callableOwner?.getContainingClass(session) == declaration
@@ -161,24 +191,24 @@ class ConeEffectExtractor(
val type = thisReceiverExpression.typeRef.coneType val type = thisReceiverExpression.typeRef.coneType
toValueParameterReference(type, -1, "this") toValueParameterReference(type, -1, "this")
} else { } 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) { return when (constExpression.kind) {
ConstantValueKind.Null -> ConeConstantReference.NULL ConstantValueKind.Null -> ConeConstantReference.NULL
ConstantValueKind.Boolean -> when (constExpression.value as Boolean) { ConstantValueKind.Boolean -> when (constExpression.value as Boolean) {
true -> ConeBooleanConstantReference.TRUE true -> ConeBooleanConstantReference.TRUE
false -> ConeBooleanConstantReference.FALSE false -> ConeBooleanConstantReference.FALSE
} }
else -> null else -> ConeContractDescriptionError.IllegalConst(constExpression, onlyNullAllowed = false).asElement()
} }
} }
override fun visitTypeOperatorCall(typeOperatorCall: FirTypeOperatorCall, data: Nothing?): ConeContractDescriptionElement? { override fun visitTypeOperatorCall(typeOperatorCall: FirTypeOperatorCall, data: Nothing?): ConeContractDescriptionElement {
val arg = typeOperatorCall.argument.accept(this, data) as? ConeValueParameterReference ?: return null val arg = typeOperatorCall.argument.asContractValueExpression()
val type = typeOperatorCall.conversionTypeRef.coneType val type = typeOperatorCall.conversionTypeRef.coneType.fullyExpandedType(session)
val isNegated = typeOperatorCall.operation == FirOperation.NOT_IS val isNegated = typeOperatorCall.operation == FirOperation.NOT_IS
return ConeIsInstancePredicate(arg, type, isNegated) return ConeIsInstancePredicate(arg, type, isNegated)
} }
@@ -194,4 +224,30 @@ class ConeEffectExtractor(
else -> null 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 package org.jetbrains.kotlin.fir.resolve.transformers.contracts
import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.contracts.FirLegacyRawContractDescription import org.jetbrains.kotlin.fir.contracts.FirLegacyRawContractDescription
import org.jetbrains.kotlin.fir.contracts.FirRawContractDescription import org.jetbrains.kotlin.fir.contracts.FirRawContractDescription
import org.jetbrains.kotlin.fir.contracts.builder.buildLegacyRawContractDescription import org.jetbrains.kotlin.fir.contracts.builder.buildLegacyRawContractDescription
import org.jetbrains.kotlin.fir.contracts.builder.buildResolvedContractDescription import org.jetbrains.kotlin.fir.contracts.builder.buildResolvedContractDescription
import org.jetbrains.kotlin.fir.contracts.description.ConeEffectDeclaration 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.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.*
import org.jetbrains.kotlin.fir.declarations.builder.buildAnonymousFunction import org.jetbrains.kotlin.fir.declarations.builder.buildAnonymousFunction
import org.jetbrains.kotlin.fir.declarations.builder.buildReceiverParameter import org.jetbrains.kotlin.fir.declarations.builder.buildReceiverParameter
@@ -179,11 +179,13 @@ abstract class FirAbstractContractResolveTransformerDispatcher(
val resolvedContractDescription = buildResolvedContractDescription { val resolvedContractDescription = buildResolvedContractDescription {
val effectExtractor = ConeEffectExtractor(session, owner, valueParameters) val effectExtractor = ConeEffectExtractor(session, owner, valueParameters)
for (statement in lambdaBody.statements) { for (statement in lambdaBody.statements) {
val effect = statement.accept(effectExtractor, null) as? ConeEffectDeclaration if (statement.source?.kind is KtFakeSourceElementKind.ImplicitReturn) continue
if (effect == null) { when (val effect = statement.accept(effectExtractor, null)) {
unresolvedEffects += ConeUnresolvedEffect(statement) is ConeEffectDeclaration -> when (effect.erroneous) {
} else { false -> effects += effect.toFirElement(statement.source)
effects += effect.toFirEffectDeclaration(statement.source) true -> unresolvedEffects += effect.toFirElement(statement.source)
}
else -> unresolvedEffects += effect.toFirElement(statement.source)
} }
} }
this.source = contractDescription.source this.source = contractDescription.source
@@ -7,9 +7,14 @@ package org.jetbrains.kotlin.fir.resolve.diagnostics
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import org.jetbrains.kotlin.KtSourceElement 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.declarations.FirVariable
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnosticWithSource 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.render
import org.jetbrains.kotlin.fir.resolve.calls.AbstractCandidate import org.jetbrains.kotlin.fir.resolve.calls.AbstractCandidate
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol 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.ConeKotlinType
import org.jetbrains.kotlin.fir.types.FirQualifierPart import org.jetbrains.kotlin.fir.types.FirQualifierPart
import org.jetbrains.kotlin.fir.types.FirTypeRef import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
@@ -138,7 +144,83 @@ object ConeVariableExpectedError : ConeDiagnostic {
override val reason: String get() = "Variable expected" 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 { class ConeIllegalAnnotationError(val name: Name) : ConeDiagnostic {
override val reason: String get() = "Not a legal annotation: $name" 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.KtSourceElement
import org.jetbrains.kotlin.fir.FirElement 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.contracts.description.ConeEffectDeclaration
import org.jetbrains.kotlin.fir.visitors.* import org.jetbrains.kotlin.fir.visitors.*
@@ -16,9 +15,9 @@ import org.jetbrains.kotlin.fir.visitors.*
* DO NOT MODIFY IT MANUALLY * DO NOT MODIFY IT MANUALLY
*/ */
abstract class FirEffectDeclaration : FirPureAbstractElement(), FirElement { abstract class FirEffectDeclaration : FirContractElementDeclaration() {
abstract override val source: KtSourceElement? 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) 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.KtSourceElement
import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.contracts.description.ConeUnresolvedEffect
import org.jetbrains.kotlin.fir.visitors.* import org.jetbrains.kotlin.fir.visitors.*
/* /*
@@ -18,7 +17,7 @@ import org.jetbrains.kotlin.fir.visitors.*
abstract class FirResolvedContractDescription : FirContractDescription() { abstract class FirResolvedContractDescription : FirContractDescription() {
abstract override val source: KtSourceElement? abstract override val source: KtSourceElement?
abstract val effects: List<FirEffectDeclaration> 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) 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 kotlin.contracts.*
import org.jetbrains.kotlin.KtSourceElement import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.fir.builder.FirBuilderDsl 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.FirEffectDeclaration
import org.jetbrains.kotlin.fir.contracts.FirResolvedContractDescription 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.contracts.impl.FirResolvedContractDescriptionImpl
import org.jetbrains.kotlin.fir.visitors.* import org.jetbrains.kotlin.fir.visitors.*
@@ -25,7 +25,7 @@ import org.jetbrains.kotlin.fir.visitors.*
class FirResolvedContractDescriptionBuilder { class FirResolvedContractDescriptionBuilder {
var source: KtSourceElement? = null var source: KtSourceElement? = null
val effects: MutableList<FirEffectDeclaration> = mutableListOf() val effects: MutableList<FirEffectDeclaration> = mutableListOf()
val unresolvedEffects: MutableList<ConeUnresolvedEffect> = mutableListOf() val unresolvedEffects: MutableList<FirContractElementDeclaration> = mutableListOf()
fun build(): FirResolvedContractDescription { fun build(): FirResolvedContractDescription {
return FirResolvedContractDescriptionImpl( 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 package org.jetbrains.kotlin.fir.contracts.impl
import org.jetbrains.kotlin.KtSourceElement 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.FirEffectDeclaration
import org.jetbrains.kotlin.fir.contracts.FirResolvedContractDescription import org.jetbrains.kotlin.fir.contracts.FirResolvedContractDescription
import org.jetbrains.kotlin.fir.contracts.description.ConeUnresolvedEffect
import org.jetbrains.kotlin.fir.visitors.* import org.jetbrains.kotlin.fir.visitors.*
/* /*
@@ -21,14 +21,16 @@ import org.jetbrains.kotlin.fir.visitors.*
internal class FirResolvedContractDescriptionImpl( internal class FirResolvedContractDescriptionImpl(
override val source: KtSourceElement?, override val source: KtSourceElement?,
override val effects: MutableList<FirEffectDeclaration>, override val effects: MutableList<FirEffectDeclaration>,
override val unresolvedEffects: MutableList<ConeUnresolvedEffect>, override val unresolvedEffects: MutableList<FirContractElementDeclaration>,
) : FirResolvedContractDescription() { ) : FirResolvedContractDescription() {
override fun <R, D> acceptChildren(visitor: FirVisitor<R, D>, data: D) { override fun <R, D> acceptChildren(visitor: FirVisitor<R, D>, data: D) {
effects.forEach { it.accept(visitor, data) } effects.forEach { it.accept(visitor, data) }
unresolvedEffects.forEach { it.accept(visitor, data) }
} }
override fun <D> transformChildren(transformer: FirTransformer<D>, data: D): FirResolvedContractDescriptionImpl { override fun <D> transformChildren(transformer: FirTransformer<D>, data: D): FirResolvedContractDescriptionImpl {
effects.transformInplace(transformer, data) effects.transformInplace(transformer, data)
unresolvedEffects.transformInplace(transformer, data)
return this 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.FirFunctionTypeRef
import org.jetbrains.kotlin.fir.types.FirIntersectionTypeRef import org.jetbrains.kotlin.fir.types.FirIntersectionTypeRef
import org.jetbrains.kotlin.fir.types.FirImplicitTypeRef 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.FirEffectDeclaration
import org.jetbrains.kotlin.fir.contracts.FirContractDescription import org.jetbrains.kotlin.fir.contracts.FirContractDescription
import org.jetbrains.kotlin.fir.contracts.FirLegacyRawContractDescription 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 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 visitLegacyRawContractDescription(legacyRawContractDescription: FirLegacyRawContractDescription, data: D): R = visitContractDescription(legacyRawContractDescription, data)
override fun visitRawContractDescription(rawContractDescription: FirRawContractDescription, data: D): R = visitContractDescription(rawContractDescription, 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.FirFunctionTypeRef
import org.jetbrains.kotlin.fir.types.FirIntersectionTypeRef import org.jetbrains.kotlin.fir.types.FirIntersectionTypeRef
import org.jetbrains.kotlin.fir.types.FirImplicitTypeRef 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.FirEffectDeclaration
import org.jetbrains.kotlin.fir.contracts.FirContractDescription import org.jetbrains.kotlin.fir.contracts.FirContractDescription
import org.jetbrains.kotlin.fir.contracts.FirLegacyRawContractDescription import org.jetbrains.kotlin.fir.contracts.FirLegacyRawContractDescription
@@ -314,6 +315,8 @@ abstract class FirDefaultVisitorVoid : FirVisitorVoid() {
override fun visitImplicitTypeRef(implicitTypeRef: FirImplicitTypeRef) = visitTypeRef(implicitTypeRef) override fun visitImplicitTypeRef(implicitTypeRef: FirImplicitTypeRef) = visitTypeRef(implicitTypeRef)
override fun visitEffectDeclaration(effectDeclaration: FirEffectDeclaration) = visitContractElementDeclaration(effectDeclaration)
override fun visitLegacyRawContractDescription(legacyRawContractDescription: FirLegacyRawContractDescription) = visitContractDescription(legacyRawContractDescription) override fun visitLegacyRawContractDescription(legacyRawContractDescription: FirLegacyRawContractDescription) = visitContractDescription(legacyRawContractDescription)
override fun visitRawContractDescription(rawContractDescription: FirRawContractDescription) = visitContractDescription(rawContractDescription) 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.FirFunctionTypeRef
import org.jetbrains.kotlin.fir.types.FirIntersectionTypeRef import org.jetbrains.kotlin.fir.types.FirIntersectionTypeRef
import org.jetbrains.kotlin.fir.types.FirImplicitTypeRef 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.FirEffectDeclaration
import org.jetbrains.kotlin.fir.contracts.FirContractDescription import org.jetbrains.kotlin.fir.contracts.FirContractDescription
import org.jetbrains.kotlin.fir.contracts.FirLegacyRawContractDescription import org.jetbrains.kotlin.fir.contracts.FirLegacyRawContractDescription
@@ -731,7 +732,11 @@ abstract class FirTransformer<in D> : FirVisitor<FirElement, D>() {
return transformElement(implicitTypeRef, data) 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) return transformElement(effectDeclaration, data)
} }
@@ -1323,7 +1328,11 @@ abstract class FirTransformer<in D> : FirVisitor<FirElement, D>() {
return transformImplicitTypeRef(implicitTypeRef, data) 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) 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.FirFunctionTypeRef
import org.jetbrains.kotlin.fir.types.FirIntersectionTypeRef import org.jetbrains.kotlin.fir.types.FirIntersectionTypeRef
import org.jetbrains.kotlin.fir.types.FirImplicitTypeRef 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.FirEffectDeclaration
import org.jetbrains.kotlin.fir.contracts.FirContractDescription import org.jetbrains.kotlin.fir.contracts.FirContractDescription
import org.jetbrains.kotlin.fir.contracts.FirLegacyRawContractDescription 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 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 visitEffectDeclaration(effectDeclaration: FirEffectDeclaration, data: D): R = visitElement(effectDeclaration, data)
open fun visitContractDescription(contractDescription: FirContractDescription, data: D): R = visitElement(contractDescription, 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.FirFunctionTypeRef
import org.jetbrains.kotlin.fir.types.FirIntersectionTypeRef import org.jetbrains.kotlin.fir.types.FirIntersectionTypeRef
import org.jetbrains.kotlin.fir.types.FirImplicitTypeRef 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.FirEffectDeclaration
import org.jetbrains.kotlin.fir.contracts.FirContractDescription import org.jetbrains.kotlin.fir.contracts.FirContractDescription
import org.jetbrains.kotlin.fir.contracts.FirLegacyRawContractDescription import org.jetbrains.kotlin.fir.contracts.FirLegacyRawContractDescription
@@ -730,6 +731,10 @@ abstract class FirVisitorVoid : FirVisitor<Unit, Nothing?>() {
visitElement(implicitTypeRef) visitElement(implicitTypeRef)
} }
open fun visitContractElementDeclaration(contractElementDeclaration: FirContractElementDeclaration) {
visitElement(contractElementDeclaration)
}
open fun visitEffectDeclaration(effectDeclaration: FirEffectDeclaration) { open fun visitEffectDeclaration(effectDeclaration: FirEffectDeclaration) {
visitElement(effectDeclaration) visitElement(effectDeclaration)
} }
@@ -1322,6 +1327,10 @@ abstract class FirVisitorVoid : FirVisitor<Unit, Nothing?>() {
visitImplicitTypeRef(implicitTypeRef) visitImplicitTypeRef(implicitTypeRef)
} }
final override fun visitContractElementDeclaration(contractElementDeclaration: FirContractElementDeclaration, data: Nothing?) {
visitContractElementDeclaration(contractElementDeclaration)
}
final override fun visitEffectDeclaration(effectDeclaration: FirEffectDeclaration, data: Nothing?) { final override fun visitEffectDeclaration(effectDeclaration: FirEffectDeclaration, data: Nothing?) {
visitEffectDeclaration(effectDeclaration) visitEffectDeclaration(effectDeclaration)
} }
@@ -6,16 +6,27 @@
package org.jetbrains.kotlin.fir.contracts package org.jetbrains.kotlin.fir.contracts
import org.jetbrains.kotlin.KtSourceElement 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.builder.buildEffectDeclaration
import org.jetbrains.kotlin.fir.contracts.description.ConeContractDescriptionElement
import org.jetbrains.kotlin.fir.contracts.description.ConeEffectDeclaration import org.jetbrains.kotlin.fir.contracts.description.ConeEffectDeclaration
val FirContractDescription.effects: List<FirEffectDeclaration>? val FirContractDescription.effects: List<FirEffectDeclaration>?
get() = (this as? FirResolvedContractDescription)?.effects get() = (this as? FirResolvedContractDescription)?.effects
fun ConeEffectDeclaration.toFirEffectDeclaration(source: KtSourceElement? = null): FirEffectDeclaration = fun ConeEffectDeclaration.toFirElement(source: KtSourceElement? = null): FirEffectDeclaration =
buildEffectDeclaration { buildEffectDeclaration {
if (source != null) { if (source != null) {
this.source = source 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 { interface ConeContractDescriptionElement {
fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R
val erroneous: Boolean
} }
abstract class ConeEffectDeclaration : ConeContractDescriptionElement { abstract class ConeEffectDeclaration : ConeContractDescriptionElement {
@@ -23,6 +23,9 @@ abstract class ConeContractDescriptionVisitor<out R, in D> {
open fun visitCallsEffectDeclaration(callsEffect: ConeCallsEffectDeclaration, data: D): R = open fun visitCallsEffectDeclaration(callsEffect: ConeCallsEffectDeclaration, data: D): R =
visitEffectDeclaration(callsEffect, data) visitEffectDeclaration(callsEffect, data)
open fun visitErroneousCallsEffectDeclaration(callsEffect: ConeErroneousCallsEffectDeclaration, data: D): R =
visitCallsEffectDeclaration(callsEffect, data)
// Expressions // Expressions
open fun visitBooleanExpression(booleanExpression: ConeBooleanExpression, data: D): R = open fun visitBooleanExpression(booleanExpression: ConeBooleanExpression, data: D): R =
visitContractDescriptionElement(booleanExpression, data) visitContractDescriptionElement(booleanExpression, data)
@@ -35,6 +38,9 @@ abstract class ConeContractDescriptionVisitor<out R, in D> {
open fun visitIsInstancePredicate(isInstancePredicate: ConeIsInstancePredicate, data: D): R = open fun visitIsInstancePredicate(isInstancePredicate: ConeIsInstancePredicate, data: D): R =
visitBooleanExpression(isInstancePredicate, data) 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) open fun visitIsNullPredicate(isNullPredicate: ConeIsNullPredicate, data: D): R = visitBooleanExpression(isNullPredicate, data)
// Values // Values
@@ -45,9 +51,19 @@ abstract class ConeContractDescriptionVisitor<out R, in D> {
open fun visitBooleanConstantDescriptor(booleanConstantDescriptor: ConeBooleanConstantReference, data: D): R = open fun visitBooleanConstantDescriptor(booleanConstantDescriptor: ConeBooleanConstantReference, data: D): R =
visitConstantDescriptor(booleanConstantDescriptor, data) visitConstantDescriptor(booleanConstantDescriptor, data)
open fun visitErroneousConstantReference(erroneousConstantReference: ConeErroneousConstantReference, data: D): R =
visitConstantDescriptor(erroneousConstantReference, data)
open fun visitValueParameterReference(valueParameterReference: ConeValueParameterReference, data: D): R = open fun visitValueParameterReference(valueParameterReference: ConeValueParameterReference, data: D): R =
visitValue(valueParameterReference, data) visitValue(valueParameterReference, data)
open fun visitBooleanValueParameterReference(booleanValueParameterReference: ConeBooleanValueParameterReference, data: D): R = open fun visitBooleanValueParameterReference(booleanValueParameterReference: ConeBooleanValueParameterReference, data: D): R =
visitValueParameterReference(booleanValueParameterReference, data) 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 package org.jetbrains.kotlin.fir.contracts.description
import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
/** /**
* Effect with condition attached to it. * 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. * - if [condition] is true, we *can't* reason that [effect] will be observed.
*/ */
class ConeConditionalEffectDeclaration(val effect: ConeEffectDeclaration, val condition: ConeBooleanExpression) : ConeEffectDeclaration() { 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 = override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitConditionalEffectDeclaration(this, data) contractDescriptionVisitor.visitConditionalEffectDeclaration(this, data)
} }
@@ -31,9 +35,11 @@ class ConeConditionalEffectDeclaration(val effect: ConeEffectDeclaration, val co
* Effect which specifies that subroutine returns some particular value * Effect which specifies that subroutine returns some particular value
*/ */
class ConeReturnsEffectDeclaration(val value: ConeConstantReference) : ConeEffectDeclaration() { 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 = override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitReturnsEffectDeclaration(this, data) 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 * 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. * [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 = override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitCallsEffectDeclaration(this, data) 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 import org.jetbrains.kotlin.fir.expressions.LogicOperationKind
class ConeBinaryLogicExpression(val left: ConeBooleanExpression, val right: ConeBooleanExpression, val kind: LogicOperationKind) : ConeBooleanExpression { 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 { override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R {
return contractDescriptionVisitor.visitLogicalBinaryOperationContractExpression(this, data) return contractDescriptionVisitor.visitLogicalBinaryOperationContractExpression(this, data)
} }
} }
class ConeLogicalNot(val arg: ConeBooleanExpression) : ConeBooleanExpression { 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 = override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitLogicalNot(this, data) contractDescriptionVisitor.visitLogicalNot(this, data)
} }
@@ -5,9 +5,13 @@
package org.jetbrains.kotlin.fir.contracts.description package org.jetbrains.kotlin.fir.contracts.description
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
import org.jetbrains.kotlin.fir.types.ConeKotlinType 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 = override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitIsInstancePredicate(this, data) contractDescriptionVisitor.visitIsInstancePredicate(this, data)
@@ -15,10 +19,26 @@ class ConeIsInstancePredicate(val arg: ConeValueParameterReference, val type: Co
ConeIsInstancePredicate(arg, type, isNegated.not()) 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 { 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 = override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitIsNullPredicate(this, data) contractDescriptionVisitor.visitIsNullPredicate(this, data)
fun negated(): ConeIsNullPredicate = fun negated(): ConeIsNullPredicate =
ConeIsNullPredicate(arg, isNegated.not()) 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 package org.jetbrains.kotlin.fir.contracts.description
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
interface ConeContractDescriptionValue : ConeContractDescriptionElement { interface ConeContractDescriptionValue : ConeContractDescriptionElement {
override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R = override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitValue(this, data) contractDescriptionVisitor.visitValue(this, data)
} }
open class ConeConstantReference protected constructor(val name: String) : ConeContractDescriptionValue { 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 = override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitConstantDescriptor(this, data) 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 * Index of value parameter of function
* -1 means that it is reference to extension receiver * -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) assert(parameterIndex >= -1)
} }
override val erroneous: Boolean
get() = false
override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R = override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitValueParameterReference(this, data) 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 { class ConeBooleanValueParameterReference(parameterIndex: Int, name: String) : ConeValueParameterReference(parameterIndex, name), ConeBooleanExpression {
override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R = override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitBooleanValueParameterReference(this, data) 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 intersectionTypeRef by element(TypeRef, typeRefWithNullability)
val implicitTypeRef by element(TypeRef, typeRef) 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 contractDescription by element(Contracts)
val legacyRawContractDescription by element(Contracts, contractDescription) val legacyRawContractDescription by element(Contracts, contractDescription)
@@ -562,6 +562,8 @@ object ImplementationConfigurator : AbstractFirTreeImplementationConfigurator()
noImpl(argumentList) noImpl(argumentList)
noImpl(annotationArgumentMapping) noImpl(annotationArgumentMapping)
impl(contractElementDeclaration)
val implementationsWithoutStatusAndTypeParameters = listOf( val implementationsWithoutStatusAndTypeParameters = listOf(
"FirAnonymousFunctionImpl", "FirAnonymousFunctionImpl",
"FirValueParameterImpl", "FirValueParameterImpl",
@@ -732,6 +732,10 @@ object NodeConfigurator : AbstractFieldConfigurator<FirTreeBuilder>(FirTreeBuild
+field(varianceType) +field(varianceType)
} }
contractElementDeclaration.configure {
+field("effect", coneContractElementType)
}
effectDeclaration.configure { effectDeclaration.configure {
+field("effect", coneEffectDeclarationType) +field("effect", coneEffectDeclarationType)
} }
@@ -742,7 +746,7 @@ object NodeConfigurator : AbstractFieldConfigurator<FirTreeBuilder>(FirTreeBuild
resolvedContractDescription.configure { resolvedContractDescription.configure {
+fieldList("effects", effectDeclaration) +fieldList("effects", effectDeclaration)
+fieldList("unresolvedEffects", coneUnresolvedEffect) +fieldList("unresolvedEffects", contractElementDeclaration)
} }
legacyRawContractDescription.configure { legacyRawContractDescription.configure {
@@ -79,11 +79,11 @@ val emptyArgumentListType = type("fir.expressions", "FirEmptyArgumentList")
val firScopeProviderType = type("fir.scopes", "FirScopeProvider") val firScopeProviderType = type("fir.scopes", "FirScopeProvider")
val pureAbstractElementType = generatedType("FirPureAbstractElement") val pureAbstractElementType = generatedType("FirPureAbstractElement")
val coneContractElementType = type("fir.contracts.description", "ConeContractDescriptionElement")
val coneEffectDeclarationType = type("fir.contracts.description", "ConeEffectDeclaration") val coneEffectDeclarationType = type("fir.contracts.description", "ConeEffectDeclaration")
val emptyContractDescriptionType = generatedType("contracts.impl", "FirEmptyContractDescription") val emptyContractDescriptionType = generatedType("contracts.impl", "FirEmptyContractDescription")
val coneDiagnosticType = generatedType("diagnostics", "ConeDiagnostic") val coneDiagnosticType = generatedType("diagnostics", "ConeDiagnostic")
val coneStubDiagnosticType = generatedType("diagnostics", "ConeStubDiagnostic") val coneStubDiagnosticType = generatedType("diagnostics", "ConeStubDiagnostic")
val coneUnresolvedEffect = type("fir.contracts.description", "ConeUnresolvedEffect")
val dslBuilderAnnotationType = generatedType("builder", "FirBuilderDsl") val dslBuilderAnnotationType = generatedType("builder", "FirBuilderDsl")
val firImplementationDetailType = generatedType("FirImplementationDetail") val firImplementationDetailType = generatedType("FirImplementationDetail")
@@ -5,5 +5,5 @@
import kotlin.contracts.* import kotlin.contracts.*
fun emptyContract() { fun emptyContract() {
contract { } <!ERROR_IN_CONTRACT_DESCRIPTION!>contract { }<!>
} }
@@ -25,9 +25,9 @@ fun whenInContract(x: Any?, boolean: Boolean) {
fun forInContract(x: Any?) { fun forInContract(x: Any?) {
contract { contract {
for (i in 0..1) { <!ERROR_IN_CONTRACT_DESCRIPTION!>for (i in 0..1) {
returns() implies (x is String) returns() implies (x is String)
} }<!>
} }
} }
@@ -5,7 +5,7 @@ import kotlin.contracts.*
// TESTCASE NUMBER: 1 // TESTCASE NUMBER: 1
inline fun case_1(block: () -> Unit) { inline fun case_1(block: () -> Unit) {
contract { } <!ERROR_IN_CONTRACT_DESCRIPTION!>contract { }<!>
return block() return block()
} }
@@ -11,24 +11,24 @@ object SampleObject {
// TESTCASE NUMBER: 1 // TESTCASE NUMBER: 1
inline fun case_1(invocationKind: InvocationKind, block: () -> Unit) { inline fun case_1(invocationKind: InvocationKind, block: () -> Unit) {
contract { callsInPlace(block, invocationKind) } contract { <!ERROR_IN_CONTRACT_DESCRIPTION!>callsInPlace(block, invocationKind)<!> }
return block() return block()
} }
// TESTCASE NUMBER: 2 // TESTCASE NUMBER: 2
inline fun <T : InvocationKind> case_2(invocationKind: T, block: () -> Unit) { inline fun <T : InvocationKind> case_2(invocationKind: T, block: () -> Unit) {
contract { callsInPlace(block, invocationKind) } contract { <!ERROR_IN_CONTRACT_DESCRIPTION!>callsInPlace(block, invocationKind)<!> }
return block() return block()
} }
// TESTCASE NUMBER: 3 // TESTCASE NUMBER: 3
inline fun case_3(block: () -> Unit) { inline fun case_3(block: () -> Unit) {
contract { callsInPlace(block, invocationKind) } contract { <!ERROR_IN_CONTRACT_DESCRIPTION!>callsInPlace(block, invocationKind)<!> }
return block() return block()
} }
// TESTCASE NUMBER: 4 // TESTCASE NUMBER: 4
inline fun case_4(block: () -> Unit) { inline fun case_4(block: () -> Unit) {
contract { callsInPlace(block, SampleObject.invocationKind) } contract { <!ERROR_IN_CONTRACT_DESCRIPTION!>callsInPlace(block, SampleObject.invocationKind)<!> }
return block() return block()
} }