Skip to content

Commit bf20c7c

Browse files
feat: validation for results of segments (#613)
Closes partially #543 ### Summary of Changes Ensure that a segment yields exactly one value for each result. --------- Co-authored-by: megalinter-bot <[email protected]>
1 parent 3a2e9cc commit bf20c7c

File tree

9 files changed

+97
-25
lines changed

9 files changed

+97
-25
lines changed
Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
1-
import { SdsParameter, SdsSegment } from '../../../generated/ast.js';
1+
import { SdsParameter } from '../../../generated/ast.js';
22
import { ValidationAcceptor } from 'langium';
3-
import { parametersOrEmpty } from '../../../helpers/nodeProperties.js';
4-
import { SafeDsServices } from '../../../safe-ds-module.js';
53

6-
export const CODE_PARAMETER_UNUSED = 'parameter/unused';
74
export const CODE_PARAMETER_VARIADIC_AND_OPTIONAL = 'parameter/variadic-and-optional';
85

96
export const parameterMustNotBeVariadicAndOptional = (node: SdsParameter, accept: ValidationAcceptor) => {
@@ -15,18 +12,3 @@ export const parameterMustNotBeVariadicAndOptional = (node: SdsParameter, accept
1512
});
1613
}
1714
};
18-
19-
export const segmentParameterShouldBeUsed =
20-
(services: SafeDsServices) => (node: SdsSegment, accept: ValidationAcceptor) => {
21-
for (const parameter of parametersOrEmpty(node)) {
22-
const usages = services.helpers.NodeMapper.parameterToReferences(parameter);
23-
24-
if (usages.isEmpty()) {
25-
accept('warning', 'This parameter is unused and can be removed.', {
26-
node: parameter,
27-
property: 'name',
28-
code: CODE_PARAMETER_UNUSED,
29-
});
30-
}
31-
}
32-
};
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { SdsSegment } from '../../../generated/ast.js';
2+
import { ValidationAcceptor } from 'langium';
3+
import { parametersOrEmpty, resultsOrEmpty } from '../../../helpers/nodeProperties.js';
4+
import { SafeDsServices } from '../../../safe-ds-module.js';
5+
6+
export const CODE_SEGMENT_DUPLICATE_YIELD = 'segment/duplicate-yield';
7+
export const CODE_SEGMENT_UNASSIGNED_RESULT = 'segment/unassigned-result';
8+
export const CODE_SEGMENT_UNUSED_PARAMETER = 'segment/unused-parameter';
9+
10+
export const segmentResultMustBeAssignedExactlyOnce =
11+
(services: SafeDsServices) => (node: SdsSegment, accept: ValidationAcceptor) => {
12+
const results = resultsOrEmpty(node.resultList);
13+
for (const result of results) {
14+
const yields = services.helpers.NodeMapper.resultToYields(result);
15+
if (yields.isEmpty()) {
16+
accept('error', 'Nothing is assigned to this result.', {
17+
node: result,
18+
property: 'name',
19+
code: CODE_SEGMENT_UNASSIGNED_RESULT,
20+
});
21+
continue;
22+
}
23+
24+
const duplicateYields = yields.tail(1);
25+
for (const duplicate of duplicateYields) {
26+
accept('error', `The result '${result.name}' has been assigned already.`, {
27+
node: duplicate,
28+
property: 'result',
29+
code: CODE_SEGMENT_DUPLICATE_YIELD,
30+
});
31+
}
32+
}
33+
};
34+
35+
export const segmentParameterShouldBeUsed =
36+
(services: SafeDsServices) => (node: SdsSegment, accept: ValidationAcceptor) => {
37+
for (const parameter of parametersOrEmpty(node)) {
38+
const usages = services.helpers.NodeMapper.parameterToReferences(parameter);
39+
40+
if (usages.isEmpty()) {
41+
accept('warning', 'This parameter is unused and can be removed.', {
42+
node: parameter,
43+
property: 'name',
44+
code: CODE_SEGMENT_UNUSED_PARAMETER,
45+
});
46+
}
47+
}
48+
};

