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