Skip to content

Commit

Permalink
feat: port validation of parameter lists
Browse files Browse the repository at this point in the history
  • Loading branch information
lars-reimann committed Sep 21, 2023
1 parent 1d73d7f commit 49255ec
Show file tree
Hide file tree
Showing 5 changed files with 455 additions and 0 deletions.
59 changes: 59 additions & 0 deletions src/language/validation/other/declarations/parameterLists.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { SdsParameterList } from '../../../generated/ast.js';
import { ValidationAcceptor } from 'langium';

export const CODE_PARAMETER_LIST_OPTIONAL_AND_VARIADIC = 'parameter-list-optional-and-variadic';
export const CODE_PARAMETER_LIST_REQUIRED_AFTER_OPTIONAL = 'parameter-list-required-after-optional';
export const CODE_PARAMETER_LIST_VARIADIC_NOT_LAST = 'parameter-list-variadic-not-last';

export const parameterListMustNotHaveOptionalAndVariadicParameters = (
node: SdsParameterList,
accept: ValidationAcceptor,
) => {
const hasOptional = node.parameters.find((p) => p.defaultValue);
if (hasOptional) {
const variadicParameters = node.parameters.filter((p) => p.variadic);

for (const variadic of variadicParameters) {
accept('error', 'A callable with optional parameters must not have a variadic parameter.', {
node: variadic,
property: 'name',
code: CODE_PARAMETER_LIST_OPTIONAL_AND_VARIADIC,
});
}
}
};

export const parameterListMustNotHaveRequiredParametersAfterOptionalParameters = (
node: SdsParameterList,
accept: ValidationAcceptor,
) => {
let foundOptional = false;
for (const parameter of node.parameters) {
if (parameter.defaultValue) {
foundOptional = true;
} else if (foundOptional && !parameter.variadic) {
accept('error', 'After the first optional parameter all parameters must be optional.', {
node: parameter,
property: 'name',
code: CODE_PARAMETER_LIST_REQUIRED_AFTER_OPTIONAL,
});
}
}
};

export const parameterListVariadicParameterMustBeLast = (node: SdsParameterList, accept: ValidationAcceptor) => {
let foundVariadic = false;
for (const parameter of node.parameters) {
if (foundVariadic) {
accept('error', 'After a variadic parameter no more parameters must be specified.', {
node: parameter,
property: 'name',
code: CODE_PARAMETER_LIST_VARIADIC_NOT_LAST,
});
}

if (parameter.variadic) {
foundVariadic = true;
}
}
};
10 changes: 10 additions & 0 deletions src/language/validation/safe-ds-validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ import { yieldMustNotBeUsedInPipeline } from './other/statements/assignments.js'
import { attributeMustHaveTypeHint, parameterMustHaveTypeHint, resultMustHaveTypeHint } from './types.js';
import { moduleDeclarationsMustMatchFileKind, moduleWithDeclarationsMustStatePackage } from './other/modules.js';
import { typeParameterConstraintLeftOperandMustBeOwnTypeParameter } from './other/declarations/typeParameterConstraints.js';
import {
parameterListMustNotHaveOptionalAndVariadicParameters,
parameterListMustNotHaveRequiredParametersAfterOptionalParameters,
parameterListVariadicParameterMustBeLast,
} from './other/declarations/parameterLists.js';

