Skip to content

Commit 17a5b7a

Browse files
feat: error if single use annotations are used multiple times (#631)
Closes partially #543 ### Summary of Changes Show an error if annotation that is not repeatable (default, unless it has the `@Repeatable` annotation) is repeated. --------- Co-authored-by: megalinter-bot <129584137+megalinter-bot@users.noreply.github.com>
1 parent e8e2bf6 commit 17a5b7a

File tree

5 files changed

+57
-0
lines changed

5 files changed

+57
-0
lines changed

src/language/builtins/safe-ds-annotations.ts

+8
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@ export class SafeDsAnnotations extends SafeDsModuleMembers<SdsAnnotation> {
3131
return this.getAnnotation(CORE_ANNOTATIONS_URI, 'Expert');
3232
}
3333

34+
isRepeatable(node: SdsAnnotation | undefined): boolean {
35+
return this.hasAnnotationCallOf(node, this.Repeatable);
36+
}
37+
38+
private get Repeatable(): SdsAnnotation | undefined {
39+
return this.getAnnotation(CORE_ANNOTATIONS_URI, 'Repeatable');
40+
}
41+
3442
private hasAnnotationCallOf(node: SdsAnnotatedObject | undefined, expected: SdsAnnotation | undefined): boolean {
3543
return annotationCallsOrEmpty(node).some((it) => {
3644
const actual = it.annotation?.ref;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { ValidationAcceptor } from 'langium';
2+
import { SdsAnnotatedObject } from '../../generated/ast.js';
3+
import { SafeDsServices } from '../../safe-ds-module.js';
4+
import { annotationCallsOrEmpty } from '../../helpers/nodeProperties.js';
5+
import { duplicatesBy } from '../../helpers/collectionUtils.js';
6+
7+
export const CODE_ANNOTATION_NOT_REPEATABLE = 'annotation/not-repeatable';
8+
9+
export const singleUseAnnotationsMustNotBeRepeated =
10+
(services: SafeDsServices) => (node: SdsAnnotatedObject, accept: ValidationAcceptor) => {
11+
const callsOfSingleUseAnnotations = annotationCallsOrEmpty(node).filter((it) => {
12+
const annotation = it.annotation?.ref;
13+
return annotation && !services.builtins.Annotations.isRepeatable(annotation);
14+
});
15+
16+
for (const duplicate of duplicatesBy(callsOfSingleUseAnnotations, (it) => it.annotation?.ref)) {
17+
accept('error', `The annotation '${duplicate.annotation.$refText}' is not repeatable.`, {
18+
node: duplicate,
19+
code: CODE_ANNOTATION_NOT_REPEATABLE,
20+
});
21+
}
22+
};

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

+2
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ import {
7676
} from './other/declarations/annotationCalls.js';
7777
import { memberAccessMustBeNullSafeIfReceiverIsNullable } from './other/expressions/memberAccesses.js';
7878
import { importPackageMustExist, importPackageShouldNotBeEmpty } from './other/imports.js';
79+
import { singleUseAnnotationsMustNotBeRepeated } from './builtins/repeatable.js';
7980

8081
/**
8182
* Register custom validation checks.
@@ -98,6 +99,7 @@ export const registerValidationChecks = function (services: SafeDsServices) {
9899
annotationCallAnnotationShouldNotBeExperimental(services),
99100
annotationCallArgumentListShouldBeNeeded,
100101
],
102+
SdsAnnotatedObject: [singleUseAnnotationsMustNotBeRepeated(services)],
101103
SdsArgument: [
102104
argumentCorrespondingParameterShouldNotBeDeprecated(services),
103105
argumentCorrespondingParameterShouldNotBeExperimental(services),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package tests.validation.builtins.annotations.repeatable
2+
3+
annotation SingleUse
4+
5+
@Repeatable
6+
annotation MultiUse
7+
8+
// $TEST$ no error r"The annotation '\w*' is not repeatable\."
9+
»@SingleUse«
10+
// $TEST$ no error r"The annotation '\w*' is not repeatable\."
11+
»@MultiUse«
12+
// $TEST$ no error r"The annotation '\w*' is not repeatable\."
13+
»@MultiUse«
14+
// $TEST$ no error r"The annotation '\w*' is not repeatable\."
15+
»@UnresolvedAnnotation«
16+
// $TEST$ no error r"The annotation '\w*' is not repeatable\."
17+
»@UnresolvedAnnotation«
18+
class CorrectUse
19+
20+
// $TEST$ no error r"The annotation '\w*' is not repeatable\."
21+
»@SingleUse«
22+
// $TEST$ error "The annotation 'SingleUse' is not repeatable."
23+
»@SingleUse«
24+
class IncorrectUse

tests/resources/validation/other/statements/assignments/nothing assigned/main.sdstest

+1
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ segment mySegment() -> (
5858
// $TEST$ error "No value is assigned to this assignee."
5959
»val k«, »val l« = unresolved();
6060

61+
6162
// $TEST$ error "No value is assigned to this assignee."
6263
»yield r1« = noResults();
6364
// $TEST$ no error "No value is assigned to this assignee."

0 commit comments

Comments
 (0)