src/language/validation/safe-ds-validator.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,7 @@ import { unionTypeMustHaveTypeArguments } from './other/types/unionTypes.js';
4545
import { callableTypeMustNotHaveOptionalParameters } from './other/types/callableTypes.js';
4646
import { typeArgumentListMustNotHavePositionalArgumentsAfterNamedArguments } from './other/types/typeArgumentLists.js';
4747
import { argumentListMustNotHavePositionalArgumentsAfterNamedArguments } from './other/argumentLists.js';
48-
import {
49-
parameterMustNotBeVariadicAndOptional,
50-
segmentParameterShouldBeUsed,
51-
} from './other/declarations/parameters.js';
48+
import { parameterMustNotBeVariadicAndOptional } from './other/declarations/parameters.js';
5249
import { referenceTargetMustNotBeAnnotationPipelineOrSchema } from './other/expressions/references.js';
5350
import {
5451
annotationCallAnnotationShouldNotBeDeprecated,
@@ -65,6 +62,7 @@ import {
6562
referenceTargetShouldNotExperimental,
6663
} from './builtins/experimental.js';
6764
import { placeholderShouldBeUsed } from './other/declarations/placeholders.js';
65+
import { segmentParameterShouldBeUsed, segmentResultMustBeAssignedExactlyOnce } from './other/declarations/segments.js';
6866

6967
/**
7068
* Register custom validation checks.
@@ -125,6 +123,7 @@ export const registerValidationChecks = function (services: SafeDsServices) {
125123
SdsSegment: [
126124
segmentMustContainUniqueNames,
127125
segmentParameterShouldBeUsed(services),
126+
segmentResultMustBeAssignedExactlyOnce(services),
128127
segmentResultListShouldNotBeEmpty,
129128
],
130129
SdsTemplateString: [templateStringMustHaveExpressionBetweenTwoStringParts],
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package tests
2+
3+
pipeline myPipeline {
4+
// $TEST$ unresolved
5+
»tests«;
6+
}

tests/resources/validation/other/declarations/placeholder/unused/main.sdstest renamed to tests/resources/validation/other/declarations/placeholders/unused/main.sdstest

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package tests.validation.declarations.placeholders.unused
1+
package tests.validation.other.declarations.placeholders.unused
22

33
fun f() -> (r1: Int, r2: Int)
44

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package tests.validation.other.declarations.segments.duplicateYield
2+
3+
segment mySegment() -> (a: Int, b: Int, c: Int) {
4+
// $TEST$ no error r"The result '\w*' has been assigned already\."
5+
yield »a« = 1;
6+
7+
// $TEST$ no error r"The result '\w*' has been assigned already\."
8+
yield »b« = 1;
9+
// $TEST$ error "The result 'b' has been assigned already."
10+
yield »b« = 1;
11+
12+
// $TEST$ no error r"The result '\w*' has been assigned already\."
13+
// $TEST$ error "The result 'c' has been assigned already."
14+
yield »c«, yield »c« = 1;
15+
16+
// $TEST$ no error r"The result '\w*' has been assigned already\."
17+
yield »unresolved« = 1;
18+
// $TEST$ no error r"The result '\w*' has been assigned already\."
19+
yield »unresolved« = 1;
20+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package tests.validation.other.declarations.segments.unassignedResult
2+
3+
// $TEST$ no error "Nothing is assigned to this result."
4+
// $TEST$ no error "Nothing is assigned to this result."
5+
// $TEST$ no error "Nothing is assigned to this result."
6+
// $TEST$ no error "Nothing is assigned to this result."
7+
// $TEST$ error "Nothing is assigned to this result."
8+
segment mySegment() -> (»a«: Int, »b«: Int, »c«: Int, »d«: Int, »e«: Int) {
9+
yield b = 1;
10+
yield a = 1;
11+
12+
yield c = 1;
13+
yield c = 1;
14+
15+
// While nothing is assigned to d, the programmer still states their intention to do so. We already show another error for this.
16+
_, yield d = 1;
17+
}

tests/resources/validation/other/declarations/parameter/unused/main.sdstest renamed to tests/resources/validation/other/declarations/segments/unused parameter/main.sdstest

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package tests.validation.declarations.parameters.unused
1+
package tests.validation.other.declarations.segments.unusedParameters
22

33
segment mySegment(
44
// $TEST$ warning "This parameter is unused and can be removed."

0 commit comments

Comments
 (0)