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<{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
992/// Creates a `TypeKind` representing the `bool` type.
993pub fn bool_kind() -> TypeKind {
994    TypeKind::Scalar(ScalarTypeKind::Bool)
995}
996
997/// Creates a `TypeKind` representing the `int` type.
998pub 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
1030/// Creates a `TypeKind` representing the `float` type.
1031pub fn float_kind() -> TypeKind {
1032    TypeKind::Scalar(ScalarTypeKind::Float)
1033}
1034
1035/// Creates a `TypeKind` representing the `string` type.
1036pub fn string_kind() -> TypeKind {
1037    TypeKind::Scalar(ScalarTypeKind::String)
1038}
1039
1040/// Creates a `TypeKind` representing the `non-empty-string` type.
1041pub fn non_empty_string_kind() -> TypeKind {
1042    TypeKind::Scalar(ScalarTypeKind::NonEmptyString)
1043}
1044
1045/// Creates a `TypeKind` representing a list of the given type.
1046pub 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
1050/// Creates a `TypeKind` representing an array with the given key and value types.
1051pub 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
1055/// Creates a `TypeKind` representing a non-empty list of the given type.
1056pub 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
1060/// Creates a `TypeKind` representing a non-empty array with the given key and value types.
1061pub 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
1087/// Creates a `TypeKind` representing the `mixed` type.
1088pub fn mixed_kind(explicit: bool) -> TypeKind {
1089    TypeKind::Mixed { explicit }
1090}
1091
1092/// Creates a `TypeKind` representing a union of the given types.
1093pub fn union_kind(kinds: Vec<TypeKind>) -> TypeKind {
1094    TypeKind::Union { kinds }
1095}
1096
1097/// Creates a `TypeKind` representing an intersection of the given types.
1098pub fn intersection_kind(kinds: Vec<TypeKind>) -> TypeKind {
1099    TypeKind::Intersection { kinds }
1100}
1101
1102/// Creates a `CallableParameter` with the given kind, optional flag, and variadic flag.
1103pub fn callable_parameter(kind: TypeKind, optional: bool, variadic: bool) -> CallableParameter {
1104    CallableParameter { kind, optional, variadic }
1105}
1106
1107/// Creates a `TypeKind` representing a callable typewith an unknown number of parameters and
1108/// an implicit mixed return type.
1109pub fn any_callable_kind() -> TypeKind {
1110    callable_kind(false, vec![], vec![callable_parameter(mixed_kind(false), true, true)], mixed_kind(false))
1111}
1112
1113/// Creates a `TypeKind` representing a callable type with the given parameters and return type.
1114pub 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
1123/// Creates a `TypeKind` representing a closure type with an unknown number of parameters and
1124/// an implicit mixed return type.
1125pub fn any_closure_kind() -> TypeKind {
1126    closure_kind(false, vec![], vec![callable_parameter(mixed_kind(false), true, true)], mixed_kind(false))
1127}
1128
1129/// Creates a `TypeKind` representing a closure type with the given parameters and return type.
1130pub 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
1139/// Creates a `TypeKind` representing a variable type with the given name.
1140pub fn variable_kind(name: StringIdentifier) -> TypeKind {
1141    TypeKind::Variable { name }
1142}
1143
1144/// Creates a `TypeKind` representing a value type for a literal string.
1145pub 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
1163/// Creates a `TypeKind` representing a value type for a literal integer.
1164pub fn value_integer_kind(value: i64) -> TypeKind {
1165    TypeKind::Value(ValueTypeKind::Integer { value })
1166}
1167
1168/// Creates a `TypeKind` representing a value type for a literal float.
1169pub fn value_float_kind(value: OrderedFloat<f64>) -> TypeKind {
1170    TypeKind::Value(ValueTypeKind::Float { value })
1171}
1172
1173/// Creates a `TypeKind` representing the `null` value.
1174pub fn null_kind() -> TypeKind {
1175    TypeKind::Value(ValueTypeKind::Null)
1176}
1177
1178/// Creates a `TypeKind` representing the `true` boolean value.
1179pub fn true_kind() -> TypeKind {
1180    TypeKind::Value(ValueTypeKind::True)
1181}
1182
1183/// Creates a `TypeKind` representing the `false` boolean value.
1184pub fn false_kind() -> TypeKind {
1185    TypeKind::Value(ValueTypeKind::False)
1186}
1187
1188/// Creates a `TypeKind` representing an iterable with the given key and value types.
1189pub 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
1193/// Creates a `TypeKind` representing an object of any type.
1194pub fn any_object_kind() -> TypeKind {
1195    TypeKind::Object(ObjectTypeKind::AnyObject)
1196}
1197
1198/// Creates a `TypeKind` representing the `static` type of the given class.
1199pub fn static_kind(scope: StringIdentifier) -> TypeKind {
1200    TypeKind::Object(ObjectTypeKind::Static { scope })
1201}
1202
1203/// Creates a `TypeKind` representing the `parent` type of the given class.
1204pub fn parent_kind(scope: StringIdentifier) -> TypeKind {
1205    TypeKind::Object(ObjectTypeKind::Parent { scope })
1206}
1207
1208/// Creates a `TypeKind` representing the `self` type of the given class.
1209pub fn self_kind(scope: StringIdentifier) -> TypeKind {
1210    TypeKind::Object(ObjectTypeKind::Self_ { scope })
1211}
1212
1213/// Creates a `TypeKind` representing a named object with the given name and type parameters.
1214pub fn named_object_kind(name: StringIdentifier, type_parameters: Vec<TypeKind>) -> TypeKind {
1215    TypeKind::Object(ObjectTypeKind::NamedObject { name, type_parameters })
1216}
1217
1218/// Creates a `TypeKind` representing an instance of an object with the given name and type parameters.
1219pub 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
1227/// Creates a `TypeKind` representing the `void` type.
1228pub fn void_kind() -> TypeKind {
1229    TypeKind::Void
1230}
1231
1232/// Creates a `TypeKind` representing the `never` type.
1233pub fn never_kind() -> TypeKind {
1234    TypeKind::Never
1235}
1236
1237/// Creates a `TypeKind` representing the `resource` type.
1238pub fn resource_kind() -> TypeKind {
1239    TypeKind::Resource
1240}
1241
1242/// Creates a `TypeKind` representing the `closed-resource` type.
1243pub fn closed_resource_kind() -> TypeKind {
1244    TypeKind::ClosedResource
1245}
1246
1247/// Creates a `TypeKind` representing a key-of type.
1248pub fn key_of_kind(kind: TypeKind) -> TypeKind {
1249    TypeKind::KeyOf { kind: Box::new(kind) }
1250}
1251
1252/// Creates a `TypeKind` representing a value-of type.
1253pub fn value_of_kind(kind: TypeKind) -> TypeKind {
1254    TypeKind::ValueOf { kind: Box::new(kind) }
1255}
1256
1257/// Creates a `TypeKind` representing a properties-of type.
1258pub fn properties_of_kind(kind: TypeKind) -> TypeKind {
1259    TypeKind::PropertiesOf { kind: Box::new(kind) }
1260}
1261
1262/// Creates a `TypeKind` representing a conditional type.
1263pub 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
1272/// Creates a `TypeKind` representing a class-string-map type.
1273pub 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
1277/// Creates a `TypeKind` representing an index type.
1278pub 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
1282/// Creates a `TypeKind` representing an array-key type.
1283pub 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}