1pub(crate) use check_instance_returns::*;
16pub(crate) use exhaustive_pattern_match::*;
17pub(crate) use invalid_expr::*;
18pub(crate) use invalid_math_expr::*;
19pub(crate) use missing_fields::*;
20pub use path::*;
21pub(crate) use type_mismatch::*;
22pub(crate) use unresolved_types::*;
23
24mod check_instance_returns;
25mod exhaustive_pattern_match;
26mod invalid_expr;
27mod invalid_math_expr;
28mod invalid_worker_name;
29mod missing_fields;
30mod path;
31mod type_check_in_function_calls;
32mod type_mismatch;
33mod unresolved_types;
34
35use crate::rib_compilation_error::RibCompilationError;
36use crate::type_checker::exhaustive_pattern_match::check_exhaustive_pattern_match;
37use crate::type_checker::invalid_expr::check_invalid_expr;
38use crate::type_checker::invalid_math_expr::check_invalid_math_expr;
39use crate::type_checker::invalid_worker_name::check_invalid_worker_name;
40use crate::type_checker::type_check_in_function_calls::check_type_error_in_function_calls;
41use crate::{Expr, FunctionTypeRegistry};
42
43pub fn type_check(
44 expr: &mut Expr,
45 function_type_registry: &FunctionTypeRegistry,
46) -> Result<(), RibCompilationError> {
47 check_type_error_in_function_calls(expr, function_type_registry)?;
48 check_unresolved_types(expr)?;
49 check_invalid_worker_name(expr)?;
50 check_invalid_expr(expr)?;
51 check_invalid_program_return(expr)?;
52 check_invalid_math_expr(expr)?;
53 check_exhaustive_pattern_match(expr, function_type_registry)?;
54 Ok(())
55}
56
57#[cfg(test)]
58mod type_check_tests {
59
60 mod type_mismatch_errors {
61 use test_r::test;
62
63 use crate::type_checker::type_check_tests::internal;
64 use crate::type_checker::type_check_tests::internal::strip_spaces;
65 use crate::{compile, Expr};
66
67 #[test]
68 async fn test_inference_pattern_match_invalid() {
69 let expr = r#"
70 let x: option<u64> = some(1);
71 match x {
72 some(x) => x,
73 none => "none"
74 }
75 "#;
76
77 let expr = Expr::from_text(expr).unwrap();
78
79 let metadata = internal::get_metadata_with_record_input_params();
80
81 let error_msg = compile(expr, &metadata).unwrap_err().to_string();
82
83 let expected = r#"
84 error in the following rib found at line 3, column 11
85 `match x { some(x) => x, none => "none" } `
86 cause: cannot determine the type
87 invalid pattern match, conflicting types inferred. u64, string
88 help: try specifying the expected type explicitly
89 help: if the issue persists, please review the script for potential type inconsistencies
90 "#;
91
92 assert_eq!(error_msg, strip_spaces(expected));
93 }
94
95 #[test]
96 fn test_type_mismatch_in_record_in_function_call1() {
97 let expr = r#"
98 let result = foo({a: {aa: 1, ab: 2, ac: [1, 2], ad: {ada: 1}, ae: (1, "foo")}, b: "foo", c: [1, 2, 3], d: {da: 4}});
99 result
100 "#;
101
102 let expr = Expr::from_text(expr).unwrap();
103
104 let metadata = internal::get_metadata_with_record_input_params();
105
106 let error_msg = compile(expr, &metadata).unwrap_err().to_string();
107
108 let expected = r#"
109 error in the following rib found at line 2, column 28
110 `{a: {aa: 1, ab: 2, ac: [1, 2], ad: {ada: 1}, ae: (1, "foo")}, b: "foo", c: [1, 2, 3], d: {da: 4}}`
111 found within:
112 `foo({a: {aa: 1, ab: 2, ac: [1, 2], ad: {ada: 1}, ae: (1, "foo")}, b: "foo", c: [1, 2, 3], d: {da: 4}})`
113 cause: type mismatch at path: `b`. expected u64
114 invalid argument to the function `foo`
115 "#;
116
117 assert_eq!(error_msg, strip_spaces(expected));
118 }
119
120 #[test]
121 fn test_type_mismatch_in_record_in_function_call2() {
122 let expr = r#"
123 let result = foo({a: {aa: 1, ab: 2, ac: [1, 2], ad: {ada: 1}, ae: (1, "foo")}, b: 2, c: ["foo", "bar"], d: {da: 4}});
124 result
125 "#;
126
127 let expr = Expr::from_text(expr).unwrap();
128
129 let metadata = internal::get_metadata_with_record_input_params();
130
131 let error_msg = compile(expr, &metadata).unwrap_err().to_string();
132
133 let expected = r#"
134 error in the following rib found at line 2, column 28
135 `{a: {aa: 1, ab: 2, ac: [1, 2], ad: {ada: 1}, ae: (1, "foo")}, b: 2, c: ["foo", "bar"], d: {da: 4}}`
136 found within:
137 `foo({a: {aa: 1, ab: 2, ac: [1, 2], ad: {ada: 1}, ae: (1, "foo")}, b: 2, c: ["foo", "bar"], d: {da: 4}})`
138 cause: type mismatch at path: `c`. expected list<s32>
139 invalid argument to the function `foo`
140 "#;
141
142 assert_eq!(error_msg, strip_spaces(expected));
143 }
144
145 #[test]
146 fn test_type_mismatch_in_record_in_function_call3() {
147 let expr = r#"
148 let result = foo({a: {aa: 1, ab: 2, ac: [1, 2], ad: {ada: 1}, ae: (1, "foo")}, b: 2, c: [1, 2], d: {da: "foo"}});
149 result
150 "#;
151
152 let expr = Expr::from_text(expr).unwrap();
153
154 let metadata = internal::get_metadata_with_record_input_params();
155
156 let error_msg = compile(expr, &metadata).unwrap_err().to_string();
157
158 let expected = r#"
159 error in the following rib found at line 2, column 28
160 `{a: {aa: 1, ab: 2, ac: [1, 2], ad: {ada: 1}, ae: (1, "foo")}, b: 2, c: [1, 2], d: {da: "foo"}}`
161 found within:
162 `foo({a: {aa: 1, ab: 2, ac: [1, 2], ad: {ada: 1}, ae: (1, "foo")}, b: 2, c: [1, 2], d: {da: "foo"}})`
163 cause: type mismatch at path: `d.da`. expected s32
164 invalid argument to the function `foo`
165 "#;
166
167 assert_eq!(error_msg, strip_spaces(expected));
168 }
169
170 #[test]
175 fn test_type_mismatch_in_record_in_function_call4() {
176 let expr = r#"
177 let result = foo({a: {aa: 1, ab: 2, ac: (1, 2), ad: {ada: 1}, ae: (1, "foo")}, b: 2, c: [1, 2], d: {da: 1}});
178 result
179 "#;
180
181 let expr = Expr::from_text(expr).unwrap();
182
183 let metadata = internal::get_metadata_with_record_input_params();
184
185 let error_msg = compile(expr, &metadata).unwrap_err().to_string();
186
187 let expected = r#"
188 error in the following rib found at line 2, column 51
189 `(1, 2)`
190 cause: ambiguous types: `list<number>`, `tuple<number, number>`
191 "#;
192
193 assert_eq!(error_msg, strip_spaces(expected));
194 }
195
196 #[test]
197 fn test_type_mismatch_in_record_in_function_call5() {
198 let expr = r#"
199 let x = {a: "foo"};
200 let result = foo({a: {aa: 1, ab: 2, ac: x, ad: {ada: 1}, ae: (1, "foo")}, b: 2, c: [1, 2], d: {da: 1}});
201 result
202 "#;
203
204 let expr = Expr::from_text(expr).unwrap();
205
206 let metadata = internal::get_metadata_with_record_input_params();
207
208 let error_msg = compile(expr, &metadata).unwrap_err().to_string();
209
210 let expected = r#"
211 error in the following rib found at line 2, column 21
212 `{a: "foo"}`
213 cause: ambiguous types: `list<number>`, `record{a: str}`
214 "#;
215
216 assert_eq!(error_msg, strip_spaces(expected));
217 }
218
219 #[test]
220 fn test_type_mismatch_in_nested_record_in_function_call1() {
221 let expr = r#"
222 let result = foo({a: {aa: "foo", ab: 2, ac: [1, 2], ad: {ada: "1"}, ae: (1, "foo")}, b: 3, c: [1, 2, 3], d: {da: 4}});
223 result
224 "#;
225
226 let expr = Expr::from_text(expr).unwrap();
227
228 let metadata = internal::get_metadata_with_record_input_params();
229
230 let error_msg = compile(expr, &metadata).unwrap_err().to_string();
231
232 let expected = r#"
233 error in the following rib found at line 2, column 28
234 `{a: {aa: "foo", ab: 2, ac: [1, 2], ad: {ada: "1"}, ae: (1, "foo")}, b: 3, c: [1, 2, 3], d: {da: 4}}`
235 found within:
236 `foo({a: {aa: "foo", ab: 2, ac: [1, 2], ad: {ada: "1"}, ae: (1, "foo")}, b: 3, c: [1, 2, 3], d: {da: 4}})`
237 cause: type mismatch at path: `a.aa`. expected s32
238 invalid argument to the function `foo`
239 "#;
240
241 assert_eq!(error_msg, strip_spaces(expected));
242 }
243
244 #[test]
245 fn test_type_mismatch_in_nested_record_in_function_call2() {
246 let expr = r#"
247 let result = foo({a: {aa: 1, ab: 2, ac: [1, 2], ad: {ada: "1"}, ae: (1, "foo")}, b: 3, c: [1, 2, 3], d: {da: 4}});
248 result
249 "#;
250
251 let expr = Expr::from_text(expr).unwrap();
252
253 let metadata = internal::get_metadata_with_record_input_params();
254
255 let error_msg = compile(expr, &metadata).unwrap_err().to_string();
256
257 let expected = r#"
258 error in the following rib found at line 2, column 28
259 `{a: {aa: 1, ab: 2, ac: [1, 2], ad: {ada: "1"}, ae: (1, "foo")}, b: 3, c: [1, 2, 3], d: {da: 4}}`
260 found within:
261 `foo({a: {aa: 1, ab: 2, ac: [1, 2], ad: {ada: "1"}, ae: (1, "foo")}, b: 3, c: [1, 2, 3], d: {da: 4}})`
262 cause: type mismatch at path: `a.ad.ada`. expected s32
263 invalid argument to the function `foo`
264 "#;
265
266 assert_eq!(error_msg, strip_spaces(expected));
267 }
268
269 #[test]
270 fn test_type_mismatch_in_nested_record_in_function_call3() {
271 let expr = r#"
272 let bar = {a: {aa: 1, ab: 2, ac: 1, ad: {ada: 1}, ae:(1, "foo")}, b: 3, c: [1, 2, 3], d: {da: 4}};
273 let result = foo(bar);
274 result
275 "#;
276
277 let expr = Expr::from_text(expr).unwrap();
278
279 let metadata = internal::get_metadata_with_record_input_params();
280
281 let error_msg = compile(expr, &metadata).unwrap_err().to_string();
282
283 let expected = r#"
284 error in the following rib found at line 3, column 30
285 `bar`
286 found within:
287 `foo(bar)`
288 cause: type mismatch at path: `a.ac`. expected list<s32>
289 invalid argument to the function `foo`
290 "#;
291
292 assert_eq!(error_msg, strip_spaces(expected));
293 }
294
295 #[test]
296 fn test_type_mismatch_in_nested_record_in_function_call4() {
297 let expr = r#"
298 let result = foo({a: {aa: 1, ab: 2, ac: [1, 2], ad: {ada: 1}, ae: (1, 2)}, b: 3, c: [1, 2, 3], d: {da: 4}});
299 result
300 "#;
301
302 let expr = Expr::from_text(expr).unwrap();
303
304 let metadata = internal::get_metadata_with_record_input_params();
305
306 let error_msg = compile(expr, &metadata).unwrap_err().to_string();
307
308 let expected = r#"
309 error in the following rib found at line 2, column 28
310 `{a: {aa: 1, ab: 2, ac: [1, 2], ad: {ada: 1}, ae: (1, 2)}, b: 3, c: [1, 2, 3], d: {da: 4}}`
311 found within:
312 `foo({a: {aa: 1, ab: 2, ac: [1, 2], ad: {ada: 1}, ae: (1, 2)}, b: 3, c: [1, 2, 3], d: {da: 4}})`
313 cause: type mismatch at path: `a.ae`. expected string
314 invalid argument to the function `foo`
315 "#;
316
317 assert_eq!(error_msg, strip_spaces(expected));
318 }
319 }
320
321 mod internal {
322 use golem_wasm_ast::analysis::analysed_type::{list, record, s32, str, tuple, u64};
323 use golem_wasm_ast::analysis::{
324 AnalysedExport, AnalysedFunction, AnalysedFunctionParameter, AnalysedFunctionResult,
325 NameTypePair,
326 };
327
328 pub(crate) fn strip_spaces(input: &str) -> String {
329 let lines = input.lines();
330
331 let first_line = lines
332 .clone()
333 .find(|line| !line.trim().is_empty())
334 .unwrap_or("");
335 let margin_width = first_line.chars().take_while(|c| c.is_whitespace()).count();
336
337 let result = lines
338 .map(|line| {
339 if line.trim().is_empty() {
340 String::new()
341 } else {
342 line[margin_width..].to_string()
343 }
344 })
345 .collect::<Vec<String>>()
346 .join("\n");
347
348 result.strip_prefix("\n").unwrap_or(&result).to_string()
349 }
350
351 pub(crate) fn get_metadata_with_record_input_params() -> Vec<AnalysedExport> {
352 let analysed_export = AnalysedExport::Function(AnalysedFunction {
353 name: "foo".to_string(),
354 parameters: vec![AnalysedFunctionParameter {
355 name: "arg1".to_string(),
356 typ: record(vec![
357 NameTypePair {
358 name: "a".to_string(),
359 typ: record(vec![
360 NameTypePair {
361 name: "aa".to_string(),
362 typ: s32(),
363 },
364 NameTypePair {
365 name: "ab".to_string(),
366 typ: s32(),
367 },
368 NameTypePair {
369 name: "ac".to_string(),
370 typ: list(s32()),
371 },
372 NameTypePair {
373 name: "ad".to_string(),
374 typ: record(vec![NameTypePair {
375 name: "ada".to_string(),
376 typ: s32(),
377 }]),
378 },
379 NameTypePair {
380 name: "ae".to_string(),
381 typ: tuple(vec![s32(), str()]),
382 },
383 ]),
384 },
385 NameTypePair {
386 name: "b".to_string(),
387 typ: u64(),
388 },
389 NameTypePair {
390 name: "c".to_string(),
391 typ: list(s32()),
392 },
393 NameTypePair {
394 name: "d".to_string(),
395 typ: record(vec![NameTypePair {
396 name: "da".to_string(),
397 typ: s32(),
398 }]),
399 },
400 ]),
401 }],
402 results: vec![AnalysedFunctionResult {
403 name: None,
404 typ: str(),
405 }],
406 });
407
408 vec![analysed_export]
409 }
410 }
411}