1#![doc = include_str!("./../README.md")]
2
3use bumpalo::Bump;
4
5use mago_span::Position;
6use mago_span::Span;
7use mago_syntax_core::input::Input;
8
9use crate::ast::Type;
10use crate::error::ParseError;
11use crate::lexer::TypeLexer;
12
13pub mod ast;
14pub mod error;
15pub mod lexer;
16pub mod parser;
17pub mod token;
18
19pub fn parse_str<'arena>(arena: &'arena Bump, span: Span, input: &'arena str) -> Result<Type<'arena>, ParseError> {
39 let input = Input::anchored_at(span.file_id, input.as_bytes(), span.start);
40 let lexer = TypeLexer::new(input);
41 parser::construct(arena, lexer)
42}
43
44pub fn parse_prefix<'arena>(
65 arena: &'arena Bump,
66 span: Span,
67 input: &'arena str,
68) -> Result<(Type<'arena>, Position), ParseError> {
69 let input_obj = Input::anchored_at(span.file_id, input.as_bytes(), span.start);
70 let lexer = TypeLexer::new(input_obj);
71 parser::construct_prefix(arena, lexer)
72}
73
74#[cfg(test)]
75mod tests {
76 use bumpalo::Bump;
77
78 use mago_database::file::FileId;
79 use mago_span::HasSpan;
80 use mago_span::Position;
81 use mago_span::Span;
82
83 use crate::ast::*;
84
85 use super::*;
86
87 fn do_parse(input: &str) -> Result<Type<'static>, ParseError> {
93 let arena: &'static Bump = Box::leak(Box::new(Bump::new()));
94 let owned: &'static str = arena.alloc_str(input);
95 let span = Span::new(FileId::zero(), Position::new(0), Position::new(owned.len() as u32));
96 parse_str(arena, span, owned)
97 }
98
99 #[test]
100 fn test_parse_simple_keyword() {
101 let result = do_parse("int");
102 assert!(result.is_ok());
103 match result.unwrap() {
104 Type::Int(k) => assert_eq!(k.value, "int"),
105 _ => panic!("Expected Type::Int"),
106 }
107 }
108
109 #[test]
110 fn test_parse_composite_keyword() {
111 let result = do_parse("non-empty-string");
112 assert!(result.is_ok());
113 match result.unwrap() {
114 Type::NonEmptyString(k) => assert_eq!(k.value, "non-empty-string"),
115 _ => panic!("Expected Type::NonEmptyString"),
116 }
117 }
118
119 #[test]
120 fn test_parse_literal_ints() {
121 let assert_parsed_literal_int = |input: &str, expected_value: u64| {
122 let result = do_parse(input);
123 assert!(result.is_ok());
124 match result.unwrap() {
125 Type::LiteralInt(LiteralIntType { value, .. }) => assert_eq!(
126 value, expected_value,
127 "Expected value to be {expected_value} for input {input}, but got {value}"
128 ),
129 _ => panic!("Expected Type::LiteralInt"),
130 }
131 };
132
133 assert_parsed_literal_int("0", 0);
134 assert_parsed_literal_int("1", 1);
135 assert_parsed_literal_int("123_345", 123_345);
136 assert_parsed_literal_int("0b1", 1);
137 assert_parsed_literal_int("0o10", 8);
138 assert_parsed_literal_int("0x1", 1);
139 assert_parsed_literal_int("0x10", 16);
140 assert_parsed_literal_int("0xFF", 255);
141 }
142
143 #[test]
144 fn test_parse_literal_floats() {
145 let assert_parsed_literal_float = |input: &str, expected_value: f64| {
146 let result = do_parse(input);
147 assert!(result.is_ok());
148 match result.unwrap() {
149 Type::LiteralFloat(LiteralFloatType { value, .. }) => assert_eq!(
150 value, expected_value,
151 "Expected value to be {expected_value} for input {input}, but got {value}"
152 ),
153 _ => panic!("Expected Type::LiteralInt"),
154 }
155 };
156
157 assert_parsed_literal_float("0.0", 0.0);
158 assert_parsed_literal_float("1.0", 1.0);
159 assert_parsed_literal_float("0.1e1", 1.0);
160 assert_parsed_literal_float("0.1e-1", 0.01);
161 assert_parsed_literal_float("0.1E1", 1.0);
162 assert_parsed_literal_float("0.1E-1", 0.01);
163 assert_parsed_literal_float("0.1e+1", 1.0);
164 assert_parsed_literal_float(".1e+1", 1.0);
165 }
166
167 #[test]
168 fn test_float_with_dangling_exponent_does_not_panic() {
169 match do_parse("3.") {
170 Ok(Type::LiteralFloat(LiteralFloatType { value, raw, .. })) => {
171 assert_eq!(*value, 3.0);
172 assert_eq!(raw, "3.");
173 }
174 other => panic!("expected `3.` to parse as LiteralFloat 3.0, got: {other:?}"),
175 }
176
177 let _ = do_parse("3.eint");
178 let _ = do_parse("3.e");
179 }
180
181 #[test]
182 fn test_parse_simple_union() {
183 match do_parse("int|string") {
184 Ok(ty) => match ty {
185 Type::Union(u) => {
186 assert!(matches!(u.left, Type::Int(_)));
187 assert!(matches!(u.right, Type::String(_)));
188 }
189 _ => panic!("Expected Type::Union"),
190 },
191 Err(err) => {
192 panic!("Failed to parse union type: {err:?}");
193 }
194 }
195 }
196
197 #[test]
198 fn test_parse_variable_union() {
199 match do_parse("$a|$b") {
200 Ok(ty) => match ty {
201 Type::Union(u) => {
202 assert!(matches!(u.left, Type::Variable(_)));
203 assert!(matches!(u.right, Type::Variable(_)));
204 }
205 _ => panic!("Expected Type::Union"),
206 },
207 Err(err) => {
208 panic!("Failed to parse union type: {err:?}");
209 }
210 }
211 }
212
213 #[test]
214 fn test_parse_nullable() {
215 let result = do_parse("?string");
216 assert!(result.is_ok());
217 match result.unwrap() {
218 Type::Nullable(n) => {
219 assert!(matches!(n.inner, Type::String(_)));
220 }
221 _ => panic!("Expected Type::Nullable"),
222 }
223 }
224
225 #[test]
226 fn test_parse_generic_array() {
227 let result = do_parse("array<int, bool>");
228 assert!(result.is_ok());
229 match result.unwrap() {
230 Type::Array(a) => {
231 assert!(a.parameters.is_some());
232 let params = a.parameters.unwrap();
233 assert_eq!(params.entries.len(), 2);
234 assert!(matches!(params.entries[0].inner, Type::Int(_)));
235 assert!(matches!(params.entries[1].inner, Type::Bool(_)));
236 }
237 _ => panic!("Expected Type::Array"),
238 }
239 }
240
241 #[test]
242 fn test_parse_generic_array_one_param() {
243 match do_parse("array<string>") {
244 Ok(Type::Array(a)) => {
245 let params = a.parameters.expect("Expected generic parameters");
246 assert_eq!(params.entries.len(), 1);
247 assert!(matches!(params.entries[0].inner, Type::String(_)));
248 }
249 res => panic!("Expected Ok(Type::Array), got {res:?}"),
250 }
251 }
252
253 #[test]
254 fn test_parse_generic_list() {
255 match do_parse("list<string>") {
256 Ok(Type::List(l)) => {
257 let params = l.parameters.expect("Expected generic parameters");
258 assert_eq!(params.entries.len(), 1);
259 assert!(matches!(params.entries[0].inner, Type::String(_)));
260 }
261 res => panic!("Expected Ok(Type::List), got {res:?}"),
262 }
263 }
264
265 #[test]
266 fn test_parse_non_empty_array() {
267 match do_parse("non-empty-array<int, bool>") {
268 Ok(Type::NonEmptyArray(a)) => {
269 let params = a.parameters.expect("Expected generic parameters");
270 assert_eq!(params.entries.len(), 2);
271 assert!(matches!(params.entries[0].inner, Type::Int(_)));
272 assert!(matches!(params.entries[1].inner, Type::Bool(_)));
273 }
274 res => panic!("Expected Ok(Type::NonEmptyArray), got {res:?}"),
275 }
276 }
277
278 #[test]
279 fn test_parse_nested_generics() {
280 match do_parse("list<array<int, string>>") {
281 Ok(Type::List(l)) => {
282 let params = l.parameters.expect("Expected generic parameters");
283 assert_eq!(params.entries.len(), 1);
284 match ¶ms.entries[0].inner {
285 Type::Array(inner_array) => {
286 let inner_params = inner_array.parameters.as_ref().expect("Inner array needs params");
287 assert_eq!(inner_params.entries.len(), 2);
288 assert!(matches!(inner_params.entries[0].inner, Type::Int(_)));
289 assert!(matches!(inner_params.entries[1].inner, Type::String(_)));
290 }
291 _ => panic!("Expected inner type to be Type::Array"),
292 }
293 }
294 res => panic!("Expected Ok(Type::List), got {res:?}"),
295 }
296 }
297
298 #[test]
299 fn test_parse_simple_shape() {
300 let result = do_parse("array{'name': string}");
301 assert!(matches!(result, Ok(Type::Shape(_))));
302 let Ok(Type::Shape(shape)) = result else {
303 panic!("Expected Type::Shape");
304 };
305
306 assert_eq!(shape.kind, ShapeTypeKind::Array);
307 assert_eq!(shape.keyword.value, "array");
308 assert_eq!(shape.fields.len(), 1);
309 assert!(shape.additional_fields.is_none());
310
311 let field = &shape.fields[0];
312 assert!(matches!(field.key.as_ref().map(|k| &k.key), Some(ShapeKey::String { value: "name", .. })));
313 assert!(matches!(field.value, Type::String(_)));
314 }
315
316 #[test]
317 fn test_parse_int_key_shape() {
318 match do_parse("array{0: string, 1: bool}") {
319 Ok(Type::Shape(shape)) => {
320 assert_eq!(shape.fields.len(), 2);
321 let first_field = &shape.fields[0];
322 assert!(matches!(first_field.key.as_ref().map(|k| &k.key), Some(ShapeKey::Integer { value: 0, .. })));
323 assert!(matches!(first_field.value, Type::String(_)));
324 let second_field = &shape.fields[1];
325 assert!(matches!(second_field.key.as_ref().map(|k| &k.key), Some(ShapeKey::Integer { value: 1, .. })));
326 assert!(matches!(second_field.value, Type::Bool(_)));
327 }
328 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
329 }
330 }
331
332 #[test]
333 fn test_parse_optional_field_shape() {
334 match do_parse("array{name: string, age?: int, address: string}") {
335 Ok(Type::Shape(shape)) => {
336 assert_eq!(shape.fields.len(), 3);
337 assert!(!shape.fields[0].is_optional());
338 assert!(shape.fields[1].is_optional());
339 assert!(!shape.fields[2].is_optional());
340 }
341 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
342 }
343 }
344
345 #[test]
346 fn test_parse_unsealed_shape() {
347 match do_parse("array{name: string, ...}") {
348 Ok(Type::Shape(shape)) => {
349 assert_eq!(shape.fields.len(), 1);
350 assert!(shape.additional_fields.is_some());
351 assert!(shape.additional_fields.unwrap().parameters.is_none()); }
353 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
354 }
355 }
356
357 #[test]
358 fn test_parse_shape_with_keys_containing_special_chars() {
359 match do_parse("array{key-with-dash: int, key-with---multiple-dashes?: int}") {
360 Ok(Type::Shape(shape)) => {
361 assert_eq!(shape.fields.len(), 2);
362
363 if let Some(ShapeKey::String { value: s, .. }) = shape.fields[0].key.as_ref().map(|k| &k.key) {
364 assert_eq!(*s, "key-with-dash");
365 } else {
366 panic!("Expected key to be a ShapeKey::String");
367 }
368
369 if let Some(ShapeKey::String { value: s, .. }) = shape.fields[1].key.as_ref().map(|k| &k.key) {
370 assert_eq!(*s, "key-with---multiple-dashes");
371 } else {
372 panic!("Expected key to be a ShapeKey::String");
373 }
374 }
375 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
376 }
377 }
378
379 #[test]
380 fn test_parse_shape_with_keys_after_types() {
381 match do_parse("array{list: list<int>, int?: int, string: string, bool: bool}") {
382 Ok(Type::Shape(shape)) => {
383 assert_eq!(shape.fields.len(), 4);
384
385 if let Some(ShapeKey::String { value: s, .. }) = shape.fields[0].key.as_ref().map(|k| &k.key) {
386 assert_eq!(*s, "list");
387 } else {
388 panic!("Expected key to be a ShapeKey::String");
389 }
390
391 if let Some(ShapeKey::String { value: s, .. }) = shape.fields[1].key.as_ref().map(|k| &k.key) {
392 assert_eq!(*s, "int");
393 } else {
394 panic!("Expected key to be a ShapeKey::String");
395 }
396
397 if let Some(ShapeKey::String { value: s, .. }) = shape.fields[2].key.as_ref().map(|k| &k.key) {
398 assert_eq!(*s, "string");
399 } else {
400 panic!("Expected key to be a ShapeKey::String");
401 }
402
403 if let Some(ShapeKey::String { value: s, .. }) = shape.fields[3].key.as_ref().map(|k| &k.key) {
404 assert_eq!(*s, "bool");
405 } else {
406 panic!("Expected key to be a ShapeKey::String");
407 }
408 }
409 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
410 }
411 }
412
413 #[test]
414 fn test_parse_shape_keyless_entry_with_commas_inside_generics() {
415 match do_parse("array{array<int, string>}") {
419 Ok(Type::Shape(shape)) => {
420 assert_eq!(shape.fields.len(), 1);
421 assert!(shape.fields[0].key.is_none(), "expected a keyless (positional) field");
422 match shape.fields[0].value {
423 Type::Array(_) => {}
424 v => panic!("expected value to be a generic array type, got {v:?}"),
425 }
426 }
427 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
428 }
429 }
430
431 #[test]
432 fn test_parse_shape_keyed_entry_with_commas_inside_value_generics() {
433 match do_parse("array{foo: array<int, string>}") {
437 Ok(Type::Shape(shape)) => {
438 assert_eq!(shape.fields.len(), 1);
439 let key = shape.fields[0].key.as_ref().expect("expected a keyed field");
440 match &key.key {
441 ShapeKey::String { value, .. } => assert_eq!(*value, "foo"),
442 other => panic!("expected identifier key, got {other:?}"),
443 }
444 match shape.fields[0].value {
445 Type::Array(_) => {}
446 v => panic!("expected value to be a generic array type, got {v:?}"),
447 }
448 }
449 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
450 }
451 }
452
453 #[test]
454 fn test_parse_shape_with_large_union_value_does_not_overflow() {
455 let input = "array{\
461 int | string | float | bool | null | \
462 array<int, string> | array<string, int> | \
463 callable(int, string): bool | \
464 list<int> | iterable<string, mixed>\
465 }";
466 match do_parse(input) {
467 Ok(Type::Shape(shape)) => {
468 assert_eq!(shape.fields.len(), 1, "expected a single keyless field");
469 assert!(shape.fields[0].key.is_none(), "value is a union, not a keyed field");
470 match shape.fields[0].value {
471 Type::Union(_) => {}
472 v => panic!("expected a union value type, got {v:?}"),
473 }
474 }
475 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
476 }
477 }
478
479 #[test]
480 fn test_parse_shape_many_fields_with_nested_generics() {
481 let input = "array{\
486 a: list<int, string>, \
487 b: array<int, string>, \
488 c: iterable<int, string>, \
489 d: callable(int, string): void, \
490 e: array<string, array<int, string>>, \
491 f: string\
492 }";
493 match do_parse(input) {
494 Ok(Type::Shape(shape)) => {
495 assert_eq!(shape.fields.len(), 6);
496 for (i, expected_key) in ["a", "b", "c", "d", "e", "f"].iter().enumerate() {
497 let key = shape.fields[i].key.as_ref().expect("expected a keyed field");
498 match &key.key {
499 ShapeKey::String { value, .. } => assert_eq!(value, expected_key),
500 other => panic!("field {i}: expected identifier key, got {other:?}"),
501 }
502 }
503 }
504 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
505 }
506 }
507
508 #[test]
509 fn test_parse_unsealed_shape_with_fallback() {
510 match do_parse(
511 "array{
512 name: string, // This is a comment
513 ...<string, string>
514 }",
515 ) {
516 Ok(Type::Shape(shape)) => {
517 assert_eq!(shape.fields.len(), 1);
518 assert!(shape.additional_fields.as_ref().is_some_and(|a| a.parameters.is_some()));
519 let params = shape.additional_fields.unwrap().parameters.unwrap();
520 assert_eq!(params.entries.len(), 2);
521 assert!(matches!(params.entries[0].inner, Type::String(_)));
522 assert!(matches!(params.entries[1].inner, Type::String(_)));
523 }
524 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
525 }
526 }
527
528 #[test]
529 fn test_parse_empty_shape() {
530 match do_parse("array{}") {
531 Ok(Type::Shape(shape)) => {
532 assert_eq!(shape.fields.len(), 0);
533 assert!(shape.additional_fields.is_none());
534 }
535 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
536 }
537 }
538
539 #[test]
540 fn test_parse_nested_spread_singleline() {
541 match do_parse("array{a?: int, ...<string, array{b?: int, ...<string, int>}>}") {
543 Ok(Type::Shape(shape)) => {
544 assert_eq!(shape.fields.len(), 1);
545 assert!(shape.additional_fields.is_some());
546 }
547 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
548 }
549 }
550
551 #[test]
552 fn test_parse_nested_spread_multiline() {
553 match do_parse(
554 "array{
555 a?: int,
556 ...<string, array{
557 b?: int,
558 ...<string, int>,
559 }>
560 }",
561 ) {
562 Ok(Type::Shape(shape)) => {
563 assert_eq!(shape.fields.len(), 1);
564 assert!(shape.additional_fields.is_some());
565 }
566 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
567 }
568 }
569
570 #[test]
571 fn test_parse_spread_with_trailing_comma() {
572 match do_parse("array{a?: int, ...<string, int>,}") {
573 Ok(Type::Shape(shape)) => {
574 assert_eq!(shape.fields.len(), 1);
575 assert!(shape.additional_fields.is_some());
576 }
577 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
578 }
579 }
580
581 #[test]
582 fn test_parse_error_unexpected_token() {
583 let result = do_parse("int|>");
584 assert!(result.is_err());
585 assert!(matches!(result.unwrap_err(), ParseError::UnexpectedToken { .. }));
586 }
587
588 #[test]
589 fn test_parse_error_eof() {
590 let result = do_parse("array<int");
591 assert!(result.is_err());
592 assert!(matches!(result.unwrap_err(), ParseError::UnexpectedEndOfFile { .. }));
593 }
594
595 #[test]
596 fn test_parse_error_trailing_token() {
597 let result = do_parse("int|string&");
598 assert!(result.is_err());
599 assert!(matches!(result.unwrap_err(), ParseError::UnexpectedEndOfFile { .. }));
600 }
601
602 #[test]
603 fn test_parse_intersection() {
604 match do_parse("Countable&Traversable") {
605 Ok(Type::Intersection(i)) => {
606 assert!(matches!(i.left, Type::Reference(_)));
607 assert!(matches!(i.right, Type::Reference(_)));
608
609 if let Type::Reference(r) = i.left {
610 assert_eq!(r.identifier.value, "Countable");
611 } else {
612 panic!();
613 }
614
615 if let Type::Reference(r) = i.right {
616 assert_eq!(r.identifier.value, "Traversable");
617 } else {
618 panic!();
619 }
620 }
621 res => panic!("Expected Ok(Type::Intersection), got {res:?}"),
622 }
623 }
624
625 #[test]
626 fn test_parse_member_ref() {
627 match do_parse("MyClass::MY_CONST") {
628 Ok(Type::MemberReference(m)) => {
629 assert_eq!(m.class.value, "MyClass");
630 assert_eq!(m.member.to_string(), "MY_CONST");
631 }
632 res => panic!("Expected Ok(Type::MemberReference), got {res:?}"),
633 }
634
635 match do_parse("\\Fully\\Qualified::class") {
636 Ok(Type::MemberReference(m)) => {
637 assert_eq!(m.class.value, "\\Fully\\Qualified"); assert_eq!(m.member.to_string(), "class");
639 }
640 res => panic!("Expected Ok(Type::MemberReference), got {res:?}"),
641 }
642 }
643
644 #[test]
645 fn test_parse_member_ref_named_new() {
646 match do_parse("Action::NEW") {
647 Ok(Type::MemberReference(m)) => {
648 assert_eq!(m.class.value, "Action");
649 assert_eq!(m.member.to_string(), "NEW");
650 }
651 res => panic!("Expected Ok(Type::MemberReference) for Action::NEW, got {res:?}"),
652 }
653
654 match do_parse("Action::new") {
655 Ok(Type::MemberReference(m)) => {
656 assert_eq!(m.class.value, "Action");
657 assert_eq!(m.member.to_string(), "new");
658 }
659 res => panic!("Expected Ok(Type::MemberReference) for Action::new, got {res:?}"),
660 }
661
662 match do_parse("Action::DELETE|Action::NEW") {
663 Ok(Type::Union(u)) => match (&u.left, &u.right) {
664 (Type::MemberReference(lhs), Type::MemberReference(rhs)) => {
665 assert_eq!(lhs.member.to_string(), "DELETE");
666 assert_eq!(rhs.member.to_string(), "NEW");
667 }
668 other => panic!("Expected two member references, got {other:?}"),
669 },
670 res => panic!("Expected Ok(Type::Union), got {res:?}"),
671 }
672
673 match do_parse("\\App\\Action::NEW") {
674 Ok(Type::MemberReference(m)) => {
675 assert_eq!(m.member.to_string(), "NEW");
676 }
677 res => panic!("Expected Ok(Type::MemberReference), got {res:?}"),
678 }
679
680 match do_parse("App\\Action::NEW") {
681 Ok(Type::MemberReference(m)) => {
682 assert_eq!(m.member.to_string(), "NEW");
683 }
684 res => panic!("Expected Ok(Type::MemberReference), got {res:?}"),
685 }
686
687 match do_parse("Action::new*") {
688 Ok(Type::MemberReference(m)) => {
689 assert_eq!(m.class.value, "Action");
690 assert!(matches!(m.member, MemberReferenceSelector::StartsWith(..)));
691 }
692 res => panic!("Expected Ok(Type::MemberReference) for Action::new*, got {res:?}"),
693 }
694
695 match do_parse("Action::*new") {
696 Ok(Type::MemberReference(m)) => {
697 assert_eq!(m.class.value, "Action");
698 assert!(matches!(m.member, MemberReferenceSelector::EndsWith(..)));
699 }
700 res => panic!("Expected Ok(Type::MemberReference) for Action::*new, got {res:?}"),
701 }
702
703 match do_parse("new<Foo>") {
704 Ok(Type::New(_)) => {}
705 res => panic!("Expected Ok(Type::New), got {res:?}"),
706 }
707 }
708
709 #[test]
710 fn test_parse_new_in_other_identifier_contexts() {
711 match do_parse("array{new: int}") {
712 Ok(Type::Shape(_)) => {}
713 res => panic!("Expected Ok(Type::Shape) for array{{new: int}}, got {res:?}"),
714 }
715
716 match do_parse("array{new?: int}") {
717 Ok(Type::Shape(_)) => {}
718 res => panic!("Expected Ok(Type::Shape) for array{{new?: int}}, got {res:?}"),
719 }
720
721 match do_parse("array{Foo::NEW: int}") {
722 Ok(Type::Shape(_)) => {}
723 res => panic!("Expected Ok(Type::Shape) for array{{Foo::NEW: int}}, got {res:?}"),
724 }
725
726 match do_parse("object{new: int}") {
727 Ok(Type::Object(_)) => {}
728 res => panic!("Expected Ok(Type::Object) for object{{new: int}}, got {res:?}"),
729 }
730
731 match do_parse("!Foo::new") {
732 Ok(Type::AliasReference(_)) => {}
733 res => panic!("Expected Ok(Type::AliasReference) for !Foo::new, got {res:?}"),
734 }
735 }
736
737 #[test]
738 fn test_parse_iterable() {
739 match do_parse("iterable<int, string>") {
740 Ok(Type::Iterable(i)) => {
741 let params = i.parameters.expect("Expected generic parameters");
742 assert_eq!(params.entries.len(), 2);
743 assert!(matches!(params.entries[0].inner, Type::Int(_)));
744 assert!(matches!(params.entries[1].inner, Type::String(_)));
745 }
746 res => panic!("Expected Ok(Type::Iterable), got {res:?}"),
747 }
748
749 match do_parse("iterable<bool>") {
750 Ok(Type::Iterable(i)) => {
752 let params = i.parameters.expect("Expected generic parameters");
753 assert_eq!(params.entries.len(), 1);
754 assert!(matches!(params.entries[0].inner, Type::Bool(_)));
755 }
756 res => panic!("Expected Ok(Type::Iterable), got {res:?}"),
757 }
758
759 match do_parse("iterable") {
760 Ok(Type::Iterable(i)) => {
761 assert!(i.parameters.is_none());
762 }
763 res => panic!("Expected Ok(Type::Iterable), got {res:?}"),
764 }
765 }
766
767 #[test]
768 fn test_parse_negated_int() {
769 let assert_negated_int = |input: &str, expected_value: u64| {
770 let result = do_parse(input);
771 assert!(result.is_ok());
772 match result.unwrap() {
773 Type::Negated(n) => {
774 assert!(matches!(n.number, LiteralIntOrFloatType::Int(_)));
775 if let LiteralIntOrFloatType::Int(lit) = n.number {
776 assert_eq!(lit.value, expected_value);
777 } else {
778 panic!()
779 }
780 }
781 _ => panic!("Expected Type::Negated"),
782 }
783 };
784
785 assert_negated_int("-0", 0);
786 assert_negated_int("-1", 1);
787 assert_negated_int(
788 "-
789 // This is a comment
790 123_345",
791 123_345,
792 );
793 assert_negated_int("-0b1", 1);
794 }
795
796 #[test]
797 fn test_parse_negated_float() {
798 let assert_negated_float = |input: &str, expected_value: f64| {
799 let result = do_parse(input);
800 assert!(result.is_ok());
801 match result.unwrap() {
802 Type::Negated(n) => {
803 assert!(matches!(n.number, LiteralIntOrFloatType::Float(_)));
804 if let LiteralIntOrFloatType::Float(lit) = n.number {
805 assert_eq!(lit.value, expected_value);
806 } else {
807 panic!()
808 }
809 }
810 _ => panic!("Expected Type::Negated"),
811 }
812 };
813
814 assert_negated_float("-0.0", 0.0);
815 assert_negated_float("-1.0", 1.0);
816 assert_negated_float("-0.1e1", 1.0);
817 assert_negated_float("-0.1e-1", 0.01);
818 }
819
820 #[test]
821 fn test_parse_negated_union() {
822 match do_parse("-1|-2.0|string") {
823 Ok(Type::Union(n)) => {
824 assert!(matches!(n.left, Type::Negated(_)));
825 assert!(matches!(n.right, Type::Union(_)));
826
827 if let Type::Negated(neg) = n.left {
828 assert!(matches!(neg.number, LiteralIntOrFloatType::Int(_)));
829 if let LiteralIntOrFloatType::Int(lit) = neg.number {
830 assert_eq!(lit.value, 1);
831 } else {
832 panic!()
833 }
834 } else {
835 panic!("Expected left side to be Type::Negated");
836 }
837
838 if let Type::Union(inner_union) = n.right {
839 assert!(matches!(inner_union.left, Type::Negated(_)));
840 assert!(matches!(inner_union.right, Type::String(_)));
841
842 if let Type::Negated(neg) = inner_union.left {
843 assert!(matches!(neg.number, LiteralIntOrFloatType::Float(_)));
844 if let LiteralIntOrFloatType::Float(lit) = neg.number {
845 assert_eq!(lit.value, 2.0);
846 } else {
847 panic!()
848 }
849 } else {
850 panic!("Expected left side of inner union to be Type::Negated");
851 }
852
853 if let Type::String(s) = inner_union.right {
854 assert_eq!(s.value, "string");
855 } else {
856 panic!("Expected right side of inner union to be Type::String");
857 }
858 } else {
859 panic!("Expected right side to be Type::Union");
860 }
861 }
862 res => panic!("Expected Ok(Type::Negated), got {res:?}"),
863 }
864 }
865
866 #[test]
867 fn test_parse_callable_no_spec() {
868 match do_parse("callable") {
869 Ok(Type::Callable(c)) => {
870 assert!(c.specification.is_none());
871 assert_eq!(c.kind, CallableTypeKind::Callable);
872 }
873 res => panic!("Expected Ok(Type::Callable), got {res:?}"),
874 }
875 }
876
877 #[test]
878 fn test_parse_callable_params_only() {
879 match do_parse("callable(int, ?string)") {
880 Ok(Type::Callable(c)) => {
881 let spec = c.specification.expect("Expected callable specification");
882 assert!(spec.return_type.is_none());
883 assert_eq!(spec.parameters.entries.len(), 2);
884 assert!(matches!(spec.parameters.entries[0].parameter_type, Some(Type::Int(_))));
885 assert!(matches!(spec.parameters.entries[1].parameter_type, Some(Type::Nullable(_))));
886 assert!(spec.parameters.entries[0].ellipsis.is_none());
887 assert!(spec.parameters.entries[0].equals.is_none());
888 }
889 res => panic!("Expected Ok(Type::Callable), got {res:?}"),
890 }
891 }
892
893 #[test]
894 fn test_parse_callable_return_only() {
895 match do_parse("callable(): void") {
896 Ok(Type::Callable(c)) => {
897 let spec = c.specification.expect("Expected callable specification");
898 assert!(spec.parameters.entries.is_empty());
899 assert!(spec.return_type.is_some());
900 assert!(matches!(spec.return_type.unwrap().return_type, Type::Void(_)));
901 }
902 res => panic!("Expected Ok(Type::Callable), got {res:?}"),
903 }
904 }
905
906 #[test]
907 fn test_parse_pure_callable_full() {
908 match do_parse("pure-callable(bool): int") {
909 Ok(Type::Callable(c)) => {
910 assert_eq!(c.kind, CallableTypeKind::PureCallable);
911 let spec = c.specification.expect("Expected callable specification");
912 assert_eq!(spec.parameters.entries.len(), 1);
913 assert!(matches!(spec.parameters.entries[0].parameter_type, Some(Type::Bool(_))));
914 assert!(spec.return_type.is_some());
915 assert!(matches!(spec.return_type.unwrap().return_type, Type::Int(_)));
916 }
917 res => panic!("Expected Ok(Type::Callable), got {res:?}"),
918 }
919 }
920
921 #[test]
922 fn test_parse_closure_via_identifier() {
923 match do_parse("Closure(string): bool") {
924 Ok(Type::Callable(c)) => {
925 assert_eq!(c.kind, CallableTypeKind::Closure);
926 assert_eq!(c.keyword.value, "Closure");
927 let spec = c.specification.expect("Expected callable specification");
928 assert_eq!(spec.parameters.entries.len(), 1);
929 assert!(matches!(spec.parameters.entries[0].parameter_type, Some(Type::String(_))));
930 assert!(spec.return_type.is_some());
931 assert!(matches!(spec.return_type.unwrap().return_type, Type::Bool(_)));
932 }
933 res => panic!("Expected Ok(Type::Callable) for Closure, got {res:?}"),
934 }
935 }
936
937 #[test]
938 fn test_parse_complex_pure_callable() {
939 match do_parse("pure-callable(list<int>, ?Closure(): void=, int...): ((Simple&Iter<T>)|null)") {
940 Ok(Type::Callable(c)) => {
941 assert_eq!(c.kind, CallableTypeKind::PureCallable);
942 let spec = c.specification.expect("Expected callable specification");
943 assert_eq!(spec.parameters.entries.len(), 3);
944 assert!(spec.return_type.is_some());
945
946 let first_param = &spec.parameters.entries[0];
947 assert!(matches!(first_param.parameter_type, Some(Type::List(_))));
948 assert!(first_param.ellipsis.is_none());
949 assert!(first_param.equals.is_none());
950
951 let second_param = &spec.parameters.entries[1];
952 assert!(matches!(second_param.parameter_type, Some(Type::Nullable(_))));
953 assert!(second_param.ellipsis.is_none());
954 assert!(second_param.equals.is_some());
955
956 let third_param = &spec.parameters.entries[2];
957 assert!(matches!(third_param.parameter_type, Some(Type::Int(_))));
958 assert!(third_param.ellipsis.is_some());
959 assert!(third_param.equals.is_none());
960
961 if let Type::Parenthesized(p) = spec.return_type.unwrap().return_type {
962 assert!(matches!(p.inner, Type::Union(_)));
963 if let Type::Union(u) = p.inner {
964 assert!(matches!(u.left, Type::Parenthesized(_)));
965 assert!(matches!(u.right, Type::Null(_)));
966 }
967 } else {
968 panic!("Expected Type::CallableReturnType");
969 }
970 }
971 res => panic!("Expected Ok(Type::Callable), got {res:?}"),
972 }
973 }
974
975 #[test]
976 fn test_parse_conditional_type() {
977 match do_parse("int is not string ? array : int") {
978 Ok(Type::Conditional(c)) => {
979 assert!(matches!(c.subject, Type::Int(_)));
980 assert!(c.not.is_some());
981 assert!(matches!(c.target, Type::String(_)));
982 assert!(matches!(c.then, Type::Array(_)));
983 assert!(matches!(c.otherwise, Type::Int(_)));
984 }
985 res => panic!("Expected Ok(Type::Conditional), got {res:?}"),
986 }
987
988 match do_parse("$input is string ? array : int") {
989 Ok(Type::Conditional(c)) => {
990 assert!(matches!(c.subject, Type::Variable(_)));
991 assert!(c.not.is_none());
992 assert!(matches!(c.target, Type::String(_)));
993 assert!(matches!(c.then, Type::Array(_)));
994 assert!(matches!(c.otherwise, Type::Int(_)));
995 }
996 res => panic!("Expected Ok(Type::Conditional), got {res:?}"),
997 }
998
999 match do_parse("int is string ? array : (int is not $bar ? string : $baz)") {
1000 Ok(Type::Conditional(c)) => {
1001 assert!(matches!(c.subject, Type::Int(_)));
1002 assert!(c.not.is_none());
1003 assert!(matches!(c.target, Type::String(_)));
1004 assert!(matches!(c.then, Type::Array(_)));
1005
1006 let Type::Parenthesized(p) = c.otherwise else {
1007 panic!("Expected Type::Parenthesized");
1008 };
1009
1010 if let Type::Conditional(inner_conditional) = p.inner {
1011 assert!(matches!(inner_conditional.subject, Type::Int(_)));
1012 assert!(inner_conditional.not.is_some());
1013 assert!(matches!(inner_conditional.target, Type::Variable(_)));
1014 assert!(matches!(inner_conditional.then, Type::String(_)));
1015 assert!(matches!(inner_conditional.otherwise, Type::Variable(_)));
1016 } else {
1017 panic!("Expected Type::Conditional");
1018 }
1019 }
1020 res => panic!("Expected Ok(Type::Conditional), got {res:?}"),
1021 }
1022 }
1023
1024 #[test]
1025 fn test_keyof() {
1026 match do_parse("key-of<MyArray>") {
1027 Ok(Type::KeyOf(k)) => {
1028 assert_eq!(k.keyword.value, "key-of");
1029 match &k.parameter.entry.inner {
1030 Type::Reference(r) => assert_eq!(r.identifier.value, "MyArray"),
1031 _ => panic!("Expected Type::Reference"),
1032 }
1033 }
1034 res => panic!("Expected Ok(Type::KeyOf), got {res:?}"),
1035 }
1036 }
1037
1038 #[test]
1039 fn test_valueof() {
1040 match do_parse("value-of<MyArray>") {
1041 Ok(Type::ValueOf(v)) => {
1042 assert_eq!(v.keyword.value, "value-of");
1043 match &v.parameter.entry.inner {
1044 Type::Reference(r) => assert_eq!(r.identifier.value, "MyArray"),
1045 _ => panic!("Expected Type::Reference"),
1046 }
1047 }
1048 res => panic!("Expected Ok(Type::ValueOf), got {res:?}"),
1049 }
1050 }
1051
1052 #[test]
1053 fn test_indexed_access() {
1054 match do_parse("MyArray[MyKey]") {
1055 Ok(Type::IndexAccess(i)) => {
1056 match i.target {
1057 Type::Reference(r) => assert_eq!(r.identifier.value, "MyArray"),
1058 _ => panic!("Expected Type::Reference"),
1059 }
1060 match i.index {
1061 Type::Reference(r) => assert_eq!(r.identifier.value, "MyKey"),
1062 _ => panic!("Expected Type::Reference"),
1063 }
1064 }
1065 res => panic!("Expected Ok(Type::IndexAccess), got {res:?}"),
1066 }
1067 }
1068
1069 #[test]
1070 fn test_slice_type() {
1071 match do_parse("string[]") {
1072 Ok(Type::Slice(s)) => {
1073 assert!(matches!(s.inner, Type::String(_)));
1074 }
1075 res => panic!("Expected Ok(Type::Slice), got {res:?}"),
1076 }
1077 }
1078
1079 #[test]
1080 fn test_slice_of_slice_of_slice_type() {
1081 match do_parse("string[][][]") {
1082 Ok(Type::Slice(s)) => {
1083 assert!(matches!(s.inner, Type::Slice(_)));
1084 if let Type::Slice(inner_slice) = s.inner {
1085 assert!(matches!(inner_slice.inner, Type::Slice(_)));
1086 if let Type::Slice(inner_inner_slice) = inner_slice.inner {
1087 assert!(matches!(inner_inner_slice.inner, Type::String(_)));
1088 } else {
1089 panic!("Expected inner slice to be a Slice");
1090 }
1091 } else {
1092 panic!("Expected outer slice to be a Slice");
1093 }
1094 }
1095 res => panic!("Expected Ok(Type::Slice), got {res:?}"),
1096 }
1097 }
1098
1099 #[test]
1100 fn test_int_range() {
1101 match do_parse("int<0, 100>") {
1102 Ok(Type::IntRange(r)) => {
1103 assert_eq!(r.keyword.value, "int");
1104
1105 match r.min {
1106 IntOrKeyword::Int(literal_int_type) => {
1107 assert_eq!(literal_int_type.value, 0);
1108 }
1109 _ => {
1110 panic!("Expected min to be a LiteralIntType, got `{}`", r.min)
1111 }
1112 }
1113
1114 match r.max {
1115 IntOrKeyword::Int(literal_int_type) => {
1116 assert_eq!(literal_int_type.value, 100);
1117 }
1118 _ => {
1119 panic!("Expected max to be a LiteralIntType, got `{}`", r.max)
1120 }
1121 }
1122 }
1123 res => panic!("Expected Ok(Type::IntRange), got {res:?}"),
1124 }
1125
1126 match do_parse("int<min, 0>") {
1127 Ok(Type::IntRange(r)) => {
1128 match r.min {
1129 IntOrKeyword::Keyword(keyword) => {
1130 assert_eq!(keyword.value, "min");
1131 }
1132 _ => {
1133 panic!("Expected min to be a Keyword, got `{}`", r.min)
1134 }
1135 }
1136
1137 match r.max {
1138 IntOrKeyword::Int(literal_int_type) => {
1139 assert_eq!(literal_int_type.value, 0);
1140 }
1141 _ => {
1142 panic!("Expected max to be a LiteralIntType, got `{}`", r.max)
1143 }
1144 }
1145 }
1146 res => panic!("Expected Ok(Type::IntRange), got {res:?}"),
1147 }
1148
1149 match do_parse("int<min, max>") {
1150 Ok(Type::IntRange(r)) => {
1151 match r.min {
1152 IntOrKeyword::Keyword(keyword) => {
1153 assert_eq!(keyword.value, "min");
1154 }
1155 _ => {
1156 panic!("Expected min to be a Keyword, got `{}`", r.min)
1157 }
1158 }
1159
1160 match r.max {
1161 IntOrKeyword::Keyword(keyword) => {
1162 assert_eq!(keyword.value, "max");
1163 }
1164 _ => {
1165 panic!("Expected max to be a Keyword, got `{}`", r.max)
1166 }
1167 }
1168 }
1169 res => panic!("Expected Ok(Type::IntRange), got {res:?}"),
1170 }
1171 }
1172
1173 #[test]
1174 fn test_properties_of() {
1175 match do_parse("properties-of<MyClass>") {
1176 Ok(Type::PropertiesOf(p)) => {
1177 assert_eq!(p.keyword.value, "properties-of");
1178 assert_eq!(p.filter, PropertiesOfFilter::All);
1179 match &p.parameter.entry.inner {
1180 Type::Reference(r) => assert_eq!(r.identifier.value, "MyClass"),
1181 _ => panic!(),
1182 }
1183 }
1184 res => panic!("Expected Ok(Type::PropertiesOf), got {res:?}"),
1185 }
1186
1187 match do_parse("protected-properties-of<T>") {
1188 Ok(Type::PropertiesOf(p)) => {
1189 assert_eq!(p.keyword.value, "protected-properties-of");
1190 assert_eq!(p.filter, PropertiesOfFilter::Protected);
1191 match &p.parameter.entry.inner {
1192 Type::Reference(r) => assert_eq!(r.identifier.value, "T"),
1193 _ => panic!(),
1194 }
1195 }
1196 res => panic!("Expected Ok(Type::PropertiesOf), got {res:?}"),
1197 }
1198
1199 match do_parse("private-properties-of<T>") {
1200 Ok(Type::PropertiesOf(p)) => {
1201 assert_eq!(p.keyword.value, "private-properties-of");
1202 assert_eq!(p.filter, PropertiesOfFilter::Private);
1203 match &p.parameter.entry.inner {
1204 Type::Reference(r) => assert_eq!(r.identifier.value, "T"),
1205 _ => panic!(),
1206 }
1207 }
1208 res => panic!("Expected Ok(Type::PropertiesOf), got {res:?}"),
1209 }
1210
1211 match do_parse("public-properties-of<T>") {
1212 Ok(Type::PropertiesOf(p)) => {
1213 assert_eq!(p.keyword.value, "public-properties-of");
1214 assert_eq!(p.filter, PropertiesOfFilter::Public);
1215 match &p.parameter.entry.inner {
1216 Type::Reference(r) => assert_eq!(r.identifier.value, "T"),
1217 _ => panic!(),
1218 }
1219 }
1220 res => panic!("Expected Ok(Type::PropertiesOf), got {res:?}"),
1221 }
1222 }
1223
1224 #[test]
1225 fn test_variable() {
1226 match do_parse("$myVar") {
1227 Ok(Type::Variable(v)) => {
1228 assert_eq!(v.value, "$myVar");
1229 }
1230 res => panic!("Expected Ok(Type::Variable), got {res:?}"),
1231 }
1232 }
1233
1234 #[test]
1235 fn test_nullable_intersection() {
1236 match do_parse("Countable&?Traversable") {
1238 Ok(Type::Intersection(i)) => {
1239 assert!(matches!(i.left, Type::Reference(r) if r.identifier.value == "Countable"));
1240 assert!(matches!(i.right, Type::Nullable(_)));
1241 if let Type::Nullable(n) = i.right {
1242 assert!(matches!(n.inner, Type::Reference(r) if r.identifier.value == "Traversable"));
1243 } else {
1244 panic!();
1245 }
1246 }
1247 res => panic!("Expected Ok(Type::Intersection), got {res:?}"),
1248 }
1249 }
1250
1251 #[test]
1252 fn test_parenthesized_nullable() {
1253 match do_parse("?(Countable&Traversable)") {
1254 Ok(Type::Nullable(n)) => {
1255 assert!(matches!(n.inner, Type::Parenthesized(_)));
1256 if let Type::Parenthesized(p) = n.inner {
1257 assert!(matches!(p.inner, Type::Intersection(_)));
1258 } else {
1259 panic!()
1260 }
1261 }
1262 res => panic!("Expected Ok(Type::Nullable), got {res:?}"),
1263 }
1264 }
1265
1266 #[test]
1267 fn test_positive_negative_int() {
1268 match do_parse("positive-int|negative-int") {
1269 Ok(Type::Union(u)) => {
1270 assert!(matches!(u.left, Type::PositiveInt(_)));
1271 assert!(matches!(u.right, Type::NegativeInt(_)));
1272 }
1273 res => panic!("Expected Ok(Type::Union), got {res:?}"),
1274 }
1275 }
1276
1277 #[test]
1278 fn test_parse_float_alias() {
1279 match do_parse("double") {
1280 Ok(Type::Float(f)) => {
1281 assert_eq!(f.value, "double");
1282 }
1283 res => panic!("Expected Ok(Type::Float), got {res:?}"),
1284 }
1285
1286 match do_parse("real") {
1287 Ok(Type::Float(f)) => {
1288 assert_eq!(f.value, "real");
1289 }
1290 res => panic!("Expected Ok(Type::Float), got {res:?}"),
1291 }
1292
1293 match do_parse("float") {
1294 Ok(Type::Float(f)) => {
1295 assert_eq!(f.value, "float");
1296 }
1297 res => panic!("Expected Ok(Type::Float), got {res:?}"),
1298 }
1299 }
1300
1301 #[test]
1302 fn test_parse_bool_alias() {
1303 match do_parse("boolean") {
1304 Ok(Type::Bool(b)) => {
1305 assert_eq!(b.value, "boolean");
1306 }
1307 res => panic!("Expected Ok(Type::Bool), got {res:?}"),
1308 }
1309
1310 match do_parse("bool") {
1311 Ok(Type::Bool(b)) => {
1312 assert_eq!(b.value, "bool");
1313 }
1314 res => panic!("Expected Ok(Type::Bool), got {res:?}"),
1315 }
1316 }
1317
1318 #[test]
1319 fn test_parse_integer_alias() {
1320 match do_parse("integer") {
1321 Ok(Type::Int(i)) => {
1322 assert_eq!(i.value, "integer");
1323 }
1324 res => panic!("Expected Ok(Type::Int), got {res:?}"),
1325 }
1326
1327 match do_parse("int") {
1328 Ok(Type::Int(i)) => {
1329 assert_eq!(i.value, "int");
1330 }
1331 res => panic!("Expected Ok(Type::Int), got {res:?}"),
1332 }
1333 }
1334
1335 #[test]
1336 fn test_parse_callable_with_variables() {
1337 match do_parse("callable(string ...$names)") {
1338 Ok(Type::Callable(callable)) => {
1339 assert_eq!(callable.keyword.value, "callable");
1340 assert!(callable.specification.is_some());
1341
1342 let specification = callable.specification.unwrap();
1343
1344 assert!(specification.return_type.is_none());
1345 assert_eq!(specification.parameters.entries.len(), 1);
1346
1347 let first_parameter = specification.parameters.entries.first().unwrap();
1348 assert!(first_parameter.variable.is_some());
1349 assert!(first_parameter.ellipsis.is_some());
1350
1351 let variable = first_parameter.variable.unwrap();
1352 assert_eq!(variable.value, "$names");
1353 }
1354 res => panic!("Expected Ok(Type::Callable), got {res:?}"),
1355 }
1356 }
1357
1358 #[test]
1359 fn test_parse_string_or_lowercase_string_union() {
1360 match do_parse("string|lowercase-string") {
1361 Ok(Type::Union(u)) => {
1362 assert!(matches!(u.left, Type::String(_)));
1363 assert!(matches!(u.right, Type::LowercaseString(_)));
1364 }
1365 res => panic!("Expected Ok(Type::Union), got {res:?}"),
1366 }
1367 }
1368
1369 #[test]
1370 fn test_parse_optional_literal_string_shape_field() {
1371 match do_parse("array{'salt'?: int, 'cost'?: int, ...}") {
1372 Ok(Type::Shape(shape)) => {
1373 assert_eq!(shape.fields.len(), 2);
1374 assert!(shape.additional_fields.is_some());
1375
1376 let first_field = &shape.fields[0];
1377 assert!(first_field.is_optional());
1378 assert!(matches!(
1379 first_field.key.as_ref().map(|k| &k.key),
1380 Some(ShapeKey::String { value: "salt", .. })
1381 ));
1382 assert!(matches!(first_field.value, Type::Int(_)));
1383
1384 let second_field = &shape.fields[1];
1385 assert!(second_field.is_optional());
1386 assert!(matches!(
1387 second_field.key.as_ref().map(|k| &k.key),
1388 Some(ShapeKey::String { value: "cost", .. })
1389 ));
1390 assert!(matches!(second_field.value, Type::Int(_)));
1391 }
1392 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
1393 }
1394 }
1395
1396 #[test]
1397 fn test_parse_keyword_keys() {
1398 match do_parse("array{string: int, bool: string, int: float, mixed: object}") {
1399 Ok(Type::Shape(shape)) => {
1400 assert_eq!(shape.fields.len(), 4);
1401
1402 assert!(matches!(
1403 shape.fields[0].key.as_ref().map(|k| &k.key),
1404 Some(ShapeKey::String { value: "string", .. })
1405 ));
1406 assert!(matches!(
1407 shape.fields[1].key.as_ref().map(|k| &k.key),
1408 Some(ShapeKey::String { value: "bool", .. })
1409 ));
1410 assert!(matches!(
1411 shape.fields[2].key.as_ref().map(|k| &k.key),
1412 Some(ShapeKey::String { value: "int", .. })
1413 ));
1414 assert!(matches!(
1415 shape.fields[3].key.as_ref().map(|k| &k.key),
1416 Some(ShapeKey::String { value: "mixed", .. })
1417 ));
1418 }
1419 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
1420 }
1421 }
1422
1423 #[test]
1424 fn test_parse_negated_integer_keys() {
1425 match do_parse("array{-1: string, -42: int, +5: bool}") {
1426 Ok(Type::Shape(shape)) => {
1427 assert_eq!(shape.fields.len(), 3);
1428
1429 assert!(matches!(
1430 shape.fields[0].key.as_ref().map(|k| &k.key),
1431 Some(ShapeKey::Integer { value: -1, .. })
1432 ));
1433 assert!(matches!(
1434 shape.fields[1].key.as_ref().map(|k| &k.key),
1435 Some(ShapeKey::Integer { value: -42, .. })
1436 ));
1437 assert!(matches!(
1438 shape.fields[2].key.as_ref().map(|k| &k.key),
1439 Some(ShapeKey::Integer { value: 5, .. })
1440 ));
1441 }
1442 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
1443 }
1444 }
1445
1446 #[test]
1447 fn test_parse_float_keys() {
1448 match do_parse("array{123.4: string, -1.2: int, +0.5: bool}") {
1449 Ok(Type::Shape(shape)) => {
1450 assert_eq!(shape.fields.len(), 3);
1451
1452 assert!(matches!(
1453 shape.fields[0].key.as_ref().map(|k| &k.key),
1454 Some(ShapeKey::String { value: "123.4", .. })
1455 ));
1456 assert!(matches!(
1457 shape.fields[1].key.as_ref().map(|k| &k.key),
1458 Some(ShapeKey::String { value: "-1.2", .. })
1459 ));
1460 assert!(matches!(
1461 shape.fields[2].key.as_ref().map(|k| &k.key),
1462 Some(ShapeKey::String { value: "+0.5", .. })
1463 ));
1464 }
1465 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
1466 }
1467 }
1468
1469 #[test]
1470 fn test_parse_complex_identifier_keys() {
1471 match do_parse(
1472 "array{key_with_underscore: int, key-with-dash: string, key\\with\\backslash: bool, +key: mixed, -key: object, \\leading_backslash: int}",
1473 ) {
1474 Ok(Type::Shape(shape)) => {
1475 assert_eq!(shape.fields.len(), 6);
1476
1477 assert!(matches!(
1478 shape.fields[0].key.as_ref().map(|k| &k.key),
1479 Some(ShapeKey::String { value: "key_with_underscore", .. })
1480 ));
1481 assert!(matches!(
1482 shape.fields[1].key.as_ref().map(|k| &k.key),
1483 Some(ShapeKey::String { value: "key-with-dash", .. })
1484 ));
1485 assert!(matches!(
1486 shape.fields[2].key.as_ref().map(|k| &k.key),
1487 Some(ShapeKey::String { value: "key\\with\\backslash", .. })
1488 ));
1489 assert!(matches!(
1490 shape.fields[3].key.as_ref().map(|k| &k.key),
1491 Some(ShapeKey::String { value: "+key", .. })
1492 ));
1493 assert!(matches!(
1494 shape.fields[4].key.as_ref().map(|k| &k.key),
1495 Some(ShapeKey::String { value: "-key", .. })
1496 ));
1497 assert!(matches!(
1498 shape.fields[5].key.as_ref().map(|k| &k.key),
1499 Some(ShapeKey::String { value: "\\leading_backslash", .. })
1500 ));
1501 }
1502 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
1503 }
1504 }
1505
1506 #[test]
1507 fn test_parse_optional_keys_with_question_mark_in_name() {
1508 match do_parse("array{key?name: int, regular?: string}") {
1509 Ok(Type::Shape(shape)) => {
1510 assert_eq!(shape.fields.len(), 2);
1511
1512 assert!(!shape.fields[0].is_optional());
1513 assert!(matches!(
1514 shape.fields[0].key.as_ref().map(|k| &k.key),
1515 Some(ShapeKey::String { value: "key?name", .. })
1516 ));
1517
1518 assert!(shape.fields[1].is_optional());
1519 assert!(matches!(
1520 shape.fields[1].key.as_ref().map(|k| &k.key),
1521 Some(ShapeKey::String { value: "regular", .. })
1522 ));
1523 }
1524 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
1525 }
1526 }
1527
1528 #[test]
1529 fn test_parse_integer_formats() {
1530 match do_parse("array{42: string, 0x2A: int, 0b101010: bool, 0o52: mixed}") {
1531 Ok(Type::Shape(shape)) => {
1532 assert_eq!(shape.fields.len(), 4);
1533
1534 assert!(matches!(
1535 shape.fields[0].key.as_ref().map(|k| &k.key),
1536 Some(ShapeKey::Integer { value: 42, .. })
1537 ));
1538 assert!(matches!(
1539 shape.fields[1].key.as_ref().map(|k| &k.key),
1540 Some(ShapeKey::Integer { value: 42, .. })
1541 ));
1542 assert!(matches!(
1543 shape.fields[2].key.as_ref().map(|k| &k.key),
1544 Some(ShapeKey::Integer { value: 42, .. })
1545 ));
1546 assert!(matches!(
1547 shape.fields[3].key.as_ref().map(|k| &k.key),
1548 Some(ShapeKey::Integer { value: 42, .. })
1549 ));
1550 }
1551 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
1552 }
1553 }
1554
1555 #[test]
1556 fn test_parse_quoted_vs_unquoted_keys() {
1557 match do_parse("array{'string': int, \"double\": bool, unquoted: mixed}") {
1558 Ok(Type::Shape(shape)) => {
1559 assert_eq!(shape.fields.len(), 3);
1560
1561 assert!(matches!(
1562 shape.fields[0].key.as_ref().map(|k| &k.key),
1563 Some(ShapeKey::String { value: "string", .. })
1564 ));
1565 assert!(matches!(
1566 shape.fields[1].key.as_ref().map(|k| &k.key),
1567 Some(ShapeKey::String { value: "double", .. })
1568 ));
1569 assert!(matches!(
1570 shape.fields[2].key.as_ref().map(|k| &k.key),
1571 Some(ShapeKey::String { value: "unquoted", .. })
1572 ));
1573 }
1574 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
1575 }
1576 }
1577
1578 #[test]
1579 fn test_parse_all_keyword_types() {
1580 let keywords = vec![
1581 "list", "int", "integer", "string", "float", "double", "real", "bool", "boolean", "false", "true",
1582 "object", "callable", "array", "iterable", "null", "mixed", "resource", "void", "scalar", "numeric",
1583 "never", "nothing", "as", "is", "not", "min", "max",
1584 ];
1585
1586 for keyword in keywords {
1587 let input = format!("array{{{keyword}: string}}");
1588 match do_parse(&input) {
1589 Ok(Type::Shape(shape)) => {
1590 assert_eq!(shape.fields.len(), 1);
1591 assert!(
1592 matches!(
1593 shape.fields[0].key.as_ref().map(|k| &k.key),
1594 Some(ShapeKey::String { value, .. }) if *value == keyword
1595 ),
1596 "Failed for keyword: {keyword}"
1597 );
1598 }
1599 res => panic!("Expected Ok(Type::Shape) for keyword '{keyword}', got {res:?}"),
1600 }
1601 }
1602 }
1603
1604 #[test]
1605 fn test_parse_php_specific_keywords() {
1606 match do_parse("array{self: string, static: int, parent: bool, class: mixed, __CLASS__: object}") {
1607 Ok(Type::Shape(shape)) => {
1608 assert_eq!(shape.fields.len(), 5);
1609
1610 assert!(matches!(
1611 shape.fields[0].key.as_ref().map(|k| &k.key),
1612 Some(ShapeKey::String { value: "self", .. })
1613 ));
1614 assert!(matches!(
1615 shape.fields[1].key.as_ref().map(|k| &k.key),
1616 Some(ShapeKey::String { value: "static", .. })
1617 ));
1618 assert!(matches!(
1619 shape.fields[2].key.as_ref().map(|k| &k.key),
1620 Some(ShapeKey::String { value: "parent", .. })
1621 ));
1622 assert!(matches!(
1623 shape.fields[3].key.as_ref().map(|k| &k.key),
1624 Some(ShapeKey::String { value: "class", .. })
1625 ));
1626 assert!(matches!(
1627 shape.fields[4].key.as_ref().map(|k| &k.key),
1628 Some(ShapeKey::String { value: "__CLASS__", .. })
1629 ));
1630 }
1631 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
1632 }
1633 }
1634
1635 #[test]
1636 fn test_shape_key_spans() {
1637 match do_parse("array{test: string}") {
1638 Ok(Type::Shape(shape)) => {
1639 assert_eq!(shape.fields.len(), 1);
1640 let field = &shape.fields[0];
1641
1642 if let Some(key) = &field.key {
1643 let span = key.key.span();
1644 assert!(span.start.offset < span.end.offset, "Span should have valid start/end");
1645
1646 assert_eq!(span.end.offset - span.start.offset, 4, "Span should cover 'test' (4 characters)");
1647 }
1648 }
1649 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
1650 }
1651 }
1652
1653 #[test]
1654 fn test_shape_key_spans_quoted() {
1655 match do_parse("array{'hello': string}") {
1656 Ok(Type::Shape(shape)) => {
1657 assert_eq!(shape.fields.len(), 1);
1658 let field = &shape.fields[0];
1659
1660 if let Some(key) = &field.key {
1661 let span = key.key.span();
1662 assert_eq!(span.end.offset - span.start.offset, 7, "Span should cover 'hello' including quotes");
1663
1664 assert!(matches!(&key.key, ShapeKey::String { value: "hello", .. }));
1665 }
1666 }
1667 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
1668 }
1669 }
1670
1671 #[test]
1672 fn test_shape_key_spans_integer() {
1673 match do_parse("array{42: string}") {
1674 Ok(Type::Shape(shape)) => {
1675 assert_eq!(shape.fields.len(), 1);
1676 let field = &shape.fields[0];
1677
1678 if let Some(key) = &field.key {
1679 let span = key.key.span();
1680 assert_eq!(span.end.offset - span.start.offset, 2, "Span should cover '42' (2 characters)");
1681
1682 assert!(matches!(&key.key, ShapeKey::Integer { value: 42, .. }));
1683 }
1684 }
1685 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
1686 }
1687 }
1688
1689 #[test]
1690 fn test_shape_key_spans_negated_integer() {
1691 match do_parse("array{-123: string}") {
1692 Ok(Type::Shape(shape)) => {
1693 assert_eq!(shape.fields.len(), 1);
1694 let field = &shape.fields[0];
1695
1696 if let Some(key) = &field.key {
1697 let span = key.key.span();
1698 assert_eq!(span.end.offset - span.start.offset, 4, "Span should cover '-123' (4 characters)");
1699
1700 assert!(matches!(&key.key, ShapeKey::Integer { value: -123, .. }));
1701 }
1702 }
1703 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
1704 }
1705 }
1706
1707 #[test]
1708 fn test_shape_key_spans_complex_identifiers() {
1709 match do_parse("array{complex-key_name: string}") {
1710 Ok(Type::Shape(shape)) => {
1711 assert_eq!(shape.fields.len(), 1);
1712 let field = &shape.fields[0];
1713
1714 if let Some(key) = &field.key {
1715 let span = key.key.span();
1716 assert_eq!(
1717 span.end.offset - span.start.offset,
1718 16,
1719 "Span should cover 'complex-key_name' (16 characters)"
1720 );
1721
1722 assert!(matches!(&key.key, ShapeKey::String { value: "complex-key_name", .. }));
1723 }
1724 }
1725 res => panic!("Expected Ok(Type::Shape), got {res:?}"),
1726 }
1727 }
1728
1729 #[test]
1730 fn test_parse_shape_key_overflow_unsigned() {
1731 let result = do_parse("array{9223372036854775808: string}");
1732 assert!(result.is_err(), "Expected parse error for shape key > i64::MAX, got: {result:?}");
1733 }
1734
1735 #[test]
1736 fn test_parse_shape_key_overflow_negated() {
1737 let result = do_parse("array{-9223372036854775808: string}");
1738 assert!(result.is_err(), "Expected parse error for negated shape key overflow, got: {result:?}");
1739 }
1740
1741 #[test]
1742 fn test_parse_wildcard_asterisk() {
1743 let result = do_parse("*");
1744 assert!(result.is_ok(), "Expected successful parse for wildcard, got: {result:?}");
1745 match result.unwrap() {
1746 Type::Wildcard(w) => assert_eq!(w.kind, WildcardKind::Asterisk),
1747 other => panic!("Expected Type::Wildcard, got: {other:?}"),
1748 }
1749 }
1750
1751 #[test]
1752 fn test_parse_wildcard_underscore() {
1753 let result = do_parse("_");
1754 assert!(result.is_ok(), "Expected successful parse for underscore wildcard, got: {result:?}");
1755 match result.unwrap() {
1756 Type::Wildcard(w) => assert_eq!(w.kind, WildcardKind::Underscore),
1757 other => panic!("Expected Type::Wildcard, got: {other:?}"),
1758 }
1759 }
1760
1761 #[test]
1762 fn test_parse_wildcard_in_generic() {
1763 let result = do_parse("array<string, *>");
1764 assert!(result.is_ok(), "Expected successful parse for wildcard in generic, got: {result:?}");
1765
1766 let result = do_parse("array<string, _>");
1767 assert!(result.is_ok(), "Expected successful parse for underscore wildcard in generic, got: {result:?}");
1768 }
1769
1770 #[test]
1771 fn test_parse_wildcard_display() {
1772 assert_eq!(do_parse("*").unwrap().to_string(), "*");
1773 assert_eq!(do_parse("_").unwrap().to_string(), "_");
1774 }
1775
1776 #[test]
1777 fn test_parse_non_zero_int() {
1778 match do_parse("non-zero-int") {
1779 Ok(Type::NonZeroInt(k)) => assert_eq!(k.value, "non-zero-int"),
1780 other => panic!("Expected Type::NonZeroInt, got: {other:?}"),
1781 }
1782 }
1783
1784 #[test]
1785 fn test_parse_int_range_int_keyword_max() {
1786 match do_parse("int<0, int>") {
1787 Ok(Type::IntRange(range)) => {
1788 assert!(matches!(range.min, IntOrKeyword::Int(LiteralIntType { value: 0, .. })));
1789 match range.max {
1790 IntOrKeyword::Keyword(ref keyword) => assert!(keyword.value.eq_ignore_ascii_case("int")),
1791 other => panic!("Expected IntOrKeyword::Keyword, got: {other:?}"),
1792 }
1793 }
1794 other => panic!("Expected Type::IntRange, got: {other:?}"),
1795 }
1796 }
1797
1798 #[test]
1799 fn test_parse_int_range_int_keyword_min() {
1800 match do_parse("int<int, 0>") {
1801 Ok(Type::IntRange(range)) => {
1802 match range.min {
1803 IntOrKeyword::Keyword(ref keyword) => assert!(keyword.value.eq_ignore_ascii_case("int")),
1804 other => panic!("Expected IntOrKeyword::Keyword, got: {other:?}"),
1805 }
1806 assert!(matches!(range.max, IntOrKeyword::Int(LiteralIntType { value: 0, .. })));
1807 }
1808 other => panic!("Expected Type::IntRange, got: {other:?}"),
1809 }
1810 }
1811
1812 #[test]
1813 fn test_parse_member_reference_reserved_keywords() {
1814 for name in [
1815 "NULL", "ARRAY", "INT", "STRING", "FLOAT", "TRUE", "FALSE", "MIXED", "CALLABLE", "ITERABLE", "RESOURCE",
1816 "BOOL", "OBJECT", "NEVER", "VOID", "NUMERIC", "SCALAR",
1817 ] {
1818 let input = format!("TypeIdentifier::{name}");
1819 match do_parse(&input) {
1820 Ok(Type::MemberReference(r)) => match r.member {
1821 MemberReferenceSelector::Identifier(ident) => {
1822 assert!(
1823 ident.value.eq_ignore_ascii_case(name),
1824 "Expected member name {name}, got {}",
1825 ident.value,
1826 );
1827 }
1828 other => panic!("Expected Identifier selector for {input}, got {other:?}"),
1829 },
1830 other => panic!("Expected Type::MemberReference for {input}, got: {other:?}"),
1831 }
1832 }
1833 }
1834
1835 #[test]
1836 fn test_parse_member_reference_reserved_prefix_wildcard() {
1837 match do_parse("Foo::INT*") {
1838 Ok(Type::MemberReference(r)) => match r.member {
1839 MemberReferenceSelector::StartsWith(ident, _) => {
1840 assert!(ident.value.eq_ignore_ascii_case("INT"), "expected INT prefix, got {}", ident.value);
1841 }
1842 other => panic!("Expected StartsWith selector, got {other:?}"),
1843 },
1844 other => panic!("Expected Type::MemberReference, got: {other:?}"),
1845 }
1846 }
1847
1848 #[test]
1849 fn test_parse_nested_generic_with_reserved_const() {
1850 match do_parse("UnionType<T|Foo::NULL>") {
1851 Ok(Type::Reference(r)) => {
1852 let params = r.parameters.expect("Expected generic parameters");
1853 assert_eq!(params.entries.len(), 1);
1854 match ¶ms.entries[0].inner {
1855 Type::Union(u) => {
1856 assert!(matches!(u.left, Type::Reference(_)));
1857 assert!(matches!(u.right, Type::MemberReference(_)));
1858 }
1859 other => panic!("Expected inner Union, got {other:?}"),
1860 }
1861 }
1862 other => panic!("Expected Type::Reference, got: {other:?}"),
1863 }
1864 }
1865
1866 #[test]
1867 fn test_parse_builtin_type_identifier_union() {
1868 let input = "BuiltinType<TypeIdentifier::ARRAY>|BuiltinType<TypeIdentifier::ITERABLE>|ObjectType|GenericType";
1869 assert!(do_parse(input).is_ok(), "expected successful parse for {input}");
1870 }
1871
1872 #[test]
1873 fn test_parse_collection_type_with_reserved_identifier() {
1874 let input = "CollectionType<BuiltinType<TypeIdentifier::ITERABLE>>";
1875 assert!(do_parse(input).is_ok(), "expected successful parse for {input}");
1876 }
1877
1878 #[test]
1879 fn test_parse_trailing_pipe() {
1880 match do_parse("int|string|") {
1881 Ok(Type::TrailingPipe(trailing)) => assert!(matches!(trailing.inner, Type::Union(_))),
1882 other => panic!("Expected Type::TrailingPipe, got: {other:?}"),
1883 }
1884 }
1885
1886 #[test]
1887 fn test_parse_trailing_pipe_single() {
1888 match do_parse("int|") {
1889 Ok(Type::TrailingPipe(trailing)) => assert!(matches!(trailing.inner, Type::Int(_))),
1890 other => panic!("Expected Type::TrailingPipe, got: {other:?}"),
1891 }
1892 }
1893
1894 #[test]
1895 fn test_parse_trailing_pipe_in_shape_value() {
1896 match do_parse("array{0: int|string|}") {
1897 Ok(Type::Shape(shape)) => {
1898 assert_eq!(shape.fields.len(), 1);
1899 assert!(matches!(shape.fields[0].value, Type::TrailingPipe(_)));
1900 }
1901 other => panic!("Expected Type::Shape, got: {other:?}"),
1902 }
1903 }
1904
1905 #[test]
1906 fn test_parse_trailing_pipe_in_generic_shape_value() {
1907 let input = "iterable<array{0: int|array<string, mixed>|}>";
1908 match do_parse(input) {
1909 Ok(Type::Iterable(iter)) => {
1910 let params = iter.parameters.expect("expected generic parameters");
1911 assert_eq!(params.entries.len(), 1);
1912 match ¶ms.entries[0].inner {
1913 Type::Shape(shape) => {
1914 assert_eq!(shape.fields.len(), 1);
1915 assert!(matches!(shape.fields[0].value, Type::TrailingPipe(_)));
1916 }
1917 other => panic!("Expected Type::Shape, got {other:?}"),
1918 }
1919 }
1920 other => panic!("Expected Type::Iterable, got: {other:?}"),
1921 }
1922 }
1923
1924 #[test]
1925 fn test_parse_global_wildcard_starts_with() {
1926 match do_parse("FILTER_FLAG_*") {
1927 Ok(Type::GlobalWildcardReference(g)) => match g.selector {
1928 GlobalWildcardSelector::StartsWith(identifier, _) => {
1929 assert_eq!(identifier.value, "FILTER_FLAG_");
1930 }
1931 other => panic!("Expected StartsWith selector, got {other:?}"),
1932 },
1933 other => panic!("Expected Type::GlobalWildcardReference, got: {other:?}"),
1934 }
1935 }
1936
1937 #[test]
1938 fn test_parse_global_wildcard_ends_with() {
1939 match do_parse("*_SUFFIX") {
1940 Ok(Type::GlobalWildcardReference(g)) => match g.selector {
1941 GlobalWildcardSelector::EndsWith(_, identifier) => {
1942 assert_eq!(identifier.value, "_SUFFIX");
1943 }
1944 other => panic!("Expected EndsWith selector, got {other:?}"),
1945 },
1946 other => panic!("Expected Type::GlobalWildcardReference, got: {other:?}"),
1947 }
1948 }
1949
1950 #[test]
1951 fn test_parse_global_wildcard_in_int_mask_of() {
1952 let input = "int-mask-of<FILTER_FLAG_*>";
1953 match do_parse(input) {
1954 Ok(Type::IntMaskOf(mask)) => {
1955 assert!(matches!(mask.parameter.entry.inner, Type::GlobalWildcardReference(_)));
1956 }
1957 other => panic!("Expected Type::IntMaskOf, got: {other:?}"),
1958 }
1959 }
1960
1961 #[test]
1962 fn test_parse_int_mask_of_class_wildcard_regression() {
1963 let input = "int-mask-of<Ulid::FORMAT_*>";
1964 match do_parse(input) {
1965 Ok(Type::IntMaskOf(mask)) => match &mask.parameter.entry.inner {
1966 Type::MemberReference(r) => {
1967 assert!(matches!(r.member, MemberReferenceSelector::StartsWith(_, _)));
1968 }
1969 other => panic!("Expected MemberReference, got {other:?}"),
1970 },
1971 other => panic!("Expected Type::IntMaskOf, got: {other:?}"),
1972 }
1973 }
1974}