@@ -8,25 +8,55 @@ import {
8
8
isSdsPipeline ,
9
9
isSdsReference ,
10
10
isSdsSchema ,
11
+ SdsArgument ,
11
12
SdsAttribute ,
12
13
SdsCall ,
14
+ SdsIndexedAccess ,
15
+ SdsInfixOperation ,
13
16
SdsNamedType ,
14
17
SdsParameter ,
18
+ SdsPrefixOperation ,
15
19
SdsResult ,
20
+ SdsYield ,
16
21
} from '../generated/ast.js' ;
17
22
import { getTypeArguments , getTypeParameters } from '../helpers/nodeProperties.js' ;
18
23
import { SafeDsServices } from '../safe-ds-module.js' ;
19
24
import { pluralize } from '../../helpers/stringUtils.js' ;
20
25
import { isEmpty } from '../../helpers/collectionUtils.js' ;
21
26
22
27
export const CODE_TYPE_CALLABLE_RECEIVER = 'type/callable-receiver' ;
28
+ export const CODE_TYPE_MISMATCH = 'type/mismatch' ;
23
29
export const CODE_TYPE_MISSING_TYPE_ARGUMENTS = 'type/missing-type-arguments' ;
24
30
export const CODE_TYPE_MISSING_TYPE_HINT = 'type/missing-type-hint' ;
25
31
26
32
// -----------------------------------------------------------------------------
27
33
// Type checking
28
34
// -----------------------------------------------------------------------------
29
35
36
+ export const argumentTypeMustMatchParameterType = ( services : SafeDsServices ) => {
37
+ const nodeMapper = services . helpers . NodeMapper ;
38
+ const typeChecker = services . types . TypeChecker ;
39
+ const typeComputer = services . types . TypeComputer ;
40
+
41
+ return ( node : SdsArgument , accept : ValidationAcceptor ) => {
42
+ const parameter = nodeMapper . argumentToParameter ( node ) ;
43
+ if ( ! parameter ) {
44
+ return ;
45
+ }
46
+
47
+ const argumentType = typeComputer . computeType ( node ) ;
48
+ const parameterType = typeComputer . computeType ( parameter ) ;
49
+
50
+ if ( ! typeChecker . isAssignableTo ( argumentType , parameterType ) ) {
51
+ accept ( 'error' , `Expected type '${ parameterType } ' but got '${ argumentType } '.` , {
52
+ node,
53
+ property : 'value' ,
54
+ code : CODE_TYPE_MISMATCH ,
55
+ } ) ;
56
+ }
57
+ } ;
58
+ } ;
59
+
30
60
export const callReceiverMustBeCallable = ( services : SafeDsServices ) => {
31
61
const nodeMapper = services . helpers . NodeMapper ;
32
62
@@ -60,6 +90,187 @@ export const callReceiverMustBeCallable = (services: SafeDsServices) => {
60
90
} ;
61
91
} ;
62
92
93
+ export const indexedAccessReceiverMustBeListOrMap = ( services : SafeDsServices ) => {
94
+ const coreTypes = services . types . CoreTypes ;
95
+ const typeChecker = services . types . TypeChecker ;
96
+ const typeComputer = services . types . TypeComputer ;
97
+
98
+ return ( node : SdsIndexedAccess , accept : ValidationAcceptor ) : void => {
99
+ const receiverType = typeComputer . computeType ( node . receiver ) ;
100
+ if (
101
+ ! typeChecker . isAssignableTo ( receiverType , coreTypes . List ) &&
102
+ ! typeChecker . isAssignableTo ( receiverType , coreTypes . Map )
103
+ ) {
104
+ accept ( 'error' , `Expected type '${ coreTypes . List } ' or '${ coreTypes . Map } ' but got '${ receiverType } '.` , {
105
+ node : node . receiver ,
106
+ code : CODE_TYPE_MISMATCH ,
107
+ } ) ;
108
+ }
109
+ } ;
110
+ } ;
111
+
112
+ export const indexedAccessIndexMustHaveCorrectType = ( services : SafeDsServices ) => {
113
+ const coreTypes = services . types . CoreTypes ;
114
+ const typeChecker = services . types . TypeChecker ;
115
+ const typeComputer = services . types . TypeComputer ;
116
+
117
+ return ( node : SdsIndexedAccess , accept : ValidationAcceptor ) : void => {
118
+ const receiverType = typeComputer . computeType ( node . receiver ) ;
119
+ if ( typeChecker . isAssignableTo ( receiverType , coreTypes . List ) ) {
120
+ const indexType = typeComputer . computeType ( node . index ) ;
121
+ if ( ! typeChecker . isAssignableTo ( indexType , coreTypes . Int ) ) {
122
+ accept ( 'error' , `Expected type '${ coreTypes . Int } ' but got '${ indexType } '.` , {
123
+ node,
124
+ property : 'index' ,
125
+ code : CODE_TYPE_MISMATCH ,
126
+ } ) ;
127
+ }
128
+ }
129
+ } ;
130
+ } ;
131
+
132
+ export const infixOperationOperandsMustHaveCorrectType = ( services : SafeDsServices ) => {
133
+ const coreTypes = services . types . CoreTypes ;
134
+ const typeChecker = services . types . TypeChecker ;
135
+ const typeComputer = services . types . TypeComputer ;
136
+
137
+ return ( node : SdsInfixOperation , accept : ValidationAcceptor ) : void => {
138
+ const leftType = typeComputer . computeType ( node . leftOperand ) ;
139
+ const rightType = typeComputer . computeType ( node . rightOperand ) ;
140
+ switch ( node . operator ) {
141
+ case 'or' :
142
+ case 'and' :
143
+ if ( ! typeChecker . isAssignableTo ( leftType , coreTypes . Boolean ) ) {
144
+ accept ( 'error' , `Expected type '${ coreTypes . Boolean } ' but got '${ leftType } '.` , {
145
+ node : node . leftOperand ,
146
+ code : CODE_TYPE_MISMATCH ,
147
+ } ) ;
148
+ }
149
+ if ( ! typeChecker . isAssignableTo ( rightType , coreTypes . Boolean ) ) {
150
+ accept ( 'error' , `Expected type '${ coreTypes . Boolean } ' but got '${ rightType } '.` , {
151
+ node : node . rightOperand ,
152
+ code : CODE_TYPE_MISMATCH ,
153
+ } ) ;
154
+ }
155
+ return ;
156
+ case '<' :
157
+ case '<=' :
158
+ case '>=' :
159
+ case '>' :
160
+ case '+' :
161
+ case '-' :
162
+ case '*' :
163
+ case '/' :
164
+ if (
165
+ ! typeChecker . isAssignableTo ( leftType , coreTypes . Float ) &&
166
+ ! typeChecker . isAssignableTo ( leftType , coreTypes . Int )
167
+ ) {
168
+ accept ( 'error' , `Expected type '${ coreTypes . Float } ' or '${ coreTypes . Int } ' but got '${ leftType } '.` , {
169
+ node : node . leftOperand ,
170
+ code : CODE_TYPE_MISMATCH ,
171
+ } ) ;
172
+ }
173
+ if (
174
+ ! typeChecker . isAssignableTo ( rightType , coreTypes . Float ) &&
175
+ ! typeChecker . isAssignableTo ( rightType , coreTypes . Int )
176
+ ) {
177
+ accept (
178
+ 'error' ,
179
+ `Expected type '${ coreTypes . Float } ' or '${ coreTypes . Int } ' but got '${ rightType } '.` ,
180
+ {
181
+ node : node . rightOperand ,
182
+ code : CODE_TYPE_MISMATCH ,
183
+ } ,
184
+ ) ;
185
+ }
186
+ return ;
187
+ }
188
+ } ;
189
+ } ;
190
+
191
+ export const parameterDefaultValueTypeMustMatchParameterType = ( services : SafeDsServices ) => {
192
+ const typeChecker = services . types . TypeChecker ;
193
+ const typeComputer = services . types . TypeComputer ;
194
+
195
+ return ( node : SdsParameter , accept : ValidationAcceptor ) => {
196
+ const defaultValue = node . defaultValue ;
197
+ if ( ! defaultValue ) {
198
+ return ;
199
+ }
200
+
201
+ const defaultValueType = typeComputer . computeType ( defaultValue ) ;
202
+ const parameterType = typeComputer . computeType ( node ) ;
203
+
204
+ if ( ! typeChecker . isAssignableTo ( defaultValueType , parameterType ) ) {
205
+ accept ( 'error' , `Expected type '${ parameterType } ' but got '${ defaultValueType } '.` , {
206
+ node,
207
+ property : 'defaultValue' ,
208
+ code : CODE_TYPE_MISMATCH ,
209
+ } ) ;
210
+ }
211
+ } ;
212
+ } ;
213
+
214
+ export const prefixOperationOperandMustHaveCorrectType = ( services : SafeDsServices ) => {
215
+ const coreTypes = services . types . CoreTypes ;
216
+ const typeChecker = services . types . TypeChecker ;
217
+ const typeComputer = services . types . TypeComputer ;
218
+
219
+ return ( node : SdsPrefixOperation , accept : ValidationAcceptor ) : void => {
220
+ const operandType = typeComputer . computeType ( node . operand ) ;
221
+ switch ( node . operator ) {
222
+ case 'not' :
223
+ if ( ! typeChecker . isAssignableTo ( operandType , coreTypes . Boolean ) ) {
224
+ accept ( 'error' , `Expected type '${ coreTypes . Boolean } ' but got '${ operandType } '.` , {
225
+ node,
226
+ property : 'operand' ,
227
+ code : CODE_TYPE_MISMATCH ,
228
+ } ) ;
229
+ }
230
+ return ;
231
+ case '-' :
232
+ if (
233
+ ! typeChecker . isAssignableTo ( operandType , coreTypes . Float ) &&
234
+ ! typeChecker . isAssignableTo ( operandType , coreTypes . Int )
235
+ ) {
236
+ accept (
237
+ 'error' ,
238
+ `Expected type '${ coreTypes . Float } ' or '${ coreTypes . Int } ' but got '${ operandType } '.` ,
239
+ {
240
+ node,
241
+ property : 'operand' ,
242
+ code : CODE_TYPE_MISMATCH ,
243
+ } ,
244
+ ) ;
245
+ }
246
+ return ;
247
+ }
248
+ } ;
249
+ } ;
250
+
251
+ export const yieldTypeMustMatchResultType = ( services : SafeDsServices ) => {
252
+ const typeChecker = services . types . TypeChecker ;
253
+ const typeComputer = services . types . TypeComputer ;
254
+
255
+ return ( node : SdsYield , accept : ValidationAcceptor ) => {
256
+ const result = node . result ?. ref ;
257
+ if ( ! result ) {
258
+ return ;
259
+ }
260
+
261
+ const yieldType = typeComputer . computeType ( node ) ;
262
+ const resultType = typeComputer . computeType ( result ) ;
263
+
264
+ if ( ! typeChecker . isAssignableTo ( yieldType , resultType ) ) {
265
+ accept ( 'error' , `Expected type '${ resultType } ' but got '${ yieldType } '.` , {
266
+ node,
267
+ property : 'result' ,
268
+ code : CODE_TYPE_MISMATCH ,
269
+ } ) ;
270
+ }
271
+ } ;
272
+ } ;
273
+
63
274
// -----------------------------------------------------------------------------
64
275
// Missing type arguments
65
276
// -----------------------------------------------------------------------------
0 commit comments