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