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