mago_codex/ttype/atomic/
mod.rs

1use serde::Deserialize;
2use serde::Serialize;
3
4use mago_atom::Atom;
5use mago_atom::ascii_lowercase_atom;
6use mago_atom::atom;
7
8use crate::metadata::CodebaseMetadata;
9use crate::reference::ReferenceSource;
10use crate::reference::SymbolReferences;
11use crate::symbol::SymbolKind;
12use crate::symbol::Symbols;
13use crate::ttype::TType;
14use crate::ttype::TypeRef;
15use crate::ttype::atomic::alias::TAlias;
16use crate::ttype::atomic::array::TArray;
17use crate::ttype::atomic::array::key::ArrayKey;
18use crate::ttype::atomic::callable::TCallable;
19use crate::ttype::atomic::conditional::TConditional;
20use crate::ttype::atomic::derived::TDerived;
21use crate::ttype::atomic::generic::TGenericParameter;
22use crate::ttype::atomic::iterable::TIterable;
23use crate::ttype::atomic::mixed::TMixed;
24use crate::ttype::atomic::object::TObject;
25use crate::ttype::atomic::object::r#enum::TEnum;
26use crate::ttype::atomic::object::named::TNamedObject;
27use crate::ttype::atomic::reference::TReference;
28use crate::ttype::atomic::reference::TReferenceMemberSelector;
29use crate::ttype::atomic::resource::TResource;
30use crate::ttype::atomic::scalar::TScalar;
31use crate::ttype::atomic::scalar::class_like_string::TClassLikeString;
32use crate::ttype::atomic::scalar::int::TInteger;
33use crate::ttype::atomic::scalar::string::TString;
34use crate::ttype::atomic::scalar::string::TStringLiteral;
35use crate::ttype::get_arraykey;
36use crate::ttype::get_mixed;
37use crate::ttype::union::TUnion;
38use crate::ttype::union::populate_union_type;
39
40pub mod alias;
41pub mod array;
42pub mod callable;
43pub mod conditional;
44pub mod derived;
45pub mod generic;
46pub mod iterable;
47pub mod mixed;
48pub mod object;
49pub mod reference;
50pub mod resource;
51pub mod scalar;
52
53#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Eq, Hash, PartialOrd, Ord)]
54pub enum TAtomic {
55    Scalar(TScalar),
56    Callable(TCallable),
57    Mixed(TMixed),
58    Object(TObject),
59    Array(TArray),
60    Iterable(TIterable),
61    Resource(TResource),
62    Reference(TReference),
63    GenericParameter(TGenericParameter),
64    Variable(Atom),
65    Conditional(TConditional),
66    Derived(TDerived),
67    Alias(TAlias),
68    Never,
69    Null,
70    Void,
71    Placeholder,
72}
73
74impl TAtomic {
75    #[must_use]
76    pub fn is_numeric(&self) -> bool {
77        match self {
78            TAtomic::Scalar(scalar) => scalar.is_numeric(),
79            TAtomic::GenericParameter(parameter) => parameter.constraint.is_numeric(),
80            _ => false,
81        }
82    }
83
84    #[must_use]
85    pub fn is_int_or_float(&self) -> bool {
86        match self {
87            TAtomic::Scalar(scalar) => scalar.is_int_or_float(),
88            TAtomic::GenericParameter(parameter) => parameter.constraint.is_int_or_float(),
89            _ => false,
90        }
91    }
92
93    /// Returns `Some(true)` if this type is effectively an int, `Some(false)` if effectively a float,
94    /// or `None` if neither. Considers generic parameter constraints (e.g., `T of int` is treated as int).
95    #[must_use]
96    pub fn effective_int_or_float(&self) -> Option<bool> {
97        match self {
98            TAtomic::Scalar(TScalar::Integer(_)) => Some(true),
99            TAtomic::Scalar(TScalar::Float(_)) => Some(false),
100            TAtomic::GenericParameter(parameter) => parameter.constraint.effective_int_or_float(),
101            _ => None,
102        }
103    }
104
105    #[must_use]
106    pub const fn is_mixed(&self) -> bool {
107        matches!(self, TAtomic::Mixed(_))
108    }
109
110    #[must_use]
111    pub const fn is_vanilla_mixed(&self) -> bool {
112        matches!(self, TAtomic::Mixed(_))
113    }
114
115    #[must_use]
116    pub const fn is_mixed_isset_from_loop(&self) -> bool {
117        matches!(self, TAtomic::Mixed(mixed) if mixed.is_isset_from_loop())
118    }
119
120    #[must_use]
121    pub const fn is_never(&self) -> bool {
122        matches!(self, TAtomic::Never)
123    }
124
125    #[must_use]
126    pub fn is_templated_as_never(&self) -> bool {
127        matches!(self, TAtomic::GenericParameter(parameter) if parameter.constraint.is_never())
128    }
129
130    #[must_use]
131    pub fn is_templated_as_mixed(&self) -> bool {
132        matches!(self, TAtomic::GenericParameter(parameter) if parameter.is_constrained_as_mixed())
133    }
134
135    #[must_use]
136    pub fn is_templated_as_vanilla_mixed(&self) -> bool {
137        matches!(self, TAtomic::GenericParameter(parameter) if parameter.is_constrained_as_vanilla_mixed())
138    }
139
140    pub fn map_generic_parameter_constraint<F, T>(&self, f: F) -> Option<T>
141    where
142        F: FnOnce(&TUnion) -> T,
143    {
144        if let TAtomic::GenericParameter(parameter) = self { Some(f(parameter.constraint.as_ref())) } else { None }
145    }
146
147    #[must_use]
148    pub fn is_enum(&self) -> bool {
149        matches!(self, TAtomic::Object(TObject::Enum(TEnum { .. })))
150    }
151
152    #[must_use]
153    pub fn is_enum_case(&self) -> bool {
154        matches!(self, TAtomic::Object(TObject::Enum(TEnum { case: Some(_), .. })))
155    }
156
157    pub fn is_object_type(&self) -> bool {
158        match self {
159            TAtomic::Object(_) => true,
160            TAtomic::Callable(callable) => {
161                callable.get_signature().is_none_or(callable::TCallableSignature::is_closure)
162            }
163            TAtomic::GenericParameter(parameter) => parameter.is_constrained_as_objecty(),
164            _ => false,
165        }
166    }
167
168    #[must_use]
169    pub fn is_this(&self) -> bool {
170        matches!(self, TAtomic::Object(TObject::Named(named_object)) if named_object.is_this())
171    }
172
173    #[must_use]
174    pub fn get_object_or_enum_name(&self) -> Option<Atom> {
175        match self {
176            TAtomic::Object(object) => match object {
177                TObject::Named(named_object) => Some(named_object.get_name()),
178                TObject::Enum(r#enum) => Some(r#enum.get_name()),
179                _ => None,
180            },
181            _ => None,
182        }
183    }
184
185    #[must_use]
186    pub fn get_all_object_names(&self) -> Vec<Atom> {
187        let mut object_names = vec![];
188
189        if let TAtomic::Object(object) = self {
190            match object {
191                TObject::Named(named_object) => object_names.push(named_object.get_name()),
192                TObject::Enum(r#enum) => object_names.push(r#enum.get_name()),
193                _ => {}
194            }
195        }
196
197        for intersection_type in self.get_intersection_types().unwrap_or_default() {
198            object_names.extend(intersection_type.get_all_object_names());
199        }
200
201        object_names
202    }
203
204    #[must_use]
205    pub fn is_stdclass(&self) -> bool {
206        matches!(&self, TAtomic::Object(object) if {
207            object.get_name().is_some_and(|name| name.eq_ignore_ascii_case("stdClass"))
208        })
209    }
210
211    #[must_use]
212    pub fn is_generator(&self) -> bool {
213        matches!(&self, TAtomic::Object(object) if {
214            object.get_name().is_some_and(|name| name.eq_ignore_ascii_case("Generator"))
215        })
216    }
217
218    #[must_use]
219    pub fn get_generator_parameters(&self) -> Option<(TUnion, TUnion, TUnion, TUnion)> {
220        let generator_parameters = 'parameters: {
221            let TAtomic::Object(TObject::Named(named_object)) = self else {
222                break 'parameters None;
223            };
224
225            let object_name = named_object.get_name_ref();
226            if !object_name.eq_ignore_ascii_case("Generator") {
227                break 'parameters None;
228            }
229
230            let parameters = named_object.get_type_parameters().unwrap_or_default();
231            match parameters.len() {
232                0 => Some((get_mixed(), get_mixed(), get_mixed(), get_mixed())),
233                1 => Some((get_mixed(), parameters[0].clone(), get_mixed(), get_mixed())),
234                2 => Some((parameters[0].clone(), parameters[1].clone(), get_mixed(), get_mixed())),
235                3 => Some((parameters[0].clone(), parameters[1].clone(), parameters[2].clone(), get_mixed())),
236                4 => Some((parameters[0].clone(), parameters[1].clone(), parameters[2].clone(), parameters[3].clone())),
237                _ => None,
238            }
239        };
240
241        if let Some(parameters) = generator_parameters {
242            return Some(parameters);
243        }
244
245        if let Some(intersection_types) = self.get_intersection_types() {
246            for intersection_type in intersection_types {
247                if let Some(parameters) = intersection_type.get_generator_parameters() {
248                    return Some(parameters);
249                }
250            }
251        }
252
253        None
254    }
255
256    #[must_use]
257    pub fn is_templated_as_object(&self) -> bool {
258        matches!(self, TAtomic::GenericParameter(parameter) if {
259            parameter.constraint.is_objecty() && parameter.intersection_types.is_none()
260        })
261    }
262
263    #[inline]
264    #[must_use]
265    pub const fn is_list(&self) -> bool {
266        matches!(self, TAtomic::Array(array) if array.is_list())
267    }
268
269    pub fn get_list_element_type(&self) -> Option<&TUnion> {
270        match self {
271            TAtomic::Array(array) => array.get_list().map(array::list::TList::get_element_type),
272            _ => None,
273        }
274    }
275
276    #[inline]
277    pub fn is_non_empty_list(&self) -> bool {
278        matches!(self, TAtomic::Array(array) if array.get_list().is_some_and(array::list::TList::is_non_empty))
279    }
280
281    #[inline]
282    #[must_use]
283    pub fn is_empty_array(&self) -> bool {
284        matches!(self, TAtomic::Array(array) if array.is_empty())
285    }
286
287    #[inline]
288    #[must_use]
289    pub const fn is_keyed_array(&self) -> bool {
290        matches!(self, TAtomic::Array(array) if array.is_keyed())
291    }
292
293    pub fn is_non_empty_keyed_array(&self) -> bool {
294        matches!(self, TAtomic::Array(array) if array.get_keyed().is_some_and(array::keyed::TKeyedArray::is_non_empty))
295    }
296
297    #[inline]
298    #[must_use]
299    pub const fn is_array(&self) -> bool {
300        matches!(self, TAtomic::Array(_))
301    }
302
303    #[inline]
304    #[must_use]
305    pub const fn is_iterable(&self) -> bool {
306        matches!(self, TAtomic::Iterable(_))
307    }
308
309    #[inline]
310    #[must_use]
311    pub fn extends_or_implements(&self, codebase: &CodebaseMetadata, interface: &str) -> bool {
312        let object = match self {
313            TAtomic::Object(object) => object,
314            TAtomic::GenericParameter(parameter) => {
315                if let Some(intersection_types) = parameter.get_intersection_types() {
316                    for intersection_type in intersection_types {
317                        if intersection_type.extends_or_implements(codebase, interface) {
318                            return true;
319                        }
320                    }
321                }
322
323                for constraint_atomic in parameter.constraint.types.as_ref() {
324                    if constraint_atomic.extends_or_implements(codebase, interface) {
325                        return true;
326                    }
327                }
328
329                return false;
330            }
331            TAtomic::Iterable(iterable) => {
332                if let Some(intersection_types) = iterable.get_intersection_types() {
333                    for intersection_type in intersection_types {
334                        if intersection_type.extends_or_implements(codebase, interface) {
335                            return true;
336                        }
337                    }
338                }
339
340                return false;
341            }
342            _ => return false,
343        };
344
345        if let Some(object_name) = object.get_name() {
346            if *object_name == interface {
347                return true;
348            }
349
350            if codebase.is_instance_of(object_name, interface) {
351                return true;
352            }
353        }
354
355        if let Some(intersection_types) = object.get_intersection_types() {
356            for intersection_type in intersection_types {
357                if intersection_type.extends_or_implements(codebase, interface) {
358                    return true;
359                }
360            }
361        }
362
363        false
364    }
365
366    #[inline]
367    #[must_use]
368    pub fn is_countable(&self, codebase: &CodebaseMetadata) -> bool {
369        match self {
370            TAtomic::Array(_) => true,
371            _ => self.extends_or_implements(codebase, "Countable"),
372        }
373    }
374
375    #[inline]
376    #[must_use]
377    pub fn could_be_countable(&self, codebase: &CodebaseMetadata) -> bool {
378        self.is_mixed() || self.is_countable(codebase)
379    }
380
381    #[inline]
382    #[must_use]
383    pub fn is_traversable(&self, codebase: &CodebaseMetadata) -> bool {
384        self.extends_or_implements(codebase, "Traversable")
385            || self.extends_or_implements(codebase, "Iterator")
386            || self.extends_or_implements(codebase, "IteratorAggregate")
387            || self.extends_or_implements(codebase, "Generator")
388    }
389
390    #[inline]
391    #[must_use]
392    pub fn is_array_or_traversable(&self, codebase: &CodebaseMetadata) -> bool {
393        match self {
394            TAtomic::Iterable(_) => true,
395            TAtomic::Array(_) => true,
396            _ => self.is_traversable(codebase),
397        }
398    }
399
400    #[inline]
401    #[must_use]
402    pub fn could_be_array_or_traversable(&self, codebase: &CodebaseMetadata) -> bool {
403        self.is_mixed() || self.is_array_or_traversable(codebase)
404    }
405
406    #[must_use]
407    pub fn is_non_empty_array(&self) -> bool {
408        matches!(self, TAtomic::Array(array) if array.is_non_empty())
409    }
410
411    pub fn to_array_key(&self) -> Option<ArrayKey> {
412        match self {
413            TAtomic::Scalar(TScalar::Integer(int)) => int.get_literal_value().map(ArrayKey::Integer),
414            TAtomic::Scalar(TScalar::String(TString { literal: Some(TStringLiteral::Value(value)), .. })) => {
415                Some(ArrayKey::String(*value))
416            }
417            _ => None,
418        }
419    }
420
421    #[must_use]
422    pub fn get_array_key_type(&self) -> Option<TUnion> {
423        match self {
424            TAtomic::Array(array) => array.get_key_type(),
425            _ => None,
426        }
427    }
428
429    #[must_use]
430    pub fn get_array_value_type(&self) -> Option<TUnion> {
431        match self {
432            TAtomic::Array(array) => array.get_value_type(),
433            _ => None,
434        }
435    }
436
437    #[inline]
438    #[must_use]
439    pub const fn is_generic_scalar(&self) -> bool {
440        matches!(self, TAtomic::Scalar(TScalar::Generic))
441    }
442
443    #[inline]
444    #[must_use]
445    pub const fn is_some_scalar(&self) -> bool {
446        matches!(self, TAtomic::Scalar(_))
447    }
448
449    #[inline]
450    #[must_use]
451    pub const fn is_boring_scalar(&self) -> bool {
452        matches!(
453            self,
454            TAtomic::Scalar(scalar) if scalar.is_boring()
455        )
456    }
457
458    #[inline]
459    #[must_use]
460    pub const fn is_any_string(&self) -> bool {
461        matches!(
462            self,
463            TAtomic::Scalar(scalar) if scalar.is_any_string()
464        )
465    }
466
467    #[inline]
468    #[must_use]
469    pub const fn is_string(&self) -> bool {
470        matches!(
471            self,
472            TAtomic::Scalar(scalar) if scalar.is_string()
473        )
474    }
475
476    #[inline]
477    #[must_use]
478    pub const fn is_string_of_literal_origin(&self) -> bool {
479        matches!(
480            self,
481            TAtomic::Scalar(scalar) if scalar.is_literal_origin_string()
482        )
483    }
484
485    #[inline]
486    #[must_use]
487    pub const fn is_non_empty_string(&self) -> bool {
488        matches!(
489            self,
490            TAtomic::Scalar(scalar) if scalar.is_non_empty_string()
491        )
492    }
493
494    #[inline]
495    #[must_use]
496    pub const fn is_known_literal_string(&self) -> bool {
497        matches!(
498            self,
499            TAtomic::Scalar(scalar) if scalar.is_known_literal_string()
500        )
501    }
502
503    #[inline]
504    #[must_use]
505    pub const fn is_literal_class_string(&self) -> bool {
506        matches!(
507            self,
508            TAtomic::Scalar(scalar) if scalar.is_literal_class_string()
509        )
510    }
511
512    #[must_use]
513    pub const fn is_string_subtype(&self) -> bool {
514        matches!(
515            self,
516            TAtomic::Scalar(scalar) if scalar.is_non_boring_string()
517        )
518    }
519
520    #[inline]
521    #[must_use]
522    pub const fn is_array_key(&self) -> bool {
523        matches!(
524            self,
525            TAtomic::Scalar(scalar) if scalar.is_array_key()
526        )
527    }
528
529    #[inline]
530    #[must_use]
531    pub const fn is_int(&self) -> bool {
532        matches!(
533            self,
534            TAtomic::Scalar(scalar) if scalar.is_int()
535        )
536    }
537
538    #[inline]
539    #[must_use]
540    pub const fn is_literal_int(&self) -> bool {
541        matches!(
542            self,
543            TAtomic::Scalar(scalar) if scalar.is_literal_int()
544        )
545    }
546
547    #[inline]
548    #[must_use]
549    pub const fn is_float(&self) -> bool {
550        matches!(
551            self,
552            TAtomic::Scalar(scalar) if scalar.is_float()
553        )
554    }
555
556    #[inline]
557    #[must_use]
558    pub const fn is_literal_float(&self) -> bool {
559        matches!(
560            self,
561            TAtomic::Scalar(scalar) if scalar.is_literal_float()
562        )
563    }
564
565    #[inline]
566    #[must_use]
567    pub const fn is_null(&self) -> bool {
568        matches!(self, TAtomic::Null)
569    }
570
571    #[inline]
572    #[must_use]
573    pub const fn is_void(&self) -> bool {
574        matches!(self, TAtomic::Void)
575    }
576
577    #[inline]
578    #[must_use]
579    pub const fn is_bool(&self) -> bool {
580        matches!(
581            self,
582            TAtomic::Scalar(scalar) if scalar.is_bool()
583        )
584    }
585
586    #[inline]
587    #[must_use]
588    pub const fn is_general_bool(&self) -> bool {
589        matches!(
590            self,
591            TAtomic::Scalar(scalar) if scalar.is_general_bool()
592        )
593    }
594
595    #[inline]
596    #[must_use]
597    pub const fn is_general_string(&self) -> bool {
598        matches!(
599            self,
600            TAtomic::Scalar(scalar) if scalar.is_general_string()
601        )
602    }
603
604    #[inline]
605    #[must_use]
606    pub const fn is_true(&self) -> bool {
607        matches!(
608            self,
609            TAtomic::Scalar(scalar) if scalar.is_true()
610        )
611    }
612
613    #[inline]
614    #[must_use]
615    pub const fn is_false(&self) -> bool {
616        matches!(
617            self,
618            TAtomic::Scalar(scalar) if scalar.is_false()
619        )
620    }
621
622    #[inline]
623    #[must_use]
624    pub const fn is_falsable(&self) -> bool {
625        matches!(
626            self,
627            TAtomic::Scalar(scalar) if scalar.is_false() || scalar.is_general_bool() || scalar.is_generic()
628        )
629    }
630
631    #[inline]
632    #[must_use]
633    pub const fn is_resource(&self) -> bool {
634        matches!(self, TAtomic::Resource(_))
635    }
636
637    #[inline]
638    #[must_use]
639    pub const fn is_closed_resource(&self) -> bool {
640        matches!(self, TAtomic::Resource(resource) if resource.is_closed())
641    }
642
643    #[inline]
644    #[must_use]
645    pub const fn is_open_resource(&self) -> bool {
646        matches!(self, TAtomic::Resource(resource) if resource.is_open())
647    }
648
649    #[inline]
650    #[must_use]
651    pub const fn is_literal(&self) -> bool {
652        match self {
653            TAtomic::Scalar(scalar) => scalar.is_literal_value(),
654            TAtomic::Null => true,
655            _ => false,
656        }
657    }
658
659    #[inline]
660    #[must_use]
661    pub const fn is_callable(&self) -> bool {
662        matches!(self, TAtomic::Callable(_))
663    }
664
665    #[inline]
666    #[must_use]
667    pub const fn is_conditional(&self) -> bool {
668        matches!(self, TAtomic::Conditional(_))
669    }
670
671    #[inline]
672    #[must_use]
673    pub const fn is_generic_parameter(&self) -> bool {
674        matches!(self, TAtomic::GenericParameter(_))
675    }
676
677    #[inline]
678    #[must_use]
679    pub const fn get_generic_parameter_name(&self) -> Option<Atom> {
680        match self {
681            TAtomic::GenericParameter(parameter) => Some(parameter.parameter_name),
682            _ => None,
683        }
684    }
685
686    /// Is this a type that could potentially be callable at runtime?
687    #[inline]
688    #[must_use]
689    pub const fn can_be_callable(&self) -> bool {
690        matches!(
691            self,
692            TAtomic::Callable(_)
693                | TAtomic::Scalar(TScalar::String(_))
694                | TAtomic::Array(TArray::List(_) | TArray::Keyed(_))
695                | TAtomic::Object(TObject::Named(_))
696        )
697    }
698
699    #[must_use]
700    pub fn is_truthy(&self) -> bool {
701        match &self {
702            TAtomic::Scalar(scalar) => scalar.is_truthy(),
703            TAtomic::Array(array) => array.is_truthy(),
704            TAtomic::Mixed(mixed) => mixed.is_truthy(),
705            TAtomic::Object(_) | TAtomic::Callable(_) => true,
706            _ => false,
707        }
708    }
709
710    #[must_use]
711    pub fn is_falsy(&self) -> bool {
712        match &self {
713            TAtomic::Scalar(scalar) if scalar.is_falsy() => true,
714            TAtomic::Array(array) if array.is_falsy() => true,
715            TAtomic::Mixed(mixed) if mixed.is_falsy() => true,
716            TAtomic::Null => true,
717            _ => false,
718        }
719    }
720
721    #[must_use]
722    pub fn is_array_accessible_with_string_key(&self) -> bool {
723        matches!(self, TAtomic::Array(array) if array.is_keyed())
724    }
725
726    #[must_use]
727    pub fn is_array_accessible_with_int_or_string_key(&self) -> bool {
728        matches!(self, TAtomic::Array(_))
729    }
730
731    #[must_use]
732    pub fn is_derived(&self) -> bool {
733        matches!(self, TAtomic::Derived(_))
734    }
735
736    #[must_use]
737    pub fn clone_without_intersection_types(&self) -> TAtomic {
738        let mut clone = self.clone();
739        match &mut clone {
740            TAtomic::Object(TObject::Named(named_object)) => {
741                named_object.intersection_types = None;
742            }
743            TAtomic::GenericParameter(parameter) => {
744                parameter.intersection_types = None;
745            }
746            TAtomic::Iterable(iterable) => {
747                iterable.intersection_types = None;
748            }
749            TAtomic::Reference(TReference::Symbol { intersection_types, .. }) => {
750                *intersection_types = None;
751            }
752            _ => {}
753        }
754
755        clone
756    }
757
758    pub fn remove_placeholders(&mut self) {
759        match self {
760            TAtomic::Array(array) => {
761                array.remove_placeholders();
762            }
763            TAtomic::Object(TObject::Named(named_object)) => {
764                let name = named_object.get_name();
765                if let Some(type_parameters) = named_object.get_type_parameters_mut() {
766                    if name.eq_ignore_ascii_case("Traversable") {
767                        let has_kv_pair = type_parameters.len() == 2;
768
769                        if let Some(key_or_value_param) = type_parameters.get_mut(0)
770                            && let TAtomic::Placeholder = key_or_value_param.get_single()
771                        {
772                            *key_or_value_param = if has_kv_pair { get_arraykey() } else { get_mixed() };
773                        }
774
775                        if has_kv_pair
776                            && let Some(value_param) = type_parameters.get_mut(1)
777                            && let TAtomic::Placeholder = value_param.get_single()
778                        {
779                            *value_param = get_mixed();
780                        }
781                    } else {
782                        for type_param in type_parameters {
783                            if let TAtomic::Placeholder = type_param.get_single() {
784                                *type_param = get_mixed();
785                            }
786                        }
787                    }
788                }
789            }
790            _ => {}
791        }
792    }
793
794    #[must_use]
795    pub fn get_literal_string_value(&self) -> Option<&str> {
796        match self {
797            TAtomic::Scalar(scalar) => scalar.get_known_literal_string_value(),
798            _ => None,
799        }
800    }
801
802    #[must_use]
803    pub fn get_class_string_value(&self) -> Option<Atom> {
804        match self {
805            TAtomic::Scalar(scalar) => scalar.get_literal_class_string_value(),
806            _ => None,
807        }
808    }
809
810    #[must_use]
811    pub fn get_integer(&self) -> Option<TInteger> {
812        match self {
813            TAtomic::Scalar(TScalar::Integer(integer)) => Some(*integer),
814            _ => None,
815        }
816    }
817
818    #[must_use]
819    pub fn get_literal_int_value(&self) -> Option<i64> {
820        match self {
821            TAtomic::Scalar(scalar) => scalar.get_literal_int_value(),
822            _ => None,
823        }
824    }
825
826    #[must_use]
827    pub fn get_maximum_int_value(&self) -> Option<i64> {
828        match self {
829            TAtomic::Scalar(scalar) => scalar.get_maximum_int_value(),
830            _ => None,
831        }
832    }
833
834    #[must_use]
835    pub fn get_minimum_int_value(&self) -> Option<i64> {
836        match self {
837            TAtomic::Scalar(scalar) => scalar.get_minimum_int_value(),
838            _ => None,
839        }
840    }
841
842    #[must_use]
843    pub fn get_literal_float_value(&self) -> Option<f64> {
844        match self {
845            TAtomic::Scalar(scalar) => scalar.get_literal_float_value(),
846            _ => None,
847        }
848    }
849}
850
851impl TType for TAtomic {
852    fn get_child_nodes(&self) -> Vec<TypeRef<'_>> {
853        match self {
854            TAtomic::Array(ttype) => ttype.get_child_nodes(),
855            TAtomic::Callable(ttype) => ttype.get_child_nodes(),
856            TAtomic::Conditional(ttype) => ttype.get_child_nodes(),
857            TAtomic::Derived(ttype) => ttype.get_child_nodes(),
858            TAtomic::GenericParameter(ttype) => ttype.get_child_nodes(),
859            TAtomic::Iterable(ttype) => ttype.get_child_nodes(),
860            TAtomic::Mixed(ttype) => ttype.get_child_nodes(),
861            TAtomic::Object(ttype) => ttype.get_child_nodes(),
862            TAtomic::Reference(ttype) => ttype.get_child_nodes(),
863            TAtomic::Resource(ttype) => ttype.get_child_nodes(),
864            TAtomic::Scalar(ttype) => ttype.get_child_nodes(),
865            TAtomic::Alias(ttype) => ttype.get_child_nodes(),
866            _ => vec![],
867        }
868    }
869
870    fn can_be_intersected(&self) -> bool {
871        match self {
872            TAtomic::Object(ttype) => ttype.can_be_intersected(),
873            TAtomic::Reference(ttype) => ttype.can_be_intersected(),
874            TAtomic::GenericParameter(ttype) => ttype.can_be_intersected(),
875            TAtomic::Iterable(ttype) => ttype.can_be_intersected(),
876            TAtomic::Array(ttype) => ttype.can_be_intersected(),
877            TAtomic::Callable(ttype) => ttype.can_be_intersected(),
878            TAtomic::Mixed(ttype) => ttype.can_be_intersected(),
879            TAtomic::Scalar(ttype) => ttype.can_be_intersected(),
880            TAtomic::Resource(ttype) => ttype.can_be_intersected(),
881            TAtomic::Conditional(ttype) => ttype.can_be_intersected(),
882            TAtomic::Derived(ttype) => ttype.can_be_intersected(),
883            TAtomic::Alias(ttype) => ttype.can_be_intersected(),
884            _ => false,
885        }
886    }
887
888    fn get_intersection_types(&self) -> Option<&[TAtomic]> {
889        match self {
890            TAtomic::Object(ttype) => ttype.get_intersection_types(),
891            TAtomic::Reference(ttype) => ttype.get_intersection_types(),
892            TAtomic::GenericParameter(ttype) => ttype.get_intersection_types(),
893            TAtomic::Iterable(ttype) => ttype.get_intersection_types(),
894            TAtomic::Array(ttype) => ttype.get_intersection_types(),
895            TAtomic::Callable(ttype) => ttype.get_intersection_types(),
896            TAtomic::Mixed(ttype) => ttype.get_intersection_types(),
897            TAtomic::Scalar(ttype) => ttype.get_intersection_types(),
898            TAtomic::Resource(ttype) => ttype.get_intersection_types(),
899            TAtomic::Conditional(ttype) => ttype.get_intersection_types(),
900            TAtomic::Derived(ttype) => ttype.get_intersection_types(),
901            TAtomic::Alias(ttype) => ttype.get_intersection_types(),
902            _ => None,
903        }
904    }
905
906    fn get_intersection_types_mut(&mut self) -> Option<&mut Vec<TAtomic>> {
907        match self {
908            TAtomic::Object(ttype) => ttype.get_intersection_types_mut(),
909            TAtomic::Reference(ttype) => ttype.get_intersection_types_mut(),
910            TAtomic::GenericParameter(ttype) => ttype.get_intersection_types_mut(),
911            TAtomic::Iterable(ttype) => ttype.get_intersection_types_mut(),
912            TAtomic::Array(ttype) => ttype.get_intersection_types_mut(),
913            TAtomic::Callable(ttype) => ttype.get_intersection_types_mut(),
914            TAtomic::Mixed(ttype) => ttype.get_intersection_types_mut(),
915            TAtomic::Scalar(ttype) => ttype.get_intersection_types_mut(),
916            TAtomic::Resource(ttype) => ttype.get_intersection_types_mut(),
917            TAtomic::Conditional(ttype) => ttype.get_intersection_types_mut(),
918            TAtomic::Derived(ttype) => ttype.get_intersection_types_mut(),
919            TAtomic::Alias(ttype) => ttype.get_intersection_types_mut(),
920            _ => None,
921        }
922    }
923
924    fn has_intersection_types(&self) -> bool {
925        match self {
926            TAtomic::Object(ttype) => ttype.has_intersection_types(),
927            TAtomic::Reference(ttype) => ttype.has_intersection_types(),
928            TAtomic::GenericParameter(ttype) => ttype.has_intersection_types(),
929            TAtomic::Iterable(ttype) => ttype.has_intersection_types(),
930            TAtomic::Array(ttype) => ttype.has_intersection_types(),
931            TAtomic::Callable(ttype) => ttype.has_intersection_types(),
932            TAtomic::Mixed(ttype) => ttype.has_intersection_types(),
933            TAtomic::Scalar(ttype) => ttype.has_intersection_types(),
934            TAtomic::Resource(ttype) => ttype.has_intersection_types(),
935            TAtomic::Conditional(ttype) => ttype.has_intersection_types(),
936            TAtomic::Derived(ttype) => ttype.has_intersection_types(),
937            TAtomic::Alias(ttype) => ttype.has_intersection_types(),
938            _ => false,
939        }
940    }
941
942    fn add_intersection_type(&mut self, intersection_type: TAtomic) -> bool {
943        match self {
944            TAtomic::Object(ttype) => ttype.add_intersection_type(intersection_type),
945            TAtomic::Reference(ttype) => ttype.add_intersection_type(intersection_type),
946            TAtomic::GenericParameter(ttype) => ttype.add_intersection_type(intersection_type),
947            TAtomic::Iterable(ttype) => ttype.add_intersection_type(intersection_type),
948            TAtomic::Array(ttype) => ttype.add_intersection_type(intersection_type),
949            TAtomic::Callable(ttype) => ttype.add_intersection_type(intersection_type),
950            TAtomic::Mixed(ttype) => ttype.add_intersection_type(intersection_type),
951            TAtomic::Scalar(ttype) => ttype.add_intersection_type(intersection_type),
952            TAtomic::Resource(ttype) => ttype.add_intersection_type(intersection_type),
953            TAtomic::Conditional(ttype) => ttype.add_intersection_type(intersection_type),
954            TAtomic::Derived(ttype) => ttype.add_intersection_type(intersection_type),
955            TAtomic::Alias(ttype) => ttype.add_intersection_type(intersection_type),
956            _ => false,
957        }
958    }
959
960    fn needs_population(&self) -> bool {
961        if let Some(intersection) = self.get_intersection_types() {
962            for intersection_type in intersection {
963                if intersection_type.needs_population() {
964                    return true;
965                }
966            }
967        }
968
969        match self {
970            TAtomic::Object(ttype) => ttype.needs_population(),
971            TAtomic::Reference(ttype) => ttype.needs_population(),
972            TAtomic::GenericParameter(ttype) => ttype.needs_population(),
973            TAtomic::Iterable(ttype) => ttype.needs_population(),
974            TAtomic::Array(ttype) => ttype.needs_population(),
975            TAtomic::Callable(ttype) => ttype.needs_population(),
976            TAtomic::Conditional(ttype) => ttype.needs_population(),
977            TAtomic::Derived(ttype) => ttype.needs_population(),
978            TAtomic::Scalar(ttype) => ttype.needs_population(),
979            TAtomic::Mixed(ttype) => ttype.needs_population(),
980            TAtomic::Resource(ttype) => ttype.needs_population(),
981            TAtomic::Alias(ttype) => ttype.needs_population(),
982            _ => false,
983        }
984    }
985
986    fn is_expandable(&self) -> bool {
987        if let Some(intersection) = self.get_intersection_types() {
988            for intersection_type in intersection {
989                if intersection_type.is_expandable() {
990                    return true;
991                }
992            }
993        }
994
995        match self {
996            TAtomic::Object(ttype) => ttype.is_expandable(),
997            TAtomic::Reference(ttype) => ttype.is_expandable(),
998            TAtomic::GenericParameter(ttype) => ttype.is_expandable(),
999            TAtomic::Iterable(ttype) => ttype.is_expandable(),
1000            TAtomic::Array(ttype) => ttype.is_expandable(),
1001            TAtomic::Callable(ttype) => ttype.is_expandable(),
1002            TAtomic::Conditional(ttype) => ttype.is_expandable(),
1003            TAtomic::Derived(ttype) => ttype.is_expandable(),
1004            TAtomic::Scalar(ttype) => ttype.is_expandable(),
1005            TAtomic::Mixed(ttype) => ttype.is_expandable(),
1006            TAtomic::Resource(ttype) => ttype.is_expandable(),
1007            TAtomic::Alias(ttype) => ttype.is_expandable(),
1008            _ => false,
1009        }
1010    }
1011
1012    fn is_complex(&self) -> bool {
1013        if let Some(intersection) = self.get_intersection_types() {
1014            for intersection_type in intersection {
1015                if intersection_type.is_complex() {
1016                    return true;
1017                }
1018            }
1019        }
1020
1021        match self {
1022            TAtomic::Object(ttype) => ttype.is_complex(),
1023            TAtomic::Reference(ttype) => ttype.is_complex(),
1024            TAtomic::GenericParameter(ttype) => ttype.is_complex(),
1025            TAtomic::Iterable(ttype) => ttype.is_complex(),
1026            TAtomic::Array(ttype) => ttype.is_complex(),
1027            TAtomic::Callable(ttype) => ttype.is_complex(),
1028            TAtomic::Conditional(ttype) => ttype.is_complex(),
1029            TAtomic::Derived(ttype) => ttype.is_complex(),
1030            TAtomic::Scalar(ttype) => ttype.is_complex(),
1031            TAtomic::Mixed(ttype) => ttype.is_complex(),
1032            TAtomic::Resource(ttype) => ttype.is_complex(),
1033            TAtomic::Alias(ttype) => ttype.is_complex(),
1034            _ => false,
1035        }
1036    }
1037
1038    fn get_id(&self) -> Atom {
1039        match self {
1040            TAtomic::Scalar(scalar) => scalar.get_id(),
1041            TAtomic::Array(array) => array.get_id(),
1042            TAtomic::Callable(callable) => callable.get_id(),
1043            TAtomic::Object(object) => object.get_id(),
1044            TAtomic::Reference(reference) => reference.get_id(),
1045            TAtomic::Mixed(mixed) => mixed.get_id(),
1046            TAtomic::Resource(resource) => resource.get_id(),
1047            TAtomic::Iterable(iterable) => iterable.get_id(),
1048            TAtomic::GenericParameter(parameter) => parameter.get_id(),
1049            TAtomic::Conditional(conditional) => conditional.get_id(),
1050            TAtomic::Alias(alias) => alias.get_id(),
1051            TAtomic::Derived(derived) => derived.get_id(),
1052            TAtomic::Variable(name) => *name,
1053            TAtomic::Never => atom("never"),
1054            TAtomic::Null => atom("null"),
1055            TAtomic::Void => atom("void"),
1056            TAtomic::Placeholder => atom("_"),
1057        }
1058    }
1059
1060    fn get_pretty_id_with_indent(&self, indent: usize) -> Atom {
1061        match self {
1062            TAtomic::Scalar(scalar) => scalar.get_pretty_id_with_indent(indent),
1063            TAtomic::Array(array) => array.get_pretty_id_with_indent(indent),
1064            TAtomic::Callable(callable) => callable.get_pretty_id_with_indent(indent),
1065            TAtomic::Object(object) => object.get_pretty_id_with_indent(indent),
1066            TAtomic::Reference(reference) => reference.get_pretty_id_with_indent(indent),
1067            TAtomic::Mixed(mixed) => mixed.get_pretty_id_with_indent(indent),
1068            TAtomic::Resource(resource) => resource.get_pretty_id_with_indent(indent),
1069            TAtomic::Iterable(iterable) => iterable.get_pretty_id_with_indent(indent),
1070            TAtomic::GenericParameter(parameter) => parameter.get_pretty_id_with_indent(indent),
1071            TAtomic::Conditional(conditional) => conditional.get_pretty_id_with_indent(indent),
1072            TAtomic::Alias(alias) => alias.get_pretty_id_with_indent(indent),
1073            TAtomic::Derived(derived) => derived.get_pretty_id_with_indent(indent),
1074            TAtomic::Variable(name) => *name,
1075            TAtomic::Never => atom("never"),
1076            TAtomic::Null => atom("null"),
1077            TAtomic::Void => atom("void"),
1078            TAtomic::Placeholder => atom("_"),
1079        }
1080    }
1081}
1082
1083pub fn populate_atomic_type(
1084    unpopulated_atomic: &mut TAtomic,
1085    codebase_symbols: &Symbols,
1086    reference_source: Option<&ReferenceSource>,
1087    symbol_references: &mut SymbolReferences,
1088    force: bool,
1089) {
1090    match unpopulated_atomic {
1091        TAtomic::Array(array) => match array {
1092            TArray::List(list) => {
1093                populate_union_type(
1094                    list.element_type.as_mut(),
1095                    codebase_symbols,
1096                    reference_source,
1097                    symbol_references,
1098                    force,
1099                );
1100
1101                if let Some(known_elements) = list.known_elements.as_mut() {
1102                    for (_, element_type) in known_elements.values_mut() {
1103                        populate_union_type(element_type, codebase_symbols, reference_source, symbol_references, force);
1104                    }
1105                }
1106            }
1107            TArray::Keyed(keyed_array) => {
1108                if let Some(known_items) = keyed_array.known_items.as_mut() {
1109                    for (_, item_type) in known_items.values_mut() {
1110                        populate_union_type(item_type, codebase_symbols, reference_source, symbol_references, force);
1111                    }
1112                }
1113
1114                if let Some(parameters) = &mut keyed_array.parameters {
1115                    populate_union_type(
1116                        parameters.0.as_mut(),
1117                        codebase_symbols,
1118                        reference_source,
1119                        symbol_references,
1120                        force,
1121                    );
1122
1123                    populate_union_type(
1124                        parameters.1.as_mut(),
1125                        codebase_symbols,
1126                        reference_source,
1127                        symbol_references,
1128                        force,
1129                    );
1130                }
1131            }
1132        },
1133        TAtomic::Callable(TCallable::Signature(signature)) => {
1134            if let Some(return_type) = signature.get_return_type_mut() {
1135                populate_union_type(return_type, codebase_symbols, reference_source, symbol_references, force);
1136            }
1137
1138            for param in signature.get_parameters_mut() {
1139                if let Some(param_type) = param.get_type_signature_mut() {
1140                    populate_union_type(param_type, codebase_symbols, reference_source, symbol_references, force);
1141                }
1142            }
1143        }
1144        TAtomic::Object(TObject::Named(named_object)) => {
1145            let name = named_object.get_name();
1146
1147            if !named_object.is_intersection()
1148                && !named_object.has_type_parameters()
1149                && codebase_symbols.contains_enum(&name)
1150            {
1151                *unpopulated_atomic = TAtomic::Object(TObject::new_enum(name));
1152            } else {
1153                if let Some(type_parameters) = named_object.get_type_parameters_mut() {
1154                    for parameter in type_parameters {
1155                        populate_union_type(parameter, codebase_symbols, reference_source, symbol_references, force);
1156                    }
1157                }
1158
1159                if let Some(intersection_types) = named_object.get_intersection_types_mut() {
1160                    for intersection_type in intersection_types {
1161                        populate_atomic_type(
1162                            intersection_type,
1163                            codebase_symbols,
1164                            reference_source,
1165                            symbol_references,
1166                            force,
1167                        );
1168                    }
1169                }
1170            }
1171
1172            if let Some(reference_source) = reference_source {
1173                match reference_source {
1174                    ReferenceSource::Symbol(in_signature, a) => {
1175                        symbol_references.add_symbol_reference_to_symbol(*a, name, *in_signature);
1176                    }
1177                    ReferenceSource::ClassLikeMember(in_signature, a, b) => {
1178                        symbol_references.add_class_member_reference_to_symbol((*a, *b), name, *in_signature);
1179                    }
1180                }
1181            }
1182        }
1183        TAtomic::Object(TObject::WithProperties(keyed_array)) => {
1184            for (_, item_type) in keyed_array.known_properties.values_mut() {
1185                populate_union_type(item_type, codebase_symbols, reference_source, symbol_references, force);
1186            }
1187        }
1188        TAtomic::Iterable(iterable) => {
1189            populate_union_type(
1190                iterable.get_key_type_mut(),
1191                codebase_symbols,
1192                reference_source,
1193                symbol_references,
1194                force,
1195            );
1196
1197            populate_union_type(
1198                iterable.get_value_type_mut(),
1199                codebase_symbols,
1200                reference_source,
1201                symbol_references,
1202                force,
1203            );
1204
1205            if let Some(intersection_types) = iterable.get_intersection_types_mut() {
1206                for intersection_type in intersection_types {
1207                    populate_atomic_type(
1208                        intersection_type,
1209                        codebase_symbols,
1210                        reference_source,
1211                        symbol_references,
1212                        force,
1213                    );
1214                }
1215            }
1216        }
1217        TAtomic::Reference(reference) => match reference {
1218            TReference::Symbol { name, parameters, intersection_types } => {
1219                if let Some(parameters) = parameters {
1220                    for parameter in parameters {
1221                        populate_union_type(parameter, codebase_symbols, reference_source, symbol_references, force);
1222                    }
1223                }
1224
1225                if let Some(reference_source) = reference_source {
1226                    match reference_source {
1227                        ReferenceSource::Symbol(in_signature, a) => {
1228                            symbol_references.add_symbol_reference_to_symbol(*a, *name, *in_signature);
1229                        }
1230                        ReferenceSource::ClassLikeMember(in_signature, a, b) => {
1231                            symbol_references.add_class_member_reference_to_symbol((*a, *b), *name, *in_signature);
1232                        }
1233                    }
1234                }
1235
1236                if let Some(symbol_kind) = codebase_symbols.get_kind(&ascii_lowercase_atom(name)) {
1237                    if symbol_kind == SymbolKind::Enum {
1238                        *unpopulated_atomic = TAtomic::Object(TObject::new_enum(*name));
1239                    } else {
1240                        let intersection_types = intersection_types.take().map(|intersection_types| {
1241                            intersection_types
1242                                .into_iter()
1243                                .map(|mut intersection_type| {
1244                                    populate_atomic_type(
1245                                        &mut intersection_type,
1246                                        codebase_symbols,
1247                                        reference_source,
1248                                        symbol_references,
1249                                        force,
1250                                    );
1251
1252                                    intersection_type
1253                                })
1254                                .collect::<Vec<_>>()
1255                        });
1256
1257                        let mut named_object = TNamedObject::new(*name).with_type_parameters(parameters.clone());
1258                        if let Some(intersection_types) = intersection_types {
1259                            for intersection_type in intersection_types {
1260                                named_object.add_intersection_type(intersection_type);
1261                            }
1262                        }
1263
1264                        *unpopulated_atomic = TAtomic::Object(TObject::Named(named_object));
1265                    }
1266                }
1267            }
1268            TReference::Member { class_like_name, member_selector } => {
1269                if let TReferenceMemberSelector::Identifier(member_name) = member_selector
1270                    && let Some(reference_source) = reference_source
1271                {
1272                    match reference_source {
1273                        ReferenceSource::Symbol(in_signature, a) => symbol_references
1274                            .add_symbol_reference_to_class_member(*a, (*class_like_name, *member_name), *in_signature),
1275                        ReferenceSource::ClassLikeMember(in_signature, a, b) => symbol_references
1276                            .add_class_member_reference_to_class_member(
1277                                (*a, *b),
1278                                (*class_like_name, *member_name),
1279                                *in_signature,
1280                            ),
1281                    }
1282                }
1283            }
1284        },
1285        TAtomic::GenericParameter(TGenericParameter { constraint, intersection_types, .. }) => {
1286            populate_union_type(constraint.as_mut(), codebase_symbols, reference_source, symbol_references, force);
1287
1288            if let Some(intersection_types) = intersection_types.as_mut() {
1289                for intersection_type in intersection_types {
1290                    populate_atomic_type(
1291                        intersection_type,
1292                        codebase_symbols,
1293                        reference_source,
1294                        symbol_references,
1295                        force,
1296                    );
1297                }
1298            }
1299        }
1300        TAtomic::Scalar(TScalar::ClassLikeString(
1301            TClassLikeString::OfType { constraint, .. } | TClassLikeString::Generic { constraint, .. },
1302        )) => {
1303            populate_atomic_type(constraint.as_mut(), codebase_symbols, reference_source, symbol_references, force);
1304        }
1305        TAtomic::Conditional(conditional) => {
1306            populate_union_type(
1307                conditional.get_subject_mut(),
1308                codebase_symbols,
1309                reference_source,
1310                symbol_references,
1311                force,
1312            );
1313
1314            populate_union_type(
1315                conditional.get_target_mut(),
1316                codebase_symbols,
1317                reference_source,
1318                symbol_references,
1319                force,
1320            );
1321
1322            populate_union_type(
1323                conditional.get_then_mut(),
1324                codebase_symbols,
1325                reference_source,
1326                symbol_references,
1327                force,
1328            );
1329
1330            populate_union_type(
1331                conditional.get_otherwise_mut(),
1332                codebase_symbols,
1333                reference_source,
1334                symbol_references,
1335                force,
1336            );
1337        }
1338        TAtomic::Derived(derived) => match derived {
1339            TDerived::IntMask(int_mask) => {
1340                for value in int_mask.get_values_mut() {
1341                    populate_union_type(value, codebase_symbols, reference_source, symbol_references, force);
1342                }
1343            }
1344            TDerived::IndexAccess(index_access) => {
1345                populate_union_type(
1346                    index_access.get_target_type_mut(),
1347                    codebase_symbols,
1348                    reference_source,
1349                    symbol_references,
1350                    force,
1351                );
1352                populate_union_type(
1353                    index_access.get_index_type_mut(),
1354                    codebase_symbols,
1355                    reference_source,
1356                    symbol_references,
1357                    force,
1358                );
1359            }
1360            _ => {
1361                if let Some(target) = derived.get_target_type_mut() {
1362                    populate_union_type(target, codebase_symbols, reference_source, symbol_references, force);
1363                }
1364            }
1365        },
1366        _ => {}
1367    }
1368}