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