[FIR] Properly track problems in contract description during effect extraction
This commit is contained in:
committed by
Space Team
parent
79d378f2bd
commit
5a92eb2c67
+82
-6
@@ -5,7 +5,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.fir.analysis.checkers.declaration
|
||||
|
||||
import org.jetbrains.kotlin.KtFakeSourceElementKind
|
||||
import org.jetbrains.kotlin.KtRealSourceElementKind
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||
@@ -13,15 +12,16 @@ import org.jetbrains.kotlin.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.contracts.FirResolvedContractDescription
|
||||
import org.jetbrains.kotlin.fir.contracts.description.*
|
||||
import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction
|
||||
import org.jetbrains.kotlin.fir.declarations.FirContractDescriptionOwner
|
||||
import org.jetbrains.kotlin.fir.declarations.FirFunction
|
||||
import org.jetbrains.kotlin.fir.declarations.FirPropertyAccessor
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.*
|
||||
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
|
||||
|
||||
object FirContractChecker : FirFunctionChecker() {
|
||||
// TODO: The message should vary. Migrate this to [ConeEffectExtractor] when creating fine-grained errors.
|
||||
private const val UNEXPECTED_CONSTRUCTION = "unexpected construction in contract description"
|
||||
private val EMPTY_CONTRACT_MESSAGE = "Empty contract block is not allowed"
|
||||
|
||||
override fun check(declaration: FirFunction, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
if (declaration !is FirContractDescriptionOwner) return
|
||||
@@ -29,6 +29,9 @@ object FirContractChecker : FirFunctionChecker() {
|
||||
|
||||
checkUnresolvedEffects(contractDescription, context, reporter)
|
||||
checkContractNotAllowed(declaration, contractDescription, context, reporter)
|
||||
if (contractDescription.effects.isEmpty() && contractDescription.unresolvedEffects.isEmpty()) {
|
||||
reporter.reportOn(contractDescription.source, FirErrors.ERROR_IN_CONTRACT_DESCRIPTION, EMPTY_CONTRACT_MESSAGE, context)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkUnresolvedEffects(
|
||||
@@ -38,12 +41,11 @@ object FirContractChecker : FirFunctionChecker() {
|
||||
) {
|
||||
// Any statements that [ConeEffectExtractor] cannot extract effects will be in `unresolvedEffects`.
|
||||
for (unresolvedEffect in contractDescription.unresolvedEffects) {
|
||||
val statement = unresolvedEffect.statement
|
||||
if (statement.source == null || statement.source!!.kind is KtFakeSourceElementKind) continue
|
||||
val diagnostic = unresolvedEffect.effect.accept(DiagnosticExtractor, null) ?: continue
|
||||
|
||||
// TODO: report on fine-grained locations, e.g., ... implies unresolved => report on unresolved, not the entire statement.
|
||||
// but, sometimes, it's just reported on `contract`...
|
||||
reporter.reportOn(statement.source, FirErrors.ERROR_IN_CONTRACT_DESCRIPTION, UNEXPECTED_CONSTRUCTION, context)
|
||||
reporter.reportOn(unresolvedEffect.source, FirErrors.ERROR_IN_CONTRACT_DESCRIPTION, diagnostic.reason, context)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,4 +66,78 @@ object FirContractChecker : FirFunctionChecker() {
|
||||
else if (declaration.symbol.callableId.isLocal || declaration.visibility == Visibilities.Local) contractNotAllowed("Contracts are not allowed for local functions")
|
||||
}
|
||||
|
||||
private object DiagnosticExtractor : ConeContractDescriptionVisitor<ConeDiagnostic?, Nothing?>() {
|
||||
override fun visitContractDescriptionElement(
|
||||
contractDescriptionElement: ConeContractDescriptionElement,
|
||||
data: Nothing?
|
||||
): ConeDiagnostic? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun visitConditionalEffectDeclaration(
|
||||
conditionalEffect: ConeConditionalEffectDeclaration,
|
||||
data: Nothing?
|
||||
): ConeDiagnostic? {
|
||||
return conditionalEffect.effect.accept(this, null) ?: conditionalEffect.condition.accept(this, null)
|
||||
}
|
||||
|
||||
override fun visitReturnsEffectDeclaration(returnsEffect: ConeReturnsEffectDeclaration, data: Nothing?): ConeDiagnostic? {
|
||||
return returnsEffect.value.accept(this, null)
|
||||
}
|
||||
|
||||
override fun visitCallsEffectDeclaration(callsEffect: ConeCallsEffectDeclaration, data: Nothing?): ConeDiagnostic? {
|
||||
return callsEffect.valueParameterReference.accept(this, data)
|
||||
}
|
||||
|
||||
override fun visitErroneousCallsEffectDeclaration(
|
||||
callsEffect: ConeErroneousCallsEffectDeclaration,
|
||||
data: Nothing?
|
||||
): ConeDiagnostic {
|
||||
return callsEffect.diagnostic
|
||||
}
|
||||
|
||||
override fun visitLogicalBinaryOperationContractExpression(
|
||||
binaryLogicExpression: ConeBinaryLogicExpression,
|
||||
data: Nothing?
|
||||
): ConeDiagnostic? {
|
||||
return binaryLogicExpression.left.accept(this, null) ?: binaryLogicExpression.right.accept(this, null)
|
||||
}
|
||||
|
||||
override fun visitLogicalNot(logicalNot: ConeLogicalNot, data: Nothing?): ConeDiagnostic? {
|
||||
return logicalNot.arg.accept(this, null)
|
||||
}
|
||||
|
||||
override fun visitIsInstancePredicate(isInstancePredicate: ConeIsInstancePredicate, data: Nothing?): ConeDiagnostic? {
|
||||
return isInstancePredicate.arg.accept(this, data)
|
||||
}
|
||||
|
||||
override fun visitErroneousIsInstancePredicate(
|
||||
isInstancePredicate: ConeErroneousIsInstancePredicate,
|
||||
data: Nothing?
|
||||
): ConeDiagnostic {
|
||||
return isInstancePredicate.diagnostic
|
||||
}
|
||||
|
||||
override fun visitIsNullPredicate(isNullPredicate: ConeIsNullPredicate, data: Nothing?): ConeDiagnostic? {
|
||||
return isNullPredicate.arg.accept(this, data)
|
||||
}
|
||||
|
||||
override fun visitErroneousConstantReference(
|
||||
erroneousConstantReference: ConeErroneousConstantReference,
|
||||
data: Nothing?
|
||||
): ConeDiagnostic {
|
||||
return erroneousConstantReference.diagnostic
|
||||
}
|
||||
|
||||
override fun visitErroneousValueParameterReference(
|
||||
valueParameterReference: ConeErroneousValueParameterReference,
|
||||
data: Nothing?
|
||||
): ConeDiagnostic {
|
||||
return valueParameterReference.diagnostic
|
||||
}
|
||||
|
||||
override fun visitErroneousElement(element: ConeErroneousContractElement, data: Nothing?): ConeDiagnostic {
|
||||
return element.diagnostic
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user