File tree 5 files changed +57
-0
lines changed
tests/resources/validation
builtins/annotations/repeatable
other/statements/assignments/nothing assigned
5 files changed +57
-0
lines changed Original file line number Diff line number Diff line change @@ -31,6 +31,14 @@ export class SafeDsAnnotations extends SafeDsModuleMembers<SdsAnnotation> {
31
31
return this . getAnnotation ( CORE_ANNOTATIONS_URI , 'Expert' ) ;
32
32
}
33
33
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
+
34
42
private hasAnnotationCallOf ( node : SdsAnnotatedObject | undefined , expected : SdsAnnotation | undefined ) : boolean {
35
43
return annotationCallsOrEmpty ( node ) . some ( ( it ) => {
36
44
const actual = it . annotation ?. ref ;
Original file line number Diff line number Diff line change
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
+ } ;
Original file line number Diff line number Diff line change @@ -76,6 +76,7 @@ import {
76
76
} from './other/declarations/annotationCalls.js' ;
77
77
import { memberAccessMustBeNullSafeIfReceiverIsNullable } from './other/expressions/memberAccesses.js' ;
78
78
import { importPackageMustExist , importPackageShouldNotBeEmpty } from './other/imports.js' ;
79
+ import { singleUseAnnotationsMustNotBeRepeated } from './builtins/repeatable.js' ;
79
80
80
81
/**
81
82
* Register custom validation checks.
@@ -98,6 +99,7 @@ export const registerValidationChecks = function (services: SafeDsServices) {
98
99
annotationCallAnnotationShouldNotBeExperimental ( services ) ,
99
100
annotationCallArgumentListShouldBeNeeded ,
100
101
] ,
102
+ SdsAnnotatedObject : [ singleUseAnnotationsMustNotBeRepeated ( services ) ] ,
101
103
SdsArgument : [
102
104
argumentCorrespondingParameterShouldNotBeDeprecated ( services ) ,
103
105
argumentCorrespondingParameterShouldNotBeExperimental ( services ) ,
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change @@ -58,6 +58,7 @@ segment mySegment() -> (
58
58
// $TEST$ error "No value is assigned to this assignee."
59
59
»val k«, »val l« = unresolved();
60
60
61
+
61
62
// $TEST$ error "No value is assigned to this assignee."
62
63
»yield r1« = noResults();
63
64
// $TEST$ no error "No value is assigned to this assignee."
You can’t perform that action at this time.
0 commit comments