mago_reflection/type/
kind.rs

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/// Represents a template type parameter with a name and a set of constraints.
14/// For example, in `T extends Foo`, `T` is the template parameter with `Foo` as its constraint.
15#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
16pub struct Template {
17    /// The name of the template parameter.
18    name: StringIdentifier,
19
20    /// A list of type constraints that the template parameter must satisfy.
21    constraints: Vec<TypeKind>,
22}
23
24/// Represents scalar types, including specialized scalar types with additional properties.
25#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
26pub enum ScalarTypeKind {
27    /// The `bool` type.
28    Bool,
29
30    /// The `int` type.
31    /// The `Option` types represent inclusive minimum and maximum bounds.
32    Integer { min: Option<isize>, max: Option<isize> },
33
34    /// The `float` type.
35    Float,
36
37    /// The `string` type.
38    String,
39
40    /// An integer mask, representing a union of integers formed by bitwise OR of the given values.
41    /// For example, `int-mask<1, 2, 4>` includes all combinations of these bits set.
42    IntegerMask(Vec<isize>),
43
44    /// An integer mask of constants from a class.
45    /// For example, `int-mask-of<Class, CONST_PREFIX_*>` represents a mask using constants from `Class` with a given prefix.
46    IntegerMaskOf(StringIdentifier, StringIdentifier),
47
48    /// A class string type, optionally specifying a class.
49    /// For example, `class-string` or `class-string<Foo>`, representing the name of a class as a string.
50    ClassString(Option<StringIdentifier>),
51
52    /// A trait string type, representing the name of a trait as a string.
53    TraitString,
54
55    /// An enum string type, representing the name of an enum as a string.
56    EnumString,
57
58    /// A callable string type, representing a string that refers to a callable function or method.
59    CallableString,
60
61    /// A numeric string type, representing a string that contains a numeric value.
62    NumericString,
63
64    /// A literal string type, representing strings known at compile time.
65    LiteralString,
66
67    /// A literal integer type, representing integers known at compile time.
68    LiteralInt,
69
70    /// A non-empty string type.
71    NonEmptyString,
72
73    /// The `array-key` type, representing values that can be used as array keys (`int` or `string`).
74    ArrayKey,
75
76    /// The `numeric` type, representing either `int` or `float`.
77    Numeric,
78
79    /// The `scalar` type, representing `bool`, `int`, `float`, or `string`.
80    Scalar,
81}
82
83/// Represents a property in an object type.
84#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
85pub struct ObjectProperty {
86    /// The name of the property.
87    pub name: StringIdentifier,
88
89    /// The type of the property.
90    pub kind: TypeKind,
91
92    /// Indicates whether the property is optional.
93    pub optional: bool,
94}
95
96/// Represents object types, including specific instances and generic types.
97#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
98pub enum ObjectTypeKind {
99    /// Represents any object (`object` type), without specifying any properties or class.
100    AnyObject,
101
102    /// A typed object with specified properties.
103    /// For example, `object{ foo: string, bar: int }` defines an object with properties `foo` and `bar`.
104    TypedObject {
105        /// The properties of the object.
106        properties: Vec<ObjectProperty>,
107    },
108
109    /// A named object with generic type parameters.
110    /// For example, `Foo<T, U>` represents an instance of class `Foo` with type parameters `T` and `U`.
111    NamedObject {
112        /// The name of the class.
113        name: StringIdentifier,
114
115        /// The type parameters of the object class.
116        type_parameters: Vec<TypeKind>,
117    },
118
119    /// An instance of an anonymous class.
120    AnonymousObject {
121        /// The span of the anonymous class definition in the source code.
122        span: Span,
123    },
124
125    /// An enum case, representing a specific case of an enum.
126    EnumCase {
127        /// The name of the enum.
128        enum_name: StringIdentifier,
129
130        /// The case of the enum.
131        case_name: StringIdentifier,
132    },
133
134    /// A generator type with specified key, value, send, and return types.
135    /// For example, `Generator<T, U, V, W>`.
136    Generator { key: Box<TypeKind>, value: Box<TypeKind>, send: Box<TypeKind>, r#return: Box<TypeKind> },
137
138    /// The `static` type, representing the class of the called context.
139    Static {
140        /// The scope of the `static` type.
141        scope: StringIdentifier,
142    },
143
144    /// The `parent` type, representing the parent class in the class hierarchy.
145    Parent {
146        /// The scope of the `parent` type.
147        scope: StringIdentifier,
148    },
149
150    /// The `self` type, representing the current class.
151    Self_ {
152        /// The scope of the `self` type.
153        scope: StringIdentifier,
154    },
155}
156
157/// Represents a key in an array shape property.
158#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
159pub enum ArrayShapePropertyKey {
160    String(StringIdentifier),
161    Integer(isize),
162}
163
164/// Represents a property in an array shape type.
165#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
166pub struct ArrayShapeProperty {
167    /// The key of the property.
168    pub key: Option<ArrayShapePropertyKey>,
169
170    /// The type of the property.
171    pub kind: TypeKind,
172
173    /// Indicates whether the property is optional.
174    pub optional: bool,
175}
176
177/// Represents an array shape type, which is an array with specified keys and types.
178#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
179pub struct ArrayShape {
180    /// The properties (key-value pairs) of the array shape.
181    pub properties: Vec<ArrayShapeProperty>,
182
183    /// Additional properties specified by key and value types.
184    /// For example, `...array<array-key, mixed>` allows additional entries beyond the specified properties.
185    pub additional_properties: Option<(
186        Box<TypeKind>, // Key type
187        Box<TypeKind>, // Value type
188    )>,
189}
190
191/// Represents array types, including specialized arrays like lists and shapes.
192#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
193pub enum ArrayTypeKind {
194    /// An array with specified key and value types.
195    /// For example, `array<string, int>` represents an array with `string` keys and `int` values.
196    Array {
197        /// Indicates whether the array is non-empty.
198        non_empty: bool,
199
200        /// The type of the array keys.
201        key: Box<TypeKind>,
202
203        /// The type of the array values.
204        value: Box<TypeKind>,
205
206        /// The size of the array, if known.
207        known_size: Option<usize>,
208    },
209
210    /// A list (array with integer keys starting from zero) with a specified value type.
211    /// For example, `list<string>` represents a list of strings.
212    List {
213        /// Indicates whether the list is non-empty.
214        non_empty: bool,
215
216        /// The type of the list elements.
217        value: Box<TypeKind>,
218
219        /// The size of the list, if known.
220        known_size: Option<usize>,
221    },
222
223    /// A callable array, representing an array that can be called as a function.
224    CallableArray,
225
226    /// An array shape with specified properties and optional additional properties.
227    /// For example, `shape{ foo: string, bar: int, ... }`.
228    Shape(ArrayShape),
229}
230
231/// Represents a parameter in a callable type, including its type and attributes.
232#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
233pub struct CallableParameter {
234    /// The type of the parameter.
235    pub kind: TypeKind,
236
237    /// Indicates whether the parameter is optional.
238    pub optional: bool,
239
240    /// Indicates whether the parameter is variadic (i.e., accepts multiple values).
241    pub variadic: bool,
242}
243
244/// Represents callable types, including functions, methods, and closures, with specified parameters and return types.
245#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
246pub enum CallableTypeKind {
247    /// A callable type with specified parameters and return type.
248    /// For example, `callable(string, int): bool` represents a callable that accepts a `string` and an `int` and returns a `bool`.
249    Callable { pure: bool, templates: Vec<Template>, parameters: Vec<CallableParameter>, return_kind: Box<TypeKind> },
250
251    /// A closure type with specified parameters and return type.
252    /// For example, `Closure(string, int): bool`.
253    Closure { pure: bool, templates: Vec<Template>, parameters: Vec<CallableParameter>, return_kind: Box<TypeKind> },
254}
255
256/// Represents value types, including literal values and class constants.
257#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
258pub enum ValueTypeKind {
259    /// A literal string value.
260    /// For example, `'foo'`.
261    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    /// A literal integer value.
271    /// For example, `42`.
272    Integer { value: i64 },
273
274    /// A literal float value.
275    /// For example, `3.14`.
276    Float { value: OrderedFloat<f64> },
277
278    /// The `null` value.
279    Null,
280
281    /// The `true` boolean value.
282    True,
283
284    /// The `false` boolean value.
285    False,
286
287    /// A class-like constant.
288    /// For example, `Foo::BAR`, where `Foo` is the class and `BAR` is the constant.
289    ClassLikeConstant { class_like: ClassLikeName, constant: StringIdentifier },
290}
291
292/// Represents a `class-string-map` type, mapping class strings to values of a specified type.
293#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
294pub struct ClassStringMapType {
295    /// The template parameter representing the class name, with constraints.
296    key: Template,
297
298    /// The value type associated with the class string.
299    value: Box<TypeKind>,
300}
301
302/// Represents all possible types in the PHP static analyzer.
303#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
304pub enum TypeKind {
305    /// A union type, representing a value that can be any of the included types.
306    /// For example, `T | U`.
307    Union { kinds: Vec<TypeKind> },
308
309    /// An intersection type, representing a value that satisfies all of the included types.
310    /// For example, `T & U`.
311    Intersection { kinds: Vec<TypeKind> },
312
313    /// A scalar type, such as `int`, `string`, `bool`, or specialized scalar types.
314    Scalar(ScalarTypeKind),
315
316    /// An object type, including specific instances and generic types.
317    Object(ObjectTypeKind),
318
319    /// An array type, including lists, shapes, and arrays with specified key and value types.
320    Array(ArrayTypeKind),
321
322    /// A callable type, representing functions, methods, and closures.
323    Callable(CallableTypeKind),
324
325    /// A value type, including literals like strings, integers, and class constants.
326    Value(ValueTypeKind),
327
328    /// A conditional type, used for type inference based on a condition.
329    /// For example, `T extends U ? V : W`.
330    Conditional {
331        /// The type being checked ( usually a generic parameter, or a variable ).
332        parameter: Box<TypeKind>,
333
334        /// The type used in the condition to compare against.
335        condition: Box<TypeKind>,
336
337        /// The type when the condition is true.
338        then: Box<TypeKind>,
339
340        /// The type when the condition is false.
341        otherwise: Box<TypeKind>,
342    },
343
344    /// Represents the keys of a type.
345    /// For example, `key-of<T>` extracts the keys from type `T`.
346    KeyOf { kind: Box<TypeKind> },
347
348    /// Represents the values of a type.
349    /// For example, `value-of<T>` extracts the values from type `T`.
350    ValueOf { kind: Box<TypeKind> },
351
352    /// Represents the properties of a type.
353    /// For example, `properties-of<T>` extracts the properties from type `T`.
354    PropertiesOf { kind: Box<TypeKind> },
355
356    /// A `class-string-map` type, mapping class strings to values of a specified type.
357    /// For example, `class-string-map<T of Foo, T>` maps class names extending `Foo` to values of type `T`.
358    ClassStringMap {
359        /// The template parameter representing the class name, with constraints.
360        key: Template,
361
362        /// The value type associated with the class string.
363        value_kind: Box<TypeKind>,
364    },
365
366    /// An indexed access type, representing the type at a specific key.
367    /// For example, `T[K]` accesses the type of property `K` in type `T`.
368    Index { base_kind: Box<TypeKind>, index_kind: Box<TypeKind> },
369
370    /// A variable type, representing a type associated with a variable.
371    /// For example, `$foo`.
372    Variable { name: StringIdentifier },
373
374    /// An iterable type with specified key and value types.
375    /// For example, `iterable<string, int>`.
376    Iterable { key: Box<TypeKind>, value: Box<TypeKind> },
377
378    /// The `void` type, representing the absence of a value.
379    Void,
380
381    /// The `resource` type, representing a resource handle.
382    Resource,
383
384    /// The `closed-resource` type, representing a resource that has been closed (e.g., using `fclose()`).
385    ClosedResource,
386
387    /// The `mixed` type, representing any type.
388    Mixed {
389        /// Whether the `mixed` type is explicit, or inferred from context (e.g., a function with no return type).
390        explicit: bool,
391    },
392
393    /// The `never` type, representing a type that never occurs (e.g., functions that always throw exceptions or exit).
394    Never,
395
396    /// A generic parameter type, representing a type parameter with constraints.
397    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, // `"0"` is a numeric string, but falsy
550                ScalarTypeKind::LiteralString => Trinary::Maybe,
551                ScalarTypeKind::LiteralInt => Trinary::Maybe,
552                ScalarTypeKind::NonEmptyString => Trinary::Maybe, // `"0"` is a non-empty string, but falsy
553                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<{}, max>", min),
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 {
754                                format!("{}?: {}", name, kind)
755                            } else {
756                                format!("{}: {}", name, kind)
757                            }
758                        })
759                        .collect::<Vec<_>>()
760                        .join(", ");
761
762                    format!("object{{{}}}", properties)
763                }
764                ObjectTypeKind::NamedObject { name, type_parameters } => {
765                    let name = interner.lookup(name);
766
767                    if type_parameters.is_empty() {
768                        name.to_string()
769                    } else {
770                        let type_parameters = type_parameters
771                            .iter()
772                            .map(|type_parameter| type_parameter.get_key(interner))
773                            .collect::<Vec<_>>()
774                            .join(", ");
775
776                        format!("{}<{}>", name, type_parameters)
777                    }
778                }
779                ObjectTypeKind::AnonymousObject { span } => {
780                    format!(
781                        "anonymous-class@{}:{}-{}",
782                        interner.lookup(&span.start.source.0),
783                        span.start.offset,
784                        span.end.offset
785                    )
786                }
787                ObjectTypeKind::Generator { key, value, send, r#return } => {
788                    let key = key.get_key(interner);
789                    let value = value.get_key(interner);
790                    let send = send.get_key(interner);
791                    let r#return = r#return.get_key(interner);
792
793                    format!("Generator<{}, {}, {}, {}>", key, value, send, r#return)
794                }
795                ObjectTypeKind::Static { .. } => "static".to_string(),
796                ObjectTypeKind::Parent { .. } => "parent".to_string(),
797                ObjectTypeKind::Self_ { .. } => "self".to_string(),
798                ObjectTypeKind::EnumCase { enum_name: name, case_name: case } => {
799                    let name = interner.lookup(name);
800                    let case = interner.lookup(case);
801
802                    format!("enum({}::{})", name, case)
803                }
804            },
805            TypeKind::Array(array_type_kind) => match &array_type_kind {
806                ArrayTypeKind::Array { non_empty, key, value, .. } => {
807                    let key = key.get_key(interner);
808                    let value = value.get_key(interner);
809
810                    if *non_empty {
811                        format!("non-empty-array<{}, {}>", key, value)
812                    } else {
813                        format!("array<{}, {}>", key, value)
814                    }
815                }
816                ArrayTypeKind::List { non_empty, value, .. } => {
817                    let value = value.get_key(interner);
818
819                    if *non_empty {
820                        format!("non-empty-list<{}>", value)
821                    } else {
822                        format!("list<{}>", value)
823                    }
824                }
825                ArrayTypeKind::CallableArray => "callable-array".to_string(),
826                ArrayTypeKind::Shape(array_shape) => {
827                    let mut properties = array_shape
828                        .properties
829                        .iter()
830                        .map(|property| {
831                            let kind = property.kind.get_key(interner);
832
833                            if let Some(key) = property.key.as_ref() {
834                                let key = key.get_key(interner);
835
836                                if property.optional {
837                                    format!("{}?: {}", key, kind)
838                                } else {
839                                    format!("{}: {}", key, kind)
840                                }
841                            } else {
842                                kind
843                            }
844                        })
845                        .collect::<Vec<_>>()
846                        .join(", ");
847
848                    if let Some((key, value)) = &array_shape.additional_properties {
849                        if matches!(
850                            (key.as_ref(), value.as_ref()),
851                            (TypeKind::Scalar(ScalarTypeKind::ArrayKey), TypeKind::Mixed { .. })
852                        ) {
853                            properties.push_str(", ...");
854                        } else {
855                            let key = key.get_key(interner);
856                            let value = value.get_key(interner);
857
858                            properties.push_str(&format!(", ...array<{}: {}>", key, value));
859                        }
860                    }
861
862                    format!("array{{{}}}", properties)
863                }
864            },
865            TypeKind::Callable(callable_type_kind) => match &callable_type_kind {
866                CallableTypeKind::Callable { pure, templates, parameters, return_kind } => {
867                    let parameters = parameters
868                        .iter()
869                        .map(|parameter| {
870                            let mut kind = parameter.kind.get_key(interner);
871                            if parameter.optional {
872                                kind.push('=');
873                            }
874
875                            if parameter.variadic {
876                                kind.push_str("...");
877                            }
878
879                            kind
880                        })
881                        .collect::<Vec<_>>()
882                        .join(", ");
883
884                    let return_kind = return_kind.get_key(interner);
885
886                    let templates =
887                        templates.iter().map(|template| template.get_key(interner)).collect::<Vec<_>>().join(", ");
888                    let templates = if !templates.is_empty() { format!("<{}>", templates) } else { "".to_string() };
889
890                    if *pure {
891                        format!("(pure-callable{}({}): {})", templates, parameters, return_kind)
892                    } else {
893                        format!("(callable{}({}): {})", templates, parameters, return_kind)
894                    }
895                }
896                CallableTypeKind::Closure { pure, templates, parameters, return_kind } => {
897                    let parameters = parameters
898                        .iter()
899                        .map(|parameter| {
900                            let mut kind = parameter.kind.get_key(interner);
901                            if parameter.optional {
902                                kind.push('=');
903                            }
904
905                            if parameter.variadic {
906                                kind.push_str("...");
907                            }
908
909                            kind
910                        })
911                        .collect::<Vec<_>>()
912                        .join(", ");
913
914                    let return_kind = return_kind.get_key(interner);
915
916                    let templates =
917                        templates.iter().map(|template| template.get_key(interner)).collect::<Vec<_>>().join(", ");
918                    let templates = if !templates.is_empty() { format!("<{}>", templates) } else { "".to_string() };
919
920                    if *pure {
921                        format!("(pure-Closure{}({}): {})", templates, parameters, return_kind)
922                    } else {
923                        format!("(Closure{}({}): {})", templates, parameters, return_kind)
924                    }
925                }
926            },
927            TypeKind::Value(value_type_kind) => match &value_type_kind {
928                ValueTypeKind::String { value, .. } => {
929                    format!("\"{}\"", value)
930                }
931                ValueTypeKind::Integer { value } => value.to_string(),
932                ValueTypeKind::Float { value } => value.to_string(),
933                ValueTypeKind::Null => "null".to_string(),
934                ValueTypeKind::True => "true".to_string(),
935                ValueTypeKind::False => "false".to_string(),
936                ValueTypeKind::ClassLikeConstant { class_like, constant } => {
937                    format!("{}::{}", class_like.get_key(interner), interner.lookup(constant))
938                }
939            },
940            TypeKind::Conditional { parameter, condition, then, otherwise } => {
941                let parameter = parameter.get_key(interner);
942                let condition = condition.get_key(interner);
943                let then = then.get_key(interner);
944                let otherwise = otherwise.get_key(interner);
945
946                format!("{} is {} ? {} : {}", parameter, condition, then, otherwise)
947            }
948            TypeKind::KeyOf { kind } => {
949                let kind = kind.get_key(interner);
950
951                format!("key-of<{}>", kind)
952            }
953            TypeKind::ValueOf { kind } => {
954                let kind = kind.get_key(interner);
955
956                format!("value-of<{}>", kind)
957            }
958            TypeKind::PropertiesOf { kind } => {
959                let kind = kind.get_key(interner);
960
961                format!("properties-of<{}>", kind)
962            }
963            TypeKind::ClassStringMap { key, value_kind } => {
964                let mut template = interner.lookup(&key.name).to_owned();
965                for constraint in &key.constraints {
966                    template.push_str(&format!(" of {}", constraint.get_key(interner)));
967                }
968
969                let value_kind = value_kind.get_key(interner);
970
971                format!("class-string-map<{}, {}>", template, value_kind)
972            }
973            TypeKind::Index { base_kind, index_kind } => {
974                let base_kind = base_kind.get_key(interner);
975                let index_kind = index_kind.get_key(interner);
976
977                format!("{}[{}]", base_kind, index_kind)
978            }
979            TypeKind::Variable { name } => interner.lookup(name).to_owned(),
980            TypeKind::Iterable { key, value } => {
981                let key = key.get_key(interner);
982                let value = value.get_key(interner);
983
984                format!("iterable<{}, {}>", key, value)
985            }
986            TypeKind::Void => "void".to_string(),
987            TypeKind::Resource => "resource".to_string(),
988            TypeKind::ClosedResource => "closed-resource".to_string(),
989            TypeKind::Mixed { explicit } => {
990                if *explicit {
991                    "mixed".to_string()
992                } else {
993                    "unknown".to_string()
994                }
995            }
996            TypeKind::Never => "never".to_string(),
997            TypeKind::GenericParameter { name, defined_in, .. } => {
998                format!("{}:{}", interner.lookup(name), interner.lookup(defined_in))
999            }
1000        }
1001    }
1002}
1003
1004/// Creates a `TypeKind` representing the `bool` type.
1005pub fn bool_kind() -> TypeKind {
1006    TypeKind::Scalar(ScalarTypeKind::Bool)
1007}
1008
1009/// Creates a `TypeKind` representing the `int` type.
1010pub fn integer_kind() -> TypeKind {
1011    TypeKind::Scalar(ScalarTypeKind::Integer { min: None, max: None })
1012}
1013
1014pub fn positive_integer_kind() -> TypeKind {
1015    TypeKind::Scalar(ScalarTypeKind::Integer { min: Some(1), max: None })
1016}
1017
1018pub fn non_negative_integer_kind() -> TypeKind {
1019    TypeKind::Scalar(ScalarTypeKind::Integer { min: Some(0), max: None })
1020}
1021
1022pub fn negative_integer_kind() -> TypeKind {
1023    TypeKind::Scalar(ScalarTypeKind::Integer { min: None, max: Some(-1) })
1024}
1025
1026pub fn non_positive_integer_kind() -> TypeKind {
1027    TypeKind::Scalar(ScalarTypeKind::Integer { min: None, max: Some(0) })
1028}
1029
1030pub fn minimum_integer_kind(min: isize) -> TypeKind {
1031    TypeKind::Scalar(ScalarTypeKind::Integer { min: Some(min), max: None })
1032}
1033
1034pub fn maximum_integer_kind(max: isize) -> TypeKind {
1035    TypeKind::Scalar(ScalarTypeKind::Integer { min: None, max: Some(max) })
1036}
1037
1038pub fn integer_range_kind(min: isize, max: isize) -> TypeKind {
1039    TypeKind::Scalar(ScalarTypeKind::Integer { min: Some(min), max: Some(max) })
1040}
1041
1042/// Creates a `TypeKind` representing the `float` type.
1043pub fn float_kind() -> TypeKind {
1044    TypeKind::Scalar(ScalarTypeKind::Float)
1045}
1046
1047/// Creates a `TypeKind` representing the `string` type.
1048pub fn string_kind() -> TypeKind {
1049    TypeKind::Scalar(ScalarTypeKind::String)
1050}
1051
1052/// Creates a `TypeKind` representing the `non-empty-string` type.
1053pub fn non_empty_string_kind() -> TypeKind {
1054    TypeKind::Scalar(ScalarTypeKind::NonEmptyString)
1055}
1056
1057/// Creates a `TypeKind` representing a list of the given type.
1058pub fn list_kind(value: TypeKind, known_size: Option<usize>) -> TypeKind {
1059    TypeKind::Array(ArrayTypeKind::List { non_empty: false, value: Box::new(value), known_size })
1060}
1061
1062/// Creates a `TypeKind` representing an array with the given key and value types.
1063pub fn array_kind(key: TypeKind, value: TypeKind, known_size: Option<usize>) -> TypeKind {
1064    TypeKind::Array(ArrayTypeKind::Array { non_empty: false, key: Box::new(key), value: Box::new(value), known_size })
1065}
1066
1067/// Creates a `TypeKind` representing a non-empty list of the given type.
1068pub fn non_empty_list_kind(value: TypeKind, known_size: Option<usize>) -> TypeKind {
1069    TypeKind::Array(ArrayTypeKind::List { non_empty: true, value: Box::new(value), known_size })
1070}
1071
1072/// Creates a `TypeKind` representing a non-empty array with the given key and value types.
1073pub fn non_empty_array_kind(key: TypeKind, value: TypeKind, known_size: Option<usize>) -> TypeKind {
1074    TypeKind::Array(ArrayTypeKind::Array { non_empty: true, key: Box::new(key), value: Box::new(value), known_size })
1075}
1076
1077pub fn indexed_shape_property(kind: TypeKind, optional: bool) -> ArrayShapeProperty {
1078    ArrayShapeProperty { key: None, kind, optional }
1079}
1080
1081pub fn string_shape_property(key: StringIdentifier, kind: TypeKind, optional: bool) -> ArrayShapeProperty {
1082    ArrayShapeProperty { key: Some(ArrayShapePropertyKey::String(key)), kind, optional }
1083}
1084
1085pub fn integer_shape_property(key: isize, kind: TypeKind, optional: bool) -> ArrayShapeProperty {
1086    ArrayShapeProperty { key: Some(ArrayShapePropertyKey::Integer(key)), kind, optional }
1087}
1088
1089pub fn array_shape_kind(
1090    properties: Vec<ArrayShapeProperty>,
1091    additional_properties: Option<(TypeKind, TypeKind)>,
1092) -> TypeKind {
1093    TypeKind::Array(ArrayTypeKind::Shape(ArrayShape {
1094        properties,
1095        additional_properties: additional_properties.map(|(k, v)| (Box::new(k), Box::new(v))),
1096    }))
1097}
1098
1099/// Creates a `TypeKind` representing the `mixed` type.
1100pub fn mixed_kind(explicit: bool) -> TypeKind {
1101    TypeKind::Mixed { explicit }
1102}
1103
1104/// Creates a `TypeKind` representing a union of the given types.
1105pub fn union_kind(kinds: Vec<TypeKind>) -> TypeKind {
1106    TypeKind::Union { kinds }
1107}
1108
1109/// Creates a `TypeKind` representing an intersection of the given types.
1110pub fn intersection_kind(kinds: Vec<TypeKind>) -> TypeKind {
1111    TypeKind::Intersection { kinds }
1112}
1113
1114/// Creates a `CallableParameter` with the given kind, optional flag, and variadic flag.
1115pub fn callable_parameter(kind: TypeKind, optional: bool, variadic: bool) -> CallableParameter {
1116    CallableParameter { kind, optional, variadic }
1117}
1118
1119/// Creates a `TypeKind` representing a callable typewith an unknown number of parameters and
1120/// an implicit mixed return type.
1121pub fn any_callable_kind() -> TypeKind {
1122    callable_kind(false, vec![], vec![callable_parameter(mixed_kind(false), true, true)], mixed_kind(false))
1123}
1124
1125/// Creates a `TypeKind` representing a callable type with the given parameters and return type.
1126pub fn callable_kind(
1127    pure: bool,
1128    templates: Vec<Template>,
1129    parameters: Vec<CallableParameter>,
1130    return_kind: TypeKind,
1131) -> TypeKind {
1132    TypeKind::Callable(CallableTypeKind::Callable { pure, templates, parameters, return_kind: Box::new(return_kind) })
1133}
1134
1135/// Creates a `TypeKind` representing a closure type with an unknown number of parameters and
1136/// an implicit mixed return type.
1137pub fn any_closure_kind() -> TypeKind {
1138    closure_kind(false, vec![], vec![callable_parameter(mixed_kind(false), true, true)], mixed_kind(false))
1139}
1140
1141/// Creates a `TypeKind` representing a closure type with the given parameters and return type.
1142pub fn closure_kind(
1143    pure: bool,
1144    templates: Vec<Template>,
1145    parameters: Vec<CallableParameter>,
1146    return_kind: TypeKind,
1147) -> TypeKind {
1148    TypeKind::Callable(CallableTypeKind::Closure { pure, templates, parameters, return_kind: Box::new(return_kind) })
1149}
1150
1151/// Creates a `TypeKind` representing a variable type with the given name.
1152pub fn variable_kind(name: StringIdentifier) -> TypeKind {
1153    TypeKind::Variable { name }
1154}
1155
1156/// Creates a `TypeKind` representing a value type for a literal string.
1157pub fn value_string_kind(
1158    value: StringIdentifier,
1159    length: usize,
1160    is_uppercase: Trinary,
1161    is_ascii_uppercase: Trinary,
1162    is_lowercase: Trinary,
1163    is_ascii_lowercase: Trinary,
1164) -> TypeKind {
1165    TypeKind::Value(ValueTypeKind::String {
1166        value,
1167        length,
1168        is_uppercase,
1169        is_lowercase,
1170        is_ascii_uppercase,
1171        is_ascii_lowercase,
1172    })
1173}
1174
1175/// Creates a `TypeKind` representing a value type for a literal integer.
1176pub fn value_integer_kind(value: i64) -> TypeKind {
1177    TypeKind::Value(ValueTypeKind::Integer { value })
1178}
1179
1180/// Creates a `TypeKind` representing a value type for a literal float.
1181pub fn value_float_kind(value: OrderedFloat<f64>) -> TypeKind {
1182    TypeKind::Value(ValueTypeKind::Float { value })
1183}
1184
1185/// Creates a `TypeKind` representing the `null` value.
1186pub fn null_kind() -> TypeKind {
1187    TypeKind::Value(ValueTypeKind::Null)
1188}
1189
1190/// Creates a `TypeKind` representing the `true` boolean value.
1191pub fn true_kind() -> TypeKind {
1192    TypeKind::Value(ValueTypeKind::True)
1193}
1194
1195/// Creates a `TypeKind` representing the `false` boolean value.
1196pub fn false_kind() -> TypeKind {
1197    TypeKind::Value(ValueTypeKind::False)
1198}
1199
1200/// Creates a `TypeKind` representing an iterable with the given key and value types.
1201pub fn iterable_kind(key_kind: TypeKind, value_kind: TypeKind) -> TypeKind {
1202    TypeKind::Iterable { key: Box::new(key_kind), value: Box::new(value_kind) }
1203}
1204
1205/// Creates a `TypeKind` representing an object of any type.
1206pub fn any_object_kind() -> TypeKind {
1207    TypeKind::Object(ObjectTypeKind::AnyObject)
1208}
1209
1210/// Creates a `TypeKind` representing the `static` type of the given class.
1211pub fn static_kind(scope: StringIdentifier) -> TypeKind {
1212    TypeKind::Object(ObjectTypeKind::Static { scope })
1213}
1214
1215/// Creates a `TypeKind` representing the `parent` type of the given class.
1216pub fn parent_kind(scope: StringIdentifier) -> TypeKind {
1217    TypeKind::Object(ObjectTypeKind::Parent { scope })
1218}
1219
1220/// Creates a `TypeKind` representing the `self` type of the given class.
1221pub fn self_kind(scope: StringIdentifier) -> TypeKind {
1222    TypeKind::Object(ObjectTypeKind::Self_ { scope })
1223}
1224
1225/// Creates a `TypeKind` representing a named object with the given name and type parameters.
1226pub fn named_object_kind(name: StringIdentifier, type_parameters: Vec<TypeKind>) -> TypeKind {
1227    TypeKind::Object(ObjectTypeKind::NamedObject { name, type_parameters })
1228}
1229
1230/// Creates a `TypeKind` representing an instance of an object with the given name and type parameters.
1231pub fn anonymous_object_kind(span: Span) -> TypeKind {
1232    TypeKind::Object(ObjectTypeKind::AnonymousObject { span })
1233}
1234
1235pub fn enum_case_kind(enum_name: StringIdentifier, case_name: StringIdentifier) -> TypeKind {
1236    TypeKind::Object(ObjectTypeKind::EnumCase { enum_name, case_name })
1237}
1238
1239/// Creates a `TypeKind` representing the `void` type.
1240pub fn void_kind() -> TypeKind {
1241    TypeKind::Void
1242}
1243
1244/// Creates a `TypeKind` representing the `never` type.
1245pub fn never_kind() -> TypeKind {
1246    TypeKind::Never
1247}
1248
1249/// Creates a `TypeKind` representing the `resource` type.
1250pub fn resource_kind() -> TypeKind {
1251    TypeKind::Resource
1252}
1253
1254/// Creates a `TypeKind` representing the `closed-resource` type.
1255pub fn closed_resource_kind() -> TypeKind {
1256    TypeKind::ClosedResource
1257}
1258
1259/// Creates a `TypeKind` representing a key-of type.
1260pub fn key_of_kind(kind: TypeKind) -> TypeKind {
1261    TypeKind::KeyOf { kind: Box::new(kind) }
1262}
1263
1264/// Creates a `TypeKind` representing a value-of type.
1265pub fn value_of_kind(kind: TypeKind) -> TypeKind {
1266    TypeKind::ValueOf { kind: Box::new(kind) }
1267}
1268
1269/// Creates a `TypeKind` representing a properties-of type.
1270pub fn properties_of_kind(kind: TypeKind) -> TypeKind {
1271    TypeKind::PropertiesOf { kind: Box::new(kind) }
1272}
1273
1274/// Creates a `TypeKind` representing a conditional type.
1275pub fn conditional_kind(parameter: TypeKind, condition: TypeKind, then: TypeKind, otherwise: TypeKind) -> TypeKind {
1276    TypeKind::Conditional {
1277        parameter: Box::new(parameter),
1278        condition: Box::new(condition),
1279        then: Box::new(then),
1280        otherwise: Box::new(otherwise),
1281    }
1282}
1283
1284/// Creates a `TypeKind` representing a class-string-map type.
1285pub fn class_string_map_kind(key_template: Template, value_kind: TypeKind) -> TypeKind {
1286    TypeKind::ClassStringMap { key: key_template, value_kind: Box::new(value_kind) }
1287}
1288
1289/// Creates a `TypeKind` representing an index type.
1290pub fn index_kind(base_kind: TypeKind, index_kind: TypeKind) -> TypeKind {
1291    TypeKind::Index { base_kind: Box::new(base_kind), index_kind: Box::new(index_kind) }
1292}
1293
1294/// Creates a `TypeKind` representing an array-key type.
1295pub fn array_key_kind() -> TypeKind {
1296    TypeKind::Scalar(ScalarTypeKind::ArrayKey)
1297}
1298
1299impl From<&FunctionLikeReflection> for TypeKind {
1300    fn from(reflection: &FunctionLikeReflection) -> Self {
1301        let parameters: Vec<_> = reflection
1302            .parameters
1303            .iter()
1304            .map(|parameter| CallableParameter {
1305                optional: parameter.default.is_some(),
1306                kind: parameter.type_reflection.as_ref().map(|r| r.kind.clone()).unwrap_or_else(|| mixed_kind(false)),
1307                variadic: parameter.is_variadic,
1308            })
1309            .collect();
1310
1311        let return_kind = reflection
1312            .return_type_reflection
1313            .as_ref()
1314            .map(|r| r.type_reflection.kind.clone())
1315            .unwrap_or_else(|| mixed_kind(false));
1316
1317        TypeKind::Callable(CallableTypeKind::Closure {
1318            pure: reflection.is_pure,
1319            templates: reflection.templates.clone(),
1320            parameters,
1321            return_kind: Box::new(return_kind),
1322        })
1323    }
1324}