1use crate::{Lexer, Span, SyntaxError, SyntaxNode};
2use crate::file::FileId;
3use crate::kind::SyntaxKind;
4use compose_error_codes::ErrorCode;
5use extension_traits::extension;
6use std::num::NonZeroU16;
7use std::ops::Range;
8
9pub const fn test_file_id() -> FileId {
11 FileId::from_raw(NonZeroU16::new(1).unwrap())
12}
13
14pub struct AstTester {
15 pub nodes: Vec<SyntaxNode>,
16 pub pos: usize,
17}
18
19impl AstTester {
20 #[track_caller]
21 pub fn new(code: &str) -> Self {
22 let p = assert_parse(code);
23 Self {
24 nodes: p.nodes,
25 pos: 0,
26 }
27 }
28}
29
30
31
32#[macro_export]
33macro_rules! assert_ast {
34 ($code:expr, $($tt:tt)*) => {{
36 let mut p = $crate::test_utils::AstTester::new($code);
37 $crate::test_utils::assert_ast!(@top p, $($tt)*);
38 }};
39
40 (@top $tester:ident, $var:ident as $ty:ty { $($inner:tt)* } $($rest:tt)*) => {
42 #[allow(unused)]
43 use $crate::ast::AstNode;
44 let idx = $tester.pos;
45 $tester.pos += 1 ;
46 let node = &$tester.nodes[idx];
47 let $var = node
48 .cast::<$ty>()
49 .expect(&format!("failed to cast to {} at {idx}, got: {:?}", stringify!($ty), node.kind()));
50
51 $crate::assert_ast!(@seq &idx.to_string(), $($inner)*);
52
53 $crate::assert_ast!(@top $tester, $($rest)*);
54 };
55
56 (@top $tester:ident, ...) => {
58 $tester.pos = $tester.nodes.len();
59 };
60
61 (@top $tester:ident, ) => {
63 assert_eq!($tester.pos, $tester.nodes.len(), "Not all nodes were consumed: {:#?}", &$tester.nodes[$tester.pos..]);
64 };
65
66 (@seq $path:expr, $expr:expr => [ $($inner:tt)* ] $($rest:tt)*) => {
68 let mut iter = $expr.enumerate();
69 $crate::assert_ast!(@loop $path, iter, $($inner)*);
70 $crate::assert_ast!(@seq $path, $($rest)*);
71 };
72
73 (@seq $path:expr, with $inner_var:ident: $ty:ty = $expr:expr => { $($inner:tt)* } $($rest:tt)*) => {
75 {
76 let $inner_var = $expr;
77 let $inner_var = $inner_var.cast::<$ty>().expect(&format!("failed to cast to {} at {}", stringify!($ty), $path));
78 let _sub_path = format!("{}.{}", $path, stringify!($inner_var));
79 $crate::assert_ast!(@seq &_sub_path, $($inner)*);
80 }
81 $crate::assert_ast!(@seq $path, $($rest)*);
82 };
83
84 (@seq $path:expr, $expr:expr; $($rest:tt)*) => {{
86 $expr;
87 $crate::assert_ast!(@seq $path, $($rest)*);
88 }};
89
90 (@seq $path:expr, $stmt:stmt; $($rest:tt)*) => {{
92 $stmt
93 $crate::assert_ast!(@seq $path, $($rest)*);
94 }};
95
96 (@seq $path:expr,) => {};
98
99 (@loop $path:expr, $iter:ident, $var:ident as $ty:ty { $($inner:tt)* } $($rest:tt)*) => {
101 {
102 let _item = $iter.next().expect("failed to get next value");
103 let idx = _item.0;
104 let _sub_path = format!("{}.{}", $path, idx);
105 let $var = _item.1.cast::<$ty>().expect(&format!("failed to cast to {}", stringify!($ty)));
106 $crate::assert_ast!(@seq &_sub_path, $($inner)*);
107 }
108
109 $crate::assert_ast!(@loop $path, $iter, $($rest)*);
110 };
111
112 (@loop $path:expr, $iter:ident, ...) => {
114 while let Some(_) = $iter.next() { }
116 };
117
118 (@loop $path:expr, $iter:ident,) => {
120 assert_eq!($iter.next().is_none(), true, "Not all values were consumed at {}", $path);
121 };
122
123
124 (@seq $path:expr, with $(rest:tt)*) => {
129 compile_error!("with usage: with <var_name>: <type> = <expr> => { ... }")
130 };
131
132 (@loop, $path:expr, $iter:ident, $(rest:tt)*) => {
133 compile_error!("loop usage: <type> as <var> { ... }")
134 };
135}
136
137pub use assert_ast;
138
139
140pub fn test_parse(code: &str) -> Vec<SyntaxNode> {
141 let file_id = FileId::new("main.comp");
142 let nodes = crate::parse(code, file_id);
143 nodes
144}
145
146#[track_caller]
147pub fn assert_parse(code: &str) -> NodesTester {
148 let nodes = test_parse(code);
149
150 let errors = nodes
151 .iter()
152 .flat_map(|node| node.errors())
153 .map(|error| error.code)
154 .collect::<Vec<_>>();
155
156 assert_eq!(errors, vec![]);
157
158 NodesTester::new(nodes)
159}
160
161pub fn assert_parse_with_warnings(code: &str, expected_warnings: &[ErrorCode]) -> NodesTester {
162 let nodes = test_parse(code);
163 let actual = nodes
164 .iter()
165 .flat_map(|node| node.warnings())
166 .map(|warning| warning.code)
167 .collect::<Vec<_>>();
168
169 assert_eq!(actual.len(), expected_warnings.len());
170
171 for (actual, expected) in actual.iter().zip(expected_warnings.iter()) {
172 assert_eq!(actual, &Some(expected));
173 }
174
175 let errors = nodes
176 .iter()
177 .flat_map(|node| node.errors())
178 .map(|error| error.code)
179 .collect::<Vec<_>>();
180
181 assert_eq!(errors, vec![]);
182
183 NodesTester::new(nodes)
184}
185
186pub fn assert_parse_with_errors(code: &str, expected_errors: &[ErrorCode]) -> NodesTester {
187 let nodes = test_parse(code);
188 let actual = nodes
189 .iter()
190 .flat_map(|node| node.errors())
191 .map(|error| error.code)
192 .collect::<Vec<_>>();
193
194 assert_eq!(actual.len(), expected_errors.len());
195
196 for (actual, expected) in actual.iter().zip(expected_errors.iter()) {
197 assert_eq!(actual, &Some(expected));
198 }
199
200 NodesTester::new(nodes)
201}
202
203#[extension(trait SyntaxNodeExt)]
204impl SyntaxNode {
205 #[track_caller]
206 fn test_assert(&self, kind: SyntaxKind, text: &str) {
207 assert_eq!(
208 self.kind(),
209 kind,
210 "expected: {:?}, got: {:?}",
211 kind,
212 self.kind()
213 );
214 assert_eq!(
215 self.text(),
216 text,
217 "expected: {:?}, got: {:?}",
218 text,
219 self.text()
220 );
221 }
222
223 #[track_caller]
224 fn test_children(&self, kind: SyntaxKind) -> NodesTester {
225 assert_eq!(self.kind(), kind);
226
227 let children = self.children().cloned().collect::<Vec<_>>();
228 NodesTester::new(children)
229 }
230}
231
232pub struct NodesTester {
233 path: Vec<SyntaxKind>,
234 pub nodes: Vec<SyntaxNode>,
235 pos: usize,
236}
237
238impl NodesTester {
239 pub fn new(nodes: Vec<SyntaxNode>) -> Self {
240 Self {
241 nodes,
242 pos: 0,
243 path: vec![],
244 }
245 }
246
247 pub fn with_path(mut self, path: Vec<SyntaxKind>) -> Self {
248 self.path = path;
249 self
250 }
251
252 #[track_caller]
253 pub fn assert_next(&mut self, kind: SyntaxKind, text: &str) -> &mut Self {
254 let node = self.nodes.get(self.pos).or_else(|| panic!("No more nodes at {:?}. Expected: {kind:?}", self.path)).cloned().unwrap();
255
256 assert_eq!(
257 node.kind(),
258 kind,
259 "expected: {:?}, got: {:?} at {:?} ({node:?})",
260 kind,
261 node.kind(),
262 self.path,
263 );
264 assert_eq!(
265 node.text(),
266 text,
267 "expected: {:?}, got: {:?} at {:?} ({node:?})",
268 text,
269 node.text(),
270 self.path
271 );
272
273 self.pos += 1;
274
275 self
276 }
277
278
279 #[track_caller]
280 pub fn assert_next_warning(&mut self, warning: ErrorCode) -> &mut Self {
281 let node = self.nodes.get(self.pos).or_else(|| panic!("No more nodes at {:?}. Expected warning: {warning:?}", self.path)).cloned().unwrap();
282
283 assert_eq!(
284 node.kind(),
285 SyntaxKind::Error,
286 "Expected an error, got {:?} at {:?}",
287 node.kind(),
288 self.path
289 );
290
291 let warnings = node.warnings();
292 assert_eq!(
293 warnings.len(),
294 1,
295 "Expected an error, got {} warnings at {:?}",
296 warnings.len(),
297 self.path
298 );
299 assert_eq!(
300 warnings[0].code,
301 Some(&warning),
302 "Expected an error with code {:?}, got {:?} at {:?}",
303 warning,
304 warnings[0].code,
305 self.path
306 );
307
308 self.pos += 1;
309
310 self
311 }
312
313 pub fn move_to_end(&mut self) {
314 self.pos = self.nodes.len();
315 }
316
317 #[track_caller]
318 pub fn assert_next_error(&mut self, error: ErrorCode) -> &mut Self {
319 let node = self.nodes.get(self.pos).or_else(|| panic!("No more nodes at {:?}. Expected error: {error:?}", self.path)).cloned().unwrap();
320
321 assert_eq!(
322 node.kind(),
323 SyntaxKind::Error,
324 "Expected an error, got {:?} at {:?}",
325 node.kind(),
326 self.path
327 );
328
329 let errors = node.errors();
330 assert_eq!(
331 errors.len(),
332 1,
333 "Expected an error, got {} errors at {:?}",
334 errors.len(),
335 self.path
336 );
337 assert_eq!(
338 errors[0].code,
339 Some(&error),
340 "Expected an error with code {:?}, got {:?} at {:?}",
341 error,
342 errors[0].code,
343 self.path
344 );
345
346 self.pos += 1;
347
348 self
349 }
350
351 #[track_caller]
352 pub fn assert_next_children(
353 &mut self,
354 kind: SyntaxKind,
355 test_children: impl FnOnce(&mut Self),
356 ) -> &mut Self {
357 let node = self.nodes.get(self.pos).or_else(|| panic!("No more nodes at {:?}. Expected: {kind:?}", self.path)).cloned().unwrap();
358
359 assert_eq!(
360 node.kind(),
361 kind,
362 "expected: {:?}, got: {:?} at {:?} ({})",
363 kind,
364 node.kind(),
365 self.path,
366 node.to_text(),
367 );
368
369 let children = node.children().cloned().collect::<Vec<_>>();
370 let mut tester = NodesTester::new(children)
371 .with_path(self.path.iter().copied().chain(vec![kind]).collect());
372
373 test_children(&mut tester);
374 self.pos += 1;
375
376 self
377 }
378
379 #[track_caller]
380 pub fn assert_end(&self) {
381 assert_eq!(
382 self.pos,
383 self.nodes.len(),
384 "Not all nodes were consumed. at {:?}. Remaining: {:#?}",
385 self.path,
386 &self.nodes[self.pos..]
387 );
388 }
389}
390
391
392#[macro_export]
513macro_rules! assert_parse_tree {
514 ($src:expr, $($tree:tt)+) => {{
516 let nodes = $crate::test_utils::test_parse($src);
517 let mut p = $crate::test_utils::NodesTester::new(nodes);
518 $crate::test_utils::assert_parse_tree!(@seq p, $($tree)+);
519 }};
520
521 (@seq $parser:ident, Warn ( $code:expr ) $($rest:tt)*) => {
523 $parser.assert_next_warning($code);
524 $crate::test_utils::assert_parse_tree!(@seq $parser, $($rest)*);
525 };
526
527 (@seq $parser:ident, Error ( $code:expr ) $($rest:tt)*) => {
529 $parser.assert_next_error($code);
530 $crate::test_utils::assert_parse_tree!(@seq $parser, $($rest)*);
531 };
532
533 (@seq $parser:ident, $kind:ident [ $($children:tt)+ ] $($rest:tt)*) => {
535 $parser.assert_next_children($crate::SyntaxKind::$kind, |p| {
536 $crate::test_utils::assert_parse_tree!(@seq p, $($children)+);
537 });
538 $crate::test_utils::assert_parse_tree!(@seq $parser, $($rest)*);
539 };
540
541
542 (@seq $parser:ident, $kind:ident ( $text:expr ) $($rest:tt)*) => {
544 $parser.assert_next($crate::SyntaxKind::$kind, $text);
545 $crate::test_utils::assert_parse_tree!(@seq $parser, $($rest)*);
546 };
547
548 (@seq $parser:ident, $kind:ident $($rest:tt)*) => {
550 compile_error!(concat!("Leaf node `", stringify!($kind), "` must provide a string literal as text, like `", stringify!($kind), "(\"...\")`."))
551 };
552
553 (@seq $parser:ident, ...) => {
555 $parser.move_to_end();
557 };
558
559 (@seq $parser:ident,) => {
561 $parser.assert_end();
562 };
563}
564
565pub use assert_parse_tree;
566
567
568#[extension(pub trait LexerAssert)]
569impl<'a> Lexer<'a> {
570 fn assert_next(&mut self, kind: SyntaxKind, text: &str, range: Range<usize>) -> &mut Lexer<'a> {
571 assert_eq!(
572 self.next(),
573 (
574 kind,
575 SyntaxNode::leaf(kind, text, Span::new(self.file_id, range))
576 )
577 );
578 self
579 }
580
581 fn assert_next_error(
582 &mut self,
583 kind: SyntaxKind,
584 message: &str,
585 text: &str,
586 range: Range<usize>,
587 ) -> &mut Lexer<'a> {
588 assert_eq!(
589 self.next(),
590 (
591 kind,
592 SyntaxNode::error(
593 SyntaxError::new(message, Span::new(self.file_id, range)),
594 text
595 )
596 )
597 );
598 self
599 }
600
601 fn assert_end(&mut self, index: usize) -> &mut Lexer<'a> {
602 assert_eq!(
603 self.next(),
604 (
605 SyntaxKind::End,
606 SyntaxNode::leaf(SyntaxKind::End, "", Span::new(self.file_id, index..index))
607 )
608 );
609 self
610 }
611}
612#[macro_export]
692macro_rules! assert_tokens {
693 ($src:expr, $($tokens:tt)+) => {{
695 use $crate::test_utils::LexerAssert;
696 let file_id = $crate::test_utils::test_file_id();
697 let mut lexer = $crate::Lexer::new($src, file_id);
698 $crate::assert_tokens!(@seq lexer, $($tokens)+);
699 }};
700
701 (@seq $lexer:ident, !Error ( $message:expr, $text:expr, $range:expr ) $($rest:tt)*) => {
703 $lexer.assert_next_error($crate::SyntaxKind::Error, $message, $text, $range);
704 $crate::assert_tokens!(@seq $lexer, $($rest)*);
705 };
706
707 (@seq $lexer:ident, !Error ( $($tt:tt)* ) $($rest:tt)*) => {
709 compile_error!(concat!("Leaf node `Error` must provide a message, source text and a range, like `Error(\"message\", \"source text\", range)`."))
710 };
711
712 (@seq $lexer:ident, $kind:ident ( $text:expr, $range:expr ) $($rest:tt)*) => {
714 $lexer.assert_next($crate::SyntaxKind::$kind, $text, $range);
715 $crate::assert_tokens!(@seq $lexer, $($rest)*);
716 };
717
718 (@seq $lexer:ident, $kind:ident $($rest:tt)*) => (
720 $crate::SyntaxKind::$kind;
721 compile_error!(concat!("Incomplete token `", stringify!($kind), "` must provide a text and a range, like `", stringify!($kind), "(\"text\", range)`."))
722 );
723
724 (@seq $lexer:ident,) => {
726 $lexer.assert_end($lexer.cursor());
727 };
728}
729
730pub use assert_tokens;