1#![doc = include_str!("./../README.md")]
2
3use mago_span::Span;
4use mago_syntax_core::input::Input;
5
6use crate::ast::Type;
7use crate::error::ParseError;
8use crate::lexer::TypeLexer;
9
10pub mod ast;
11pub mod error;
12pub mod lexer;
13pub mod parser;
14pub mod token;
15
16pub fn parse_str(span: Span, input: &str) -> Result<Type<'_>, ParseError> {
33 let input = Input::anchored_at(span.file_id, input.as_bytes(), span.start);
35 let lexer = TypeLexer::new(input);
37 parser::construct(lexer)
39}
40
41#[cfg(test)]
42mod tests {
43 use mago_database::file::FileId;
44 use mago_span::Position;
45 use mago_span::Span;
46
47 use crate::ast::*;
48
49 use super::*;
50
51 fn do_parse(input: &str) -> Result<Type<'_>, ParseError> {
52 parse_str(Span::new(FileId::zero(), Position::new(0), Position::new(input.len() as u32)), input)
53 }
54
55 #[test]
56 fn test_parse_simple_keyword() {
57 let result = do_parse("int");
58 assert!(result.is_ok());
59 match result.unwrap() {
60 Type::Int(k) => assert_eq!(k.value, "int"),
61 _ => panic!("Expected Type::Int"),
62 }
63 }
64
65 #[test]
66 fn test_parse_composite_keyword() {
67 let result = do_parse("non-empty-string");
68 assert!(result.is_ok());
69 match result.unwrap() {
70 Type::NonEmptyString(k) => assert_eq!(k.value, "non-empty-string"),
71 _ => panic!("Expected Type::NonEmptyString"),
72 }
73 }
74
75 #[test]
76 fn test_parse_literal_ints() {
77 let assert_parsed_literal_int = |input: &str, expected_value: u64| {
78 let result = do_parse(input);
79 assert!(result.is_ok());
80 match result.unwrap() {
81 Type::LiteralInt(LiteralIntType { value, .. }) => assert_eq!(
82 value, expected_value,
83 "Expected value to be {expected_value} for input {input}, but got {value}"
84 ),
85 _ => panic!("Expected Type::LiteralInt"),
86 }
87 };
88
89 assert_parsed_literal_int("0", 0);
90 assert_parsed_literal_int("1", 1);
91 assert_parsed_literal_int("123_345", 123345);
92 assert_parsed_literal_int("0b1", 1);
93 assert_parsed_literal_int("0o10", 8);
94 assert_parsed_literal_int("0x1", 1);
95 assert_parsed_literal_int("0x10", 16);
96 assert_parsed_literal_int("0xFF", 255);
97 }
98
99 #[test]
100 fn test_parse_literal_floats() {
101 let assert_parsed_literal_float = |input: &str, expected_value: f64| {
102 let result = do_parse(input);
103 assert!(result.is_ok());
104 match result.unwrap() {
105 Type::LiteralFloat(LiteralFloatType { value, .. }) => assert_eq!(
106 value, expected_value,
107 "Expected value to be {expected_value} for input {input}, but got {value}"
108 ),
109 _ => panic!("Expected Type::LiteralInt"),
110 }
111 };
112
113 assert_parsed_literal_float("0.0", 0.0);
114 assert_parsed_literal_float("1.0", 1.0);
115 assert_parsed_literal_float("0.1e1", 1.0);
116 assert_parsed_literal_float("0.1e-1", 0.01);
117 assert_parsed_literal_float("0.1E1", 1.0);
118 assert_parsed_literal_float("0.1E-1", 0.01);
119 assert_parsed_literal_float("0.1e+1", 1.0);
120 assert_parsed_literal_float(".1e+1", 1.0);
121 }
122
123 #[test]
124 fn test_parse_simple_union() {
125 match do_parse("int|string") {
126 Ok(ty) => match ty {
127 Type::Union(u) => {
128 assert!(matches!(*u.left, Type::Int(_)));
129 assert!(matches!(*u.right, Type::String(_)));
130 }
131 _ => panic!("Expected Type::Union"),
132 },
133 Err(err) => {
134 panic!("Failed to parse union type: {err:?}");
135 }
136 }
137 }
138
139 #[test]
140 fn test_parse_variable_union() {
141 match do_parse("$a|$b") {
142 Ok(ty) => match ty {
143 Type::Union(u) => {
144 assert!(matches!(*u.left, Type::Variable(_)));
145 assert!(matches!(*u.right, Type::Variable(_)));
146 }
147 _ => panic!("Expected Type::Union"),
148 },
149 Err(err) => {
150 panic!("Failed to parse union type: {err:?}");
151 }
152 }
153 }
154
155 #[test]
156 fn test_parse_nullable() {
157 let result = do_parse("?string");
158 assert!(result.is_ok());
159 match result.unwrap() {
160 Type::Nullable(n) => {
161 assert!(matches!(*n.inner, Type::String(_)));
162 }
163 _ => panic!("Expected Type::Nullable"),
164 }
165 }
166
167 #[test]
168 fn test_parse_generic_array() {
169 let result = do_parse("array<int, bool>");
170 assert!(result.is_ok());
171 match result.unwrap() {
172 Type::Array(a) => {
173 assert!(a.parameters.is_some());
174 let params = a.parameters.unwrap();
175 assert_eq!(params.entries.len(), 2);
176 assert!(matches!(params.entries[0].inner, Type::Int(_)));
177 assert!(matches!(params.entries[1].inner, Type::Bool(_)));
178 }
179 _ => panic!("Expected Type::Array"),
180 }
181 }
182
183 #[test]
184 fn test_parse_generic_array_one_param() {
185 match do_parse("array<string>") {
186 Ok(Type::Array(a)) => {
187 let params = a.parameters.expect("Expected generic parameters");
188 assert_eq!(params.entries.len(), 1);
189 assert!(matches!(params.entries[0].inner, Type::String(_)));
190 }
191 res => panic!("Expected Ok(Type::Array), got {res:?}"),
192 }
193 }
194
195 #[test]
196 fn test_parse_generic_list() {
197 match do_parse("list<string>") {
198 Ok(Type::List(l)) => {
199 let params = l.parameters.expect("Expected generic parameters");
200 assert_eq!(params.entries.len(), 1);
201 assert!(matches!(params.entries[0].inner, Type::String(_)));
202 }
203 res => panic!("Expected Ok(Type::List), got {res:?}"),
204 }
205 }
206
207 #[test]
208 fn test_parse_non_empty_array() {
209 match do_parse("non-empty-array<int, bool>") {
210 Ok(Type::NonEmptyArray(a)) => {
211 let params = a.parameters.expect("Expected generic parameters");
212 assert_eq!(params.entries.len(), 2);
213 assert!(matches!(params.entries[0].inner, Type::Int(_)));
214 assert!(matches!(params.entries[1].inner, Type::Bool(_)));
215 }
216 res => panic!("Expected Ok(Type::NonEmptyArray), got {res:?}"),
217 }
218 }
219
220 #[test]
221 fn test_parse_nested_generics() {
222 match do_parse("list<array<int, string>>") {
223 Ok(Type::List(l)) => {
224 let params = l.parameters.expect("Expected generic parameters");
225 assert_eq!(params.entries.len(), 1);
226 match ¶ms.entries[0].inner {
227 Type::Array(inner_array) => {
228 let inner_params = inner_array.parameters.as_ref().expect("Inner array needs params");
229 assert_eq!(inner_params.entries.len(), 2);
230 assert!(matches!(inner_params.entries[0].inner, Type::Int(_)));
231 assert!(matches!(inner_params.entries[1].inner, Type::String(_)));
232 }
233 _ => panic!("Expected inner type to be Type::Array"),
234 }
235 }
236 res => panic!("Expected Ok(Type::List), got {res:?}"),
237 }
238 }
239
240 #[test]
241 fn test_parse_simple_shape() {
242 let result = do_parse("array{'name': string}");
243 assert!(matches!(result, Ok(Type::Shape(_))));
244 let Ok(Type::Shape(shape)) = result else {
245 panic!("Expected Type::Shape");
246 };
247
248 assert_eq!(shape.kind, ShapeTypeKind::Array);
249 assert_eq!(shape.keyword.value, "array");
250 assert_eq!(shape.fields.len(), 1);
251 assert!(shape.additional_fields.is_none());
252
253 let field = &shape.fields[0];
254 assert!(matches!(
255 field.key.as_ref().map(|k| k.name.as_ref()),
256 Some(Type::LiteralString(LiteralStringType { raw: "'name'", value: "name", .. }))
257 ));
258 assert!(matches!(field.value.as_ref(), Type::String(_)));
259 }
260
261 #[test]
262 fn test_parse_int_key_shape() {
263 match do_parse("array{0: string, 1: bool}") {
264 Ok(Type::Shape(shape)) => {
265 assert_eq!(shape.fields.len(), 2);
266 let first_field = &shape.fields[0];
267 assert!(matches!(
268 first_field.key.as_ref().map(|k| k.name.as_ref()),
269 Some(Type::LiteralInt(LiteralIntType { value: 0, .. }))
270 ));
271 assert!(matches!(first_field.value.as_ref(), Type::String(_)));
272 let second_field = &shape.fields[1];
273 assert!(matches!(
274 second_field.key.as_ref().map(|k| k.name.as_ref()),
275 Some(Type::LiteralInt(LiteralIntType { value: 1, .. }))
276 ));
277 assert!(matches!(second_field.value.as_ref(), Type::Bool(_)));
278 }
279 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
280 }
281 }
282
283 #[test]
284 fn test_parse_optional_field_shape() {
285 match do_parse("array{name: string, age?: int, address: string}") {
286 Ok(Type::Shape(shape)) => {
287 assert_eq!(shape.fields.len(), 3);
288 assert!(!shape.fields[0].is_optional());
289 assert!(shape.fields[1].is_optional());
290 assert!(!shape.fields[2].is_optional());
291 }
292 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
293 }
294 }
295
296 #[test]
297 fn test_parse_unsealed_shape() {
298 match do_parse("array{name: string, ...}") {
299 Ok(Type::Shape(shape)) => {
300 assert_eq!(shape.fields.len(), 1);
301 assert!(shape.additional_fields.is_some());
302 assert!(shape.additional_fields.unwrap().parameters.is_none()); }
304 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
305 }
306 }
307
308 #[test]
309 fn test_parse_shape_with_keys_containing_special_chars() {
310 match do_parse("array{key-with-dash: int, key-with---multiple-dashes?: int}") {
311 Ok(Type::Shape(shape)) => {
312 assert_eq!(shape.fields.len(), 2);
313
314 if let Some(Type::Reference(r)) = shape.fields[0].key.as_ref().map(|k| k.name.as_ref()) {
315 assert_eq!(r.identifier.value, "key-with-dash");
316 } else {
317 panic!("Expected key to be a Type::Reference");
318 }
319
320 if let Some(Type::Reference(r)) = shape.fields[1].key.as_ref().map(|k| k.name.as_ref()) {
321 assert_eq!(r.identifier.value, "key-with---multiple-dashes");
322 } else {
323 panic!("Expected key to be a Type::Reference");
324 }
325 }
326 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
327 }
328 }
329
330 #[test]
331 fn test_parse_shape_with_keys_after_types() {
332 match do_parse("array{list: list<int>, int?: int, string: string, bool: bool}") {
333 Ok(Type::Shape(shape)) => {
334 assert_eq!(shape.fields.len(), 4);
335
336 if let Some(Type::Reference(r)) = shape.fields[0].key.as_ref().map(|k| k.name.as_ref()) {
337 assert_eq!(r.identifier.value, "list");
338 } else {
339 panic!("Expected key to be a Type::Reference");
340 }
341
342 if let Some(Type::Reference(r)) = shape.fields[1].key.as_ref().map(|k| k.name.as_ref()) {
343 assert_eq!(r.identifier.value, "int");
344 } else {
345 panic!("Expected key to be a Type::Reference");
346 }
347
348 if let Some(Type::Reference(r)) = shape.fields[2].key.as_ref().map(|k| k.name.as_ref()) {
349 assert_eq!(r.identifier.value, "string");
350 } else {
351 panic!("Expected key to be a Type::Reference");
352 }
353
354 if let Some(Type::Reference(r)) = shape.fields[3].key.as_ref().map(|k| k.name.as_ref()) {
355 assert_eq!(r.identifier.value, "bool");
356 } else {
357 panic!("Expected key to be a Type::Reference");
358 }
359 }
360 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
361 }
362 }
363
364 #[test]
365 fn test_parse_unsealed_shape_with_fallback() {
366 match do_parse(
367 "array{
368 name: string, // This is a comment
369 ...<string, string>
370 }",
371 ) {
372 Ok(Type::Shape(shape)) => {
373 assert_eq!(shape.fields.len(), 1);
374 assert!(shape.additional_fields.as_ref().is_some_and(|a| a.parameters.is_some()));
375 let params = shape.additional_fields.unwrap().parameters.unwrap();
376 assert_eq!(params.entries.len(), 2);
377 assert!(matches!(params.entries[0].inner, Type::String(_)));
378 assert!(matches!(params.entries[1].inner, Type::String(_)));
379 }
380 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
381 }
382 }
383
384 #[test]
385 fn test_parse_empty_shape() {
386 match do_parse("array{}") {
387 Ok(Type::Shape(shape)) => {
388 assert_eq!(shape.fields.len(), 0);
389 assert!(shape.additional_fields.is_none());
390 }
391 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
392 }
393 }
394
395 #[test]
396 fn test_parse_error_unexpected_token() {
397 let result = do_parse("int|>");
398 assert!(result.is_err());
399 assert!(matches!(result.unwrap_err(), ParseError::UnexpectedToken { .. }));
400 }
401
402 #[test]
403 fn test_parse_error_eof() {
404 let result = do_parse("array<int");
405 assert!(result.is_err());
406 assert!(matches!(result.unwrap_err(), ParseError::UnexpectedEndOfFile { .. }));
407 }
408
409 #[test]
410 fn test_parse_error_trailing_token() {
411 let result = do_parse("int|string&");
412 assert!(result.is_err());
413 assert!(matches!(result.unwrap_err(), ParseError::UnexpectedEndOfFile { .. }));
414 }
415
416 #[test]
417 fn test_parse_intersection() {
418 match do_parse("Countable&Traversable") {
419 Ok(Type::Intersection(i)) => {
420 assert!(matches!(*i.left, Type::Reference(_)));
421 assert!(matches!(*i.right, Type::Reference(_)));
422
423 if let Type::Reference(r) = *i.left {
424 assert_eq!(r.identifier.value, "Countable");
425 } else {
426 panic!();
427 }
428
429 if let Type::Reference(r) = *i.right {
430 assert_eq!(r.identifier.value, "Traversable");
431 } else {
432 panic!();
433 }
434 }
435 res => panic!("Expected Ok(Type::Intersection), got {res:?}"),
436 }
437 }
438
439 #[test]
440 fn test_parse_member_ref() {
441 match do_parse("MyClass::MY_CONST") {
442 Ok(Type::MemberReference(m)) => {
443 assert_eq!(m.class.value, "MyClass");
444 assert_eq!(m.member.to_string(), "MY_CONST");
445 }
446 res => panic!("Expected Ok(Type::MemberReference), got {res:?}"),
447 }
448
449 match do_parse("\\Fully\\Qualified::class") {
450 Ok(Type::MemberReference(m)) => {
451 assert_eq!(m.class.value, "\\Fully\\Qualified"); assert_eq!(m.member.to_string(), "class");
453 }
454 res => panic!("Expected Ok(Type::MemberReference), got {res:?}"),
455 }
456 }
457
458 #[test]
459 fn test_parse_iterable() {
460 match do_parse("iterable<int, string>") {
461 Ok(Type::Iterable(i)) => {
462 let params = i.parameters.expect("Expected generic parameters");
463 assert_eq!(params.entries.len(), 2);
464 assert!(matches!(params.entries[0].inner, Type::Int(_)));
465 assert!(matches!(params.entries[1].inner, Type::String(_)));
466 }
467 res => panic!("Expected Ok(Type::Iterable), got {res:?}"),
468 }
469
470 match do_parse("iterable<bool>") {
471 Ok(Type::Iterable(i)) => {
473 let params = i.parameters.expect("Expected generic parameters");
474 assert_eq!(params.entries.len(), 1);
475 assert!(matches!(params.entries[0].inner, Type::Bool(_)));
476 }
477 res => panic!("Expected Ok(Type::Iterable), got {res:?}"),
478 }
479
480 match do_parse("iterable") {
481 Ok(Type::Iterable(i)) => {
482 assert!(i.parameters.is_none());
483 }
484 res => panic!("Expected Ok(Type::Iterable), got {res:?}"),
485 }
486 }
487
488 #[test]
489 fn test_parse_negated_int() {
490 let assert_negated_int = |input: &str, expected_value: u64| {
491 let result = do_parse(input);
492 assert!(result.is_ok());
493 match result.unwrap() {
494 Type::Negated(n) => {
495 assert!(matches!(n.number, LiteralIntOrFloatType::Int(_)));
496 if let LiteralIntOrFloatType::Int(lit) = n.number {
497 assert_eq!(lit.value, expected_value);
498 } else {
499 panic!()
500 }
501 }
502 _ => panic!("Expected Type::Negated"),
503 }
504 };
505
506 assert_negated_int("-0", 0);
507 assert_negated_int("-1", 1);
508 assert_negated_int(
509 "-
510 // This is a comment
511 123_345",
512 123345,
513 );
514 assert_negated_int("-0b1", 1);
515 }
516
517 #[test]
518 fn test_parse_negated_float() {
519 let assert_negated_float = |input: &str, expected_value: f64| {
520 let result = do_parse(input);
521 assert!(result.is_ok());
522 match result.unwrap() {
523 Type::Negated(n) => {
524 assert!(matches!(n.number, LiteralIntOrFloatType::Float(_)));
525 if let LiteralIntOrFloatType::Float(lit) = n.number {
526 assert_eq!(lit.value, expected_value);
527 } else {
528 panic!()
529 }
530 }
531 _ => panic!("Expected Type::Negated"),
532 }
533 };
534
535 assert_negated_float("-0.0", 0.0);
536 assert_negated_float("-1.0", 1.0);
537 assert_negated_float("-0.1e1", 1.0);
538 assert_negated_float("-0.1e-1", 0.01);
539 }
540
541 #[test]
542 fn test_parse_negated_union() {
543 match do_parse("-1|-2.0|string") {
544 Ok(Type::Union(n)) => {
545 assert!(matches!(*n.left, Type::Negated(_)));
546 assert!(matches!(*n.right, Type::Union(_)));
547
548 if let Type::Negated(neg) = *n.left {
549 assert!(matches!(neg.number, LiteralIntOrFloatType::Int(_)));
550 if let LiteralIntOrFloatType::Int(lit) = neg.number {
551 assert_eq!(lit.value, 1);
552 } else {
553 panic!()
554 }
555 } else {
556 panic!("Expected left side to be Type::Negated");
557 }
558
559 if let Type::Union(inner_union) = *n.right {
560 assert!(matches!(*inner_union.left, Type::Negated(_)));
561 assert!(matches!(*inner_union.right, Type::String(_)));
562
563 if let Type::Negated(neg) = *inner_union.left {
564 assert!(matches!(neg.number, LiteralIntOrFloatType::Float(_)));
565 if let LiteralIntOrFloatType::Float(lit) = neg.number {
566 assert_eq!(lit.value, 2.0);
567 } else {
568 panic!()
569 }
570 } else {
571 panic!("Expected left side of inner union to be Type::Negated");
572 }
573
574 if let Type::String(s) = *inner_union.right {
575 assert_eq!(s.value, "string");
576 } else {
577 panic!("Expected right side of inner union to be Type::String");
578 }
579 } else {
580 panic!("Expected right side to be Type::Union");
581 }
582 }
583 res => panic!("Expected Ok(Type::Negated), got {res:?}"),
584 }
585 }
586
587 #[test]
588 fn test_parse_callable_no_spec() {
589 match do_parse("callable") {
590 Ok(Type::Callable(c)) => {
591 assert!(c.specification.is_none());
592 assert_eq!(c.kind, CallableTypeKind::Callable);
593 }
594 res => panic!("Expected Ok(Type::Callable), got {res:?}"),
595 }
596 }
597
598 #[test]
599 fn test_parse_callable_params_only() {
600 match do_parse("callable(int, ?string)") {
601 Ok(Type::Callable(c)) => {
602 let spec = c.specification.expect("Expected callable specification");
603 assert!(spec.return_type.is_none());
604 assert_eq!(spec.parameters.entries.len(), 2);
605 assert!(matches!(spec.parameters.entries[0].parameter_type, Some(Type::Int(_))));
606 assert!(matches!(spec.parameters.entries[1].parameter_type, Some(Type::Nullable(_))));
607 assert!(spec.parameters.entries[0].ellipsis.is_none());
608 assert!(spec.parameters.entries[0].equals.is_none());
609 }
610 res => panic!("Expected Ok(Type::Callable), got {res:?}"),
611 }
612 }
613
614 #[test]
615 fn test_parse_callable_return_only() {
616 match do_parse("callable(): void") {
617 Ok(Type::Callable(c)) => {
618 let spec = c.specification.expect("Expected callable specification");
619 assert!(spec.parameters.entries.is_empty());
620 assert!(spec.return_type.is_some());
621 assert!(matches!(*spec.return_type.unwrap().return_type, Type::Void(_)));
622 }
623 res => panic!("Expected Ok(Type::Callable), got {res:?}"),
624 }
625 }
626
627 #[test]
628 fn test_parse_pure_callable_full() {
629 match do_parse("pure-callable(bool): int") {
630 Ok(Type::Callable(c)) => {
631 assert_eq!(c.kind, CallableTypeKind::PureCallable);
632 let spec = c.specification.expect("Expected callable specification");
633 assert_eq!(spec.parameters.entries.len(), 1);
634 assert!(matches!(spec.parameters.entries[0].parameter_type, Some(Type::Bool(_))));
635 assert!(spec.return_type.is_some());
636 assert!(matches!(*spec.return_type.unwrap().return_type, Type::Int(_)));
637 }
638 res => panic!("Expected Ok(Type::Callable), got {res:?}"),
639 }
640 }
641
642 #[test]
643 fn test_parse_closure_via_identifier() {
644 match do_parse("Closure(string): bool") {
645 Ok(Type::Callable(c)) => {
646 assert_eq!(c.kind, CallableTypeKind::Closure);
647 assert_eq!(c.keyword.value, "Closure");
648 let spec = c.specification.expect("Expected callable specification");
649 assert_eq!(spec.parameters.entries.len(), 1);
650 assert!(matches!(spec.parameters.entries[0].parameter_type, Some(Type::String(_))));
651 assert!(spec.return_type.is_some());
652 assert!(matches!(*spec.return_type.unwrap().return_type, Type::Bool(_)));
653 }
654 res => panic!("Expected Ok(Type::Callable) for Closure, got {res:?}"),
655 }
656 }
657
658 #[test]
659 fn test_parse_complex_pure_callable() {
660 match do_parse("pure-callable(list<int>, ?Closure(): void=, int...): ((Simple&Iter<T>)|null)") {
661 Ok(Type::Callable(c)) => {
662 assert_eq!(c.kind, CallableTypeKind::PureCallable);
663 let spec = c.specification.expect("Expected callable specification");
664 assert_eq!(spec.parameters.entries.len(), 3);
665 assert!(spec.return_type.is_some());
666
667 let first_param = &spec.parameters.entries[0];
668 assert!(matches!(first_param.parameter_type, Some(Type::List(_))));
669 assert!(first_param.ellipsis.is_none());
670 assert!(first_param.equals.is_none());
671
672 let second_param = &spec.parameters.entries[1];
673 assert!(matches!(second_param.parameter_type, Some(Type::Nullable(_))));
674 assert!(second_param.ellipsis.is_none());
675 assert!(second_param.equals.is_some());
676
677 let third_param = &spec.parameters.entries[2];
678 assert!(matches!(third_param.parameter_type, Some(Type::Int(_))));
679 assert!(third_param.ellipsis.is_some());
680 assert!(third_param.equals.is_none());
681
682 if let Type::Parenthesized(p) = *spec.return_type.unwrap().return_type {
683 assert!(matches!(*p.inner, Type::Union(_)));
684 if let Type::Union(u) = *p.inner {
685 assert!(matches!(u.left.as_ref(), Type::Parenthesized(_)));
686 assert!(matches!(u.right.as_ref(), Type::Null(_)));
687 }
688 } else {
689 panic!("Expected Type::CallableReturnType");
690 }
691 }
692 res => panic!("Expected Ok(Type::Callable), got {res:?}"),
693 }
694 }
695
696 #[test]
697 fn test_parse_conditional_type() {
698 match do_parse("int is not string ? array : int") {
699 Ok(Type::Conditional(c)) => {
700 assert!(matches!(*c.subject, Type::Int(_)));
701 assert!(c.not.is_some());
702 assert!(matches!(*c.target, Type::String(_)));
703 assert!(matches!(*c.then, Type::Array(_)));
704 assert!(matches!(*c.otherwise, Type::Int(_)));
705 }
706 res => panic!("Expected Ok(Type::Conditional), got {res:?}"),
707 }
708
709 match do_parse("$input is string ? array : int") {
710 Ok(Type::Conditional(c)) => {
711 assert!(matches!(*c.subject, Type::Variable(_)));
712 assert!(c.not.is_none());
713 assert!(matches!(*c.target, Type::String(_)));
714 assert!(matches!(*c.then, Type::Array(_)));
715 assert!(matches!(*c.otherwise, Type::Int(_)));
716 }
717 res => panic!("Expected Ok(Type::Conditional), got {res:?}"),
718 }
719
720 match do_parse("int is string ? array : (int is not $bar ? string : $baz)") {
721 Ok(Type::Conditional(c)) => {
722 assert!(matches!(*c.subject, Type::Int(_)));
723 assert!(c.not.is_none());
724 assert!(matches!(*c.target, Type::String(_)));
725 assert!(matches!(*c.then, Type::Array(_)));
726
727 let Type::Parenthesized(p) = *c.otherwise else {
728 panic!("Expected Type::Parenthesized");
729 };
730
731 if let Type::Conditional(inner_conditional) = *p.inner {
732 assert!(matches!(*inner_conditional.subject, Type::Int(_)));
733 assert!(inner_conditional.not.is_some());
734 assert!(matches!(*inner_conditional.target, Type::Variable(_)));
735 assert!(matches!(*inner_conditional.then, Type::String(_)));
736 assert!(matches!(*inner_conditional.otherwise, Type::Variable(_)));
737 } else {
738 panic!("Expected Type::Conditional");
739 }
740 }
741 res => panic!("Expected Ok(Type::Conditional), got {res:?}"),
742 }
743 }
744
745 #[test]
746 fn test_keyof() {
747 match do_parse("key-of<MyArray>") {
748 Ok(Type::KeyOf(k)) => {
749 assert_eq!(k.keyword.value, "key-of");
750 match &k.parameter.entry.inner {
751 Type::Reference(r) => assert_eq!(r.identifier.value, "MyArray"),
752 _ => panic!("Expected Type::Reference"),
753 }
754 }
755 res => panic!("Expected Ok(Type::KeyOf), got {res:?}"),
756 }
757 }
758
759 #[test]
760 fn test_valueof() {
761 match do_parse("value-of<MyArray>") {
762 Ok(Type::ValueOf(v)) => {
763 assert_eq!(v.keyword.value, "value-of");
764 match &v.parameter.entry.inner {
765 Type::Reference(r) => assert_eq!(r.identifier.value, "MyArray"),
766 _ => panic!("Expected Type::Reference"),
767 }
768 }
769 res => panic!("Expected Ok(Type::ValueOf), got {res:?}"),
770 }
771 }
772
773 #[test]
774 fn test_indexed_access() {
775 match do_parse("MyArray[MyKey]") {
776 Ok(Type::IndexAccess(i)) => {
777 match *i.target {
778 Type::Reference(r) => assert_eq!(r.identifier.value, "MyArray"),
779 _ => panic!("Expected Type::Reference"),
780 }
781 match *i.index {
782 Type::Reference(r) => assert_eq!(r.identifier.value, "MyKey"),
783 _ => panic!("Expected Type::Reference"),
784 }
785 }
786 res => panic!("Expected Ok(Type::IndexAccess), got {res:?}"),
787 }
788 }
789
790 #[test]
791 fn test_slice_type() {
792 match do_parse("string[]") {
793 Ok(Type::Slice(s)) => {
794 assert!(matches!(*s.inner, Type::String(_)));
795 }
796 res => panic!("Expected Ok(Type::Slice), got {res:?}"),
797 }
798 }
799
800 #[test]
801 fn test_slice_of_slice_of_slice_type() {
802 match do_parse("string[][][]") {
803 Ok(Type::Slice(s)) => {
804 assert!(matches!(*s.inner, Type::Slice(_)));
805 if let Type::Slice(inner_slice) = *s.inner {
806 assert!(matches!(*inner_slice.inner, Type::Slice(_)));
807 if let Type::Slice(inner_inner_slice) = *inner_slice.inner {
808 assert!(matches!(*inner_inner_slice.inner, Type::String(_)));
809 } else {
810 panic!("Expected inner slice to be a Slice");
811 }
812 } else {
813 panic!("Expected outer slice to be a Slice");
814 }
815 }
816 res => panic!("Expected Ok(Type::Slice), got {res:?}"),
817 }
818 }
819
820 #[test]
821 fn test_int_range() {
822 match do_parse("int<0, 100>") {
823 Ok(Type::IntRange(r)) => {
824 assert_eq!(r.keyword.value, "int");
825
826 match r.min {
827 IntOrKeyword::Int(literal_int_type) => {
828 assert_eq!(literal_int_type.value, 0);
829 }
830 _ => {
831 panic!("Expected min to be a LiteralIntType, got `{}`", r.min)
832 }
833 };
834
835 match r.max {
836 IntOrKeyword::Int(literal_int_type) => {
837 assert_eq!(literal_int_type.value, 100);
838 }
839 _ => {
840 panic!("Expected max to be a LiteralIntType, got `{}`", r.max)
841 }
842 };
843 }
844 res => panic!("Expected Ok(Type::IntRange), got {res:?}"),
845 }
846
847 match do_parse("int<min, 0>") {
848 Ok(Type::IntRange(r)) => {
849 match r.min {
850 IntOrKeyword::Keyword(keyword) => {
851 assert_eq!(keyword.value, "min");
852 }
853 _ => {
854 panic!("Expected min to be a Keyword, got `{}`", r.min)
855 }
856 };
857
858 match r.max {
859 IntOrKeyword::Int(literal_int_type) => {
860 assert_eq!(literal_int_type.value, 0);
861 }
862 _ => {
863 panic!("Expected max to be a LiteralIntType, got `{}`", r.max)
864 }
865 };
866 }
867 res => panic!("Expected Ok(Type::IntRange), got {res:?}"),
868 }
869
870 match do_parse("int<min, max>") {
871 Ok(Type::IntRange(r)) => {
872 match r.min {
873 IntOrKeyword::Keyword(keyword) => {
874 assert_eq!(keyword.value, "min");
875 }
876 _ => {
877 panic!("Expected min to be a Keyword, got `{}`", r.min)
878 }
879 };
880
881 match r.max {
882 IntOrKeyword::Keyword(keyword) => {
883 assert_eq!(keyword.value, "max");
884 }
885 _ => {
886 panic!("Expected max to be a Keyword, got `{}`", r.max)
887 }
888 };
889 }
890 res => panic!("Expected Ok(Type::IntRange), got {res:?}"),
891 }
892 }
893
894 #[test]
895 fn test_properties_of() {
896 match do_parse("properties-of<MyClass>") {
897 Ok(Type::PropertiesOf(p)) => {
898 assert_eq!(p.keyword.value, "properties-of");
899 assert_eq!(p.filter, PropertiesOfFilter::All);
900 match &p.parameter.entry.inner {
901 Type::Reference(r) => assert_eq!(r.identifier.value, "MyClass"),
902 _ => panic!(),
903 }
904 }
905 res => panic!("Expected Ok(Type::PropertiesOf), got {res:?}"),
906 }
907
908 match do_parse("protected-properties-of<T>") {
909 Ok(Type::PropertiesOf(p)) => {
910 assert_eq!(p.keyword.value, "protected-properties-of");
911 assert_eq!(p.filter, PropertiesOfFilter::Protected);
912 match &p.parameter.entry.inner {
913 Type::Reference(r) => assert_eq!(r.identifier.value, "T"),
914 _ => panic!(),
915 }
916 }
917 res => panic!("Expected Ok(Type::PropertiesOf), got {res:?}"),
918 }
919
920 match do_parse("private-properties-of<T>") {
921 Ok(Type::PropertiesOf(p)) => {
922 assert_eq!(p.keyword.value, "private-properties-of");
923 assert_eq!(p.filter, PropertiesOfFilter::Private);
924 match &p.parameter.entry.inner {
925 Type::Reference(r) => assert_eq!(r.identifier.value, "T"),
926 _ => panic!(),
927 }
928 }
929 res => panic!("Expected Ok(Type::PropertiesOf), got {res:?}"),
930 }
931
932 match do_parse("public-properties-of<T>") {
933 Ok(Type::PropertiesOf(p)) => {
934 assert_eq!(p.keyword.value, "public-properties-of");
935 assert_eq!(p.filter, PropertiesOfFilter::Public);
936 match &p.parameter.entry.inner {
937 Type::Reference(r) => assert_eq!(r.identifier.value, "T"),
938 _ => panic!(),
939 }
940 }
941 res => panic!("Expected Ok(Type::PropertiesOf), got {res:?}"),
942 }
943 }
944
945 #[test]
946 fn test_variable() {
947 match do_parse("$myVar") {
948 Ok(Type::Variable(v)) => {
949 assert_eq!(v.value, "$myVar");
950 }
951 res => panic!("Expected Ok(Type::Variable), got {res:?}"),
952 }
953 }
954
955 #[test]
956 fn test_nullable_intersection() {
957 match do_parse("Countable&?Traversable") {
959 Ok(Type::Intersection(i)) => {
960 assert!(matches!(*i.left, Type::Reference(r) if r.identifier.value == "Countable"));
961 assert!(matches!(*i.right, Type::Nullable(_)));
962 if let Type::Nullable(n) = *i.right {
963 assert!(matches!(*n.inner, Type::Reference(r) if r.identifier.value == "Traversable"));
964 } else {
965 panic!();
966 }
967 }
968 res => panic!("Expected Ok(Type::Intersection), got {res:?}"),
969 }
970 }
971
972 #[test]
973 fn test_parenthesized_nullable() {
974 match do_parse("?(Countable&Traversable)") {
975 Ok(Type::Nullable(n)) => {
976 assert!(matches!(*n.inner, Type::Parenthesized(_)));
977 if let Type::Parenthesized(p) = *n.inner {
978 assert!(matches!(*p.inner, Type::Intersection(_)));
979 } else {
980 panic!()
981 }
982 }
983 res => panic!("Expected Ok(Type::Nullable), got {res:?}"),
984 }
985 }
986
987 #[test]
988 fn test_positive_negative_int() {
989 match do_parse("positive-int|negative-int") {
990 Ok(Type::Union(u)) => {
991 assert!(matches!(*u.left, Type::PositiveInt(_)));
992 assert!(matches!(*u.right, Type::NegativeInt(_)));
993 }
994 res => panic!("Expected Ok(Type::Union), got {res:?}"),
995 }
996 }
997
998 #[test]
999 fn test_parse_float_alias() {
1000 match do_parse("double") {
1001 Ok(Type::Float(f)) => {
1002 assert_eq!(f.value, "double");
1003 }
1004 res => panic!("Expected Ok(Type::Float), got {res:?}"),
1005 }
1006
1007 match do_parse("real") {
1008 Ok(Type::Float(f)) => {
1009 assert_eq!(f.value, "real");
1010 }
1011 res => panic!("Expected Ok(Type::Float), got {res:?}"),
1012 }
1013
1014 match do_parse("float") {
1015 Ok(Type::Float(f)) => {
1016 assert_eq!(f.value, "float");
1017 }
1018 res => panic!("Expected Ok(Type::Float), got {res:?}"),
1019 }
1020 }
1021
1022 #[test]
1023 fn test_parse_bool_alias() {
1024 match do_parse("boolean") {
1025 Ok(Type::Bool(b)) => {
1026 assert_eq!(b.value, "boolean");
1027 }
1028 res => panic!("Expected Ok(Type::Bool), got {res:?}"),
1029 }
1030
1031 match do_parse("bool") {
1032 Ok(Type::Bool(b)) => {
1033 assert_eq!(b.value, "bool");
1034 }
1035 res => panic!("Expected Ok(Type::Bool), got {res:?}"),
1036 }
1037 }
1038
1039 #[test]
1040 fn test_parse_integer_alias() {
1041 match do_parse("integer") {
1042 Ok(Type::Int(i)) => {
1043 assert_eq!(i.value, "integer");
1044 }
1045 res => panic!("Expected Ok(Type::Int), got {res:?}"),
1046 }
1047
1048 match do_parse("int") {
1049 Ok(Type::Int(i)) => {
1050 assert_eq!(i.value, "int");
1051 }
1052 res => panic!("Expected Ok(Type::Int), got {res:?}"),
1053 }
1054 }
1055
1056 #[test]
1057 fn test_parse_callable_with_variables() {
1058 match do_parse("callable(string ...$names)") {
1059 Ok(Type::Callable(callable)) => {
1060 assert_eq!(callable.keyword.value, "callable");
1061 assert!(callable.specification.is_some());
1062
1063 let specification = callable.specification.unwrap();
1064
1065 assert!(specification.return_type.is_none());
1066 assert_eq!(specification.parameters.entries.len(), 1);
1067
1068 let first_parameter = specification.parameters.entries.first().unwrap();
1069 assert!(first_parameter.variable.is_some());
1070 assert!(first_parameter.ellipsis.is_some());
1071
1072 let variable = first_parameter.variable.unwrap();
1073 assert_eq!(variable.value, "$names");
1074 }
1075 res => panic!("Expected Ok(Type::Callable), got {res:?}"),
1076 }
1077 }
1078
1079 #[test]
1080 fn test_parse_string_or_lowercase_string_union() {
1081 match do_parse("string|lowercase-string") {
1082 Ok(Type::Union(u)) => {
1083 assert!(matches!(*u.left, Type::String(_)));
1084 assert!(matches!(*u.right, Type::LowercaseString(_)));
1085 }
1086 res => panic!("Expected Ok(Type::Union), got {res:?}"),
1087 }
1088 }
1089
1090 #[test]
1091 fn test_parse_optional_literal_string_shape_field() {
1092 match do_parse("array{'salt'?: int, 'cost'?: int, ...}") {
1093 Ok(Type::Shape(shape)) => {
1094 assert_eq!(shape.fields.len(), 2);
1095 assert!(shape.additional_fields.is_some());
1096
1097 let first_field = &shape.fields[0];
1098 assert!(first_field.is_optional());
1099 assert!(matches!(
1100 first_field.key.as_ref().map(|k| k.name.as_ref()),
1101 Some(Type::LiteralString(LiteralStringType { raw: "'salt'", value: "salt", .. }))
1102 ));
1103 assert!(matches!(first_field.value.as_ref(), Type::Int(_)));
1104
1105 let second_field = &shape.fields[1];
1106 assert!(second_field.is_optional());
1107 assert!(matches!(
1108 second_field.key.as_ref().map(|k| k.name.as_ref()),
1109 Some(Type::LiteralString(LiteralStringType { raw: "'cost'", value: "cost", .. }))
1110 ));
1111 assert!(matches!(second_field.value.as_ref(), Type::Int(_)));
1112 }
1113 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
1114 }
1115 }
1116}