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(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_source::SourceIdentifier;
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 let source = SourceIdentifier::dummy();
53 let span = Span::new(Position::new(source, 0), Position::new(source, input.len()));
54 parse_str(span, input)
55 }
56
57 #[test]
58 fn test_parse_simple_keyword() {
59 let result = do_parse("int");
60 assert!(result.is_ok());
61 match result.unwrap() {
62 Type::Int(k) => assert_eq!(k.value, "int"),
63 _ => panic!("Expected Type::Int"),
64 }
65 }
66
67 #[test]
68 fn test_parse_composite_keyword() {
69 let result = do_parse("non-empty-string");
70 assert!(result.is_ok());
71 match result.unwrap() {
72 Type::NonEmptyString(k) => assert_eq!(k.value, "non-empty-string"),
73 _ => panic!("Expected Type::NonEmptyString"),
74 }
75 }
76
77 #[test]
78 fn test_parse_literal_ints() {
79 let assert_parsed_literal_int = |input: &str, expected_value: u64| {
80 let result = do_parse(input);
81 assert!(result.is_ok());
82 match result.unwrap() {
83 Type::LiteralInt(LiteralIntType { value, .. }) => assert_eq!(
84 value, expected_value,
85 "Expected value to be {expected_value} for input {input}, but got {value}"
86 ),
87 _ => panic!("Expected Type::LiteralInt"),
88 }
89 };
90
91 assert_parsed_literal_int("0", 0);
92 assert_parsed_literal_int("1", 1);
93 assert_parsed_literal_int("123_345", 123345);
94 assert_parsed_literal_int("0b1", 1);
95 assert_parsed_literal_int("0o10", 8);
96 assert_parsed_literal_int("0x1", 1);
97 assert_parsed_literal_int("0x10", 16);
98 assert_parsed_literal_int("0xFF", 255);
99 }
100
101 #[test]
102 fn test_parse_literal_floats() {
103 let assert_parsed_literal_float = |input: &str, expected_value: f64| {
104 let result = do_parse(input);
105 assert!(result.is_ok());
106 match result.unwrap() {
107 Type::LiteralFloat(LiteralFloatType { value, .. }) => assert_eq!(
108 value, expected_value,
109 "Expected value to be {expected_value} for input {input}, but got {value}"
110 ),
111 _ => panic!("Expected Type::LiteralInt"),
112 }
113 };
114
115 assert_parsed_literal_float("0.0", 0.0);
116 assert_parsed_literal_float("1.0", 1.0);
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.1E1", 1.0);
120 assert_parsed_literal_float("0.1E-1", 0.01);
121 assert_parsed_literal_float("0.1e+1", 1.0);
122 assert_parsed_literal_float(".1e+1", 1.0);
123 }
124
125 #[test]
126 fn test_parse_simple_union() {
127 match do_parse("int|string") {
128 Ok(ty) => match ty {
129 Type::Union(u) => {
130 assert!(matches!(*u.left, Type::Int(_)));
131 assert!(matches!(*u.right, Type::String(_)));
132 }
133 _ => panic!("Expected Type::Union"),
134 },
135 Err(err) => {
136 panic!("Failed to parse union type: {err:?}");
137 }
138 }
139 }
140
141 #[test]
142 fn test_parse_variable_union() {
143 match do_parse("$a|$b") {
144 Ok(ty) => match ty {
145 Type::Union(u) => {
146 assert!(matches!(*u.left, Type::Variable(_)));
147 assert!(matches!(*u.right, Type::Variable(_)));
148 }
149 _ => panic!("Expected Type::Union"),
150 },
151 Err(err) => {
152 panic!("Failed to parse union type: {err:?}");
153 }
154 }
155 }
156
157 #[test]
158 fn test_parse_nullable() {
159 let result = do_parse("?string");
160 assert!(result.is_ok());
161 match result.unwrap() {
162 Type::Nullable(n) => {
163 assert!(matches!(*n.inner, Type::String(_)));
164 }
165 _ => panic!("Expected Type::Nullable"),
166 }
167 }
168
169 #[test]
170 fn test_parse_generic_array() {
171 let result = do_parse("array<int, bool>");
172 assert!(result.is_ok());
173 match result.unwrap() {
174 Type::Array(a) => {
175 assert!(a.parameters.is_some());
176 let params = a.parameters.unwrap();
177 assert_eq!(params.entries.len(), 2);
178 assert!(matches!(params.entries[0].inner, Type::Int(_)));
179 assert!(matches!(params.entries[1].inner, Type::Bool(_)));
180 }
181 _ => panic!("Expected Type::Array"),
182 }
183 }
184
185 #[test]
186 fn test_parse_generic_array_one_param() {
187 match do_parse("array<string>") {
188 Ok(Type::Array(a)) => {
189 let params = a.parameters.expect("Expected generic parameters");
190 assert_eq!(params.entries.len(), 1);
191 assert!(matches!(params.entries[0].inner, Type::String(_)));
192 }
193 res => panic!("Expected Ok(Type::Array), got {res:?}"),
194 }
195 }
196
197 #[test]
198 fn test_parse_generic_list() {
199 match do_parse("list<string>") {
200 Ok(Type::List(l)) => {
201 let params = l.parameters.expect("Expected generic parameters");
202 assert_eq!(params.entries.len(), 1);
203 assert!(matches!(params.entries[0].inner, Type::String(_)));
204 }
205 res => panic!("Expected Ok(Type::List), got {res:?}"),
206 }
207 }
208
209 #[test]
210 fn test_parse_non_empty_array() {
211 match do_parse("non-empty-array<int, bool>") {
212 Ok(Type::NonEmptyArray(a)) => {
213 let params = a.parameters.expect("Expected generic parameters");
214 assert_eq!(params.entries.len(), 2);
215 assert!(matches!(params.entries[0].inner, Type::Int(_)));
216 assert!(matches!(params.entries[1].inner, Type::Bool(_)));
217 }
218 res => panic!("Expected Ok(Type::NonEmptyArray), got {res:?}"),
219 }
220 }
221
222 #[test]
223 fn test_parse_nested_generics() {
224 match do_parse("list<array<int, string>>") {
225 Ok(Type::List(l)) => {
226 let params = l.parameters.expect("Expected generic parameters");
227 assert_eq!(params.entries.len(), 1);
228 match ¶ms.entries[0].inner {
229 Type::Array(inner_array) => {
230 let inner_params = inner_array.parameters.as_ref().expect("Inner array needs params");
231 assert_eq!(inner_params.entries.len(), 2);
232 assert!(matches!(inner_params.entries[0].inner, Type::Int(_)));
233 assert!(matches!(inner_params.entries[1].inner, Type::String(_)));
234 }
235 _ => panic!("Expected inner type to be Type::Array"),
236 }
237 }
238 res => panic!("Expected Ok(Type::List), got {res:?}"),
239 }
240 }
241
242 #[test]
243 fn test_parse_simple_shape() {
244 let result = do_parse("array{'name': string}");
245 assert!(matches!(result, Ok(Type::Shape(_))));
246 let Ok(Type::Shape(shape)) = result else {
247 panic!("Expected Type::Shape");
248 };
249
250 assert_eq!(shape.kind, ShapeTypeKind::Array);
251 assert_eq!(shape.keyword.value, "array");
252 assert_eq!(shape.fields.len(), 1);
253 assert!(shape.additional_fields.is_none());
254
255 let field = &shape.fields[0];
256 assert!(matches!(
257 field.key.as_ref().map(|k| k.name.as_ref()),
258 Some(Type::LiteralString(LiteralStringType { raw: "'name'", value: "name", .. }))
259 ));
260 assert!(matches!(field.value.as_ref(), Type::String(_)));
261 }
262
263 #[test]
264 fn test_parse_int_key_shape() {
265 match do_parse("array{0: string, 1: bool}") {
266 Ok(Type::Shape(shape)) => {
267 assert_eq!(shape.fields.len(), 2);
268 let first_field = &shape.fields[0];
269 assert!(matches!(
270 first_field.key.as_ref().map(|k| k.name.as_ref()),
271 Some(Type::LiteralInt(LiteralIntType { value: 0, .. }))
272 ));
273 assert!(matches!(first_field.value.as_ref(), Type::String(_)));
274 let second_field = &shape.fields[1];
275 assert!(matches!(
276 second_field.key.as_ref().map(|k| k.name.as_ref()),
277 Some(Type::LiteralInt(LiteralIntType { value: 1, .. }))
278 ));
279 assert!(matches!(second_field.value.as_ref(), Type::Bool(_)));
280 }
281 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
282 }
283 }
284
285 #[test]
286 fn test_parse_optional_field_shape() {
287 match do_parse("array{name: string, age?: int, address: string}") {
288 Ok(Type::Shape(shape)) => {
289 assert_eq!(shape.fields.len(), 3);
290 assert!(!shape.fields[0].is_optional());
291 assert!(shape.fields[1].is_optional());
292 assert!(!shape.fields[2].is_optional());
293 }
294 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
295 }
296 }
297
298 #[test]
299 fn test_parse_unsealed_shape() {
300 match do_parse("array{name: string, ...}") {
301 Ok(Type::Shape(shape)) => {
302 assert_eq!(shape.fields.len(), 1);
303 assert!(shape.additional_fields.is_some());
304 assert!(shape.additional_fields.unwrap().parameters.is_none()); }
306 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
307 }
308 }
309
310 #[test]
311 fn test_parse_unsealed_shape_with_fallback() {
312 match do_parse(
313 "array{
314 name: string, // This is a comment
315 ...<string, string>
316 }",
317 ) {
318 Ok(Type::Shape(shape)) => {
319 assert_eq!(shape.fields.len(), 1);
320 assert!(shape.additional_fields.as_ref().is_some_and(|a| a.parameters.is_some()));
321 let params = shape.additional_fields.unwrap().parameters.unwrap();
322 assert_eq!(params.entries.len(), 2);
323 assert!(matches!(params.entries[0].inner, Type::String(_)));
324 assert!(matches!(params.entries[1].inner, Type::String(_)));
325 }
326 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
327 }
328 }
329
330 #[test]
331 fn test_parse_empty_shape() {
332 match do_parse("array{}") {
333 Ok(Type::Shape(shape)) => {
334 assert_eq!(shape.fields.len(), 0);
335 assert!(shape.additional_fields.is_none());
336 }
337 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
338 }
339 }
340
341 #[test]
342 fn test_parse_error_unexpected_token() {
343 let result = do_parse("int|>");
344 assert!(result.is_err());
345 assert!(matches!(result.unwrap_err(), ParseError::UnexpectedToken { .. }));
346 }
347
348 #[test]
349 fn test_parse_error_eof() {
350 let result = do_parse("array<int");
351 assert!(result.is_err());
352 assert!(matches!(result.unwrap_err(), ParseError::UnexpectedEndOfFile { .. }));
353 }
354
355 #[test]
356 fn test_parse_error_trailing_token() {
357 let result = do_parse("int|string&");
358 assert!(result.is_err());
359 assert!(matches!(result.unwrap_err(), ParseError::UnexpectedEndOfFile { .. }));
360 }
361
362 #[test]
363 fn test_parse_intersection() {
364 match do_parse("Countable&Traversable") {
365 Ok(Type::Intersection(i)) => {
366 assert!(matches!(*i.left, Type::Reference(_)));
367 assert!(matches!(*i.right, Type::Reference(_)));
368
369 if let Type::Reference(r) = *i.left {
370 assert_eq!(r.identifier.value, "Countable");
371 } else {
372 panic!();
373 }
374
375 if let Type::Reference(r) = *i.right {
376 assert_eq!(r.identifier.value, "Traversable");
377 } else {
378 panic!();
379 }
380 }
381 res => panic!("Expected Ok(Type::Intersection), got {res:?}"),
382 }
383 }
384
385 #[test]
386 fn test_parse_member_ref() {
387 match do_parse("MyClass::MY_CONST") {
388 Ok(Type::MemberReference(m)) => {
389 assert_eq!(m.class.value, "MyClass");
390 assert_eq!(m.member.value, "MY_CONST");
391 }
392 res => panic!("Expected Ok(Type::MemberReference), got {res:?}"),
393 }
394
395 match do_parse("\\Fully\\Qualified::class") {
396 Ok(Type::MemberReference(m)) => {
397 assert_eq!(m.class.value, "\\Fully\\Qualified"); assert_eq!(m.member.value, "class");
399 }
400 res => panic!("Expected Ok(Type::MemberReference), got {res:?}"),
401 }
402 }
403
404 #[test]
405 fn test_parse_iterable() {
406 match do_parse("iterable<int, string>") {
407 Ok(Type::Iterable(i)) => {
408 let params = i.parameters.expect("Expected generic parameters");
409 assert_eq!(params.entries.len(), 2);
410 assert!(matches!(params.entries[0].inner, Type::Int(_)));
411 assert!(matches!(params.entries[1].inner, Type::String(_)));
412 }
413 res => panic!("Expected Ok(Type::Iterable), got {res:?}"),
414 }
415
416 match do_parse("iterable<bool>") {
417 Ok(Type::Iterable(i)) => {
419 let params = i.parameters.expect("Expected generic parameters");
420 assert_eq!(params.entries.len(), 1);
421 assert!(matches!(params.entries[0].inner, Type::Bool(_)));
422 }
423 res => panic!("Expected Ok(Type::Iterable), got {res:?}"),
424 }
425
426 match do_parse("iterable") {
427 Ok(Type::Iterable(i)) => {
428 assert!(i.parameters.is_none());
429 }
430 res => panic!("Expected Ok(Type::Iterable), got {res:?}"),
431 }
432 }
433
434 #[test]
435 fn test_parse_negated_int() {
436 let assert_negated_int = |input: &str, expected_value: u64| {
437 let result = do_parse(input);
438 assert!(result.is_ok());
439 match result.unwrap() {
440 Type::Negated(n) => {
441 assert!(matches!(*n.inner, Type::LiteralInt(_)));
442 if let Type::LiteralInt(lit) = *n.inner {
443 assert_eq!(lit.value, expected_value);
444 } else {
445 panic!()
446 }
447 }
448 _ => panic!("Expected Type::Negated"),
449 }
450 };
451
452 assert_negated_int("-0", 0);
453 assert_negated_int("-1", 1);
454 assert_negated_int(
455 "-
456 // This is a comment
457 123_345",
458 123345,
459 );
460 assert_negated_int("-0b1", 1);
461 }
462
463 #[test]
464 fn test_parse_callable_no_spec() {
465 match do_parse("callable") {
466 Ok(Type::Callable(c)) => {
467 assert!(c.specification.is_none());
468 assert_eq!(c.kind, CallableTypeKind::Callable);
469 }
470 res => panic!("Expected Ok(Type::Callable), got {res:?}"),
471 }
472 }
473
474 #[test]
475 fn test_parse_callable_params_only() {
476 match do_parse("callable(int, ?string)") {
477 Ok(Type::Callable(c)) => {
478 let spec = c.specification.expect("Expected callable specification");
479 assert!(spec.return_type.is_none());
480 assert_eq!(spec.parameters.entries.len(), 2);
481 assert!(matches!(*spec.parameters.entries[0].parameter_type, Type::Int(_)));
482 assert!(matches!(*spec.parameters.entries[1].parameter_type, Type::Nullable(_)));
483 assert!(spec.parameters.entries[0].ellipsis.is_none());
484 assert!(spec.parameters.entries[0].equals.is_none());
485 }
486 res => panic!("Expected Ok(Type::Callable), got {res:?}"),
487 }
488 }
489
490 #[test]
491 fn test_parse_callable_return_only() {
492 match do_parse("callable(): void") {
493 Ok(Type::Callable(c)) => {
494 let spec = c.specification.expect("Expected callable specification");
495 assert!(spec.parameters.entries.is_empty());
496 assert!(spec.return_type.is_some());
497 assert!(matches!(*spec.return_type.unwrap().return_type, Type::Void(_)));
498 }
499 res => panic!("Expected Ok(Type::Callable), got {res:?}"),
500 }
501 }
502
503 #[test]
504 fn test_parse_pure_callable_full() {
505 match do_parse("pure-callable(bool): int") {
506 Ok(Type::Callable(c)) => {
507 assert_eq!(c.kind, CallableTypeKind::PureCallable);
508 let spec = c.specification.expect("Expected callable specification");
509 assert_eq!(spec.parameters.entries.len(), 1);
510 assert!(matches!(*spec.parameters.entries[0].parameter_type, Type::Bool(_)));
511 assert!(spec.return_type.is_some());
512 assert!(matches!(*spec.return_type.unwrap().return_type, Type::Int(_)));
513 }
514 res => panic!("Expected Ok(Type::Callable), got {res:?}"),
515 }
516 }
517
518 #[test]
519 fn test_parse_closure_via_identifier() {
520 match do_parse("Closure(string): bool") {
521 Ok(Type::Callable(c)) => {
522 assert_eq!(c.kind, CallableTypeKind::Closure);
523 assert_eq!(c.keyword.value, "Closure");
524 let spec = c.specification.expect("Expected callable specification");
525 assert_eq!(spec.parameters.entries.len(), 1);
526 assert!(matches!(*spec.parameters.entries[0].parameter_type, Type::String(_)));
527 assert!(spec.return_type.is_some());
528 assert!(matches!(*spec.return_type.unwrap().return_type, Type::Bool(_)));
529 }
530 res => panic!("Expected Ok(Type::Callable) for Closure, got {res:?}"),
531 }
532 }
533
534 #[test]
535 fn test_parse_complex_pure_callable() {
536 match do_parse("pure-callable(list<int>, ?Closure(): void=, int...): ((Simple&Iter<T>)|null)") {
537 Ok(Type::Callable(c)) => {
538 assert_eq!(c.kind, CallableTypeKind::PureCallable);
539 let spec = c.specification.expect("Expected callable specification");
540 assert_eq!(spec.parameters.entries.len(), 3);
541 assert!(spec.return_type.is_some());
542
543 let first_param = &spec.parameters.entries[0];
544 assert!(matches!(*first_param.parameter_type, Type::List(_)));
545 assert!(first_param.ellipsis.is_none());
546 assert!(first_param.equals.is_none());
547
548 let second_param = &spec.parameters.entries[1];
549 assert!(matches!(*second_param.parameter_type, Type::Nullable(_)));
550 assert!(second_param.ellipsis.is_none());
551 assert!(second_param.equals.is_some());
552
553 let third_param = &spec.parameters.entries[2];
554 assert!(matches!(*third_param.parameter_type, Type::Int(_)));
555 assert!(third_param.ellipsis.is_some());
556 assert!(third_param.equals.is_none());
557
558 if let Type::Parenthesized(p) = *spec.return_type.unwrap().return_type {
559 assert!(matches!(*p.inner, Type::Union(_)));
560 if let Type::Union(u) = *p.inner {
561 assert!(matches!(u.left.as_ref(), Type::Parenthesized(_)));
562 assert!(matches!(u.right.as_ref(), Type::Null(_)));
563 }
564 } else {
565 panic!("Expected Type::CallableReturnType");
566 }
567 }
568 res => panic!("Expected Ok(Type::Callable), got {res:?}"),
569 }
570 }
571
572 #[test]
573 fn test_parse_conditional_type() {
574 match do_parse("int is not string ? array : int") {
575 Ok(Type::Conditional(c)) => {
576 assert!(matches!(*c.subject, Type::Int(_)));
577 assert!(c.not.is_some());
578 assert!(matches!(*c.target, Type::String(_)));
579 assert!(matches!(*c.then, Type::Array(_)));
580 assert!(matches!(*c.otherwise, Type::Int(_)));
581 }
582 res => panic!("Expected Ok(Type::Conditional), got {res:?}"),
583 }
584
585 match do_parse("$input is string ? array : int") {
586 Ok(Type::Conditional(c)) => {
587 assert!(matches!(*c.subject, Type::Variable(_)));
588 assert!(c.not.is_none());
589 assert!(matches!(*c.target, Type::String(_)));
590 assert!(matches!(*c.then, Type::Array(_)));
591 assert!(matches!(*c.otherwise, Type::Int(_)));
592 }
593 res => panic!("Expected Ok(Type::Conditional), got {res:?}"),
594 }
595
596 match do_parse("int is string ? array : (int is not $bar ? string : $baz)") {
597 Ok(Type::Conditional(c)) => {
598 assert!(matches!(*c.subject, Type::Int(_)));
599 assert!(c.not.is_none());
600 assert!(matches!(*c.target, Type::String(_)));
601 assert!(matches!(*c.then, Type::Array(_)));
602
603 let Type::Parenthesized(p) = *c.otherwise else {
604 panic!("Expected Type::Parenthesized");
605 };
606
607 if let Type::Conditional(inner_conditional) = *p.inner {
608 assert!(matches!(*inner_conditional.subject, Type::Int(_)));
609 assert!(inner_conditional.not.is_some());
610 assert!(matches!(*inner_conditional.target, Type::Variable(_)));
611 assert!(matches!(*inner_conditional.then, Type::String(_)));
612 assert!(matches!(*inner_conditional.otherwise, Type::Variable(_)));
613 } else {
614 panic!("Expected Type::Conditional");
615 }
616 }
617 res => panic!("Expected Ok(Type::Conditional), got {res:?}"),
618 }
619 }
620
621 #[test]
622 fn test_keyof() {
623 match do_parse("key-of<MyArray>") {
624 Ok(Type::KeyOf(k)) => {
625 assert_eq!(k.keyword.value, "key-of");
626 match &k.parameter.entry.inner {
627 Type::Reference(r) => assert_eq!(r.identifier.value, "MyArray"),
628 _ => panic!("Expected Type::Reference"),
629 }
630 }
631 res => panic!("Expected Ok(Type::KeyOf), got {res:?}"),
632 }
633 }
634
635 #[test]
636 fn test_valueof() {
637 match do_parse("value-of<MyArray>") {
638 Ok(Type::ValueOf(v)) => {
639 assert_eq!(v.keyword.value, "value-of");
640 match &v.parameter.entry.inner {
641 Type::Reference(r) => assert_eq!(r.identifier.value, "MyArray"),
642 _ => panic!("Expected Type::Reference"),
643 }
644 }
645 res => panic!("Expected Ok(Type::ValueOf), got {res:?}"),
646 }
647 }
648
649 #[test]
650 fn test_indexed_access() {
651 match do_parse("MyArray[MyKey]") {
652 Ok(Type::IndexAccess(i)) => {
653 match *i.target {
654 Type::Reference(r) => assert_eq!(r.identifier.value, "MyArray"),
655 _ => panic!("Expected Type::Reference"),
656 }
657 match *i.index {
658 Type::Reference(r) => assert_eq!(r.identifier.value, "MyKey"),
659 _ => panic!("Expected Type::Reference"),
660 }
661 }
662 res => panic!("Expected Ok(Type::IndexAccess), got {res:?}"),
663 }
664 }
665
666 #[test]
667 fn test_slice_type() {
668 match do_parse("string[]") {
669 Ok(Type::Slice(s)) => {
670 assert!(matches!(*s.inner, Type::String(_)));
671 }
672 res => panic!("Expected Ok(Type::Slice), got {res:?}"),
673 }
674 }
675
676 #[test]
677 fn test_int_range() {
678 match do_parse("int<0, 100>") {
679 Ok(Type::IntRange(r)) => {
680 assert_eq!(r.keyword.value, "int");
681
682 match r.min {
683 IntOrKeyword::Int(literal_int_type) => {
684 assert_eq!(literal_int_type.value, 0);
685 }
686 _ => {
687 panic!("Expected min to be a LiteralIntType, got `{}`", r.min)
688 }
689 };
690
691 match r.max {
692 IntOrKeyword::Int(literal_int_type) => {
693 assert_eq!(literal_int_type.value, 100);
694 }
695 _ => {
696 panic!("Expected max to be a LiteralIntType, got `{}`", r.max)
697 }
698 };
699 }
700 res => panic!("Expected Ok(Type::IntRange), got {res:?}"),
701 }
702
703 match do_parse("int<min, 0>") {
704 Ok(Type::IntRange(r)) => {
705 match r.min {
706 IntOrKeyword::Keyword(keyword) => {
707 assert_eq!(keyword.value, "min");
708 }
709 _ => {
710 panic!("Expected min to be a Keyword, got `{}`", r.min)
711 }
712 };
713
714 match r.max {
715 IntOrKeyword::Int(literal_int_type) => {
716 assert_eq!(literal_int_type.value, 0);
717 }
718 _ => {
719 panic!("Expected max to be a LiteralIntType, got `{}`", r.max)
720 }
721 };
722 }
723 res => panic!("Expected Ok(Type::IntRange), got {res:?}"),
724 }
725
726 match do_parse("int<min, max>") {
727 Ok(Type::IntRange(r)) => {
728 match r.min {
729 IntOrKeyword::Keyword(keyword) => {
730 assert_eq!(keyword.value, "min");
731 }
732 _ => {
733 panic!("Expected min to be a Keyword, got `{}`", r.min)
734 }
735 };
736
737 match r.max {
738 IntOrKeyword::Keyword(keyword) => {
739 assert_eq!(keyword.value, "max");
740 }
741 _ => {
742 panic!("Expected max to be a Keyword, got `{}`", r.max)
743 }
744 };
745 }
746 res => panic!("Expected Ok(Type::IntRange), got {res:?}"),
747 }
748 }
749
750 #[test]
751 fn test_properties_of() {
752 match do_parse("properties-of<MyClass>") {
753 Ok(Type::PropertiesOf(p)) => {
754 assert_eq!(p.keyword.value, "properties-of");
755 assert_eq!(p.filter, PropertiesOfFilter::All);
756 match &p.parameter.entry.inner {
757 Type::Reference(r) => assert_eq!(r.identifier.value, "MyClass"),
758 _ => panic!(),
759 }
760 }
761 res => panic!("Expected Ok(Type::PropertiesOf), got {res:?}"),
762 }
763
764 match do_parse("protected-properties-of<T>") {
765 Ok(Type::PropertiesOf(p)) => {
766 assert_eq!(p.keyword.value, "protected-properties-of");
767 assert_eq!(p.filter, PropertiesOfFilter::Protected);
768 match &p.parameter.entry.inner {
769 Type::Reference(r) => assert_eq!(r.identifier.value, "T"),
770 _ => panic!(),
771 }
772 }
773 res => panic!("Expected Ok(Type::PropertiesOf), got {res:?}"),
774 }
775
776 match do_parse("private-properties-of<T>") {
777 Ok(Type::PropertiesOf(p)) => {
778 assert_eq!(p.keyword.value, "private-properties-of");
779 assert_eq!(p.filter, PropertiesOfFilter::Private);
780 match &p.parameter.entry.inner {
781 Type::Reference(r) => assert_eq!(r.identifier.value, "T"),
782 _ => panic!(),
783 }
784 }
785 res => panic!("Expected Ok(Type::PropertiesOf), got {res:?}"),
786 }
787
788 match do_parse("public-properties-of<T>") {
789 Ok(Type::PropertiesOf(p)) => {
790 assert_eq!(p.keyword.value, "public-properties-of");
791 assert_eq!(p.filter, PropertiesOfFilter::Public);
792 match &p.parameter.entry.inner {
793 Type::Reference(r) => assert_eq!(r.identifier.value, "T"),
794 _ => panic!(),
795 }
796 }
797 res => panic!("Expected Ok(Type::PropertiesOf), got {res:?}"),
798 }
799 }
800
801 #[test]
802 fn test_variable() {
803 match do_parse("$myVar") {
804 Ok(Type::Variable(v)) => {
805 assert_eq!(v.value, "$myVar");
806 }
807 res => panic!("Expected Ok(Type::Variable), got {res:?}"),
808 }
809 }
810
811 #[test]
812 fn test_nullable_intersection() {
813 match do_parse("Countable&?Traversable") {
815 Ok(Type::Intersection(i)) => {
816 assert!(matches!(*i.left, Type::Reference(r) if r.identifier.value == "Countable"));
817 assert!(matches!(*i.right, Type::Nullable(_)));
818 if let Type::Nullable(n) = *i.right {
819 assert!(matches!(*n.inner, Type::Reference(r) if r.identifier.value == "Traversable"));
820 } else {
821 panic!();
822 }
823 }
824 res => panic!("Expected Ok(Type::Intersection), got {res:?}"),
825 }
826 }
827
828 #[test]
829 fn test_parenthesized_nullable() {
830 match do_parse("?(Countable&Traversable)") {
831 Ok(Type::Nullable(n)) => {
832 assert!(matches!(*n.inner, Type::Parenthesized(_)));
833 if let Type::Parenthesized(p) = *n.inner {
834 assert!(matches!(*p.inner, Type::Intersection(_)));
835 } else {
836 panic!()
837 }
838 }
839 res => panic!("Expected Ok(Type::Nullable), got {res:?}"),
840 }
841 }
842
843 #[test]
844 fn test_positive_negative_int() {
845 match do_parse("positive-int|negative-int") {
846 Ok(Type::Union(u)) => {
847 assert!(matches!(*u.left, Type::PositiveInt(_)));
848 assert!(matches!(*u.right, Type::NegativeInt(_)));
849 }
850 res => panic!("Expected Ok(Type::Union), got {res:?}"),
851 }
852 }
853}