diff --git a/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/ContractParsingDiagnosticsCollector.kt b/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/ContractParsingDiagnosticsCollector.kt new file mode 100644 index 00000000000..3f1fb1d5a92 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/ContractParsingDiagnosticsCollector.kt @@ -0,0 +1,63 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. 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.contracts.parsing + +import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.config.LanguageVersionSettings +import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl +import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.psi.KtCallExpression +import org.jetbrains.kotlin.psi.KtElement +import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.resolve.BindingTrace + +interface ContractParsingDiagnosticsCollector { + fun unsupportedFeature(languageVersionSettings: LanguageVersionSettings) + fun contractNotAllowed(message: String) + fun badDescription(message: String, reportOn: KtElement) + + fun flushDiagnostics(parsingFailed: Boolean) + fun hasErrors(): Boolean + + + object EMPTY : ContractParsingDiagnosticsCollector { + override fun contractNotAllowed(message: String) {} + override fun badDescription(message: String, reportOn: KtElement) {} + override fun unsupportedFeature(languageVersionSettings: LanguageVersionSettings) { } + + override fun flushDiagnostics(parsingFailed: Boolean) {} + + override fun hasErrors(): Boolean = false + } +} + +class TraceBasedCollector(private val bindingTrace: BindingTrace, private val mainCall: KtExpression) : ContractParsingDiagnosticsCollector { + private val diagnostics: MutableList = mutableListOf() + + override fun contractNotAllowed(message: String) { + diagnostics += Errors.CONTRACT_NOT_ALLOWED.on((mainCall as? KtCallExpression)?.calleeExpression ?: mainCall, message) + } + + override fun badDescription(message: String, reportOn: KtElement) { + diagnostics += Errors.ERROR_IN_CONTRACT_DESCRIPTION.on(reportOn, message) + } + + override fun unsupportedFeature(languageVersionSettings: LanguageVersionSettings) { + diagnostics += Errors.UNSUPPORTED_FEATURE.on(mainCall, LanguageFeature.AllowContractsForCustomFunctions to languageVersionSettings) + } + + override fun flushDiagnostics(parsingFailed: Boolean) { + if (parsingFailed && diagnostics.isEmpty()) { + diagnostics += Errors.ERROR_IN_CONTRACT_DESCRIPTION.on(mainCall, "Error in contract description") + } + + diagnostics.forEach { bindingTrace.report(it) } + } + + + override fun hasErrors(): Boolean = diagnostics.isNotEmpty() +} \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/ContractParsingServices.kt b/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/ContractParsingServices.kt index e4cf8a85d7d..fc48da263e6 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/ContractParsingServices.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/ContractParsingServices.kt @@ -19,10 +19,11 @@ package org.jetbrains.kotlin.contracts.parsing import org.jetbrains.kotlin.config.AnalysisFlag import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageVersionSettings +import org.jetbrains.kotlin.contracts.description.ContractDescription import org.jetbrains.kotlin.contracts.description.ContractProviderKey +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.diagnostics.Errors -import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.isContractDescriptionCallPsiCheck import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingTrace @@ -32,41 +33,70 @@ import org.jetbrains.kotlin.resolve.scopes.LexicalScopeKind class ContractParsingServices(val languageVersionSettings: LanguageVersionSettings) { fun checkContractAndRecordIfPresent(expression: KtExpression, trace: BindingTrace, scope: LexicalScope, isFirstStatement: Boolean) { - val ownerDescriptor = scope.ownerDescriptor - if (!expression.isContractDescriptionCallPsiCheck() || ownerDescriptor !is FunctionDescriptor) return - val contractProvider = ownerDescriptor.getUserData(ContractProviderKey) + if (!expression.isContractDescriptionCallPsiCheck()) return // fastpath + val collector = TraceBasedCollector(trace, expression) + val callContext = ContractCallContext(expression, isFirstStatement, scope, trace.bindingContext) + val parsedContract = doCheckContract(collector, callContext) + + collector.flushDiagnostics(parsingFailed = parsedContract == null) + + val contractProviderIfAny = (scope.ownerDescriptor as? FunctionDescriptor)?.getUserData(ContractProviderKey) + + if (collector.hasErrors()) + contractProviderIfAny?.setContractDescription(null) + else + contractProviderIfAny?.setContractDescription(parsedContract) + } + + private fun doCheckContract(collector: ContractParsingDiagnosticsCollector, callContext: ContractCallContext): ContractDescription? { + val expression = callContext.contractCallExpression + val bindingContext = callContext.bindingContext + + if (!expression.isContractDescriptionCallPreciseCheck(bindingContext)) return null + + checkFeatureEnabled(collector) + checkContractAllowedHere(collector, callContext) + + return if (!collector.hasErrors()) + PsiContractParserDispatcher(collector, callContext).parseContract() + else + null + } + + private fun checkFeatureEnabled(collector: ContractParsingDiagnosticsCollector) { val isFeatureTurnedOn = languageVersionSettings.supportsFeature(LanguageFeature.AllowContractsForCustomFunctions) || // This condition is here for technical purposes of compiling 1.2-runtime with contracts languageVersionSettings.getFlag(AnalysisFlag.Flags.allowKotlinPackage) - - val contractDescriptor = when { - !expression.isContractDescriptionCallPreciseCheck(trace.bindingContext) -> null - - !isFeatureTurnedOn -> { - trace.report( - Errors.UNSUPPORTED_FEATURE.on( - expression, - LanguageFeature.AllowContractsForCustomFunctions to languageVersionSettings - ) - ) - null - } - - !isContractAllowedHere(scope) || !isFirstStatement -> { - trace.report(Errors.CONTRACT_NOT_ALLOWED.on(expression)) - null - } - - else -> PsiContractParserDispatcher(trace).parseContract(expression, ownerDescriptor) + if (!isFeatureTurnedOn) { + collector.unsupportedFeature(languageVersionSettings) } - - contractProvider?.setContractDescription(contractDescriptor) } - private fun isContractAllowedHere(scope: LexicalScope): Boolean = - scope.kind == LexicalScopeKind.CODE_BLOCK && (scope.parent as? LexicalScope)?.kind == LexicalScopeKind.FUNCTION_INNER_SCOPE + private fun checkContractAllowedHere(collector: ContractParsingDiagnosticsCollector, callContext: ContractCallContext) { + val functionDescriptor = callContext.ownerDescriptor as? FunctionDescriptor + val scope = callContext.scope + + if (!callContext.isFirstStatement) + collector.contractNotAllowed("Contract should be the first statement") + + if (functionDescriptor == null) + collector.contractNotAllowed("Contracts are allowed only for functions") + + if (scope.kind != LexicalScopeKind.CODE_BLOCK || (scope.parent as? LexicalScope)?.kind != LexicalScopeKind.FUNCTION_INNER_SCOPE) + collector.contractNotAllowed("Contracts are allowed only for top-level functions") + } private fun KtExpression.isContractDescriptionCallPreciseCheck(context: BindingContext): Boolean = getResolvedCall(context)?.resultingDescriptor?.isContractCallDescriptor() ?: false } + +class ContractCallContext( + val contractCallExpression: KtExpression, + val isFirstStatement: Boolean, + val scope: LexicalScope, + val bindingContext: BindingContext +) { + val ownerDescriptor: DeclarationDescriptor = scope.ownerDescriptor + val functionDescriptor: FunctionDescriptor = ownerDescriptor as FunctionDescriptor +} \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/PsiConditionParser.kt b/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/PsiConditionParser.kt index bc638d2447d..93c0a0ff49c 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/PsiConditionParser.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/PsiConditionParser.kt @@ -27,18 +27,22 @@ import org.jetbrains.kotlin.resolve.BindingTrace import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver -internal class PsiConditionParser(val trace: BindingTrace, val dispatcher: PsiContractParserDispatcher) : - KtVisitor() { +internal class PsiConditionParser( + private val collector: ContractParsingDiagnosticsCollector, + private val callContext: ContractCallContext, + private val dispatcher: PsiContractParserDispatcher +) : KtVisitor() { + override fun visitIsExpression(expression: KtIsExpression, data: Unit): BooleanExpression? { val variable = dispatcher.parseVariable(expression.leftHandSide) ?: return null val typeReference = expression.typeReference ?: return null - val type = trace[BindingContext.TYPE, typeReference] ?: return null + val type = callContext.bindingContext[BindingContext.TYPE, typeReference] ?: return null return IsInstancePredicate(variable, type, expression.isNegated) } override fun visitKtElement(element: KtElement, data: Unit): BooleanExpression? { - val resolvedCall = element.getResolvedCall(trace.bindingContext) + val resolvedCall = element.getResolvedCall(callContext.bindingContext) val descriptor = resolvedCall?.resultingDescriptor ?: return null // boolean variable @@ -63,12 +67,12 @@ internal class PsiConditionParser(val trace: BindingTrace, val dispatcher: PsiCo return IsNullPredicate(left, isNegated) } - trace.report(Errors.ERROR_IN_CONTRACT_DESCRIPTION.on(element, "only equality comparisons with 'null' allowed")) + collector.badDescription("only equality comparisons with 'null' allowed", element) return null } else -> { - trace.report(Errors.ERROR_IN_CONTRACT_DESCRIPTION.on(element, "unsupported construction")) + collector.badDescription("unsupported construction", element) return null } } @@ -80,7 +84,7 @@ internal class PsiConditionParser(val trace: BindingTrace, val dispatcher: PsiCo } override fun visitCallExpression(expression: KtCallExpression, data: Unit?): BooleanExpression? { - trace.report(Errors.ERROR_IN_CONTRACT_DESCRIPTION.on(expression, "call-expressions are not supported yet")) + collector.badDescription("call-expressions are not supported yet", expression) return null } @@ -102,11 +106,9 @@ internal class PsiConditionParser(val trace: BindingTrace, val dispatcher: PsiCo if (expression.operationToken != KtTokens.EXCL) return super.visitUnaryExpression(expression, data) val arg = expression.baseExpression?.accept(this, data) ?: return null if (arg !is ContractDescriptionValue) { - trace.report( - Errors.ERROR_IN_CONTRACT_DESCRIPTION.on( - expression.baseExpression!!, - "negations in contract description can be applied only to variables/values" - ) + collector.badDescription( + "negations in contract description can be applied only to variables/values", + expression.baseExpression!! ) } return LogicalNot(arg) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/PsiConstantParser.kt b/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/PsiConstantParser.kt index 48f3bd2b048..a9329b4ede9 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/PsiConstantParser.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/PsiConstantParser.kt @@ -22,17 +22,18 @@ import org.jetbrains.kotlin.psi.KtConstantExpression import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtVisitor import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.BindingTrace import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant import org.jetbrains.kotlin.types.KotlinType -internal class PsiConstantParser(val trace: BindingTrace) : KtVisitor() { +internal class PsiConstantParser(private val callContext: ContractCallContext) : KtVisitor() { override fun visitKtElement(element: KtElement, data: Unit?): ConstantReference? = null override fun visitConstantExpression(expression: KtConstantExpression, data: Unit?): ConstantReference? { - val type: KotlinType = trace.getType(expression) ?: return null + val type: KotlinType = callContext.bindingContext.getType(expression) ?: return null + + val compileTimeConstant: CompileTimeConstant<*> = callContext.bindingContext.get(BindingContext.COMPILE_TIME_VALUE, expression) + ?: return null - val compileTimeConstant: CompileTimeConstant<*> = trace.get(BindingContext.COMPILE_TIME_VALUE, expression) ?: return null val value: Any? = compileTimeConstant.getValue(type) return when (value) { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/PsiContractParserDispatcher.kt b/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/PsiContractParserDispatcher.kt index 61364e70c31..9dea11baa6a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/PsiContractParserDispatcher.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/PsiContractParserDispatcher.kt @@ -31,32 +31,30 @@ import org.jetbrains.kotlin.contracts.parsing.ContractsDslNames.RETURNS_NOT_NULL import org.jetbrains.kotlin.contracts.parsing.effects.PsiCallsEffectParser import org.jetbrains.kotlin.contracts.parsing.effects.PsiConditionalEffectParser import org.jetbrains.kotlin.contracts.parsing.effects.PsiReturnsEffectParser -import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.ParameterDescriptor import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor -import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtLambdaExpression -import org.jetbrains.kotlin.resolve.BindingTrace import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.callUtil.getType -internal class PsiContractParserDispatcher(val trace: BindingTrace) { - private val conditionParser = PsiConditionParser(trace, this) - private val constantParser = PsiConstantParser(trace) +internal class PsiContractParserDispatcher( + private val collector: ContractParsingDiagnosticsCollector, + private val callContext: ContractCallContext +) { + private val conditionParser = PsiConditionParser(collector, callContext, this) + private val constantParser = PsiConstantParser(callContext) private val effectsParsers: Map = mapOf( - RETURNS_EFFECT to PsiReturnsEffectParser(trace, this), - RETURNS_NOT_NULL_EFFECT to PsiReturnsEffectParser(trace, this), - CALLS_IN_PLACE_EFFECT to PsiCallsEffectParser(trace, this), - CONDITIONAL_EFFECT to PsiConditionalEffectParser(trace, this) + RETURNS_EFFECT to PsiReturnsEffectParser(collector, callContext, this), + RETURNS_NOT_NULL_EFFECT to PsiReturnsEffectParser(collector, callContext, this), + CALLS_IN_PLACE_EFFECT to PsiCallsEffectParser(collector, callContext, this), + CONDITIONAL_EFFECT to PsiConditionalEffectParser(collector, callContext, this) ) - fun parseContract(expression: KtExpression?, ownerDescriptor: FunctionDescriptor): ContractDescription? { - if (expression == null) return null - + fun parseContract(): ContractDescription? { // Must be non-null because of checks in 'checkContractAndRecordIfPresent', but actually is not, see EA-124365 - val resolvedCall = expression.getResolvedCall(trace.bindingContext) ?: return null + val resolvedCall = callContext.contractCallExpression.getResolvedCall(callContext.bindingContext) ?: return null val lambda = resolvedCall.firstArgumentAsExpressionOrNull() as? KtLambdaExpression ?: return null @@ -64,17 +62,17 @@ internal class PsiContractParserDispatcher(val trace: BindingTrace) { if (effects.isEmpty()) return null - return ContractDescription(effects, ownerDescriptor) + return ContractDescription(effects, callContext.functionDescriptor) } fun parseCondition(expression: KtExpression?): BooleanExpression? = expression?.accept(conditionParser, Unit) fun parseEffect(expression: KtExpression?): EffectDeclaration? { if (expression == null) return null - val returnType = expression.getType(trace.bindingContext) ?: return null + val returnType = expression.getType(callContext.bindingContext) ?: return null val parser = effectsParsers[returnType.constructor.declarationDescriptor?.name] if (parser == null) { - trace.report(Errors.ERROR_IN_CONTRACT_DESCRIPTION.on(expression, "Unrecognized effect")) + collector.badDescription("unrecognized effect", expression) return null } return parser.tryParseEffect(expression) @@ -87,24 +85,15 @@ internal class PsiContractParserDispatcher(val trace: BindingTrace) { fun parseVariable(expression: KtExpression?): VariableReference? { if (expression == null) return null - val descriptor = expression.getResolvedCall(trace.bindingContext)?.resultingDescriptor ?: return null + val descriptor = expression.getResolvedCall(callContext.bindingContext)?.resultingDescriptor ?: return null if (descriptor !is ParameterDescriptor) { - trace.report( - Errors.ERROR_IN_CONTRACT_DESCRIPTION.on( - expression, - "only references to parameters are allowed in contract description" - ) - ) + collector.badDescription("only references to parameters are allowed in contract description", expression) return null } if (descriptor is ReceiverParameterDescriptor && descriptor.type.constructor.declarationDescriptor?.isFromContractDsl() == true) { - trace.report( - Errors.ERROR_IN_CONTRACT_DESCRIPTION.on( - expression, - "only references to parameters are allowed. Did you miss label on ?" - ) - ) + collector.badDescription("only references to parameters are allowed. Did you miss label on ?", expression) + return null } return if (KotlinBuiltIns.isBoolean(descriptor.type)) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/PsiEffectParser.kt b/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/PsiEffectParser.kt index 3076a55b920..db21d125124 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/PsiEffectParser.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/PsiEffectParser.kt @@ -24,5 +24,8 @@ internal interface PsiEffectParser { fun tryParseEffect(expression: KtExpression): EffectDeclaration? } -internal abstract class AbstractPsiEffectParser(val trace: BindingTrace, val contractParserDispatcher: PsiContractParserDispatcher) : - PsiEffectParser +internal abstract class AbstractPsiEffectParser( + val collector: ContractParsingDiagnosticsCollector, + val callContext: ContractCallContext, + val contractParserDispatcher: PsiContractParserDispatcher +) : PsiEffectParser diff --git a/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/effects/PsiCallsEffectParser.kt b/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/effects/PsiCallsEffectParser.kt index a9c93d23757..97d2c19d615 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/effects/PsiCallsEffectParser.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/effects/PsiCallsEffectParser.kt @@ -21,7 +21,7 @@ import org.jetbrains.kotlin.contracts.description.EffectDeclaration import org.jetbrains.kotlin.contracts.description.InvocationKind import org.jetbrains.kotlin.contracts.parsing.* import org.jetbrains.kotlin.psi.KtExpression -import org.jetbrains.kotlin.resolve.BindingTrace +import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument @@ -29,12 +29,13 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.descriptorUtil.parents internal class PsiCallsEffectParser( - trace: BindingTrace, + collector: ContractParsingDiagnosticsCollector, + callContext: ContractCallContext, contractParserDispatcher: PsiContractParserDispatcher -) : AbstractPsiEffectParser(trace, contractParserDispatcher) { +) : AbstractPsiEffectParser(collector, callContext, contractParserDispatcher) { override fun tryParseEffect(expression: KtExpression): EffectDeclaration? { - val resolvedCall = expression.getResolvedCall(trace.bindingContext) ?: return null + val resolvedCall = expression.getResolvedCall(callContext.bindingContext) ?: return null val descriptor = resolvedCall.resultingDescriptor if (!descriptor.isCallsInPlaceEffectDescriptor()) return null @@ -45,15 +46,15 @@ internal class PsiCallsEffectParser( val kind = when (kindArgument) { is DefaultValueArgument -> InvocationKind.UNKNOWN - is ExpressionValueArgument -> kindArgument.valueArgument?.getArgumentExpression()?.toInvocationKind(trace) ?: return null + is ExpressionValueArgument -> kindArgument.valueArgument?.getArgumentExpression()?.toInvocationKind(callContext.bindingContext) ?: return null else -> return null } return CallsEffectDeclaration(lambda, kind) } - private fun KtExpression.toInvocationKind(trace: BindingTrace): InvocationKind? { - val descriptor = this.getResolvedCall(trace.bindingContext)?.resultingDescriptor ?: return null + private fun KtExpression.toInvocationKind(bindingContext: BindingContext): InvocationKind? { + val descriptor = this.getResolvedCall(bindingContext)?.resultingDescriptor ?: return null if (!descriptor.parents.first().isInvocationKindEnum()) return null return when (descriptor.fqNameSafe.shortName()) { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/effects/PsiConditionalEffectParser.kt b/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/effects/PsiConditionalEffectParser.kt index bb4a8f00e20..467426abc00 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/effects/PsiConditionalEffectParser.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/effects/PsiConditionalEffectParser.kt @@ -18,23 +18,19 @@ package org.jetbrains.kotlin.contracts.parsing.effects import org.jetbrains.kotlin.contracts.description.ConditionalEffectDeclaration import org.jetbrains.kotlin.contracts.description.EffectDeclaration -import org.jetbrains.kotlin.contracts.parsing.AbstractPsiEffectParser -import org.jetbrains.kotlin.contracts.parsing.PsiContractParserDispatcher -import org.jetbrains.kotlin.contracts.parsing.firstArgumentAsExpressionOrNull -import org.jetbrains.kotlin.contracts.parsing.isImpliesCallDescriptor +import org.jetbrains.kotlin.contracts.parsing.* import org.jetbrains.kotlin.psi.KtExpression -import org.jetbrains.kotlin.resolve.BindingTrace import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver import org.jetbrains.kotlin.utils.addToStdlib.safeAs internal class PsiConditionalEffectParser( - trace: BindingTrace, + collector: ContractParsingDiagnosticsCollector, + callContext: ContractCallContext, dispatcher: PsiContractParserDispatcher -) : AbstractPsiEffectParser(trace, dispatcher) { +) : AbstractPsiEffectParser(collector, callContext, dispatcher) { override fun tryParseEffect(expression: KtExpression): EffectDeclaration? { - val resolvedCall = expression.getResolvedCall(trace.bindingContext) ?: return null - + val resolvedCall = expression.getResolvedCall(callContext.bindingContext) ?: return null if (!resolvedCall.resultingDescriptor.isImpliesCallDescriptor()) return null val effect = contractParserDispatcher.parseEffect(resolvedCall.dispatchReceiver.safeAs()?.expression) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/effects/PsiReturnsEffectParser.kt b/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/effects/PsiReturnsEffectParser.kt index bcc9de261a7..30ff759c51b 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/effects/PsiReturnsEffectParser.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/effects/PsiReturnsEffectParser.kt @@ -26,11 +26,12 @@ import org.jetbrains.kotlin.resolve.BindingTrace import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall internal class PsiReturnsEffectParser( - trace: BindingTrace, + collector: ContractParsingDiagnosticsCollector, + callContext: ContractCallContext, contractParserDispatcher: PsiContractParserDispatcher -) : AbstractPsiEffectParser(trace, contractParserDispatcher) { +) : AbstractPsiEffectParser(collector, callContext, contractParserDispatcher) { override fun tryParseEffect(expression: KtExpression): EffectDeclaration? { - val resolvedCall = expression.getResolvedCall(trace.bindingContext) ?: return null + val resolvedCall = expression.getResolvedCall(callContext.bindingContext) ?: return null val descriptor = resolvedCall.resultingDescriptor if (descriptor.isReturnsNotNullDescriptor()) @@ -45,11 +46,9 @@ internal class PsiReturnsEffectParser( // Note that we distinguish absence of an argument and unparsed argument val constant = contractParserDispatcher.parseConstant(argumentExpression) if (constant == null) { - trace.report( - Errors.ERROR_IN_CONTRACT_DESCRIPTION.on( - argumentExpression, - "only true/false/null constants in Returns-effect are currently supported" - ) + collector.badDescription( + "only true/false/null constants in Returns-effect are currently supported", + argumentExpression ) return null } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 64a818f9211..66ef73cf14d 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -1049,7 +1049,7 @@ public interface Errors { // Function contracts DiagnosticFactory1 ERROR_IN_CONTRACT_DESCRIPTION = DiagnosticFactory1.create(ERROR); - DiagnosticFactory0 CONTRACT_NOT_ALLOWED = DiagnosticFactory0.create(ERROR); + DiagnosticFactory1 CONTRACT_NOT_ALLOWED = DiagnosticFactory1.create(ERROR); // Error sets ImmutableSet> UNRESOLVED_REFERENCE_DIAGNOSTICS = ImmutableSet.of( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index d5b245031db..568ac8bb72f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -941,7 +941,7 @@ public class DefaultErrorMessages { MAP.put(PLUGIN_INFO, "{0}", (d, c) -> d.getText()); MAP.put(ERROR_IN_CONTRACT_DESCRIPTION, "Error in contract description: {0}", TO_STRING); - MAP.put(CONTRACT_NOT_ALLOWED, "Contract is not allowed here"); + MAP.put(CONTRACT_NOT_ALLOWED, "{0}", TO_STRING); MAP.setImmutable();