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