Skip to content

Commit

Permalink
feat: show the custom error messages for parameter bounds
Browse files Browse the repository at this point in the history
  • Loading branch information
lars-reimann committed Nov 29, 2024
1 parent f352826 commit 750eb76
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@ import {
} from '../../../generated/ast.js';
import { AstUtils, ValidationAcceptor } from 'langium';
import { getArguments, getParameters, Parameter } from '../../../helpers/nodeProperties.js';
import { Constant, EvaluatedNode, FloatConstant, IntConstant } from '../../../partialEvaluation/model.js';
import {
Constant,
EvaluatedNode,
FloatConstant,
IntConstant,
StringConstant,
} from '../../../partialEvaluation/model.js';

export const CODE_PARAMETER_BOUND_INVALID_VALUE = 'parameter-bound/invalid-value';
export const CODE_PARAMETER_BOUND_PARAMETER = 'parameter-bound/parameter';
Expand All @@ -34,10 +40,13 @@ export const callArgumentMustRespectParameterBounds = (services: SafeDsServices)

for (const bound of Parameter.getBounds(parameter)) {
const rightOperand = partialEvaluator.evaluate(bound.rightOperand, substitutions);
const errorMessage = checkBound(parameter.name, value, bound.operator, rightOperand);
const messageEvaluatedNode = partialEvaluator.evaluate(bound.message, substitutions);
const customMessage =
messageEvaluatedNode instanceof StringConstant ? messageEvaluatedNode.value : undefined;

if (errorMessage) {
accept('error', errorMessage, {
const error = checkBound(parameter.name, value, bound.operator, rightOperand, customMessage);
if (error) {
accept('error', error, {
node: argument,
property: 'value',
code: CODE_PARAMETER_BOUND_INVALID_VALUE,
Expand Down Expand Up @@ -74,12 +83,18 @@ export const parameterDefaultValueMustRespectParameterBounds = (services: SafeDs
}
}

const substitutions = new Map([[node, value]]);

// Error if the default value violates some bounds
for (const bound of Parameter.getBounds(node)) {
const rightOperand = partialEvaluator.evaluate(bound.rightOperand);
const errorMessage = checkBound(node.name, value, bound.operator, rightOperand);
if (errorMessage) {
accept('error', errorMessage, {
const messageEvaluatedNode = partialEvaluator.evaluate(bound.message, substitutions);
const customMessage =
messageEvaluatedNode instanceof StringConstant ? messageEvaluatedNode.value : undefined;

const error = checkBound(node.name, value, bound.operator, rightOperand, customMessage);
if (error) {
accept('error', error, {
node,
property: 'defaultValue',
code: CODE_PARAMETER_BOUND_INVALID_VALUE,
Expand All @@ -94,18 +109,23 @@ const checkBound = (
leftOperand: EvaluatedNode,
operator: string,
rightOperand: EvaluatedNode,
customMessage?: string,
): string | undefined => {
// Arguments must be valid
if (
(!(leftOperand instanceof FloatConstant) && !(leftOperand instanceof IntConstant)) ||
!isSdsComparisonOperator(operator) ||
(!(rightOperand instanceof FloatConstant) && !(rightOperand instanceof IntConstant))
) {
return;
return undefined;
}

const createMessage = (relation: string) => {
return `The value of '${parameterName}' must be ${relation} ${rightOperand.toString()} but was ${leftOperand.toString()}.`;
if (customMessage) {
return customMessage;
} else {
return `The value of '${parameterName}' must be ${relation} ${rightOperand.toString()} but was ${leftOperand.toString()}.`;
}
};

if (operator === '<') {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package tests.validation.other.declarations.parameterBounds.argumentsMustMatchParameterBounds

@Pure fun f6(
const p1: Int,
const p2: Int = -2,
) where {
p1 >= 0 else "This parameter must be non-negative.",
p1 > p2 else "p1 must be greater than p2, but p1 was {{ p1 }} and p2 was {{ p2 }}.",
}

@Pure fun f7(
// $TEST$ error "This parameter must be non-negative."
const p1: Int = »-1«,
// $TEST$ error "This parameter must be non-negative, but was -2."
const p2: Int = »-2«,
// $TEST$ error "The value of 'p3' must be greater than or equal to 0 but was -3."
const p3: Int = »-3«,
) where {
p1 >= 0 else "This parameter must be non-negative.",
p2 >= 0 else "This parameter must be non-negative, but was {{ p2 }}.",
p3 >= 0 else "This parameter must be non-negative, but was {{ p3 }}. p2 was {{ p2 }} by the way.",
}

segment mySegment(p: Int) {
// $TEST$ error "This parameter must be non-negative."
f6(»-1«);

// $TEST$ error "p1 must be greater than p2, but p1 was -3 and p2 was -2."
f6(»-3«);

// $TEST$ error "p1 must be greater than p2, but p1 was 1 and p2 was 1."
f6(»1«, 1);
}

0 comments on commit 750eb76

Please sign in to comment.