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
This commit is contained in:
Dmitry Savvinov
2018-08-20 13:43:37 +03:00
committed by Ilya Gorbunov
parent a6783176ea
commit 749fa049ca
11 changed files with 187 additions and 103 deletions
@@ -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<Diagnostic> = 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()
}
@@ -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
}
@@ -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<BooleanExpression?, Unit>() {
internal class PsiConditionParser(
private val collector: ContractParsingDiagnosticsCollector,
private val callContext: ContractCallContext,
private val dispatcher: PsiContractParserDispatcher
) : KtVisitor<BooleanExpression?, Unit>() {
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)
@@ -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<ConstantReference?, Unit>() {
internal class PsiConstantParser(private val callContext: ContractCallContext) : KtVisitor<ConstantReference?, Unit>() {
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) {
@@ -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<Name, PsiEffectParser> = 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 <this>?"
)
)
collector.badDescription("only references to parameters are allowed. Did you miss label on <this>?", expression)
return null
}
return if (KotlinBuiltIns.isBoolean(descriptor.type))
@@ -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
@@ -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()) {
@@ -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<ExpressionReceiver>()?.expression)
@@ -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
}
@@ -1049,7 +1049,7 @@ public interface Errors {
// Function contracts
DiagnosticFactory1<KtElement, String> ERROR_IN_CONTRACT_DESCRIPTION = DiagnosticFactory1.create(ERROR);
DiagnosticFactory0<KtElement> CONTRACT_NOT_ALLOWED = DiagnosticFactory0.create(ERROR);
DiagnosticFactory1<KtElement, String> CONTRACT_NOT_ALLOWED = DiagnosticFactory1.create(ERROR);
// Error sets
ImmutableSet<? extends DiagnosticFactory<?>> UNRESOLVED_REFERENCE_DIAGNOSTICS = ImmutableSet.of(
@@ -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();