From f946ddeb40fb8c785d9bf09588a63f0239a5b269 Mon Sep 17 00:00:00 2001 From: Kirill Rakhman Date: Wed, 8 Feb 2023 12:03:36 +0100 Subject: [PATCH] [FIR] Implement checks for contract not allowed ^KT-55423 Fixed --- .../diagnostics/KtFirDataClassConverters.kt | 7 +++ .../api/fir/diagnostics/KtFirDiagnostics.kt | 5 ++ .../fir/diagnostics/KtFirDiagnosticsImpl.kt | 6 +++ .../lazy/resolve/FirLazyBodiesCalculator.kt | 1 + .../resolve/contracts/disabledFeature.kt | 2 +- .../bad/returnsImplies/propertyGetter.kt | 4 +- .../good/returnsImplies/propertyAccessors.kt | 4 +- .../diagnostics/FirDiagnosticsList.kt | 3 ++ .../fir/analysis/diagnostics/FirErrors.kt | 1 + .../checkers/CommonExpressionCheckers.kt | 1 + .../declaration/FirContractChecker.kt | 45 ++++++++++++++---- .../FirContractNotFirstStatementChecker.kt | 39 +++++++++++++++ .../diagnostics/FirErrorsDefaultMessages.kt | 2 + .../java/declarations/FirJavaConstructor.kt | 10 ++++ .../converter/DeclarationsConverter.kt | 8 ++-- .../converter/ExpressionsConverter.kt | 8 ++++ .../kotlin/fir/builder/RawFirBuilder.kt | 32 ++++++++----- .../kotlin/fir/builder/ConversionUtils.kt | 24 ++++++++-- .../FirDeclarationsResolveTransformer.kt | 11 +++++ ...actContractResolveTransformerDispatcher.kt | 41 ++++++++-------- .../FirContractResolveTransformerAdapter.kt | 11 +++++ .../fir/declarations/FirAnonymousFunction.kt | 8 +++- .../kotlin/fir/declarations/FirConstructor.kt | 8 +++- .../builder/FirAbstractConstructorBuilder.kt | 2 + .../builder/FirAnonymousFunctionBuilder.kt | 5 ++ .../builder/FirConstructorBuilder.kt | 5 ++ .../builder/FirPrimaryConstructorBuilder.kt | 4 ++ .../impl/FirAnonymousFunctionImpl.kt | 13 +++++ .../declarations/impl/FirConstructorImpl.kt | 13 +++++ .../impl/FirPrimaryConstructor.kt | 13 +++++ .../fir/tree/generator/BuilderConfigurator.kt | 4 ++ .../fir/tree/generator/FirTreeBuilder.kt | 4 +- .../dsl/errors/contractCallSites.1.3.fir.kt | 30 ++++++------ .../dsl/errors/contractCallSites.1.3.kt | 10 ++-- .../dsl/errors/contractCallSites.1.4.fir.kt | 33 +++++++------ .../dsl/errors/contractCallSites.1.4.kt | 5 ++ .../dsl/errors/contractCallSites.1.4.txt | 2 +- .../dsl/errors/notFirstStatement.fir.kt | 22 ++++----- ...ropertyAccessorsContractDescription.fir.kt | 4 +- .../contractBuilder/common/neg/1.fir.kt | 18 +++---- .../contractBuilder/common/neg/14.fir.kt | 10 ++-- .../contractBuilder/common/neg/16.fir.kt | 47 ------------------- .../contractBuilder/common/neg/16.kt | 1 + .../contractBuilder/common/neg/5.fir.kt | 2 +- .../contractBuilder/common/neg/6.fir.kt | 2 +- .../contractBuilder/common/neg/7.fir.kt | 2 +- .../contractBuilder/common/neg/9.fir.kt | 4 +- .../contractFunction/neg/2.fir.kt | 6 +-- .../contractFunction/neg/4.fir.kt | 10 ++-- .../jetbrains/kotlin/name/StandardClassIds.kt | 3 ++ 50 files changed, 377 insertions(+), 178 deletions(-) create mode 100644 compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirContractNotFirstStatementChecker.kt delete mode 100644 compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractBuilder/common/neg/16.fir.kt diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt index cd531eb7c7e..df659c1cf2d 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -3678,6 +3678,13 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.CONTRACT_NOT_ALLOWED) { firDiagnostic -> + ContractNotAllowedImpl( + firDiagnostic.a, + firDiagnostic as KtPsiDiagnostic, + token, + ) + } add(FirErrors.NO_GET_METHOD) { firDiagnostic -> NoGetMethodImpl( firDiagnostic as KtPsiDiagnostic, diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt index 0968eb46859..082fb80c4b7 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt @@ -2567,6 +2567,11 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { abstract val reason: String } + abstract class ContractNotAllowed : KtFirDiagnostic() { + override val diagnosticClass get() = ContractNotAllowed::class + abstract val reason: String + } + abstract class NoGetMethod : KtFirDiagnostic() { override val diagnosticClass get() = NoGetMethod::class } diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index 3bcc10ec767..9d333f742b5 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -3096,6 +3096,12 @@ internal class ErrorInContractDescriptionImpl( override val token: KtLifetimeToken, ) : KtFirDiagnostic.ErrorInContractDescription(), KtAbstractFirDiagnostic +internal class ContractNotAllowedImpl( + override val reason: String, + override val firDiagnostic: KtPsiDiagnostic, + override val token: KtLifetimeToken, +) : KtFirDiagnostic.ContractNotAllowed(), KtAbstractFirDiagnostic + internal class NoGetMethodImpl( override val firDiagnostic: KtPsiDiagnostic, override val token: KtLifetimeToken, diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/lazy/resolve/FirLazyBodiesCalculator.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/lazy/resolve/FirLazyBodiesCalculator.kt index 577922d6f09..66e2fc9e744 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/lazy/resolve/FirLazyBodiesCalculator.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/lazy/resolve/FirLazyBodiesCalculator.kt @@ -107,6 +107,7 @@ internal object FirLazyBodiesCalculator { constructor.apply { replaceBody(newConstructor.body) + replaceContractDescription(newConstructor.contractDescription) replaceDelegatedConstructor(newConstructor.delegatedConstructor) replaceValueParameterDefaultValues(valueParameters, newConstructor.valueParameters) } diff --git a/compiler/fir/analysis-tests/testData/resolve/contracts/disabledFeature.kt b/compiler/fir/analysis-tests/testData/resolve/contracts/disabledFeature.kt index 989ebd479bd..85473b930d7 100644 --- a/compiler/fir/analysis-tests/testData/resolve/contracts/disabledFeature.kt +++ b/compiler/fir/analysis-tests/testData/resolve/contracts/disabledFeature.kt @@ -10,7 +10,7 @@ inline fun requreIsInstance(value: Any) contract [ + get() contract [ returnsNotNull() implies (this@length is String) ] = (this as? String)?.length diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/bad/returnsImplies/propertyGetter.kt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/bad/returnsImplies/propertyGetter.kt index cfda18e878e..c5a23efc648 100644 --- a/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/bad/returnsImplies/propertyGetter.kt +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/bad/returnsImplies/propertyGetter.kt @@ -12,8 +12,8 @@ fun Any?.isNotNull(): Boolean { @OptIn(ExperimentalContracts::class) val Any?.isNotNull: Boolean get() { - contract { + contract { returns(true) implies (this@isNotNull != null) } return this@isNotNull != null - } \ No newline at end of file + } diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/propertyAccessors.kt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/propertyAccessors.kt index 4b18197ce28..0b7ae99348b 100644 --- a/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/propertyAccessors.kt +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/propertyAccessors.kt @@ -7,13 +7,13 @@ interface A { @OptIn(ExperimentalContracts::class) var Any?.isNotNull: Boolean get() { - contract { + contract { returns(true) implies (this@isNotNull != null) } return this != null } set(value) { - contract { + contract { returns() implies (this@isNotNull != null) require(this != null) } diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt index c3f08c69e36..563353d98af 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt @@ -1272,6 +1272,9 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { val ERROR_IN_CONTRACT_DESCRIPTION by error(PositioningStrategy.SELECTOR_BY_QUALIFIED) { parameter("reason") } + val CONTRACT_NOT_ALLOWED by error(PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED) { + parameter("reason") + } } val CONVENTIONS by object : DiagnosticGroup("Conventions") { diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index 5ffb69e39c5..3011aa389c0 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -668,6 +668,7 @@ object FirErrors { // Function contracts val ERROR_IN_CONTRACT_DESCRIPTION by error1(SourceElementPositioningStrategies.SELECTOR_BY_QUALIFIED) + val CONTRACT_NOT_ALLOWED by error1(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED) // Conventions val NO_GET_METHOD by error0(SourceElementPositioningStrategies.ARRAY_ACCESS) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonExpressionCheckers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonExpressionCheckers.kt index d6fc28d33ac..612b96aaaf2 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonExpressionCheckers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonExpressionCheckers.kt @@ -64,6 +64,7 @@ object CommonExpressionCheckers : ExpressionCheckers() { FirAssignmentOperatorCallChecker, FirNamedVarargChecker, FirUnderscoredTypeArgumentSyntaxChecker, + FirContractNotFirstStatementChecker, ) override val propertyAccessExpressionCheckers: Set diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirContractChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirContractChecker.kt index 423776b4a50..176fa8689c4 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirContractChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirContractChecker.kt @@ -6,27 +6,38 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration import org.jetbrains.kotlin.KtFakeSourceElementKind -import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.KtRealSourceElementKind +import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.diagnostics.DiagnosticReporter -import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors 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.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.* 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" override fun check(declaration: FirFunction, context: CheckerContext, reporter: DiagnosticReporter) { - if (declaration !is FirContractDescriptionOwner || - declaration.contractDescription !is FirResolvedContractDescription - ) { - return - } + if (declaration !is FirContractDescriptionOwner) return + val contractDescription = declaration.contractDescription as? FirResolvedContractDescription ?: return + checkUnresolvedEffects(contractDescription, context, reporter) + checkContractNotAllowed(declaration, contractDescription, context, reporter) + } + + private fun checkUnresolvedEffects( + contractDescription: FirResolvedContractDescription, + context: CheckerContext, + reporter: DiagnosticReporter + ) { // Any statements that [ConeEffectExtractor] cannot extract effects will be in `unresolvedEffects`. - for (unresolvedEffect in (declaration.contractDescription as FirResolvedContractDescription).unresolvedEffects) { + for (unresolvedEffect in contractDescription.unresolvedEffects) { val statement = unresolvedEffect.statement if (statement.source == null || statement.source!!.kind is KtFakeSourceElementKind) continue @@ -35,4 +46,22 @@ object FirContractChecker : FirFunctionChecker() { reporter.reportOn(statement.source, FirErrors.ERROR_IN_CONTRACT_DESCRIPTION, UNEXPECTED_CONSTRUCTION, context) } } + + private fun checkContractNotAllowed( + declaration: FirFunction, + contractDescription: FirResolvedContractDescription, + context: CheckerContext, + reporter: DiagnosticReporter + ) { + val source = contractDescription.source + if (source?.kind !is KtRealSourceElementKind) return + + fun contractNotAllowed(message: String) = reporter.reportOn(source, FirErrors.CONTRACT_NOT_ALLOWED, message, context) + + if (declaration is FirPropertyAccessor || declaration is FirAnonymousFunction) contractNotAllowed("Contracts are only allowed for functions") + else if (declaration.isAbstract || declaration.isOpen || declaration.isOverride) contractNotAllowed("Contracts are not allowed for open or override functions") + else if (declaration.isOperator) contractNotAllowed("Contracts are not allowed for operator functions") + else if (declaration.symbol.callableId.isLocal || declaration.visibility == Visibilities.Local) contractNotAllowed("Contracts are not allowed for local functions") + } + } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirContractNotFirstStatementChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirContractNotFirstStatementChecker.kt new file mode 100644 index 00000000000..df9512d17c8 --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirContractNotFirstStatementChecker.kt @@ -0,0 +1,39 @@ +/* + * 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.analysis.checkers.expression + +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +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.declarations.FirFunction +import org.jetbrains.kotlin.fir.expressions.FirFunctionCall +import org.jetbrains.kotlin.fir.expressions.impl.FirContractCallBlock +import org.jetbrains.kotlin.fir.expressions.impl.FirSingleExpressionBlock +import org.jetbrains.kotlin.fir.expressions.toResolvedCallableSymbol +import org.jetbrains.kotlin.name.StandardClassIds + +object FirContractNotFirstStatementChecker : FirFunctionCallChecker() { + override fun check(expression: FirFunctionCall, context: CheckerContext, reporter: DiagnosticReporter) { + if (StandardClassIds.Callables.contract != expression.toResolvedCallableSymbol()?.callableId) return + + val containingDeclaration = context.containingDeclarations.last() + if (!(containingDeclaration is FirFunction && expression.isCorrectlyPlacedIn(containingDeclaration))) { + val message = if (containingDeclaration is FirFunction && containingDeclaration.body is FirSingleExpressionBlock) { + "Contracts are only allowed in function body blocks" + } else { + "Contract should be the first statement" + } + + reporter.reportOn(expression.source, FirErrors.CONTRACT_NOT_ALLOWED, message, context) + } + } + + private fun FirFunctionCall.isCorrectlyPlacedIn(functionDeclaration: FirFunction): Boolean { + val firstStatement = functionDeclaration.body?.statements?.first() + return firstStatement is FirContractCallBlock && firstStatement.call == this + } +} diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt index 8a17c060890..0616b706d85 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt @@ -134,6 +134,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CONST_VAL_WITHOUT import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CONST_VAL_WITH_DELEGATE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CONST_VAL_WITH_GETTER import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CONST_VAL_WITH_NON_CONST_INITIALIZER +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CONTRACT_NOT_ALLOWED import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CREATING_AN_INSTANCE_OF_ABSTRACT_CLASS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CYCLIC_CONSTRUCTOR_DELEGATION_CALL import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CYCLIC_GENERIC_UPPER_BOUND @@ -1909,6 +1910,7 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() { // Function contracts map.put(ERROR_IN_CONTRACT_DESCRIPTION, "Error in contract description", TO_STRING) + map.put(CONTRACT_NOT_ALLOWED, "{0}", TO_STRING) // Conventions map.put(NO_GET_METHOD, "No get method providing array access") diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/declarations/FirJavaConstructor.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/declarations/FirJavaConstructor.kt index 881a4edd409..049d5c69a6d 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/declarations/FirJavaConstructor.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/declarations/FirJavaConstructor.kt @@ -10,6 +10,8 @@ import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.fir.FirImplementationDetail import org.jetbrains.kotlin.fir.FirModuleData import org.jetbrains.kotlin.fir.builder.FirBuilderDsl +import org.jetbrains.kotlin.fir.contracts.FirContractDescription +import org.jetbrains.kotlin.fir.contracts.impl.FirEmptyContractDescription import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.builder.FirConstructorBuilder import org.jetbrains.kotlin.fir.expressions.FirAnnotation @@ -44,6 +46,7 @@ class FirJavaConstructor @FirImplementationDetail constructor( ) : FirConstructor() { override val receiverParameter: FirReceiverParameter? get() = null override var deprecationsProvider: DeprecationsProvider = UnresolvedDeprecationProvider + override val contractDescription: FirContractDescription get() = FirEmptyContractDescription init { symbol.bind(this) @@ -100,6 +103,10 @@ class FirJavaConstructor @FirImplementationDetail constructor( return this } + override fun transformContractDescription(transformer: FirTransformer, data: D): FirConstructor { + return this + } + override fun transformStatus(transformer: FirTransformer, data: D): FirJavaConstructor { status = status.transformSingle(transformer, data) return this @@ -152,6 +159,9 @@ class FirJavaConstructor @FirImplementationDetail constructor( } override fun replaceControlFlowGraphReference(newControlFlowGraphReference: FirControlFlowGraphReference?) {} + override fun replaceContractDescription(newContractDescription: FirContractDescription) { + error("Contract description cannot be replaced for FirJavaConstructor") + } override fun replaceBody(newBody: FirBlock?) { error("Body cannot be replaced for FirJavaConstructor") diff --git a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/DeclarationsConverter.kt b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/DeclarationsConverter.kt index 6305c66fb28..ff224401394 100644 --- a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/DeclarationsConverter.kt +++ b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/DeclarationsConverter.kt @@ -1045,8 +1045,9 @@ class DeclarationsConverter( annotations += modifiers.annotations typeParameters += constructorTypeParametersFromConstructedClass(classWrapper.classBuilder.typeParameters) valueParameters += firValueParameters.map { it.firValueParameter } - val (body, _) = convertFunctionBody(block, null, allowLegacyContractDescription = true) + val (body, contractDescription) = convertFunctionBody(block, null, allowLegacyContractDescription = true) this.body = body + contractDescription?.let { this.contractDescription = it } context.firFunctionTargets.removeLast() this.contextReceivers.addAll(convertContextReceivers(secondaryConstructor.getParent()!!.getParent()!!)) }.also { @@ -1736,14 +1737,15 @@ class DeclarationsConverter( ).map { it.firValueParameter } } - val allowLegacyContractDescription = outerContractDescription == null && !isLocal + val allowLegacyContractDescription = outerContractDescription == null val bodyWithContractDescription = convertFunctionBody(block, expression, allowLegacyContractDescription) this.body = bodyWithContractDescription.first val contractDescription = outerContractDescription ?: bodyWithContractDescription.second contractDescription?.let { - // TODO: add error reporting for contracts on lambdas if (this is FirSimpleFunctionBuilder) { this.contractDescription = it + } else if (this is FirAnonymousFunctionBuilder) { + this.contractDescription = it } } } diff --git a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/ExpressionsConverter.kt b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/ExpressionsConverter.kt index b84bf58bd80..d75afb11e63 100644 --- a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/ExpressionsConverter.kt +++ b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/ExpressionsConverter.kt @@ -30,6 +30,7 @@ import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusIm import org.jetbrains.kotlin.fir.diagnostics.* import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.expressions.builder.* +import org.jetbrains.kotlin.fir.expressions.impl.FirContractCallBlock import org.jetbrains.kotlin.fir.expressions.impl.FirSingleExpressionBlock import org.jetbrains.kotlin.fir.expressions.impl.buildSingleExpressionBlock import org.jetbrains.kotlin.fir.lightTree.LightTree2Fir @@ -201,6 +202,13 @@ class ExpressionsConverter( body = if (block != null) { declarationsConverter.withOffset(expressionSource.startOffset) { declarationsConverter.convertBlockExpressionWithoutBuilding(block!!).apply { + statements.firstOrNull()?.let { + if (it.isContractBlockFirCheck()) { + this@buildAnonymousFunction.contractDescription = it.toLegacyRawContractDescription() + statements[0] = FirContractCallBlock(it) + } + } + if (statements.isEmpty()) { statements.add( buildReturnExpression { diff --git a/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt b/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt index d4660cf4c45..95eb14b268d 100644 --- a/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt +++ b/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt @@ -23,6 +23,7 @@ import org.jetbrains.kotlin.fir.declarations.utils.* import org.jetbrains.kotlin.fir.diagnostics.* import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.expressions.builder.* +import org.jetbrains.kotlin.fir.expressions.impl.FirContractCallBlock import org.jetbrains.kotlin.fir.expressions.impl.FirSingleExpressionBlock import org.jetbrains.kotlin.fir.extensions.extensionService import org.jetbrains.kotlin.fir.references.builder.* @@ -365,13 +366,13 @@ open class RawFirBuilder( } } - private fun KtDeclarationWithBody.buildFirBody(allowLegacyContractDescription: Boolean): Pair = + private fun KtDeclarationWithBody.buildFirBody(): Pair = if (hasBody()) { buildOrLazyBlock { if (hasBlockBody()) { val block = bodyBlockExpression?.accept(this@Visitor, Unit) as? FirBlock val contractDescription = when { - allowLegacyContractDescription && !hasContractEffectList() -> block?.let(::processLegacyContractDescription) + !hasContractEffectList() -> block?.let(::processLegacyContractDescription) else -> null } return@buildFirBody block to contractDescription @@ -471,9 +472,9 @@ open class RawFirBuilder( } } val outerContractDescription = this@toFirPropertyAccessor.obtainContractDescription() - val bodyWithContractDescription = this@toFirPropertyAccessor.buildFirBody(allowLegacyContractDescription = true) - this.body = bodyWithContractDescription.first - val contractDescription = outerContractDescription ?: bodyWithContractDescription.second + val (body, innerContractDescription) = this@toFirPropertyAccessor.buildFirBody() + this.body = body + val contractDescription = outerContractDescription ?: innerContractDescription contractDescription?.let { this.contractDescription = it } @@ -1484,13 +1485,14 @@ open class RawFirBuilder( listOf() withCapturedTypeParameters(true, functionSource, actualTypeParameters) { val outerContractDescription = function.obtainContractDescription() - val bodyWithContractDescription = function.buildFirBody(!isLocalFunction) - this.body = bodyWithContractDescription.first - val contractDescription = outerContractDescription ?: bodyWithContractDescription.second + val (body, innerContractDescription) = function.buildFirBody() + this.body = body + val contractDescription = outerContractDescription ?: innerContractDescription contractDescription?.let { - // TODO: add error reporting for contracts on lambdas if (this is FirSimpleFunctionBuilder) { this.contractDescription = it + } else if (this is FirAnonymousFunctionBuilder) { + this.contractDescription = it } } } @@ -1601,6 +1603,13 @@ open class RawFirBuilder( FirSingleExpressionBlock(errorExpression.toReturn()) } else { configureBlockWithoutBuilding(ktBody).apply { + statements.firstOrNull()?.let { + if (it.isContractBlockFirCheck()) { + this@buildAnonymousFunction.contractDescription = it.toLegacyRawContractDescription() + statements[0] = FirContractCallBlock(it) + } + } + if (statements.isEmpty()) { statements.add( buildReturnExpression { @@ -1660,7 +1669,8 @@ open class RawFirBuilder( extractValueParametersTo(this, symbol, ValueParameterDeclaration.FUNCTION) - val (body, _) = buildFirBody(allowLegacyContractDescription = true) + val (body, contractDescription) = buildFirBody() + contractDescription?.let { this.contractDescription = it } this.body = body this@RawFirBuilder.context.firFunctionTargets.removeLast() }.also { @@ -2757,7 +2767,7 @@ open class RawFirBuilder( } } - private fun buildErrorTopLevelDeclarationForDanglingModifierList(modifierList : KtModifierList) = buildDanglingModifierList { + private fun buildErrorTopLevelDeclarationForDanglingModifierList(modifierList: KtModifierList) = buildDanglingModifierList { this.source = modifierList.toFirSourceElement(KtFakeSourceElementKind.DanglingModifierList) moduleData = baseModuleData origin = FirDeclarationOrigin.Source diff --git a/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/ConversionUtils.kt b/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/ConversionUtils.kt index 99e78b9e48b..2947cd0ae15 100644 --- a/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/ConversionUtils.kt +++ b/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/ConversionUtils.kt @@ -14,6 +14,7 @@ import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget import org.jetbrains.kotlin.fakeElement import org.jetbrains.kotlin.fir.* import org.jetbrains.kotlin.fir.contracts.FirContractDescription +import org.jetbrains.kotlin.fir.contracts.FirLegacyRawContractDescription import org.jetbrains.kotlin.fir.contracts.builder.buildLegacyRawContractDescription import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin @@ -43,6 +44,8 @@ import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.types.ConstantValueKind import org.jetbrains.kotlin.types.expressions.OperatorConventions import org.jetbrains.kotlin.util.OperatorNameConventions +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.contract fun String.parseCharacter(): CharacterWithDiagnostic { // Strip the quotes @@ -495,18 +498,29 @@ fun FirPropertyBuilder.generateAccessorsByDelegate( fun processLegacyContractDescription(block: FirBlock): FirContractDescription? { if (block.isContractPresentFirCheck()) { val contractCall = block.replaceFirstStatement { FirContractCallBlock(it) } - return buildLegacyRawContractDescription { - source = contractCall.source - this.contractCall = contractCall - } + return contractCall.toLegacyRawContractDescription() } return null } +fun FirFunctionCall.toLegacyRawContractDescription(): FirLegacyRawContractDescription { + return buildLegacyRawContractDescription { + this.source = this@toLegacyRawContractDescription.source + this.contractCall = this@toLegacyRawContractDescription + } +} + fun FirBlock.isContractPresentFirCheck(): Boolean { val firstStatement = statements.firstOrNull() ?: return false - val contractCall = firstStatement as? FirFunctionCall ?: return false + return firstStatement.isContractBlockFirCheck() +} + +@OptIn(ExperimentalContracts::class) +fun FirStatement.isContractBlockFirCheck(): Boolean { + contract { returns(true) implies (this@isContractBlockFirCheck is FirFunctionCall) } + + val contractCall = this as? FirFunctionCall ?: return false if (contractCall.calleeReference.name.asString() != "contract") return false val receiver = contractCall.explicitReceiver as? FirQualifiedAccessExpression ?: return true if (!contractCall.checkReceiver("contracts")) return false diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt index 5ccb1e962f9..a3c94320b80 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt @@ -10,6 +10,7 @@ import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.fakeElement import org.jetbrains.kotlin.fir.* +import org.jetbrains.kotlin.fir.contracts.impl.FirEmptyContractDescription import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.builder.buildAnonymousFunctionCopy import org.jetbrains.kotlin.fir.declarations.builder.buildContextReceiver @@ -36,6 +37,7 @@ import org.jetbrains.kotlin.fir.resolve.inference.extractLambdaInfoFromFunctionT import org.jetbrains.kotlin.fir.resolve.substitution.createTypeSubstitutorByTypeConstructor import org.jetbrains.kotlin.fir.resolve.transformers.FirCallCompletionResultsWriterTransformer import org.jetbrains.kotlin.fir.resolve.transformers.FirStatusResolver +import org.jetbrains.kotlin.fir.resolve.transformers.contracts.runContractResolveForFunction import org.jetbrains.kotlin.fir.resolve.transformers.transformVarargTypeToArrayType import org.jetbrains.kotlin.fir.scopes.impl.FirMemberTypeParameterScope import org.jetbrains.kotlin.fir.symbols.impl.FirValueParameterSymbol @@ -590,6 +592,10 @@ open class FirDeclarationsResolveTransformer( // For class members everything should be already prepared prepareSignatureForBodyResolve(simpleFunction) simpleFunction.transformStatus(this, simpleFunction.resolveStatus().mode()) + + if (simpleFunction.contractDescription != FirEmptyContractDescription) { + simpleFunction.runContractResolveForFunction(session, scopeSession, context) + } } context.forFunctionBody(simpleFunction, components) { withFullBodyResolve { @@ -730,6 +736,11 @@ open class FirDeclarationsResolveTransformer( anonymousFunction.transformReceiverParameter(transformer, ResolutionMode.ContextIndependent) anonymousFunction.valueParameters.forEach { it.transformReturnTypeRef(transformer, ResolutionMode.ContextIndependent) } } + + if (anonymousFunction.contractDescription != FirEmptyContractDescription) { + anonymousFunction.runContractResolveForFunction(session, scopeSession, context) + } + return when (data) { is ResolutionMode.ContextDependent, is ResolutionMode.ContextDependentDelegate -> { context.storeContextForAnonymousFunction(anonymousFunction) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/contracts/FirAbstractContractResolveTransformerDispatcher.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/contracts/FirAbstractContractResolveTransformerDispatcher.kt index 481fae40f45..eb344229607 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/contracts/FirAbstractContractResolveTransformerDispatcher.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/contracts/FirAbstractContractResolveTransformerDispatcher.kt @@ -18,7 +18,6 @@ import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.builder.buildAnonymousFunction import org.jetbrains.kotlin.fir.declarations.builder.buildReceiverParameter import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticProperty -import org.jetbrains.kotlin.fir.errorTypeFromPrototype import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.expressions.builder.* import org.jetbrains.kotlin.fir.expressions.impl.FirContractCallBlock @@ -26,7 +25,6 @@ import org.jetbrains.kotlin.fir.moduleData import org.jetbrains.kotlin.fir.references.builder.buildSimpleNamedReference import org.jetbrains.kotlin.fir.resolve.ResolutionMode import org.jetbrains.kotlin.fir.resolve.ScopeSession -import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeContractDescriptionError import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.BodyResolveContext import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirAbstractBodyResolveTransformerDispatcher import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer @@ -72,19 +70,8 @@ abstract class FirAbstractContractResolveTransformerDispatcher( simpleFunction: FirSimpleFunction, data: ResolutionMode ): FirSimpleFunction { - if (!simpleFunction.hasContractToResolve) { - return simpleFunction - } - val containingDeclaration = context.containerIfAny - if (containingDeclaration != null && containingDeclaration !is FirClass) { - simpleFunction.replaceReturnTypeRef( - simpleFunction.returnTypeRef.errorTypeFromPrototype( - ConeContractDescriptionError("Local function can not be used in contract description") - ) - ) - return simpleFunction - } - @Suppress("UNCHECKED_CAST") + if (!simpleFunction.hasContractToResolve) return simpleFunction + return context.withSimpleFunction(simpleFunction, session) { context.forFunctionBody(simpleFunction, components) { transformContractDescriptionOwner(simpleFunction) @@ -92,6 +79,15 @@ abstract class FirAbstractContractResolveTransformerDispatcher( } } + override fun transformAnonymousFunction(anonymousFunction: FirAnonymousFunction, data: ResolutionMode): FirAnonymousFunction { + if (!anonymousFunction.hasContractToResolve) return anonymousFunction + + return context.forFunctionBody(anonymousFunction, components) { + transformContractDescriptionOwner(anonymousFunction) + } + } + + override fun transformScript(script: FirScript, data: ResolutionMode): FirScript { return script } @@ -282,7 +278,14 @@ abstract class FirAbstractContractResolveTransformerDispatcher( } override fun transformConstructor(constructor: FirConstructor, data: ResolutionMode): FirConstructor { - return constructor + if (!constructor.hasContractToResolve) { + return constructor + } + return context.withConstructor(constructor) { + context.forConstructorBody(constructor, session) { + transformContractDescriptionOwner(constructor) + } + } } override fun transformEnumEntry(enumEntry: FirEnumEntry, data: ResolutionMode): FirEnumEntry { @@ -302,15 +305,13 @@ abstract class FirAbstractContractResolveTransformerDispatcher( private val FirContractDescriptionOwner.valueParameters: List get() = when (this) { - is FirSimpleFunction -> valueParameters - is FirPropertyAccessor -> valueParameters + is FirFunction -> valueParameters else -> error() } private val FirContractDescriptionOwner.body: FirBlock get() = when (this) { - is FirSimpleFunction -> body!! - is FirPropertyAccessor -> body!! + is FirFunction -> body!! else -> error() } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/contracts/FirContractResolveTransformerAdapter.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/contracts/FirContractResolveTransformerAdapter.kt index ef3b3637ee3..f492525a511 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/contracts/FirContractResolveTransformerAdapter.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/contracts/FirContractResolveTransformerAdapter.kt @@ -9,6 +9,7 @@ import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.declarations.FirClassLikeDeclaration import org.jetbrains.kotlin.fir.declarations.FirFile +import org.jetbrains.kotlin.fir.declarations.FirFunction import org.jetbrains.kotlin.fir.declarations.FirResolvePhase import org.jetbrains.kotlin.fir.resolve.ResolutionMode import org.jetbrains.kotlin.fir.resolve.ScopeSession @@ -52,3 +53,13 @@ fun F.runContractResolveForLocalClass( return this.transformSingle(transformer, ResolutionMode.ContextIndependent) } + +fun F.runContractResolveForFunction( + session: FirSession, + scopeSession: ScopeSession, + outerBodyResolveContext: BodyResolveContext, +): F { + val transformer = FirContractResolveTransformer(session, scopeSession, outerBodyResolveContext) + + return this.transformSingle(transformer, ResolutionMode.ContextIndependent) +} diff --git a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/FirAnonymousFunction.kt b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/FirAnonymousFunction.kt index 6fb1a518377..de3cd4d75a7 100644 --- a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/FirAnonymousFunction.kt +++ b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/FirAnonymousFunction.kt @@ -10,6 +10,7 @@ import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.FirLabel import org.jetbrains.kotlin.fir.FirModuleData +import org.jetbrains.kotlin.fir.contracts.FirContractDescription import org.jetbrains.kotlin.fir.expressions.FirAnnotation import org.jetbrains.kotlin.fir.expressions.FirBlock import org.jetbrains.kotlin.fir.references.FirControlFlowGraphReference @@ -24,7 +25,7 @@ import org.jetbrains.kotlin.fir.visitors.* * DO NOT MODIFY IT MANUALLY */ -abstract class FirAnonymousFunction : FirFunction(), FirTypeParametersOwner { +abstract class FirAnonymousFunction : FirFunction(), FirTypeParametersOwner, FirContractDescriptionOwner { abstract override val source: KtSourceElement? abstract override val resolvePhase: FirResolvePhase abstract override val annotations: List @@ -41,6 +42,7 @@ abstract class FirAnonymousFunction : FirFunction(), FirTypeParametersOwner { abstract override val controlFlowGraphReference: FirControlFlowGraphReference? abstract override val valueParameters: List abstract override val body: FirBlock? + abstract override val contractDescription: FirContractDescription abstract override val symbol: FirAnonymousFunctionSymbol abstract val label: FirLabel? abstract val invocationKind: EventOccurrencesRange? @@ -76,6 +78,8 @@ abstract class FirAnonymousFunction : FirFunction(), FirTypeParametersOwner { abstract override fun replaceBody(newBody: FirBlock?) + abstract override fun replaceContractDescription(newContractDescription: FirContractDescription) + abstract fun replaceInvocationKind(newInvocationKind: EventOccurrencesRange?) abstract fun replaceInlineStatus(newInlineStatus: InlineStatus) @@ -94,5 +98,7 @@ abstract class FirAnonymousFunction : FirFunction(), FirTypeParametersOwner { abstract override fun transformBody(transformer: FirTransformer, data: D): FirAnonymousFunction + abstract override fun transformContractDescription(transformer: FirTransformer, data: D): FirAnonymousFunction + abstract override fun transformTypeParameters(transformer: FirTransformer, data: D): FirAnonymousFunction } diff --git a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/FirConstructor.kt b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/FirConstructor.kt index 73774171877..8e8a6e84684 100644 --- a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/FirConstructor.kt +++ b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/FirConstructor.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.fir.declarations import org.jetbrains.kotlin.KtSourceElement import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.FirModuleData +import org.jetbrains.kotlin.fir.contracts.FirContractDescription import org.jetbrains.kotlin.fir.expressions.FirAnnotation import org.jetbrains.kotlin.fir.expressions.FirBlock import org.jetbrains.kotlin.fir.expressions.FirDelegatedConstructorCall @@ -23,7 +24,7 @@ import org.jetbrains.kotlin.fir.visitors.* * DO NOT MODIFY IT MANUALLY */ -abstract class FirConstructor : FirFunction(), FirTypeParameterRefsOwner { +abstract class FirConstructor : FirFunction(), FirTypeParameterRefsOwner, FirContractDescriptionOwner { abstract override val source: KtSourceElement? abstract override val resolvePhase: FirResolvePhase abstract override val moduleData: FirModuleData @@ -39,6 +40,7 @@ abstract class FirConstructor : FirFunction(), FirTypeParameterRefsOwner { abstract override val contextReceivers: List abstract override val controlFlowGraphReference: FirControlFlowGraphReference? abstract override val valueParameters: List + abstract override val contractDescription: FirContractDescription abstract override val annotations: List abstract override val symbol: FirConstructorSymbol abstract val delegatedConstructor: FirDelegatedConstructorCall? @@ -67,6 +69,8 @@ abstract class FirConstructor : FirFunction(), FirTypeParameterRefsOwner { abstract override fun replaceValueParameters(newValueParameters: List) + abstract override fun replaceContractDescription(newContractDescription: FirContractDescription) + abstract override fun replaceAnnotations(newAnnotations: List) abstract fun replaceDelegatedConstructor(newDelegatedConstructor: FirDelegatedConstructorCall?) @@ -83,6 +87,8 @@ abstract class FirConstructor : FirFunction(), FirTypeParameterRefsOwner { abstract override fun transformValueParameters(transformer: FirTransformer, data: D): FirConstructor + abstract override fun transformContractDescription(transformer: FirTransformer, data: D): FirConstructor + abstract override fun transformAnnotations(transformer: FirTransformer, data: D): FirConstructor abstract fun transformDelegatedConstructor(transformer: FirTransformer, data: D): FirConstructor diff --git a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/builder/FirAbstractConstructorBuilder.kt b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/builder/FirAbstractConstructorBuilder.kt index 41b045dfba4..4d778991292 100644 --- a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/builder/FirAbstractConstructorBuilder.kt +++ b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/builder/FirAbstractConstructorBuilder.kt @@ -10,6 +10,7 @@ package org.jetbrains.kotlin.fir.declarations.builder import org.jetbrains.kotlin.KtSourceElement import org.jetbrains.kotlin.fir.FirModuleData import org.jetbrains.kotlin.fir.builder.FirBuilderDsl +import org.jetbrains.kotlin.fir.contracts.FirContractDescription import org.jetbrains.kotlin.fir.declarations.DeprecationsProvider import org.jetbrains.kotlin.fir.declarations.FirConstructor import org.jetbrains.kotlin.fir.declarations.FirContextReceiver @@ -55,6 +56,7 @@ interface FirAbstractConstructorBuilder : FirFunctionBuilder { abstract val typeParameters: MutableList abstract var receiverParameter: FirReceiverParameter? abstract var controlFlowGraphReference: FirControlFlowGraphReference? + abstract var contractDescription: FirContractDescription abstract var symbol: FirConstructorSymbol abstract var delegatedConstructor: FirDelegatedConstructorCall? override fun build(): FirConstructor diff --git a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/builder/FirAnonymousFunctionBuilder.kt b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/builder/FirAnonymousFunctionBuilder.kt index a2c6040955f..454eead126f 100644 --- a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/builder/FirAnonymousFunctionBuilder.kt +++ b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/builder/FirAnonymousFunctionBuilder.kt @@ -15,6 +15,8 @@ import org.jetbrains.kotlin.fir.FirModuleData import org.jetbrains.kotlin.fir.builder.FirAnnotationContainerBuilder import org.jetbrains.kotlin.fir.builder.FirBuilderDsl import org.jetbrains.kotlin.fir.builder.toMutableOrEmpty +import org.jetbrains.kotlin.fir.contracts.FirContractDescription +import org.jetbrains.kotlin.fir.contracts.impl.FirEmptyContractDescription import org.jetbrains.kotlin.fir.declarations.DeprecationsProvider import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction import org.jetbrains.kotlin.fir.declarations.FirContextReceiver @@ -61,6 +63,7 @@ class FirAnonymousFunctionBuilder : FirFunctionBuilder, FirAnnotationContainerBu var controlFlowGraphReference: FirControlFlowGraphReference? = null override val valueParameters: MutableList = mutableListOf() override var body: FirBlock? = null + var contractDescription: FirContractDescription = FirEmptyContractDescription lateinit var symbol: FirAnonymousFunctionSymbol var label: FirLabel? = null var invocationKind: EventOccurrencesRange? = null @@ -86,6 +89,7 @@ class FirAnonymousFunctionBuilder : FirFunctionBuilder, FirAnnotationContainerBu controlFlowGraphReference, valueParameters, body, + contractDescription, symbol, label, invocationKind, @@ -141,6 +145,7 @@ inline fun buildAnonymousFunctionCopy(original: FirAnonymousFunction, init: FirA copyBuilder.controlFlowGraphReference = original.controlFlowGraphReference copyBuilder.valueParameters.addAll(original.valueParameters) copyBuilder.body = original.body + copyBuilder.contractDescription = original.contractDescription copyBuilder.symbol = original.symbol copyBuilder.label = original.label copyBuilder.invocationKind = original.invocationKind diff --git a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/builder/FirConstructorBuilder.kt b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/builder/FirConstructorBuilder.kt index 3c582773199..2d40588fd0d 100644 --- a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/builder/FirConstructorBuilder.kt +++ b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/builder/FirConstructorBuilder.kt @@ -13,6 +13,8 @@ import org.jetbrains.kotlin.fir.FirModuleData import org.jetbrains.kotlin.fir.builder.FirAnnotationContainerBuilder import org.jetbrains.kotlin.fir.builder.FirBuilderDsl import org.jetbrains.kotlin.fir.builder.toMutableOrEmpty +import org.jetbrains.kotlin.fir.contracts.FirContractDescription +import org.jetbrains.kotlin.fir.contracts.impl.FirEmptyContractDescription import org.jetbrains.kotlin.fir.declarations.DeprecationsProvider import org.jetbrains.kotlin.fir.declarations.FirConstructor import org.jetbrains.kotlin.fir.declarations.FirContextReceiver @@ -57,6 +59,7 @@ open class FirConstructorBuilder : FirAbstractConstructorBuilder, FirAnnotationC override var dispatchReceiverType: ConeSimpleKotlinType? = null override val contextReceivers: MutableList = mutableListOf() override val valueParameters: MutableList = mutableListOf() + override var contractDescription: FirContractDescription = FirEmptyContractDescription override val annotations: MutableList = mutableListOf() override lateinit var symbol: FirConstructorSymbol override var delegatedConstructor: FirDelegatedConstructorCall? = null @@ -78,6 +81,7 @@ open class FirConstructorBuilder : FirAbstractConstructorBuilder, FirAnnotationC dispatchReceiverType, contextReceivers.toMutableOrEmpty(), valueParameters, + contractDescription, annotations.toMutableOrEmpty(), symbol, delegatedConstructor, @@ -122,6 +126,7 @@ inline fun buildConstructorCopy(original: FirConstructor, init: FirConstructorBu copyBuilder.dispatchReceiverType = original.dispatchReceiverType copyBuilder.contextReceivers.addAll(original.contextReceivers) copyBuilder.valueParameters.addAll(original.valueParameters) + copyBuilder.contractDescription = original.contractDescription copyBuilder.annotations.addAll(original.annotations) copyBuilder.symbol = original.symbol copyBuilder.delegatedConstructor = original.delegatedConstructor diff --git a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/builder/FirPrimaryConstructorBuilder.kt b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/builder/FirPrimaryConstructorBuilder.kt index 64dbaaef07c..d2b2dee64cb 100644 --- a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/builder/FirPrimaryConstructorBuilder.kt +++ b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/builder/FirPrimaryConstructorBuilder.kt @@ -14,6 +14,8 @@ import org.jetbrains.kotlin.fir.FirModuleData import org.jetbrains.kotlin.fir.builder.FirAnnotationContainerBuilder import org.jetbrains.kotlin.fir.builder.FirBuilderDsl import org.jetbrains.kotlin.fir.builder.toMutableOrEmpty +import org.jetbrains.kotlin.fir.contracts.FirContractDescription +import org.jetbrains.kotlin.fir.contracts.impl.FirEmptyContractDescription import org.jetbrains.kotlin.fir.declarations.DeprecationsProvider import org.jetbrains.kotlin.fir.declarations.FirConstructor import org.jetbrains.kotlin.fir.declarations.FirContextReceiver @@ -58,6 +60,7 @@ class FirPrimaryConstructorBuilder : FirAbstractConstructorBuilder, FirAnnotatio override var dispatchReceiverType: ConeSimpleKotlinType? = null override val contextReceivers: MutableList = mutableListOf() override val valueParameters: MutableList = mutableListOf() + override var contractDescription: FirContractDescription = FirEmptyContractDescription override val annotations: MutableList = mutableListOf() override lateinit var symbol: FirConstructorSymbol override var delegatedConstructor: FirDelegatedConstructorCall? = null @@ -80,6 +83,7 @@ class FirPrimaryConstructorBuilder : FirAbstractConstructorBuilder, FirAnnotatio dispatchReceiverType, contextReceivers.toMutableOrEmpty(), valueParameters, + contractDescription, annotations.toMutableOrEmpty(), symbol, delegatedConstructor, diff --git a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/impl/FirAnonymousFunctionImpl.kt b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/impl/FirAnonymousFunctionImpl.kt index a719fd10f53..2ddec862632 100644 --- a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/impl/FirAnonymousFunctionImpl.kt +++ b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/impl/FirAnonymousFunctionImpl.kt @@ -11,6 +11,7 @@ import org.jetbrains.kotlin.KtSourceElement import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange import org.jetbrains.kotlin.fir.FirLabel import org.jetbrains.kotlin.fir.FirModuleData +import org.jetbrains.kotlin.fir.contracts.FirContractDescription import org.jetbrains.kotlin.fir.declarations.DeprecationsProvider import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction import org.jetbrains.kotlin.fir.declarations.FirContextReceiver @@ -54,6 +55,7 @@ internal class FirAnonymousFunctionImpl( override var controlFlowGraphReference: FirControlFlowGraphReference?, override val valueParameters: MutableList, override var body: FirBlock?, + override var contractDescription: FirContractDescription, override val symbol: FirAnonymousFunctionSymbol, override var label: FirLabel?, override var invocationKind: EventOccurrencesRange?, @@ -80,6 +82,7 @@ internal class FirAnonymousFunctionImpl( controlFlowGraphReference?.accept(visitor, data) valueParameters.forEach { it.accept(visitor, data) } body?.accept(visitor, data) + contractDescription.accept(visitor, data) label?.accept(visitor, data) typeParameters.forEach { it.accept(visitor, data) } typeRef.accept(visitor, data) @@ -94,6 +97,7 @@ internal class FirAnonymousFunctionImpl( controlFlowGraphReference = controlFlowGraphReference?.transform(transformer, data) transformValueParameters(transformer, data) transformBody(transformer, data) + transformContractDescription(transformer, data) label = label?.transform(transformer, data) transformTypeParameters(transformer, data) typeRef = typeRef.transform(transformer, data) @@ -130,6 +134,11 @@ internal class FirAnonymousFunctionImpl( return this } + override fun transformContractDescription(transformer: FirTransformer, data: D): FirAnonymousFunctionImpl { + contractDescription = contractDescription.transform(transformer, data) + return this + } + override fun transformTypeParameters(transformer: FirTransformer, data: D): FirAnonymousFunctionImpl { typeParameters.transformInplace(transformer, data) return this @@ -176,6 +185,10 @@ internal class FirAnonymousFunctionImpl( body = newBody } + override fun replaceContractDescription(newContractDescription: FirContractDescription) { + contractDescription = newContractDescription + } + override fun replaceInvocationKind(newInvocationKind: EventOccurrencesRange?) { invocationKind = newInvocationKind } diff --git a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/impl/FirConstructorImpl.kt b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/impl/FirConstructorImpl.kt index 16c8d711ede..c9230e52ff3 100644 --- a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/impl/FirConstructorImpl.kt +++ b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/impl/FirConstructorImpl.kt @@ -9,6 +9,7 @@ package org.jetbrains.kotlin.fir.declarations.impl import org.jetbrains.kotlin.KtSourceElement import org.jetbrains.kotlin.fir.FirModuleData +import org.jetbrains.kotlin.fir.contracts.FirContractDescription import org.jetbrains.kotlin.fir.declarations.DeprecationsProvider import org.jetbrains.kotlin.fir.declarations.FirConstructor import org.jetbrains.kotlin.fir.declarations.FirContextReceiver @@ -52,6 +53,7 @@ internal class FirConstructorImpl( override val dispatchReceiverType: ConeSimpleKotlinType?, override var contextReceivers: MutableOrEmptyList, override val valueParameters: MutableList, + override var contractDescription: FirContractDescription, override var annotations: MutableOrEmptyList, override val symbol: FirConstructorSymbol, override var delegatedConstructor: FirDelegatedConstructorCall?, @@ -72,6 +74,7 @@ internal class FirConstructorImpl( contextReceivers.forEach { it.accept(visitor, data) } controlFlowGraphReference?.accept(visitor, data) valueParameters.forEach { it.accept(visitor, data) } + contractDescription.accept(visitor, data) annotations.forEach { it.accept(visitor, data) } delegatedConstructor?.accept(visitor, data) body?.accept(visitor, data) @@ -85,6 +88,7 @@ internal class FirConstructorImpl( contextReceivers.transformInplace(transformer, data) controlFlowGraphReference = controlFlowGraphReference?.transform(transformer, data) transformValueParameters(transformer, data) + transformContractDescription(transformer, data) transformAnnotations(transformer, data) transformDelegatedConstructor(transformer, data) transformBody(transformer, data) @@ -116,6 +120,11 @@ internal class FirConstructorImpl( return this } + override fun transformContractDescription(transformer: FirTransformer, data: D): FirConstructorImpl { + contractDescription = contractDescription.transform(transformer, data) + return this + } + override fun transformAnnotations(transformer: FirTransformer, data: D): FirConstructorImpl { annotations.transformInplace(transformer, data) return this @@ -164,6 +173,10 @@ internal class FirConstructorImpl( valueParameters.addAll(newValueParameters) } + override fun replaceContractDescription(newContractDescription: FirContractDescription) { + contractDescription = newContractDescription + } + override fun replaceAnnotations(newAnnotations: List) { annotations = newAnnotations.toMutableOrEmpty() } diff --git a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/impl/FirPrimaryConstructor.kt b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/impl/FirPrimaryConstructor.kt index ae36ffaa262..06968f2c4cf 100644 --- a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/impl/FirPrimaryConstructor.kt +++ b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/impl/FirPrimaryConstructor.kt @@ -10,6 +10,7 @@ package org.jetbrains.kotlin.fir.declarations.impl import org.jetbrains.kotlin.KtSourceElement import org.jetbrains.kotlin.fir.FirImplementationDetail import org.jetbrains.kotlin.fir.FirModuleData +import org.jetbrains.kotlin.fir.contracts.FirContractDescription import org.jetbrains.kotlin.fir.declarations.DeprecationsProvider import org.jetbrains.kotlin.fir.declarations.FirConstructor import org.jetbrains.kotlin.fir.declarations.FirContextReceiver @@ -53,6 +54,7 @@ class FirPrimaryConstructor @FirImplementationDetail constructor( override val dispatchReceiverType: ConeSimpleKotlinType?, override var contextReceivers: MutableOrEmptyList, override val valueParameters: MutableList, + override var contractDescription: FirContractDescription, override var annotations: MutableOrEmptyList, override val symbol: FirConstructorSymbol, override var delegatedConstructor: FirDelegatedConstructorCall?, @@ -73,6 +75,7 @@ class FirPrimaryConstructor @FirImplementationDetail constructor( contextReceivers.forEach { it.accept(visitor, data) } controlFlowGraphReference?.accept(visitor, data) valueParameters.forEach { it.accept(visitor, data) } + contractDescription.accept(visitor, data) annotations.forEach { it.accept(visitor, data) } delegatedConstructor?.accept(visitor, data) body?.accept(visitor, data) @@ -86,6 +89,7 @@ class FirPrimaryConstructor @FirImplementationDetail constructor( contextReceivers.transformInplace(transformer, data) controlFlowGraphReference = controlFlowGraphReference?.transform(transformer, data) transformValueParameters(transformer, data) + transformContractDescription(transformer, data) transformAnnotations(transformer, data) transformDelegatedConstructor(transformer, data) transformBody(transformer, data) @@ -117,6 +121,11 @@ class FirPrimaryConstructor @FirImplementationDetail constructor( return this } + override fun transformContractDescription(transformer: FirTransformer, data: D): FirPrimaryConstructor { + contractDescription = contractDescription.transform(transformer, data) + return this + } + override fun transformAnnotations(transformer: FirTransformer, data: D): FirPrimaryConstructor { annotations.transformInplace(transformer, data) return this @@ -165,6 +174,10 @@ class FirPrimaryConstructor @FirImplementationDetail constructor( valueParameters.addAll(newValueParameters) } + override fun replaceContractDescription(newContractDescription: FirContractDescription) { + contractDescription = newContractDescription + } + override fun replaceAnnotations(newAnnotations: List) { annotations = newAnnotations.toMutableOrEmpty() } diff --git a/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/BuilderConfigurator.kt b/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/BuilderConfigurator.kt index 84b89b41f05..5177e648830 100644 --- a/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/BuilderConfigurator.kt +++ b/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/BuilderConfigurator.kt @@ -89,6 +89,8 @@ object BuilderConfigurator : AbstractBuilderConfigurator(FirTree parents += abstractConstructorBuilder defaultNull("delegatedConstructor") defaultNull("body") + default("contractDescription", "FirEmptyContractDescription") + useTypes(emptyContractDescriptionType) } } @@ -266,7 +268,9 @@ object BuilderConfigurator : AbstractBuilderConfigurator(FirTree parents += functionBuilder defaultNull("invocationKind", "label", "body", "controlFlowGraphReference") default("inlineStatus", "InlineStatus.Unknown") + default("contractDescription", "FirEmptyContractDescription") withCopy() + useTypes(emptyContractDescriptionType) } builder(propertyAccessor) { diff --git a/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/FirTreeBuilder.kt b/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/FirTreeBuilder.kt index 847bb75ac27..da4d7f5646f 100644 --- a/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/FirTreeBuilder.kt +++ b/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/FirTreeBuilder.kt @@ -60,12 +60,12 @@ object FirTreeBuilder : AbstractFirTreeBuilder() { val simpleFunction by element(Declaration, function, contractDescriptionOwner, typeParametersOwner) val propertyAccessor by element(Declaration, function, contractDescriptionOwner, typeParametersOwner) val backingField by element(Declaration, variable, typeParametersOwner, statement) - val constructor by element(Declaration, function, typeParameterRefsOwner) + val constructor by element(Declaration, function, typeParameterRefsOwner, contractDescriptionOwner) val file by element(Declaration, declaration) val script by element(Declaration, declaration) val packageDirective by element(Other) - val anonymousFunction by element(Declaration, function, typeParametersOwner) + val anonymousFunction by element(Declaration, function, typeParametersOwner, contractDescriptionOwner) val anonymousFunctionExpression by element(Expression, expression) val anonymousObject by element(Declaration, klass, controlFlowGraphOwner) diff --git a/compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors/contractCallSites.1.3.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors/contractCallSites.1.3.fir.kt index ab5c3af1a97..549702b06f5 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors/contractCallSites.1.3.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors/contractCallSites.1.3.fir.kt @@ -10,19 +10,19 @@ open class Class { fun member(x: Boolean) { contract { returns() implies (x) } } - + inline fun inlineMember(x: Boolean) { contract { returns() implies (x) } } - + abstract fun abstractMember(x: Boolean) { - contract { returns() implies (x) } + contract { returns() implies (x) } } - + open fun openMemeber(x: Boolean) { - contract { returns() implies (x) } + contract { returns() implies (x) } } - + suspend fun suspendMember(x: Boolean) { contract { returns() implies (x) } } @@ -44,43 +44,43 @@ suspend fun suspendTopLevel(x: Boolean) { // Top-level operator operator fun Boolean.plus(x: Boolean): Boolean { - contract { returns() implies (x) } + contract { returns() implies (x) } return x } val topLevelLambda: (Boolean) -> Unit = { x: Boolean -> - contract { returns() implies (x) } + contract { returns() implies (x) } } val topLevelAnonymousFunction = fun (x: Boolean) { - contract { returns() implies (x) } + contract { returns() implies (x) } } var topLevelPropertyAccessors: Int? = 42 get() { - contract { returns() implies (field != null) } + contract { returns() implies (field != null) } return 42 } set(value) { - contract { returns() implies (field != null) } + contract { returns() implies (field != null) } } // ============= Local ===================== fun test() { fun localDeclaration(x: Boolean) { - contract { returns() implies (x) } + contract { returns() implies (x) } } suspend fun suspendlocalDeclaration(x: Boolean) { - contract { returns() implies (x) } + contract { returns() implies (x) } } val localAnonymousFunction = fun (x: Boolean) { - contract { returns() implies (x) } + contract { returns() implies (x) } } val localLambda: (Boolean) -> Unit = { x: Boolean -> - contract { returns() implies (x) } + contract { returns() implies (x) } } } diff --git a/compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors/contractCallSites.1.3.kt b/compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors/contractCallSites.1.3.kt index 7ea692b13b6..95425280592 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors/contractCallSites.1.3.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors/contractCallSites.1.3.kt @@ -10,19 +10,19 @@ open class Class { fun member(x: Boolean) { contract { returns() implies (x) } } - + inline fun inlineMember(x: Boolean) { contract { returns() implies (x) } } - + abstract fun abstractMember(x: Boolean) { contract { returns() implies (x) } } - + open fun openMemeber(x: Boolean) { contract { returns() implies (x) } } - + suspend fun suspendMember(x: Boolean) { contract { returns() implies (x) } } @@ -83,4 +83,4 @@ fun test() { val localLambda: (Boolean) -> Unit = { x: Boolean -> contract { returns() implies (x) } } -} \ No newline at end of file +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors/contractCallSites.1.4.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors/contractCallSites.1.4.fir.kt index 0af91dc3760..8b4e49087a7 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors/contractCallSites.1.4.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors/contractCallSites.1.4.fir.kt @@ -7,6 +7,11 @@ import kotlin.contracts.* // ============= Class ===================== open class Class { + constructor(f: () -> Unit = {}) { + contract { callsInPlace(f, InvocationKind.EXACTLY_ONCE) } + f() + } + fun member(x: Boolean) { contract { returns() implies (x) } } @@ -16,11 +21,11 @@ open class Class { } abstract fun abstractMember(x: Boolean) { - contract { returns() implies (x) } + contract { returns() implies (x) } } open fun openMemeber(x: Boolean) { - contract { returns() implies (x) } + contract { returns() implies (x) } } suspend fun suspendMember(x: Boolean) { @@ -30,17 +35,17 @@ open class Class { open class Inheritor : Class() { override fun openMemeber(x: Boolean) { - contract { returns() implies (x) } + contract { returns() implies (x) } } final override fun abstractMember(x: Boolean) { - contract { returns() implies (x) } + contract { returns() implies (x) } } } interface Interface { fun implicitlyOpenMember(x: Boolean) { - contract { returns() implies (x) } + contract { returns() implies (x) } } } @@ -59,43 +64,43 @@ suspend fun suspendTopLevel(x: Boolean) { // Top-level operator operator fun Boolean.plus(x: Boolean): Boolean { - contract { returns() implies (x) } + contract { returns() implies (x) } return x } val topLevelLambda: (Boolean) -> Unit = { x: Boolean -> - contract { returns() implies (x) } + contract { returns() implies (x) } } val topLevelAnonymousFunction = fun (x: Boolean) { - contract { returns() implies (x) } + contract { returns() implies (x) } } var topLevelPropertyAccessors: Int? = 42 get() { - contract { returns() implies (field != null) } + contract { returns() implies (field != null) } return 42 } set(value) { - contract { returns() implies (field != null) } + contract { returns() implies (field != null) } } // ============= Local ===================== fun test() { fun localDeclaration(x: Boolean) { - contract { returns() implies (x) } + contract { returns() implies (x) } } suspend fun suspendlocalDeclaration(x: Boolean) { - contract { returns() implies (x) } + contract { returns() implies (x) } } val localAnonymousFunction = fun (x: Boolean) { - contract { returns() implies (x) } + contract { returns() implies (x) } } val localLambda: (Boolean) -> Unit = { x: Boolean -> - contract { returns() implies (x) } + contract { returns() implies (x) } } } diff --git a/compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors/contractCallSites.1.4.kt b/compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors/contractCallSites.1.4.kt index 389f357fec4..1ed58c63986 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors/contractCallSites.1.4.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors/contractCallSites.1.4.kt @@ -7,6 +7,11 @@ import kotlin.contracts.* // ============= Class ===================== open class Class { + constructor(f: () -> Unit = {}) { + contract { callsInPlace(f, InvocationKind.EXACTLY_ONCE) } + f() + } + fun member(x: Boolean) { contract { returns() implies (x) } } diff --git a/compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors/contractCallSites.1.4.txt b/compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors/contractCallSites.1.4.txt index 9cc74749b47..7d46ef5fcae 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors/contractCallSites.1.4.txt +++ b/compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors/contractCallSites.1.4.txt @@ -16,7 +16,7 @@ public fun topLevel(/*0*/ x: kotlin.Boolean): kotlin.Unit public operator fun kotlin.Boolean.plus(/*0*/ x: kotlin.Boolean): kotlin.Boolean public open class Class { - public constructor Class() + public constructor Class(/*0*/ f: () -> kotlin.Unit = ...) public abstract fun abstractMember(/*0*/ x: kotlin.Boolean): kotlin.Unit public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int diff --git a/compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors/notFirstStatement.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors/notFirstStatement.fir.kt index d39866c1dbb..e7c90013406 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors/notFirstStatement.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors/notFirstStatement.fir.kt @@ -7,30 +7,30 @@ import kotlin.contracts.* fun foo(y: Boolean) { val x: Int = 42 - contract { + contract { returns() implies y } } inline fun case1(block: () -> Unit) { val contracts = listOf( - contract { + contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) - }, contract { + }, contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } ) block() } -inline fun case_2(block: () -> Unit) = contract { +inline fun case_2(block: () -> Unit) = contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } fun case_3(block: () -> Unit) { class Class { fun innerFun(block2: () -> Unit) { - contract { + contract { callsInPlace(block2, InvocationKind.EXACTLY_ONCE) } block2() @@ -41,7 +41,7 @@ fun case_3(block: () -> Unit) { inline fun case_4(number: Int?): Boolean { val cond = number != null - contract { + contract { returns(false) implies (cond) } as ContractBuilder return number == null @@ -49,8 +49,8 @@ inline fun case_4(number: Int?): Boolean { inline fun case_5(cond: Boolean): Boolean { run { - contract { - returns(true) implies (cond) + contract { + returns(true) implies (cond) } } return true @@ -59,7 +59,7 @@ inline fun case_5(cond: Boolean): Boolean { inline fun case_6(cond: Boolean): Boolean { run { val x = 10 - contract { + contract { returns(true) implies (cond) } } @@ -68,8 +68,8 @@ inline fun case_6(cond: Boolean): Boolean { fun case_7(cond: Boolean): Boolean { fun innerFun() { - contract { - returns(true) implies (cond) + contract { + returns(true) implies (cond) } } return true diff --git a/compiler/testData/diagnostics/testsWithStdLib/contracts/newSyntax/propertyAccessorsContractDescription.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/contracts/newSyntax/propertyAccessorsContractDescription.fir.kt index c8d16730220..1e38820ed68 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/contracts/newSyntax/propertyAccessorsContractDescription.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/contracts/newSyntax/propertyAccessorsContractDescription.fir.kt @@ -4,13 +4,13 @@ import kotlin.contracts.* class A { var x: Int = 0 get() = f(x) - set(value) contract [returns() implies (value != null)] { + set(value) contract [returns() implies (value != null)] { field = value + 1 } var y: Double = 0.0 get() = g(y) - set(value) contract [returns() implies (value != null)] { + set(value) contract [returns() implies (value != null)] { field = value * 2 } diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractBuilder/common/neg/1.fir.kt b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractBuilder/common/neg/1.fir.kt index 98333dbe157..bfa5ca7f910 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractBuilder/common/neg/1.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractBuilder/common/neg/1.fir.kt @@ -6,14 +6,14 @@ import kotlin.contracts.* // TESTCASE NUMBER: 1 inline fun case_1(block: () -> Unit) { val value_1 = 1 - contract { } + contract { } return block() } // TESTCASE NUMBER: 2 inline fun case_2(block: () -> Unit) { 10 - 1 - contract { + contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return block() @@ -22,7 +22,7 @@ inline fun case_2(block: () -> Unit) { // TESTCASE NUMBER: 3 inline fun case_3(block: () -> Unit) { throw Exception() - contract { + contract { callsInPlace(block, InvocationKind.UNKNOWN) } return block() @@ -34,7 +34,7 @@ inline fun case_3(block: () -> Unit) { */ inline fun case_4(block: () -> Unit) { .0009 - return contract { + return contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } } @@ -45,7 +45,7 @@ inline fun case_4(block: () -> Unit) { */ fun case_5(value_1: Int?) { println("!") - contract { + contract { returns(true) implies (value_1 != null) } as ContractBuilder } @@ -56,7 +56,7 @@ fun case_5(value_1: Int?) { */ fun case_6(value_1: Int?) { 100 + 10 - throw Exception(contract { + throw Exception(contract { returns(true) implies (value_1 != null) }.toString()) } @@ -69,7 +69,7 @@ fun case_7(value_1: Int?) { for (i in 0..10) { println(i) } - return contract { + return contract { returns(true) implies (value_1 != null) } } @@ -80,7 +80,7 @@ fun case_7(value_1: Int?) { */ fun case_8(value_1: Int?) { val f = 10 - 20 - val g = contract { + val g = contract { returns(true) implies (value_1 != null) } } @@ -91,7 +91,7 @@ fun case_8(value_1: Int?) { */ fun case_9(number: Int?): Boolean { val value_1 = number != null - contract { + contract { returns(false) implies (value_1) } as ContractBuilder return number == null diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractBuilder/common/neg/14.fir.kt b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractBuilder/common/neg/14.fir.kt index 78c15773f00..1f5630592cc 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractBuilder/common/neg/14.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractBuilder/common/neg/14.fir.kt @@ -5,17 +5,17 @@ import kotlin.contracts.* // TESTCASE NUMBER: 1 inline fun case_1(block: () -> Unit) { - return contract { + return contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } } // TESTCASE NUMBER: 2 -fun case_2() = contract { } +fun case_2() = contract { } // TESTCASE NUMBER: 3 inline fun case_3(block: () -> Unit) { - val value_1 = contract { + val value_1 = contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } block() @@ -39,14 +39,14 @@ inline fun case_5(block: () -> Unit) { // TESTCASE NUMBER: 6 inline fun case_6(block: () -> Unit) { - throw Exception(contract { + throw Exception(contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }.toString()) } // TESTCASE NUMBER: 7 inline fun case_7(block: () -> Unit) { - funWithAnyArg(contract { + funWithAnyArg(contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }) } diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractBuilder/common/neg/16.fir.kt b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractBuilder/common/neg/16.fir.kt deleted file mode 100644 index 515a4ce2bf2..00000000000 --- a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractBuilder/common/neg/16.fir.kt +++ /dev/null @@ -1,47 +0,0 @@ -// !DIAGNOSTICS: -UNUSED_VARIABLE -// !OPT_IN: kotlin.contracts.ExperimentalContracts - -import kotlin.contracts.* - -// TESTCASE NUMBER: 1 -fun case_1(value_1: Int?) { - println("!") - contract { - returns(true) implies (value_1 != null) - } as ContractBuilder -} - -// TESTCASE NUMBER: 2 -fun case_2(value_1: Int?) { - 100 + 10 - throw Exception(contract { - returns(true) implies (value_1 != null) - }.toString()) -} - -// TESTCASE NUMBER: 3 -fun case_3(value_1: Int?) { - for (i in 0..10) { - println(i) - } - return contract { - returns(true) implies (value_1 != null) - } -} - -// TESTCASE NUMBER: 4 -fun case_4(value_1: Int?) { - val f = 10 - 20 - val g = contract { - returns(true) implies (value_1 != null) - } -} - -// TESTCASE NUMBER: 5 -fun case_5(number: Int?): Boolean { - val value_1 = number != null - contract { - returns(false) implies (value_1) - } as ContractBuilder - return number == null -} diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractBuilder/common/neg/16.kt b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractBuilder/common/neg/16.kt index eddec041646..192865a8671 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractBuilder/common/neg/16.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractBuilder/common/neg/16.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !DIAGNOSTICS: -UNUSED_VARIABLE // !OPT_IN: kotlin.contracts.ExperimentalContracts diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractBuilder/common/neg/5.fir.kt b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractBuilder/common/neg/5.fir.kt index 33a6eec7309..abe919315e1 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractBuilder/common/neg/5.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractBuilder/common/neg/5.fir.kt @@ -38,7 +38,7 @@ fun case_5(): Boolean? { // TESTCASE NUMBER: 6 fun case_6(value_1: Boolean): Boolean? { - contract { returns(null) implies contract { returns(null) implies (!value_1) } } + contract { returns(null) implies contract { returns(null) implies (!value_1) } } return null } diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractBuilder/common/neg/6.fir.kt b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractBuilder/common/neg/6.fir.kt index de00926233e..ffec00815ab 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractBuilder/common/neg/6.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractBuilder/common/neg/6.fir.kt @@ -36,7 +36,7 @@ class case_2(value_5: Boolean, val value_1: Boolean) { init { fun case_2_1(): Boolean { - contract { returns(false) implies (value_5) } + contract { returns(false) implies (value_5) } return !(value_5) } } diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractBuilder/common/neg/7.fir.kt b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractBuilder/common/neg/7.fir.kt index e265b84274e..485c111e4d2 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractBuilder/common/neg/7.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractBuilder/common/neg/7.fir.kt @@ -6,7 +6,7 @@ import kotlin.contracts.* // TESTCASE NUMBER: 1 fun T?.case_1() { fun K?.case_1_1(): Boolean { - contract { returns(true) implies (this@case_1 != null) } + contract { returns(true) implies (this@case_1 != null) } return this@case_1 != null } } diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractBuilder/common/neg/9.fir.kt b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractBuilder/common/neg/9.fir.kt index 364a1015d95..0b9eff94ad0 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractBuilder/common/neg/9.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractBuilder/common/neg/9.fir.kt @@ -5,7 +5,7 @@ import kotlin.contracts.* // TESTCASE NUMBER: 1 inline fun case_1(block: () -> Unit) = { - contract { - callsInPlace(block, InvocationKind.EXACTLY_ONCE) + contract { + callsInPlace(block, InvocationKind.EXACTLY_ONCE) } } diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractFunction/neg/2.fir.kt b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractFunction/neg/2.fir.kt index 8ce0f5ae64d..4e145c98140 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractFunction/neg/2.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractFunction/neg/2.fir.kt @@ -6,7 +6,7 @@ import kotlin.contracts.* // TESTCASE NUMBER: 1 fun case_1() { val fun_1 = fun(block: () -> Unit) { - contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return block() } @@ -17,7 +17,7 @@ fun case_1() { // TESTCASE NUMBER: 2 fun case_2() { val lambda_1 = { block: () -> Unit -> - contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } block() } @@ -43,7 +43,7 @@ class case_4 : ClassLevel3() { fun T.case_4_3_wrap() { fun case_4_3_contract() { - contract { returns() implies (this@case_4_3_wrap is ClassLevel1) } + contract { returns() implies (this@case_4_3_wrap is ClassLevel1) } if (this@case_4_3_wrap !is ClassLevel1) throw Exception() } case_4_3_contract() diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractFunction/neg/4.fir.kt b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractFunction/neg/4.fir.kt index aa4ecffb2e0..3d9a2f07bb8 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractFunction/neg/4.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/declarations/contractFunction/neg/4.fir.kt @@ -6,7 +6,7 @@ import kotlin.contracts.* // TESTCASE NUMBER: 1 val Boolean.case_1: () -> Unit get() { - contract { + contract { returns() implies (this@case_1) } return {} @@ -15,7 +15,7 @@ val Boolean.case_1: () -> Unit // TESTCASE NUMBER: 2 val (() -> Unit).case_2: () -> Unit get() { - contract { + contract { callsInPlace(this@case_2, InvocationKind.EXACTLY_ONCE) } return {} @@ -27,7 +27,7 @@ var Boolean.case_3: () -> Unit return {} } set(value) { - contract { + contract { callsInPlace(value, InvocationKind.EXACTLY_ONCE) } } @@ -38,7 +38,7 @@ var (() -> Unit).case_4: () -> Unit return {} } set(value) { - contract { + contract { callsInPlace(this@case_4, InvocationKind.EXACTLY_ONCE) } } @@ -46,7 +46,7 @@ var (() -> Unit).case_4: () -> Unit // TESTCASE NUMBER: 5 val Boolean.case_5: () -> Unit get() { - contract { + contract { returns() implies (this@case_5) } diff --git a/core/compiler.common/src/org/jetbrains/kotlin/name/StandardClassIds.kt b/core/compiler.common/src/org/jetbrains/kotlin/name/StandardClassIds.kt index 5247349e931..41084f9f2b5 100644 --- a/core/compiler.common/src/org/jetbrains/kotlin/name/StandardClassIds.kt +++ b/core/compiler.common/src/org/jetbrains/kotlin/name/StandardClassIds.kt @@ -17,6 +17,7 @@ object StandardClassIds { val BASE_INTERNAL_IR_PACKAGE = BASE_INTERNAL_PACKAGE.child(Name.identifier("ir")) val BASE_COROUTINES_PACKAGE = BASE_KOTLIN_PACKAGE.child(Name.identifier("coroutines")) val BASE_ENUMS_PACKAGE = BASE_KOTLIN_PACKAGE.child(Name.identifier("enums")) + val BASE_CONTRACTS_PACKAGE = BASE_KOTLIN_PACKAGE.child(Name.identifier("contracts")) val builtInsPackages = setOf( BASE_KOTLIN_PACKAGE, @@ -216,6 +217,8 @@ object StandardClassIds { val clone = "clone".callableId(Cloneable) val not = "not".callableId(Boolean) + + val contract = "contract".callableId(BASE_CONTRACTS_PACKAGE) } object Java {