/**
* Register custom validation checks.
Expand All @@ -38,6 +43,11 @@ export const registerValidationChecks = function (services: SafeDsServices) {
SdsFunction: [functionResultListShouldNotBeEmpty],
SdsModule: [moduleDeclarationsMustMatchFileKind, moduleWithDeclarationsMustStatePackage],
SdsParameter: [parameterMustHaveTypeHint],
SdsParameterList: [
parameterListMustNotHaveOptionalAndVariadicParameters,
parameterListMustNotHaveRequiredParametersAfterOptionalParameters,
parameterListVariadicParameterMustBeLast,
],
SdsResult: [resultMustHaveTypeHint],
SdsSegment: [segmentResultListShouldNotBeEmpty],
SdsTemplateString: [templateStringMustHaveExpressionBetweenTwoStringParts],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package tests.validation.other.declarations.parameterLists.mustNotHaveRequiredAfterOptional

// $TEST$ no error "After the first optional parameter all parameters must be optional."
// $TEST$ no error "After the first optional parameter all parameters must be optional."
// $TEST$ error "After the first optional parameter all parameters must be optional."
// $TEST$ no error "After the first optional parameter all parameters must be optional."
// $TEST$ no error "After the first optional parameter all parameters must be optional."
annotation MyAnnotation1(»a«: Int, »b«: Int = 1, »c«: Int, »d«: Int = 2, vararg »e«: Int)

// $TEST$ no error "After the first optional parameter all parameters must be optional."
// $TEST$ no error "After the first optional parameter all parameters must be optional."
annotation MyAnnotation2(»a«: Int, »b«: Int = 1)

// $TEST$ no error "After the first optional parameter all parameters must be optional."
annotation MyAnnotation3(»a«: Int)


// $TEST$ no error "After the first optional parameter all parameters must be optional."
// $TEST$ no error "After the first optional parameter all parameters must be optional."
// $TEST$ error "After the first optional parameter all parameters must be optional."
// $TEST$ no error "After the first optional parameter all parameters must be optional."
// $TEST$ no error "After the first optional parameter all parameters must be optional."
class MyClass1(»a«: Int, »b«: Int = 1, »c«: Int, »d«: Int = 2, vararg »e«: Int)

// $TEST$ no error "After the first optional parameter all parameters must be optional."
// $TEST$ no error "After the first optional parameter all parameters must be optional."
class MyClass2(»a«: Int, »b«: Int = 1)

// $TEST$ no error "After the first optional parameter all parameters must be optional."
class MyClass3(»a«: Int)


enum MyEnum {
// $TEST$ no error "After the first optional parameter all parameters must be optional."
// $TEST$ no error "After the first optional parameter all parameters must be optional."
// $TEST$ error "After the first optional parameter all parameters must be optional."
// $TEST$ no error "After the first optional parameter all parameters must be optional."
// $TEST$ no error "After the first optional parameter all parameters must be optional."
MyEnumVariant1(»a«: Int, »b«: Int = 1, »c«: Int, »d«: Int = 2, vararg »e«: Int)

// $TEST$ no error "After the first optional parameter all parameters must be optional."
// $TEST$ no error "After the first optional parameter all parameters must be optional."
MyEnumVariant2(»a«: Int, »b«: Int = 1)

// $TEST$ no error "After the first optional parameter all parameters must be optional."
MyEnumVariant3(»a«: Int)
}


// $TEST$ no error "After the first optional parameter all parameters must be optional."
// $TEST$ no error "After the first optional parameter all parameters must be optional."
// $TEST$ error "After the first optional parameter all parameters must be optional."
// $TEST$ no error "After the first optional parameter all parameters must be optional."
// $TEST$ no error "After the first optional parameter all parameters must be optional."
fun myFunction1(»a«: Int, »b«: Int = 1, »c«: Int, »d«: Int = 2, vararg »e«: Int)

// $TEST$ no error "After the first optional parameter all parameters must be optional."
// $TEST$ no error "After the first optional parameter all parameters must be optional."
fun myFunction2(»a«: Int, »b«: Int = 1)

// $TEST$ no error "After the first optional parameter all parameters must be optional."
fun myFunction3(»a«: Int)


// $TEST$ no error "After the first optional parameter all parameters must be optional."
// $TEST$ no error "After the first optional parameter all parameters must be optional."
// $TEST$ error "After the first optional parameter all parameters must be optional."
// $TEST$ no error "After the first optional parameter all parameters must be optional."
// $TEST$ no error "After the first optional parameter all parameters must be optional."
segment mySegment1(»a«: Int, »b«: Int = 1, »c«: Int, »d«: Int = 2, vararg »e«: Int) {}

// $TEST$ no error "After the first optional parameter all parameters must be optional."
// $TEST$ no error "After the first optional parameter all parameters must be optional."
segment mySegment2(»a«: Int, »b«: Int = 1) {}

// $TEST$ no error "After the first optional parameter all parameters must be optional."
segment mySegment3(»a«: Int) {}


pipeline myPipeline {
// $TEST$ no error "After the first optional parameter all parameters must be optional."
// $TEST$ no error "After the first optional parameter all parameters must be optional."
// $TEST$ error "After the first optional parameter all parameters must be optional."
// $TEST$ no error "After the first optional parameter all parameters must be optional."
// $TEST$ no error "After the first optional parameter all parameters must be optional."
(»a«: Int, »b«: Int = 1, »c«: Int, »d«: Int = 2, vararg »e«: Int) {};

// $TEST$ no error "After the first optional parameter all parameters must be optional."
// $TEST$ no error "After the first optional parameter all parameters must be optional."
(»a«: Int, »b«: Int = 1) {};

// $TEST$ no error "After the first optional parameter all parameters must be optional."
(»a«: Int) {};


// $TEST$ no error "After the first optional parameter all parameters must be optional."
// $TEST$ no error "After the first optional parameter all parameters must be optional."
// $TEST$ error "After the first optional parameter all parameters must be optional."
// $TEST$ no error "After the first optional parameter all parameters must be optional."
// $TEST$ no error "After the first optional parameter all parameters must be optional."
(»a«: Int, »b«: Int = 1, »c«: Int, »d«: Int = 2, vararg »e«: Int) -> 1;

// $TEST$ no error "After the first optional parameter all parameters must be optional."
// $TEST$ no error "After the first optional parameter all parameters must be optional."
(»a«: Int, »b«: Int = 1) -> 1;

// $TEST$ no error "After the first optional parameter all parameters must be optional."
(»a«: Int) -> 1;
}


fun myFunction4(
// $TEST$ no error "After the first optional parameter all parameters must be optional."
// $TEST$ no error "After the first optional parameter all parameters must be optional."
// $TEST$ error "After the first optional parameter all parameters must be optional."
// $TEST$ no error "After the first optional parameter all parameters must be optional."
// $TEST$ no error "After the first optional parameter all parameters must be optional."
p1: (»a«: Int, »b«: Int = 1, »c«: Int, »d«: Int = 2, vararg »e«: Int) -> (),

// $TEST$ no error "After the first optional parameter all parameters must be optional."
// $TEST$ no error "After the first optional parameter all parameters must be optional."
p2: (»a«: Int, »b«: Int = 1) -> (),

// $TEST$ no error "After the first optional parameter all parameters must be optional."
p3: (»a«: Int) -> (),
)
Loading

0 comments on commit 49255ec

Please sign in to comment.