From 749fa049ca97d11648cda03096352fb3b8e0a732 Mon Sep 17 00:00:00 2001 From: Dmitry Savvinov Date: Mon, 20 Aug 2018 13:43:37 +0300 Subject: [PATCH] Refactor diagnostic reporting of effects parsing - Introduce ContractParsingDiagnosticsCollector It allows us to make sure that we return 'null'-contract iff there's some error. Note that it differs from previous behavior in two major points: a) if contract was unparsed, but no error were reported for some reason, then generic error will be reported (previously we were swallowing incorrect contract silently) b) if there were some errors during parsing, return only 'null' (previously, we could return some contract. It was unusable due to errors in code, but we want to be extra-safe here) - Introduce ContractCallContext to not pass a bunch of random arguments around - Make message of Error.CONTRACT_NOT_ALLOWED more flexible --- .../ContractParsingDiagnosticsCollector.kt | 63 +++++++++++++ .../parsing/ContractParsingServices.kt | 88 +++++++++++++------ .../contracts/parsing/PsiConditionParser.kt | 26 +++--- .../contracts/parsing/PsiConstantParser.kt | 9 +- .../parsing/PsiContractParserDispatcher.kt | 49 ++++------- .../contracts/parsing/PsiEffectParser.kt | 7 +- .../parsing/effects/PsiCallsEffectParser.kt | 15 ++-- .../effects/PsiConditionalEffectParser.kt | 14 ++- .../parsing/effects/PsiReturnsEffectParser.kt | 15 ++-- .../jetbrains/kotlin/diagnostics/Errors.java | 2 +- .../rendering/DefaultErrorMessages.java | 2 +- 11 files changed, 187 insertions(+), 103 deletions(-) create mode 100644 compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/ContractParsingDiagnosticsCollector.kt 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();