1use ordered_float::OrderedFloat;
2use serde::Deserialize;
3use serde::Serialize;
4
5use mago_interner::StringIdentifier;
6use mago_interner::ThreadedInterner;
7use mago_span::Span;
8use mago_trinary::Trinary;
9
10use crate::function_like::FunctionLikeReflection;
11use crate::identifier::ClassLikeName;
12
13#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
16pub struct Template {
17 name: StringIdentifier,
19
20 constraints: Vec<TypeKind>,
22}
23
24#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
26pub enum ScalarTypeKind {
27 Bool,
29
30 Integer { min: Option<isize>, max: Option<isize> },
33
34 Float,
36
37 String,
39
40 IntegerMask(Vec<isize>),
43
44 IntegerMaskOf(StringIdentifier, StringIdentifier),
47
48 ClassString(Option<StringIdentifier>),
51
52 TraitString,
54
55 EnumString,
57
58 CallableString,
60
61 NumericString,
63
64 LiteralString,
66
67 LiteralInt,
69
70 NonEmptyString,
72
73 ArrayKey,
75
76 Numeric,
78
79 Scalar,
81}
82
83#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
85pub struct ObjectProperty {
86 pub name: StringIdentifier,
88
89 pub kind: TypeKind,
91
92 pub optional: bool,
94}
95
96#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
98pub enum ObjectTypeKind {
99 AnyObject,
101
102 TypedObject {
105 properties: Vec<ObjectProperty>,
107 },
108
109 NamedObject {
112 name: StringIdentifier,
114
115 type_parameters: Vec<TypeKind>,
117 },
118
119 AnonymousObject {
121 span: Span,
123 },
124
125 EnumCase {
127 enum_name: StringIdentifier,
129
130 case_name: StringIdentifier,
132 },
133
134 Generator { key: Box<TypeKind>, value: Box<TypeKind>, send: Box<TypeKind>, r#return: Box<TypeKind> },
137
138 Static {
140 scope: StringIdentifier,
142 },
143
144 Parent {
146 scope: StringIdentifier,
148 },
149
150 Self_ {
152 scope: StringIdentifier,
154 },
155}
156
157#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
159pub enum ArrayShapePropertyKey {
160 String(StringIdentifier),
161 Integer(isize),
162}
163
164#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
166pub struct ArrayShapeProperty {
167 pub key: Option<ArrayShapePropertyKey>,
169
170 pub kind: TypeKind,
172
173 pub optional: bool,
175}
176
177#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
179pub struct ArrayShape {
180 pub properties: Vec<ArrayShapeProperty>,
182
183 pub additional_properties: Option<(
186 Box<TypeKind>, Box<TypeKind>, )>,
189}
190
191#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
193pub enum ArrayTypeKind {
194 Array {
197 non_empty: bool,
199
200 key: Box<TypeKind>,
202
203 value: Box<TypeKind>,
205
206 known_size: Option<usize>,
208 },
209
210 List {
213 non_empty: bool,
215
216 value: Box<TypeKind>,
218
219 known_size: Option<usize>,
221 },
222
223 CallableArray,
225
226 Shape(ArrayShape),
229}
230
231#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
233pub struct CallableParameter {
234 pub kind: TypeKind,
236
237 pub optional: bool,
239
240 pub variadic: bool,
242}
243
244#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
246pub enum CallableTypeKind {
247 Callable { pure: bool, templates: Vec<Template>, parameters: Vec<CallableParameter>, return_kind: Box<TypeKind> },
250
251 Closure { pure: bool, templates: Vec<Template>, parameters: Vec<CallableParameter>, return_kind: Box<TypeKind> },
254}
255
256#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
258pub enum ValueTypeKind {
259 String {
262 value: StringIdentifier,
263 length: usize,
264 is_uppercase: Trinary,
265 is_lowercase: Trinary,
266 is_ascii_lowercase: Trinary,
267 is_ascii_uppercase: Trinary,
268 },
269
270 Integer { value: i64 },
273
274 Float { value: OrderedFloat<f64> },
277
278 Null,
280
281 True,
283
284 False,
286
287 ClassLikeConstant { class_like: ClassLikeName, constant: StringIdentifier },
290}
291
292#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
294pub struct ClassStringMapType {
295 key: Template,
297
298 value: Box<TypeKind>,
300}
301
302#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
304pub enum TypeKind {
305 Union { kinds: Vec<TypeKind> },
308
309 Intersection { kinds: Vec<TypeKind> },
312
313 Scalar(ScalarTypeKind),
315
316 Object(ObjectTypeKind),
318
319 Array(ArrayTypeKind),
321
322 Callable(CallableTypeKind),
324
325 Value(ValueTypeKind),
327
328 Conditional {
331 parameter: Box<TypeKind>,
333
334 condition: Box<TypeKind>,
336
337 then: Box<TypeKind>,
339
340 otherwise: Box<TypeKind>,
342 },
343
344 KeyOf { kind: Box<TypeKind> },
347
348 ValueOf { kind: Box<TypeKind> },
351
352 PropertiesOf { kind: Box<TypeKind> },
355
356 ClassStringMap {
359 key: Template,
361
362 value_kind: Box<TypeKind>,
364 },
365
366 Index { base_kind: Box<TypeKind>, index_kind: Box<TypeKind> },
369
370 Variable { name: StringIdentifier },
373
374 Iterable { key: Box<TypeKind>, value: Box<TypeKind> },
377
378 Void,
380
381 Resource,
383
384 ClosedResource,
386
387 Mixed {
389 explicit: bool,
391 },
392
393 Never,
395
396 GenericParameter { name: StringIdentifier, of: Box<TypeKind>, defined_in: StringIdentifier },
398}
399
400impl Template {
401 pub fn get_key(&self, interner: &ThreadedInterner) -> String {
402 let mut key = String::from(interner.lookup(&self.name));
403
404 for constraint in &self.constraints {
405 key.push_str(" of ");
406 key.push_str(&constraint.get_key(interner));
407 }
408
409 key
410 }
411}
412
413impl ArrayShapePropertyKey {
414 pub fn get_key(&self, interner: &ThreadedInterner) -> String {
415 match &self {
416 ArrayShapePropertyKey::String(string_identifier) => interner.lookup(string_identifier).to_owned(),
417 ArrayShapePropertyKey::Integer(i) => i.to_string(),
418 }
419 }
420}
421
422impl TypeKind {
423 #[inline]
424 pub fn is_nullable(&self) -> Trinary {
425 match &self {
426 TypeKind::Union { kinds } => kinds.iter().map(|k| k.is_nullable()).collect(),
427 TypeKind::Intersection { kinds } => kinds.iter().map(|k| k.is_nullable()).collect(),
428 TypeKind::Value(ValueTypeKind::Null) => Trinary::True,
429 TypeKind::Mixed { .. } => Trinary::Maybe,
430 TypeKind::Scalar(_) => Trinary::False,
431 TypeKind::Object(_) => todo!(),
432 TypeKind::Array(_) => todo!(),
433 TypeKind::Callable(_) => todo!(),
434 TypeKind::Conditional { then, otherwise, .. } => then.is_nullable() & otherwise.is_nullable(),
435 TypeKind::KeyOf { .. } => Trinary::False,
436 TypeKind::ValueOf { .. } => Trinary::Maybe,
437 TypeKind::PropertiesOf { .. } => Trinary::Maybe,
438 TypeKind::ClassStringMap { .. } => Trinary::False,
439 TypeKind::Index { .. } => Trinary::Maybe,
440 TypeKind::Variable { .. } => Trinary::Maybe,
441 TypeKind::Iterable { .. } => Trinary::False,
442 TypeKind::Void => Trinary::True,
443 TypeKind::Resource => Trinary::False,
444 TypeKind::ClosedResource => Trinary::False,
445 TypeKind::Never => Trinary::False,
446 TypeKind::GenericParameter { of, .. } => of.is_nullable(),
447 TypeKind::Value(_) => Trinary::False,
448 }
449 }
450
451 #[inline]
452 pub fn is_object(&self) -> bool {
453 match &self {
454 TypeKind::Union { kinds } => kinds.iter().all(|k| k.is_object()),
455 TypeKind::Intersection { kinds } => kinds.iter().any(|k| k.is_object()),
456 TypeKind::Conditional { then, otherwise, .. } => then.is_object() && otherwise.is_object(),
457 TypeKind::Callable(CallableTypeKind::Closure { .. }) => true,
458 TypeKind::GenericParameter { of, .. } => of.is_object(),
459 TypeKind::Object(_) => true,
460 _ => false,
461 }
462 }
463
464 #[inline]
465 pub fn is_resource(&self) -> bool {
466 match &self {
467 TypeKind::Union { kinds } => kinds.iter().all(|k| k.is_resource()),
468 TypeKind::Intersection { kinds } => kinds.iter().any(|k| k.is_resource()),
469 TypeKind::Conditional { then, otherwise, .. } => then.is_resource() && otherwise.is_resource(),
470 TypeKind::Resource | TypeKind::ClosedResource => true,
471 _ => false,
472 }
473 }
474
475 #[inline]
476 pub fn is_array(&self) -> bool {
477 match &self {
478 TypeKind::Union { kinds } => kinds.iter().all(|k| k.is_array()),
479 TypeKind::Intersection { kinds } => kinds.iter().any(|k| k.is_array()),
480 TypeKind::Conditional { then, otherwise, .. } => then.is_array() && otherwise.is_array(),
481 TypeKind::Array(_) => true,
482 _ => false,
483 }
484 }
485
486 #[inline]
487 pub fn is_bool(&self) -> Trinary {
488 match &self {
489 TypeKind::Union { kinds } => kinds.iter().map(|k| k.is_bool()).collect(),
490 TypeKind::Intersection { kinds } => kinds.iter().map(|k| k.is_bool()).collect(),
491 TypeKind::Conditional { then, otherwise, .. } => then.is_bool().and(otherwise.is_bool()),
492 TypeKind::Value(ValueTypeKind::True)
493 | TypeKind::Value(ValueTypeKind::False)
494 | TypeKind::Scalar(ScalarTypeKind::Bool) => Trinary::True,
495 TypeKind::Value(ValueTypeKind::ClassLikeConstant { .. }) => Trinary::Maybe,
496 TypeKind::GenericParameter { of, .. } => of.is_bool(),
497 _ => Trinary::False,
498 }
499 }
500
501 #[inline]
502 pub fn is_truthy(&self) -> Trinary {
503 match &self {
504 TypeKind::Union { kinds } => kinds.iter().map(|k| k.is_truthy()).collect(),
505 TypeKind::Intersection { kinds } => kinds.iter().map(|k| k.is_truthy()).collect(),
506 TypeKind::Conditional { then, otherwise, .. } => then.is_truthy().and(otherwise.is_truthy()),
507 TypeKind::Array(array_kind) => match array_kind {
508 ArrayTypeKind::Array { non_empty, known_size, .. }
509 if *non_empty || known_size.map(|s| s > 0).unwrap_or(false) =>
510 {
511 Trinary::True
512 }
513 ArrayTypeKind::List { non_empty, known_size, .. }
514 if *non_empty || known_size.map(|s| s > 0).unwrap_or(false) =>
515 {
516 Trinary::True
517 }
518 ArrayTypeKind::CallableArray => Trinary::True,
519 ArrayTypeKind::Shape(array_shape) => Trinary::from(!array_shape.properties.is_empty()),
520 _ => Trinary::Maybe,
521 },
522 TypeKind::Scalar(scalar_type_kind) => match scalar_type_kind {
523 ScalarTypeKind::Bool => Trinary::Maybe,
524 ScalarTypeKind::Integer { min, max } => {
525 if min.map(|m| m > 0).unwrap_or(false) {
526 Trinary::True
527 } else if max.map(|m| m < 0).unwrap_or(false) {
528 Trinary::False
529 } else {
530 Trinary::Maybe
531 }
532 }
533 ScalarTypeKind::Float => Trinary::Maybe,
534 ScalarTypeKind::String => Trinary::Maybe,
535 ScalarTypeKind::IntegerMask(bits) => {
536 if bits.iter().all(|b| *b > 0) {
537 Trinary::True
538 } else if bits.iter().all(|b| *b < 0) {
539 Trinary::False
540 } else {
541 Trinary::Maybe
542 }
543 }
544 ScalarTypeKind::IntegerMaskOf(_, _) => Trinary::Maybe,
545 ScalarTypeKind::ClassString(_)
546 | ScalarTypeKind::TraitString
547 | ScalarTypeKind::EnumString
548 | ScalarTypeKind::CallableString => Trinary::True,
549 ScalarTypeKind::NumericString => Trinary::Maybe, ScalarTypeKind::LiteralString => Trinary::Maybe,
551 ScalarTypeKind::LiteralInt => Trinary::Maybe,
552 ScalarTypeKind::NonEmptyString => Trinary::Maybe, ScalarTypeKind::ArrayKey => Trinary::Maybe,
554 ScalarTypeKind::Numeric => Trinary::Maybe,
555 ScalarTypeKind::Scalar => Trinary::Maybe,
556 },
557 TypeKind::Object(_) => Trinary::True,
558 TypeKind::Callable(_) => Trinary::True,
559 TypeKind::Value(value_type_kind) => match &value_type_kind {
560 ValueTypeKind::String { .. } => Trinary::Maybe,
561 ValueTypeKind::Integer { value } => {
562 if *value > 0 {
563 Trinary::True
564 } else {
565 Trinary::False
566 }
567 }
568 ValueTypeKind::Float { value } => {
569 if *value > OrderedFloat(0.0) {
570 Trinary::True
571 } else {
572 Trinary::False
573 }
574 }
575 ValueTypeKind::Null => Trinary::False,
576 ValueTypeKind::True => Trinary::True,
577 ValueTypeKind::False => Trinary::False,
578 ValueTypeKind::ClassLikeConstant { .. } => Trinary::Maybe,
579 },
580 TypeKind::Variable { .. } => Trinary::Maybe,
581 TypeKind::Iterable { .. } => Trinary::Maybe,
582 TypeKind::Void => Trinary::False,
583 TypeKind::Resource => Trinary::True,
584 TypeKind::ClosedResource => Trinary::True,
585 TypeKind::Never => Trinary::False,
586 TypeKind::GenericParameter { of, .. } => of.is_truthy(),
587 _ => Trinary::Maybe,
588 }
589 }
590
591 #[inline]
592 pub fn is_falsy(&self) -> Trinary {
593 self.is_truthy().negate()
594 }
595
596 #[inline]
597 pub fn is_float(&self) -> Trinary {
598 match &self {
599 TypeKind::Union { kinds } => kinds.iter().map(|k| k.is_float()).collect(),
600 TypeKind::Intersection { kinds } => kinds.iter().map(|k| k.is_float()).collect(),
601 TypeKind::Conditional { then, otherwise, .. } => then.is_float().and(otherwise.is_float()),
602 TypeKind::Scalar(scalar_type_kind) => match scalar_type_kind {
603 ScalarTypeKind::Float => Trinary::True,
604 ScalarTypeKind::Integer { .. } => Trinary::False,
605 ScalarTypeKind::IntegerMask(_) => Trinary::False,
606 ScalarTypeKind::IntegerMaskOf(_, _) => Trinary::False,
607 ScalarTypeKind::Numeric => Trinary::Maybe,
608 _ => Trinary::False,
609 },
610 TypeKind::Value(ValueTypeKind::Float { .. }) => Trinary::True,
611 TypeKind::Value(ValueTypeKind::ClassLikeConstant { .. }) => Trinary::Maybe,
612 _ => Trinary::False,
613 }
614 }
615
616 #[inline]
617 pub fn is_integer(&self) -> Trinary {
618 match &self {
619 TypeKind::Union { kinds } => kinds.iter().map(|k| k.is_integer()).collect(),
620 TypeKind::Intersection { kinds } => kinds.iter().map(|k| k.is_integer()).collect(),
621 TypeKind::Conditional { then, otherwise, .. } => then.is_integer().and(otherwise.is_integer()),
622 TypeKind::Scalar(scalar_type_kind) => match scalar_type_kind {
623 ScalarTypeKind::Integer { .. } => Trinary::True,
624 ScalarTypeKind::IntegerMask(_) => Trinary::True,
625 ScalarTypeKind::IntegerMaskOf(_, _) => Trinary::True,
626 ScalarTypeKind::LiteralInt => Trinary::True,
627 ScalarTypeKind::Numeric => Trinary::Maybe,
628 ScalarTypeKind::ArrayKey => Trinary::Maybe,
629 _ => Trinary::False,
630 },
631 TypeKind::Value(ValueTypeKind::Integer { .. }) => Trinary::True,
632 TypeKind::Value(ValueTypeKind::ClassLikeConstant { .. }) => Trinary::Maybe,
633 _ => Trinary::False,
634 }
635 }
636
637 #[inline]
638 pub fn is_string(&self) -> Trinary {
639 match &self {
640 TypeKind::Union { kinds } => kinds.iter().map(|k| k.is_string()).collect(),
641 TypeKind::Intersection { kinds } => kinds.iter().map(|k| k.is_string()).collect(),
642 TypeKind::Conditional { then, otherwise, .. } => then.is_string().and(otherwise.is_string()),
643 TypeKind::Scalar(scalar_type_kind) => match scalar_type_kind {
644 ScalarTypeKind::String => Trinary::True,
645 ScalarTypeKind::ClassString(_) => Trinary::True,
646 ScalarTypeKind::TraitString => Trinary::True,
647 ScalarTypeKind::EnumString => Trinary::True,
648 ScalarTypeKind::CallableString => Trinary::True,
649 ScalarTypeKind::NumericString => Trinary::True,
650 ScalarTypeKind::LiteralString => Trinary::True,
651 ScalarTypeKind::NonEmptyString => Trinary::True,
652 _ => Trinary::False,
653 },
654 TypeKind::Value(ValueTypeKind::String { .. }) => Trinary::True,
655 TypeKind::Value(ValueTypeKind::ClassLikeConstant { .. }) => Trinary::Maybe,
656 _ => Trinary::False,
657 }
658 }
659
660 #[inline]
661 pub fn is_non_empty_string(&self) -> Trinary {
662 match &self {
663 TypeKind::Union { kinds } => kinds.iter().map(|k| k.is_non_empty_string()).collect(),
664 TypeKind::Intersection { kinds } => kinds.iter().map(|k| k.is_non_empty_string()).collect(),
665 TypeKind::Conditional { then, otherwise, .. } => {
666 then.is_non_empty_string().and(otherwise.is_non_empty_string())
667 }
668 TypeKind::Scalar(scalar_type_kind) => match scalar_type_kind {
669 ScalarTypeKind::String => Trinary::Maybe,
670 ScalarTypeKind::ClassString(_) => Trinary::True,
671 ScalarTypeKind::TraitString => Trinary::True,
672 ScalarTypeKind::EnumString => Trinary::True,
673 ScalarTypeKind::CallableString => Trinary::True,
674 ScalarTypeKind::NumericString => Trinary::True,
675 ScalarTypeKind::LiteralString => Trinary::Maybe,
676 ScalarTypeKind::NonEmptyString => Trinary::True,
677 _ => Trinary::False,
678 },
679 TypeKind::Value(ValueTypeKind::String { length, .. }) => (*length > 0).into(),
680 TypeKind::Value(ValueTypeKind::ClassLikeConstant { .. }) => Trinary::Maybe,
681 _ => Trinary::False,
682 }
683 }
684
685 #[inline]
686 pub fn is_value(&self) -> bool {
687 matches!(self, TypeKind::Value(_))
688 }
689
690 #[inline]
691 pub fn is_templated_as_object(&self) -> bool {
692 matches!(self, TypeKind::GenericParameter { of, .. } if of.is_object())
693 }
694
695 #[inline]
696 pub fn is_generator(&self) -> bool {
697 matches!(self, TypeKind::Object(ObjectTypeKind::Generator { .. }))
698 }
699
700 pub fn get_key(&self, interner: &ThreadedInterner) -> String {
701 match &self {
702 TypeKind::Union { kinds } => kinds.iter().map(|k| k.get_key(interner)).collect::<Vec<_>>().join("|"),
703 TypeKind::Intersection { kinds } => kinds.iter().map(|k| k.get_key(interner)).collect::<Vec<_>>().join("&"),
704 TypeKind::Scalar(scalar_type_kind) => match &scalar_type_kind {
705 ScalarTypeKind::Bool => "bool".to_string(),
706 ScalarTypeKind::Float => "float".to_string(),
707 ScalarTypeKind::String => "string".to_string(),
708 ScalarTypeKind::Integer { min, max } => match (min, max) {
709 (None, None) => "int".to_string(),
710 (Some(min), None) => format!("int<{min}, max>"),
711 (None, Some(max)) => format!("int<min, {max}>"),
712 (Some(min), Some(max)) => format!("int<{min}, {max}>"),
713 },
714 ScalarTypeKind::IntegerMask(vec) => {
715 let vec = vec.iter().map(|v| v.to_string()).collect::<Vec<_>>().join(", ");
716
717 format!("int-mask<{vec}>")
718 }
719 ScalarTypeKind::IntegerMaskOf(string_identifier, string_identifier1) => {
720 format!(
721 "int-mask-of<{}, {}>",
722 interner.lookup(string_identifier),
723 interner.lookup(string_identifier1)
724 )
725 }
726 ScalarTypeKind::ClassString(string_identifier) => {
727 if let Some(string_identifier) = string_identifier {
728 format!("class-string<{}>", interner.lookup(string_identifier))
729 } else {
730 "class-string".to_string()
731 }
732 }
733 ScalarTypeKind::TraitString => "trait-string".to_string(),
734 ScalarTypeKind::EnumString => "enum-string".to_string(),
735 ScalarTypeKind::CallableString => "callable-string".to_string(),
736 ScalarTypeKind::NumericString => "numeric-string".to_string(),
737 ScalarTypeKind::LiteralString => "literal-string".to_string(),
738 ScalarTypeKind::LiteralInt => "literal-int".to_string(),
739 ScalarTypeKind::NonEmptyString => "non-empty-string".to_string(),
740 ScalarTypeKind::ArrayKey => "array-key".to_string(),
741 ScalarTypeKind::Numeric => "numeric".to_string(),
742 ScalarTypeKind::Scalar => "scalar".to_string(),
743 },
744 TypeKind::Object(object_type_kind) => match &object_type_kind {
745 ObjectTypeKind::AnyObject => "object".to_string(),
746 ObjectTypeKind::TypedObject { properties } => {
747 let properties = properties
748 .iter()
749 .map(|property| {
750 let name = interner.lookup(&property.name);
751 let kind = property.kind.get_key(interner);
752
753 if property.optional { format!("{name}?: {kind}") } else { format!("{name}: {kind}") }
754 })
755 .collect::<Vec<_>>()
756 .join(", ");
757
758 format!("object{{{properties}}}")
759 }
760 ObjectTypeKind::NamedObject { name, type_parameters } => {
761 let name = interner.lookup(name);
762
763 if type_parameters.is_empty() {
764 name.to_string()
765 } else {
766 let type_parameters = type_parameters
767 .iter()
768 .map(|type_parameter| type_parameter.get_key(interner))
769 .collect::<Vec<_>>()
770 .join(", ");
771
772 format!("{name}<{type_parameters}>")
773 }
774 }
775 ObjectTypeKind::AnonymousObject { span } => {
776 format!(
777 "anonymous-class@{}:{}-{}",
778 interner.lookup(&span.start.source.0),
779 span.start.offset,
780 span.end.offset
781 )
782 }
783 ObjectTypeKind::Generator { key, value, send, r#return } => {
784 let key = key.get_key(interner);
785 let value = value.get_key(interner);
786 let send = send.get_key(interner);
787 let r#return = r#return.get_key(interner);
788
789 format!("Generator<{key}, {value}, {send}, {return}>")
790 }
791 ObjectTypeKind::Static { .. } => "static".to_string(),
792 ObjectTypeKind::Parent { .. } => "parent".to_string(),
793 ObjectTypeKind::Self_ { .. } => "self".to_string(),
794 ObjectTypeKind::EnumCase { enum_name: name, case_name: case } => {
795 let name = interner.lookup(name);
796 let case = interner.lookup(case);
797
798 format!("enum({name}::{case})")
799 }
800 },
801 TypeKind::Array(array_type_kind) => match &array_type_kind {
802 ArrayTypeKind::Array { non_empty, key, value, .. } => {
803 let key = key.get_key(interner);
804 let value = value.get_key(interner);
805
806 if *non_empty {
807 format!("non-empty-array<{key}, {value}>")
808 } else {
809 format!("array<{key}, {value}>")
810 }
811 }
812 ArrayTypeKind::List { non_empty, value, .. } => {
813 let value = value.get_key(interner);
814
815 if *non_empty { format!("non-empty-list<{value}>") } else { format!("list<{value}>") }
816 }
817 ArrayTypeKind::CallableArray => "callable-array".to_string(),
818 ArrayTypeKind::Shape(array_shape) => {
819 let mut properties = array_shape
820 .properties
821 .iter()
822 .map(|property| {
823 let kind = property.kind.get_key(interner);
824
825 if let Some(key) = property.key.as_ref() {
826 let key = key.get_key(interner);
827
828 if property.optional { format!("{key}?: {kind}") } else { format!("{key}: {kind}") }
829 } else {
830 kind
831 }
832 })
833 .collect::<Vec<_>>()
834 .join(", ");
835
836 if let Some((key, value)) = &array_shape.additional_properties {
837 if matches!(
838 (key.as_ref(), value.as_ref()),
839 (TypeKind::Scalar(ScalarTypeKind::ArrayKey), TypeKind::Mixed { .. })
840 ) {
841 properties.push_str(", ...");
842 } else {
843 let key = key.get_key(interner);
844 let value = value.get_key(interner);
845
846 properties.push_str(&format!(", ...array<{key}: {value}>"));
847 }
848 }
849
850 format!("array{{{properties}}}")
851 }
852 },
853 TypeKind::Callable(callable_type_kind) => match &callable_type_kind {
854 CallableTypeKind::Callable { pure, templates, parameters, return_kind } => {
855 let parameters = parameters
856 .iter()
857 .map(|parameter| {
858 let mut kind = parameter.kind.get_key(interner);
859 if parameter.optional {
860 kind.push('=');
861 }
862
863 if parameter.variadic {
864 kind.push_str("...");
865 }
866
867 kind
868 })
869 .collect::<Vec<_>>()
870 .join(", ");
871
872 let return_kind = return_kind.get_key(interner);
873
874 let templates =
875 templates.iter().map(|template| template.get_key(interner)).collect::<Vec<_>>().join(", ");
876 let templates = if !templates.is_empty() { format!("<{templates}>") } else { "".to_string() };
877
878 if *pure {
879 format!("(pure-callable{templates}({parameters}): {return_kind})")
880 } else {
881 format!("(callable{templates}({parameters}): {return_kind})")
882 }
883 }
884 CallableTypeKind::Closure { pure, templates, parameters, return_kind } => {
885 let parameters = parameters
886 .iter()
887 .map(|parameter| {
888 let mut kind = parameter.kind.get_key(interner);
889 if parameter.optional {
890 kind.push('=');
891 }
892
893 if parameter.variadic {
894 kind.push_str("...");
895 }
896
897 kind
898 })
899 .collect::<Vec<_>>()
900 .join(", ");
901
902 let return_kind = return_kind.get_key(interner);
903
904 let templates =
905 templates.iter().map(|template| template.get_key(interner)).collect::<Vec<_>>().join(", ");
906 let templates = if !templates.is_empty() { format!("<{templates}>") } else { "".to_string() };
907
908 if *pure {
909 format!("(pure-Closure{templates}({parameters}): {return_kind})")
910 } else {
911 format!("(Closure{templates}({parameters}): {return_kind})")
912 }
913 }
914 },
915 TypeKind::Value(value_type_kind) => match &value_type_kind {
916 ValueTypeKind::String { value, .. } => {
917 format!("\"{value}\"")
918 }
919 ValueTypeKind::Integer { value } => value.to_string(),
920 ValueTypeKind::Float { value } => value.to_string(),
921 ValueTypeKind::Null => "null".to_string(),
922 ValueTypeKind::True => "true".to_string(),
923 ValueTypeKind::False => "false".to_string(),
924 ValueTypeKind::ClassLikeConstant { class_like, constant } => {
925 format!("{}::{}", class_like.get_key(interner), interner.lookup(constant))
926 }
927 },
928 TypeKind::Conditional { parameter, condition, then, otherwise } => {
929 let parameter = parameter.get_key(interner);
930 let condition = condition.get_key(interner);
931 let then = then.get_key(interner);
932 let otherwise = otherwise.get_key(interner);
933
934 format!("{parameter} is {condition} ? {then} : {otherwise}")
935 }
936 TypeKind::KeyOf { kind } => {
937 let kind = kind.get_key(interner);
938
939 format!("key-of<{kind}>")
940 }
941 TypeKind::ValueOf { kind } => {
942 let kind = kind.get_key(interner);
943
944 format!("value-of<{kind}>")
945 }
946 TypeKind::PropertiesOf { kind } => {
947 let kind = kind.get_key(interner);
948
949 format!("properties-of<{kind}>")
950 }
951 TypeKind::ClassStringMap { key, value_kind } => {
952 let mut template = interner.lookup(&key.name).to_owned();
953 for constraint in &key.constraints {
954 template.push_str(&format!(" of {}", constraint.get_key(interner)));
955 }
956
957 let value_kind = value_kind.get_key(interner);
958
959 format!("class-string-map<{template}, {value_kind}>")
960 }
961 TypeKind::Index { base_kind, index_kind } => {
962 let base_kind = base_kind.get_key(interner);
963 let index_kind = index_kind.get_key(interner);
964
965 format!("{base_kind}[{index_kind}]")
966 }
967 TypeKind::Variable { name } => interner.lookup(name).to_owned(),
968 TypeKind::Iterable { key, value } => {
969 let key = key.get_key(interner);
970 let value = value.get_key(interner);
971
972 format!("iterable<{key}, {value}>")
973 }
974 TypeKind::Void => "void".to_string(),
975 TypeKind::Resource => "resource".to_string(),
976 TypeKind::ClosedResource => "closed-resource".to_string(),
977 TypeKind::Mixed { explicit } => {
978 if *explicit {
979 "mixed".to_string()
980 } else {
981 "unknown".to_string()
982 }
983 }
984 TypeKind::Never => "never".to_string(),
985 TypeKind::GenericParameter { name, defined_in, .. } => {
986 format!("{}:{}", interner.lookup(name), interner.lookup(defined_in))
987 }
988 }
989 }
990}
991
992pub fn bool_kind() -> TypeKind {
994 TypeKind::Scalar(ScalarTypeKind::Bool)
995}
996
997pub fn integer_kind() -> TypeKind {
999 TypeKind::Scalar(ScalarTypeKind::Integer { min: None, max: None })
1000}
1001
1002pub fn positive_integer_kind() -> TypeKind {
1003 TypeKind::Scalar(ScalarTypeKind::Integer { min: Some(1), max: None })
1004}
1005
1006pub fn non_negative_integer_kind() -> TypeKind {
1007 TypeKind::Scalar(ScalarTypeKind::Integer { min: Some(0), max: None })
1008}
1009
1010pub fn negative_integer_kind() -> TypeKind {
1011 TypeKind::Scalar(ScalarTypeKind::Integer { min: None, max: Some(-1) })
1012}
1013
1014pub fn non_positive_integer_kind() -> TypeKind {
1015 TypeKind::Scalar(ScalarTypeKind::Integer { min: None, max: Some(0) })
1016}
1017
1018pub fn minimum_integer_kind(min: isize) -> TypeKind {
1019 TypeKind::Scalar(ScalarTypeKind::Integer { min: Some(min), max: None })
1020}
1021
1022pub fn maximum_integer_kind(max: isize) -> TypeKind {
1023 TypeKind::Scalar(ScalarTypeKind::Integer { min: None, max: Some(max) })
1024}
1025
1026pub fn integer_range_kind(min: isize, max: isize) -> TypeKind {
1027 TypeKind::Scalar(ScalarTypeKind::Integer { min: Some(min), max: Some(max) })
1028}
1029
1030pub fn float_kind() -> TypeKind {
1032 TypeKind::Scalar(ScalarTypeKind::Float)
1033}
1034
1035pub fn string_kind() -> TypeKind {
1037 TypeKind::Scalar(ScalarTypeKind::String)
1038}
1039
1040pub fn non_empty_string_kind() -> TypeKind {
1042 TypeKind::Scalar(ScalarTypeKind::NonEmptyString)
1043}
1044
1045pub fn list_kind(value: TypeKind, known_size: Option<usize>) -> TypeKind {
1047 TypeKind::Array(ArrayTypeKind::List { non_empty: false, value: Box::new(value), known_size })
1048}
1049
1050pub fn array_kind(key: TypeKind, value: TypeKind, known_size: Option<usize>) -> TypeKind {
1052 TypeKind::Array(ArrayTypeKind::Array { non_empty: false, key: Box::new(key), value: Box::new(value), known_size })
1053}
1054
1055pub fn non_empty_list_kind(value: TypeKind, known_size: Option<usize>) -> TypeKind {
1057 TypeKind::Array(ArrayTypeKind::List { non_empty: true, value: Box::new(value), known_size })
1058}
1059
1060pub fn non_empty_array_kind(key: TypeKind, value: TypeKind, known_size: Option<usize>) -> TypeKind {
1062 TypeKind::Array(ArrayTypeKind::Array { non_empty: true, key: Box::new(key), value: Box::new(value), known_size })
1063}
1064
1065pub fn indexed_shape_property(kind: TypeKind, optional: bool) -> ArrayShapeProperty {
1066 ArrayShapeProperty { key: None, kind, optional }
1067}
1068
1069pub fn string_shape_property(key: StringIdentifier, kind: TypeKind, optional: bool) -> ArrayShapeProperty {
1070 ArrayShapeProperty { key: Some(ArrayShapePropertyKey::String(key)), kind, optional }
1071}
1072
1073pub fn integer_shape_property(key: isize, kind: TypeKind, optional: bool) -> ArrayShapeProperty {
1074 ArrayShapeProperty { key: Some(ArrayShapePropertyKey::Integer(key)), kind, optional }
1075}
1076
1077pub fn array_shape_kind(
1078 properties: Vec<ArrayShapeProperty>,
1079 additional_properties: Option<(TypeKind, TypeKind)>,
1080) -> TypeKind {
1081 TypeKind::Array(ArrayTypeKind::Shape(ArrayShape {
1082 properties,
1083 additional_properties: additional_properties.map(|(k, v)| (Box::new(k), Box::new(v))),
1084 }))
1085}
1086
1087pub fn mixed_kind(explicit: bool) -> TypeKind {
1089 TypeKind::Mixed { explicit }
1090}
1091
1092pub fn union_kind(kinds: Vec<TypeKind>) -> TypeKind {
1094 TypeKind::Union { kinds }
1095}
1096
1097pub fn intersection_kind(kinds: Vec<TypeKind>) -> TypeKind {
1099 TypeKind::Intersection { kinds }
1100}
1101
1102pub fn callable_parameter(kind: TypeKind, optional: bool, variadic: bool) -> CallableParameter {
1104 CallableParameter { kind, optional, variadic }
1105}
1106
1107pub fn any_callable_kind() -> TypeKind {
1110 callable_kind(false, vec![], vec![callable_parameter(mixed_kind(false), true, true)], mixed_kind(false))
1111}
1112
1113pub fn callable_kind(
1115 pure: bool,
1116 templates: Vec<Template>,
1117 parameters: Vec<CallableParameter>,
1118 return_kind: TypeKind,
1119) -> TypeKind {
1120 TypeKind::Callable(CallableTypeKind::Callable { pure, templates, parameters, return_kind: Box::new(return_kind) })
1121}
1122
1123pub fn any_closure_kind() -> TypeKind {
1126 closure_kind(false, vec![], vec![callable_parameter(mixed_kind(false), true, true)], mixed_kind(false))
1127}
1128
1129pub fn closure_kind(
1131 pure: bool,
1132 templates: Vec<Template>,
1133 parameters: Vec<CallableParameter>,
1134 return_kind: TypeKind,
1135) -> TypeKind {
1136 TypeKind::Callable(CallableTypeKind::Closure { pure, templates, parameters, return_kind: Box::new(return_kind) })
1137}
1138
1139pub fn variable_kind(name: StringIdentifier) -> TypeKind {
1141 TypeKind::Variable { name }
1142}
1143
1144pub fn value_string_kind(
1146 value: StringIdentifier,
1147 length: usize,
1148 is_uppercase: Trinary,
1149 is_ascii_uppercase: Trinary,
1150 is_lowercase: Trinary,
1151 is_ascii_lowercase: Trinary,
1152) -> TypeKind {
1153 TypeKind::Value(ValueTypeKind::String {
1154 value,
1155 length,
1156 is_uppercase,
1157 is_lowercase,
1158 is_ascii_uppercase,
1159 is_ascii_lowercase,
1160 })
1161}
1162
1163pub fn value_integer_kind(value: i64) -> TypeKind {
1165 TypeKind::Value(ValueTypeKind::Integer { value })
1166}
1167
1168pub fn value_float_kind(value: OrderedFloat<f64>) -> TypeKind {
1170 TypeKind::Value(ValueTypeKind::Float { value })
1171}
1172
1173pub fn null_kind() -> TypeKind {
1175 TypeKind::Value(ValueTypeKind::Null)
1176}
1177
1178pub fn true_kind() -> TypeKind {
1180 TypeKind::Value(ValueTypeKind::True)
1181}
1182
1183pub fn false_kind() -> TypeKind {
1185 TypeKind::Value(ValueTypeKind::False)
1186}
1187
1188pub fn iterable_kind(key_kind: TypeKind, value_kind: TypeKind) -> TypeKind {
1190 TypeKind::Iterable { key: Box::new(key_kind), value: Box::new(value_kind) }
1191}
1192
1193pub fn any_object_kind() -> TypeKind {
1195 TypeKind::Object(ObjectTypeKind::AnyObject)
1196}
1197
1198pub fn static_kind(scope: StringIdentifier) -> TypeKind {
1200 TypeKind::Object(ObjectTypeKind::Static { scope })
1201}
1202
1203pub fn parent_kind(scope: StringIdentifier) -> TypeKind {
1205 TypeKind::Object(ObjectTypeKind::Parent { scope })
1206}
1207
1208pub fn self_kind(scope: StringIdentifier) -> TypeKind {
1210 TypeKind::Object(ObjectTypeKind::Self_ { scope })
1211}
1212
1213pub fn named_object_kind(name: StringIdentifier, type_parameters: Vec<TypeKind>) -> TypeKind {
1215 TypeKind::Object(ObjectTypeKind::NamedObject { name, type_parameters })
1216}
1217
1218pub fn anonymous_object_kind(span: Span) -> TypeKind {
1220 TypeKind::Object(ObjectTypeKind::AnonymousObject { span })
1221}
1222
1223pub fn enum_case_kind(enum_name: StringIdentifier, case_name: StringIdentifier) -> TypeKind {
1224 TypeKind::Object(ObjectTypeKind::EnumCase { enum_name, case_name })
1225}
1226
1227pub fn void_kind() -> TypeKind {
1229 TypeKind::Void
1230}
1231
1232pub fn never_kind() -> TypeKind {
1234 TypeKind::Never
1235}
1236
1237pub fn resource_kind() -> TypeKind {
1239 TypeKind::Resource
1240}
1241
1242pub fn closed_resource_kind() -> TypeKind {
1244 TypeKind::ClosedResource
1245}
1246
1247pub fn key_of_kind(kind: TypeKind) -> TypeKind {
1249 TypeKind::KeyOf { kind: Box::new(kind) }
1250}
1251
1252pub fn value_of_kind(kind: TypeKind) -> TypeKind {
1254 TypeKind::ValueOf { kind: Box::new(kind) }
1255}
1256
1257pub fn properties_of_kind(kind: TypeKind) -> TypeKind {
1259 TypeKind::PropertiesOf { kind: Box::new(kind) }
1260}
1261
1262pub fn conditional_kind(parameter: TypeKind, condition: TypeKind, then: TypeKind, otherwise: TypeKind) -> TypeKind {
1264 TypeKind::Conditional {
1265 parameter: Box::new(parameter),
1266 condition: Box::new(condition),
1267 then: Box::new(then),
1268 otherwise: Box::new(otherwise),
1269 }
1270}
1271
1272pub fn class_string_map_kind(key_template: Template, value_kind: TypeKind) -> TypeKind {
1274 TypeKind::ClassStringMap { key: key_template, value_kind: Box::new(value_kind) }
1275}
1276
1277pub fn index_kind(base_kind: TypeKind, index_kind: TypeKind) -> TypeKind {
1279 TypeKind::Index { base_kind: Box::new(base_kind), index_kind: Box::new(index_kind) }
1280}
1281
1282pub fn array_key_kind() -> TypeKind {
1284 TypeKind::Scalar(ScalarTypeKind::ArrayKey)
1285}
1286
1287impl From<&FunctionLikeReflection> for TypeKind {
1288 fn from(reflection: &FunctionLikeReflection) -> Self {
1289 let parameters: Vec<_> = reflection
1290 .parameters
1291 .iter()
1292 .map(|parameter| CallableParameter {
1293 optional: parameter.default.is_some(),
1294 kind: parameter.type_reflection.as_ref().map(|r| r.kind.clone()).unwrap_or_else(|| mixed_kind(false)),
1295 variadic: parameter.is_variadic,
1296 })
1297 .collect();
1298
1299 let return_kind = reflection
1300 .return_type_reflection
1301 .as_ref()
1302 .map(|r| r.type_reflection.kind.clone())
1303 .unwrap_or_else(|| mixed_kind(false));
1304
1305 TypeKind::Callable(CallableTypeKind::Closure {
1306 pure: reflection.is_pure,
1307 templates: reflection.templates.clone(),
1308 parameters,
1309 return_kind: Box::new(return_kind),
1310 })
1311 }
1312}