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.inner, Type::LiteralInt(_)));
496 if let Type::LiteralInt(lit) = *n.inner {
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_callable_no_spec() {
519 match do_parse("callable") {
520 Ok(Type::Callable(c)) => {
521 assert!(c.specification.is_none());
522 assert_eq!(c.kind, CallableTypeKind::Callable);
523 }
524 res => panic!("Expected Ok(Type::Callable), got {res:?}"),
525 }
526 }
527
528 #[test]
529 fn test_parse_callable_params_only() {
530 match do_parse("callable(int, ?string)") {
531 Ok(Type::Callable(c)) => {
532 let spec = c.specification.expect("Expected callable specification");
533 assert!(spec.return_type.is_none());
534 assert_eq!(spec.parameters.entries.len(), 2);
535 assert!(matches!(spec.parameters.entries[0].parameter_type, Some(Type::Int(_))));
536 assert!(matches!(spec.parameters.entries[1].parameter_type, Some(Type::Nullable(_))));
537 assert!(spec.parameters.entries[0].ellipsis.is_none());
538 assert!(spec.parameters.entries[0].equals.is_none());
539 }
540 res => panic!("Expected Ok(Type::Callable), got {res:?}"),
541 }
542 }
543
544 #[test]
545 fn test_parse_callable_return_only() {
546 match do_parse("callable(): void") {
547 Ok(Type::Callable(c)) => {
548 let spec = c.specification.expect("Expected callable specification");
549 assert!(spec.parameters.entries.is_empty());
550 assert!(spec.return_type.is_some());
551 assert!(matches!(*spec.return_type.unwrap().return_type, Type::Void(_)));
552 }
553 res => panic!("Expected Ok(Type::Callable), got {res:?}"),
554 }
555 }
556
557 #[test]
558 fn test_parse_pure_callable_full() {
559 match do_parse("pure-callable(bool): int") {
560 Ok(Type::Callable(c)) => {
561 assert_eq!(c.kind, CallableTypeKind::PureCallable);
562 let spec = c.specification.expect("Expected callable specification");
563 assert_eq!(spec.parameters.entries.len(), 1);
564 assert!(matches!(spec.parameters.entries[0].parameter_type, Some(Type::Bool(_))));
565 assert!(spec.return_type.is_some());
566 assert!(matches!(*spec.return_type.unwrap().return_type, Type::Int(_)));
567 }
568 res => panic!("Expected Ok(Type::Callable), got {res:?}"),
569 }
570 }
571
572 #[test]
573 fn test_parse_closure_via_identifier() {
574 match do_parse("Closure(string): bool") {
575 Ok(Type::Callable(c)) => {
576 assert_eq!(c.kind, CallableTypeKind::Closure);
577 assert_eq!(c.keyword.value, "Closure");
578 let spec = c.specification.expect("Expected callable specification");
579 assert_eq!(spec.parameters.entries.len(), 1);
580 assert!(matches!(spec.parameters.entries[0].parameter_type, Some(Type::String(_))));
581 assert!(spec.return_type.is_some());
582 assert!(matches!(*spec.return_type.unwrap().return_type, Type::Bool(_)));
583 }
584 res => panic!("Expected Ok(Type::Callable) for Closure, got {res:?}"),
585 }
586 }
587
588 #[test]
589 fn test_parse_complex_pure_callable() {
590 match do_parse("pure-callable(list<int>, ?Closure(): void=, int...): ((Simple&Iter<T>)|null)") {
591 Ok(Type::Callable(c)) => {
592 assert_eq!(c.kind, CallableTypeKind::PureCallable);
593 let spec = c.specification.expect("Expected callable specification");
594 assert_eq!(spec.parameters.entries.len(), 3);
595 assert!(spec.return_type.is_some());
596
597 let first_param = &spec.parameters.entries[0];
598 assert!(matches!(first_param.parameter_type, Some(Type::List(_))));
599 assert!(first_param.ellipsis.is_none());
600 assert!(first_param.equals.is_none());
601
602 let second_param = &spec.parameters.entries[1];
603 assert!(matches!(second_param.parameter_type, Some(Type::Nullable(_))));
604 assert!(second_param.ellipsis.is_none());
605 assert!(second_param.equals.is_some());
606
607 let third_param = &spec.parameters.entries[2];
608 assert!(matches!(third_param.parameter_type, Some(Type::Int(_))));
609 assert!(third_param.ellipsis.is_some());
610 assert!(third_param.equals.is_none());
611
612 if let Type::Parenthesized(p) = *spec.return_type.unwrap().return_type {
613 assert!(matches!(*p.inner, Type::Union(_)));
614 if let Type::Union(u) = *p.inner {
615 assert!(matches!(u.left.as_ref(), Type::Parenthesized(_)));
616 assert!(matches!(u.right.as_ref(), Type::Null(_)));
617 }
618 } else {
619 panic!("Expected Type::CallableReturnType");
620 }
621 }
622 res => panic!("Expected Ok(Type::Callable), got {res:?}"),
623 }
624 }
625
626 #[test]
627 fn test_parse_conditional_type() {
628 match do_parse("int is not string ? array : int") {
629 Ok(Type::Conditional(c)) => {
630 assert!(matches!(*c.subject, Type::Int(_)));
631 assert!(c.not.is_some());
632 assert!(matches!(*c.target, Type::String(_)));
633 assert!(matches!(*c.then, Type::Array(_)));
634 assert!(matches!(*c.otherwise, Type::Int(_)));
635 }
636 res => panic!("Expected Ok(Type::Conditional), got {res:?}"),
637 }
638
639 match do_parse("$input is string ? array : int") {
640 Ok(Type::Conditional(c)) => {
641 assert!(matches!(*c.subject, Type::Variable(_)));
642 assert!(c.not.is_none());
643 assert!(matches!(*c.target, Type::String(_)));
644 assert!(matches!(*c.then, Type::Array(_)));
645 assert!(matches!(*c.otherwise, Type::Int(_)));
646 }
647 res => panic!("Expected Ok(Type::Conditional), got {res:?}"),
648 }
649
650 match do_parse("int is string ? array : (int is not $bar ? string : $baz)") {
651 Ok(Type::Conditional(c)) => {
652 assert!(matches!(*c.subject, Type::Int(_)));
653 assert!(c.not.is_none());
654 assert!(matches!(*c.target, Type::String(_)));
655 assert!(matches!(*c.then, Type::Array(_)));
656
657 let Type::Parenthesized(p) = *c.otherwise else {
658 panic!("Expected Type::Parenthesized");
659 };
660
661 if let Type::Conditional(inner_conditional) = *p.inner {
662 assert!(matches!(*inner_conditional.subject, Type::Int(_)));
663 assert!(inner_conditional.not.is_some());
664 assert!(matches!(*inner_conditional.target, Type::Variable(_)));
665 assert!(matches!(*inner_conditional.then, Type::String(_)));
666 assert!(matches!(*inner_conditional.otherwise, Type::Variable(_)));
667 } else {
668 panic!("Expected Type::Conditional");
669 }
670 }
671 res => panic!("Expected Ok(Type::Conditional), got {res:?}"),
672 }
673 }
674
675 #[test]
676 fn test_keyof() {
677 match do_parse("key-of<MyArray>") {
678 Ok(Type::KeyOf(k)) => {
679 assert_eq!(k.keyword.value, "key-of");
680 match &k.parameter.entry.inner {
681 Type::Reference(r) => assert_eq!(r.identifier.value, "MyArray"),
682 _ => panic!("Expected Type::Reference"),
683 }
684 }
685 res => panic!("Expected Ok(Type::KeyOf), got {res:?}"),
686 }
687 }
688
689 #[test]
690 fn test_valueof() {
691 match do_parse("value-of<MyArray>") {
692 Ok(Type::ValueOf(v)) => {
693 assert_eq!(v.keyword.value, "value-of");
694 match &v.parameter.entry.inner {
695 Type::Reference(r) => assert_eq!(r.identifier.value, "MyArray"),
696 _ => panic!("Expected Type::Reference"),
697 }
698 }
699 res => panic!("Expected Ok(Type::ValueOf), got {res:?}"),
700 }
701 }
702
703 #[test]
704 fn test_indexed_access() {
705 match do_parse("MyArray[MyKey]") {
706 Ok(Type::IndexAccess(i)) => {
707 match *i.target {
708 Type::Reference(r) => assert_eq!(r.identifier.value, "MyArray"),
709 _ => panic!("Expected Type::Reference"),
710 }
711 match *i.index {
712 Type::Reference(r) => assert_eq!(r.identifier.value, "MyKey"),
713 _ => panic!("Expected Type::Reference"),
714 }
715 }
716 res => panic!("Expected Ok(Type::IndexAccess), got {res:?}"),
717 }
718 }
719
720 #[test]
721 fn test_slice_type() {
722 match do_parse("string[]") {
723 Ok(Type::Slice(s)) => {
724 assert!(matches!(*s.inner, Type::String(_)));
725 }
726 res => panic!("Expected Ok(Type::Slice), got {res:?}"),
727 }
728 }
729
730 #[test]
731 fn test_slice_of_slice_of_slice_type() {
732 match do_parse("string[][][]") {
733 Ok(Type::Slice(s)) => {
734 assert!(matches!(*s.inner, Type::Slice(_)));
735 if let Type::Slice(inner_slice) = *s.inner {
736 assert!(matches!(*inner_slice.inner, Type::Slice(_)));
737 if let Type::Slice(inner_inner_slice) = *inner_slice.inner {
738 assert!(matches!(*inner_inner_slice.inner, Type::String(_)));
739 } else {
740 panic!("Expected inner slice to be a Slice");
741 }
742 } else {
743 panic!("Expected outer slice to be a Slice");
744 }
745 }
746 res => panic!("Expected Ok(Type::Slice), got {res:?}"),
747 }
748 }
749
750 #[test]
751 fn test_int_range() {
752 match do_parse("int<0, 100>") {
753 Ok(Type::IntRange(r)) => {
754 assert_eq!(r.keyword.value, "int");
755
756 match r.min {
757 IntOrKeyword::Int(literal_int_type) => {
758 assert_eq!(literal_int_type.value, 0);
759 }
760 _ => {
761 panic!("Expected min to be a LiteralIntType, got `{}`", r.min)
762 }
763 };
764
765 match r.max {
766 IntOrKeyword::Int(literal_int_type) => {
767 assert_eq!(literal_int_type.value, 100);
768 }
769 _ => {
770 panic!("Expected max to be a LiteralIntType, got `{}`", r.max)
771 }
772 };
773 }
774 res => panic!("Expected Ok(Type::IntRange), got {res:?}"),
775 }
776
777 match do_parse("int<min, 0>") {
778 Ok(Type::IntRange(r)) => {
779 match r.min {
780 IntOrKeyword::Keyword(keyword) => {
781 assert_eq!(keyword.value, "min");
782 }
783 _ => {
784 panic!("Expected min to be a Keyword, got `{}`", r.min)
785 }
786 };
787
788 match r.max {
789 IntOrKeyword::Int(literal_int_type) => {
790 assert_eq!(literal_int_type.value, 0);
791 }
792 _ => {
793 panic!("Expected max to be a LiteralIntType, got `{}`", r.max)
794 }
795 };
796 }
797 res => panic!("Expected Ok(Type::IntRange), got {res:?}"),
798 }
799
800 match do_parse("int<min, max>") {
801 Ok(Type::IntRange(r)) => {
802 match r.min {
803 IntOrKeyword::Keyword(keyword) => {
804 assert_eq!(keyword.value, "min");
805 }
806 _ => {
807 panic!("Expected min to be a Keyword, got `{}`", r.min)
808 }
809 };
810
811 match r.max {
812 IntOrKeyword::Keyword(keyword) => {
813 assert_eq!(keyword.value, "max");
814 }
815 _ => {
816 panic!("Expected max to be a Keyword, got `{}`", r.max)
817 }
818 };
819 }
820 res => panic!("Expected Ok(Type::IntRange), got {res:?}"),
821 }
822 }
823
824 #[test]
825 fn test_properties_of() {
826 match do_parse("properties-of<MyClass>") {
827 Ok(Type::PropertiesOf(p)) => {
828 assert_eq!(p.keyword.value, "properties-of");
829 assert_eq!(p.filter, PropertiesOfFilter::All);
830 match &p.parameter.entry.inner {
831 Type::Reference(r) => assert_eq!(r.identifier.value, "MyClass"),
832 _ => panic!(),
833 }
834 }
835 res => panic!("Expected Ok(Type::PropertiesOf), got {res:?}"),
836 }
837
838 match do_parse("protected-properties-of<T>") {
839 Ok(Type::PropertiesOf(p)) => {
840 assert_eq!(p.keyword.value, "protected-properties-of");
841 assert_eq!(p.filter, PropertiesOfFilter::Protected);
842 match &p.parameter.entry.inner {
843 Type::Reference(r) => assert_eq!(r.identifier.value, "T"),
844 _ => panic!(),
845 }
846 }
847 res => panic!("Expected Ok(Type::PropertiesOf), got {res:?}"),
848 }
849
850 match do_parse("private-properties-of<T>") {
851 Ok(Type::PropertiesOf(p)) => {
852 assert_eq!(p.keyword.value, "private-properties-of");
853 assert_eq!(p.filter, PropertiesOfFilter::Private);
854 match &p.parameter.entry.inner {
855 Type::Reference(r) => assert_eq!(r.identifier.value, "T"),
856 _ => panic!(),
857 }
858 }
859 res => panic!("Expected Ok(Type::PropertiesOf), got {res:?}"),
860 }
861
862 match do_parse("public-properties-of<T>") {
863 Ok(Type::PropertiesOf(p)) => {
864 assert_eq!(p.keyword.value, "public-properties-of");
865 assert_eq!(p.filter, PropertiesOfFilter::Public);
866 match &p.parameter.entry.inner {
867 Type::Reference(r) => assert_eq!(r.identifier.value, "T"),
868 _ => panic!(),
869 }
870 }
871 res => panic!("Expected Ok(Type::PropertiesOf), got {res:?}"),
872 }
873 }
874
875 #[test]
876 fn test_variable() {
877 match do_parse("$myVar") {
878 Ok(Type::Variable(v)) => {
879 assert_eq!(v.value, "$myVar");
880 }
881 res => panic!("Expected Ok(Type::Variable), got {res:?}"),
882 }
883 }
884
885 #[test]
886 fn test_nullable_intersection() {
887 match do_parse("Countable&?Traversable") {
889 Ok(Type::Intersection(i)) => {
890 assert!(matches!(*i.left, Type::Reference(r) if r.identifier.value == "Countable"));
891 assert!(matches!(*i.right, Type::Nullable(_)));
892 if let Type::Nullable(n) = *i.right {
893 assert!(matches!(*n.inner, Type::Reference(r) if r.identifier.value == "Traversable"));
894 } else {
895 panic!();
896 }
897 }
898 res => panic!("Expected Ok(Type::Intersection), got {res:?}"),
899 }
900 }
901
902 #[test]
903 fn test_parenthesized_nullable() {
904 match do_parse("?(Countable&Traversable)") {
905 Ok(Type::Nullable(n)) => {
906 assert!(matches!(*n.inner, Type::Parenthesized(_)));
907 if let Type::Parenthesized(p) = *n.inner {
908 assert!(matches!(*p.inner, Type::Intersection(_)));
909 } else {
910 panic!()
911 }
912 }
913 res => panic!("Expected Ok(Type::Nullable), got {res:?}"),
914 }
915 }
916
917 #[test]
918 fn test_positive_negative_int() {
919 match do_parse("positive-int|negative-int") {
920 Ok(Type::Union(u)) => {
921 assert!(matches!(*u.left, Type::PositiveInt(_)));
922 assert!(matches!(*u.right, Type::NegativeInt(_)));
923 }
924 res => panic!("Expected Ok(Type::Union), got {res:?}"),
925 }
926 }
927
928 #[test]
929 fn test_parse_float_alias() {
930 match do_parse("double") {
931 Ok(Type::Float(f)) => {
932 assert_eq!(f.value, "double");
933 }
934 res => panic!("Expected Ok(Type::Float), got {res:?}"),
935 }
936
937 match do_parse("real") {
938 Ok(Type::Float(f)) => {
939 assert_eq!(f.value, "real");
940 }
941 res => panic!("Expected Ok(Type::Float), got {res:?}"),
942 }
943
944 match do_parse("float") {
945 Ok(Type::Float(f)) => {
946 assert_eq!(f.value, "float");
947 }
948 res => panic!("Expected Ok(Type::Float), got {res:?}"),
949 }
950 }
951
952 #[test]
953 fn test_parse_bool_alias() {
954 match do_parse("boolean") {
955 Ok(Type::Bool(b)) => {
956 assert_eq!(b.value, "boolean");
957 }
958 res => panic!("Expected Ok(Type::Bool), got {res:?}"),
959 }
960
961 match do_parse("bool") {
962 Ok(Type::Bool(b)) => {
963 assert_eq!(b.value, "bool");
964 }
965 res => panic!("Expected Ok(Type::Bool), got {res:?}"),
966 }
967 }
968
969 #[test]
970 fn test_parse_integer_alias() {
971 match do_parse("integer") {
972 Ok(Type::Int(i)) => {
973 assert_eq!(i.value, "integer");
974 }
975 res => panic!("Expected Ok(Type::Int), got {res:?}"),
976 }
977
978 match do_parse("int") {
979 Ok(Type::Int(i)) => {
980 assert_eq!(i.value, "int");
981 }
982 res => panic!("Expected Ok(Type::Int), got {res:?}"),
983 }
984 }
985
986 #[test]
987 fn test_parse_callable_with_variables() {
988 match do_parse("callable(string ...$names)") {
989 Ok(Type::Callable(callable)) => {
990 assert_eq!(callable.keyword.value, "callable");
991 assert!(callable.specification.is_some());
992
993 let specification = callable.specification.unwrap();
994
995 assert!(specification.return_type.is_none());
996 assert_eq!(specification.parameters.entries.len(), 1);
997
998 let first_parameter = specification.parameters.entries.first().unwrap();
999 assert!(first_parameter.variable.is_some());
1000 assert!(first_parameter.ellipsis.is_some());
1001
1002 let variable = first_parameter.variable.unwrap();
1003 assert_eq!(variable.value, "$names");
1004 }
1005 res => panic!("Expected Ok(Type::Callable), got {res:?}"),
1006 }
1007 }
1008
1009 #[test]
1010 fn test_parse_string_or_lowercase_string_union() {
1011 match do_parse("string|lowercase-string") {
1012 Ok(Type::Union(u)) => {
1013 assert!(matches!(*u.left, Type::String(_)));
1014 assert!(matches!(*u.right, Type::LowercaseString(_)));
1015 }
1016 res => panic!("Expected Ok(Type::Union), got {res:?}"),
1017 }
1018 }
1019
1020 #[test]
1021 fn test_parse_optional_literal_string_shape_field() {
1022 match do_parse("array{'salt'?: int, 'cost'?: int, ...}") {
1023 Ok(Type::Shape(shape)) => {
1024 assert_eq!(shape.fields.len(), 2);
1025 assert!(shape.additional_fields.is_some());
1026
1027 let first_field = &shape.fields[0];
1028 assert!(first_field.is_optional());
1029 assert!(matches!(
1030 first_field.key.as_ref().map(|k| k.name.as_ref()),
1031 Some(Type::LiteralString(LiteralStringType { raw: "'salt'", value: "salt", .. }))
1032 ));
1033 assert!(matches!(first_field.value.as_ref(), Type::Int(_)));
1034
1035 let second_field = &shape.fields[1];
1036 assert!(second_field.is_optional());
1037 assert!(matches!(
1038 second_field.key.as_ref().map(|k| k.name.as_ref()),
1039 Some(Type::LiteralString(LiteralStringType { raw: "'cost'", value: "cost", .. }))
1040 ));
1041 assert!(matches!(second_field.value.as_ref(), Type::Int(_)));
1042 }
1043 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
1044 }
1045 }
1046}