Skip to main content

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    #[inline]
270    #[must_use]
271    pub fn is_vanilla_array(&self) -> bool {
272        matches!(self, TAtomic::Array(array) if array.is_vanilla())
273    }
274
275    pub fn get_list_element_type(&self) -> Option<&TUnion> {
276        match self {
277            TAtomic::Array(array) => array.get_list().map(array::list::TList::get_element_type),
278            _ => None,
279        }
280    }
281
282    #[inline]
283    pub fn is_non_empty_list(&self) -> bool {
284        matches!(self, TAtomic::Array(array) if array.get_list().is_some_and(array::list::TList::is_non_empty))
285    }
286
287    #[inline]
288    #[must_use]
289    pub fn is_empty_array(&self) -> bool {
290        matches!(self, TAtomic::Array(array) if array.is_empty())
291    }
292
293    #[inline]
294    #[must_use]
295    pub const fn is_keyed_array(&self) -> bool {
296        matches!(self, TAtomic::Array(array) if array.is_keyed())
297    }
298
299    pub fn is_non_empty_keyed_array(&self) -> bool {
300        matches!(self, TAtomic::Array(array) if array.get_keyed().is_some_and(array::keyed::TKeyedArray::is_non_empty))
301    }
302
303    #[inline]
304    #[must_use]
305    pub const fn is_array(&self) -> bool {
306        matches!(self, TAtomic::Array(_))
307    }
308
309    #[inline]
310    #[must_use]
311    pub const fn is_iterable(&self) -> bool {
312        matches!(self, TAtomic::Iterable(_))
313    }
314
315    #[inline]
316    #[must_use]
317    pub fn extends_or_implements(&self, codebase: &CodebaseMetadata, interface: &str) -> bool {
318        let object = match self {
319            TAtomic::Object(object) => object,
320            TAtomic::GenericParameter(parameter) => {
321                if let Some(intersection_types) = parameter.get_intersection_types() {
322                    for intersection_type in intersection_types {
323                        if intersection_type.extends_or_implements(codebase, interface) {
324                            return true;
325                        }
326                    }
327                }
328
329                for constraint_atomic in parameter.constraint.types.as_ref() {
330                    if constraint_atomic.extends_or_implements(codebase, interface) {
331                        return true;
332                    }
333                }
334
335                return false;
336            }
337            TAtomic::Iterable(iterable) => {
338                if let Some(intersection_types) = iterable.get_intersection_types() {
339                    for intersection_type in intersection_types {
340                        if intersection_type.extends_or_implements(codebase, interface) {
341                            return true;
342                        }
343                    }
344                }
345
346                return false;
347            }
348            _ => return false,
349        };
350
351        if let Some(object_name) = object.get_name() {
352            if *object_name == interface {
353                return true;
354            }
355
356            if codebase.is_instance_of(object_name, interface) {
357                return true;
358            }
359        }
360
361        if let Some(intersection_types) = object.get_intersection_types() {
362            for intersection_type in intersection_types {
363                if intersection_type.extends_or_implements(codebase, interface) {
364                    return true;
365                }
366            }
367        }
368
369        false
370    }
371
372    #[inline]
373    #[must_use]
374    pub fn is_countable(&self, codebase: &CodebaseMetadata) -> bool {
375        match self {
376            TAtomic::Array(_) => true,
377            _ => self.extends_or_implements(codebase, "Countable"),
378        }
379    }
380
381    #[inline]
382    #[must_use]
383    pub fn could_be_countable(&self, codebase: &CodebaseMetadata) -> bool {
384        self.is_mixed() || self.is_countable(codebase)
385    }
386
387    #[inline]
388    #[must_use]
389    pub fn is_traversable(&self, codebase: &CodebaseMetadata) -> bool {
390        self.extends_or_implements(codebase, "Traversable")
391            || self.extends_or_implements(codebase, "Iterator")
392            || self.extends_or_implements(codebase, "IteratorAggregate")
393            || self.extends_or_implements(codebase, "Generator")
394    }
395
396    #[inline]
397    #[must_use]
398    pub fn is_array_or_traversable(&self, codebase: &CodebaseMetadata) -> bool {
399        match self {
400            TAtomic::Iterable(_) => true,
401            TAtomic::Array(_) => true,
402            _ => self.is_traversable(codebase),
403        }
404    }
405
406    #[inline]
407    #[must_use]
408    pub fn could_be_array_or_traversable(&self, codebase: &CodebaseMetadata) -> bool {
409        self.is_mixed() || self.is_array_or_traversable(codebase)
410    }
411
412    #[must_use]
413    pub fn is_non_empty_array(&self) -> bool {
414        matches!(self, TAtomic::Array(array) if array.is_non_empty())
415    }
416
417    pub fn to_array_key(&self) -> Option<ArrayKey> {
418        match self {
419            TAtomic::Scalar(TScalar::Integer(int)) => int.get_literal_value().map(ArrayKey::Integer),
420            TAtomic::Scalar(TScalar::String(TString { literal: Some(TStringLiteral::Value(value)), .. })) => {
421                Some(ArrayKey::String(*value))
422            }
423            _ => None,
424        }
425    }
426
427    #[must_use]
428    pub fn get_array_key_type(&self) -> Option<TUnion> {
429        match self {
430            TAtomic::Array(array) => array.get_key_type(),
431            _ => None,
432        }
433    }
434
435    #[must_use]
436    pub fn get_array_value_type(&self) -> Option<TUnion> {
437        match self {
438            TAtomic::Array(array) => array.get_value_type(),
439            _ => None,
440        }
441    }
442
443    #[inline]
444    #[must_use]
445    pub const fn is_generic_scalar(&self) -> bool {
446        matches!(self, TAtomic::Scalar(TScalar::Generic))
447    }
448
449    #[inline]
450    #[must_use]
451    pub const fn is_some_scalar(&self) -> bool {
452        matches!(self, TAtomic::Scalar(_))
453    }
454
455    #[inline]
456    #[must_use]
457    pub const fn is_boring_scalar(&self) -> bool {
458        matches!(
459            self,
460            TAtomic::Scalar(scalar) if scalar.is_boring()
461        )
462    }
463
464    #[inline]
465    #[must_use]
466    pub const fn is_any_string(&self) -> bool {
467        matches!(
468            self,
469            TAtomic::Scalar(scalar) if scalar.is_any_string()
470        )
471    }
472
473    #[inline]
474    #[must_use]
475    pub const fn is_string(&self) -> bool {
476        matches!(
477            self,
478            TAtomic::Scalar(scalar) if scalar.is_string()
479        )
480    }
481
482    #[inline]
483    #[must_use]
484    pub const fn is_string_of_literal_origin(&self) -> bool {
485        matches!(
486            self,
487            TAtomic::Scalar(scalar) if scalar.is_literal_origin_string()
488        )
489    }
490
491    #[inline]
492    #[must_use]
493    pub const fn is_non_empty_string(&self) -> bool {
494        matches!(
495            self,
496            TAtomic::Scalar(scalar) if scalar.is_non_empty_string()
497        )
498    }
499
500    #[inline]
501    #[must_use]
502    pub const fn is_known_literal_string(&self) -> bool {
503        matches!(
504            self,
505            TAtomic::Scalar(scalar) if scalar.is_known_literal_string()
506        )
507    }
508
509    #[inline]
510    #[must_use]
511    pub const fn is_literal_class_string(&self) -> bool {
512        matches!(
513            self,
514            TAtomic::Scalar(scalar) if scalar.is_literal_class_string()
515        )
516    }
517
518    #[must_use]
519    pub const fn is_string_subtype(&self) -> bool {
520        matches!(
521            self,
522            TAtomic::Scalar(scalar) if scalar.is_non_boring_string()
523        )
524    }
525
526    #[inline]
527    #[must_use]
528    pub const fn is_array_key(&self) -> bool {
529        matches!(
530            self,
531            TAtomic::Scalar(scalar) if scalar.is_array_key()
532        )
533    }
534
535    #[inline]
536    #[must_use]
537    pub const fn is_int(&self) -> bool {
538        matches!(
539            self,
540            TAtomic::Scalar(scalar) if scalar.is_int()
541        )
542    }
543
544    #[inline]
545    #[must_use]
546    pub const fn is_literal_int(&self) -> bool {
547        matches!(
548            self,
549            TAtomic::Scalar(scalar) if scalar.is_literal_int()
550        )
551    }
552
553    #[inline]
554    #[must_use]
555    pub const fn is_float(&self) -> bool {
556        matches!(
557            self,
558            TAtomic::Scalar(scalar) if scalar.is_float()
559        )
560    }
561
562    #[inline]
563    #[must_use]
564    pub const fn is_literal_float(&self) -> bool {
565        matches!(
566            self,
567            TAtomic::Scalar(scalar) if scalar.is_literal_float()
568        )
569    }
570
571    #[inline]
572    #[must_use]
573    pub const fn is_null(&self) -> bool {
574        matches!(self, TAtomic::Null)
575    }
576
577    #[inline]
578    #[must_use]
579    pub const fn is_void(&self) -> bool {
580        matches!(self, TAtomic::Void)
581    }
582
583    #[inline]
584    #[must_use]
585    pub const fn is_bool(&self) -> bool {
586        matches!(
587            self,
588            TAtomic::Scalar(scalar) if scalar.is_bool()
589        )
590    }
591
592    #[inline]
593    #[must_use]
594    pub const fn is_general_bool(&self) -> bool {
595        matches!(
596            self,
597            TAtomic::Scalar(scalar) if scalar.is_general_bool()
598        )
599    }
600
601    #[inline]
602    #[must_use]
603    pub const fn is_general_string(&self) -> bool {
604        matches!(
605            self,
606            TAtomic::Scalar(scalar) if scalar.is_general_string()
607        )
608    }
609
610    #[inline]
611    #[must_use]
612    pub const fn is_true(&self) -> bool {
613        matches!(
614            self,
615            TAtomic::Scalar(scalar) if scalar.is_true()
616        )
617    }
618
619    #[inline]
620    #[must_use]
621    pub const fn is_false(&self) -> bool {
622        matches!(
623            self,
624            TAtomic::Scalar(scalar) if scalar.is_false()
625        )
626    }
627
628    #[inline]
629    #[must_use]
630    pub const fn is_falsable(&self) -> bool {
631        matches!(
632            self,
633            TAtomic::Scalar(scalar) if scalar.is_false() || scalar.is_general_bool() || scalar.is_generic()
634        )
635    }
636
637    #[inline]
638    #[must_use]
639    pub const fn is_resource(&self) -> bool {
640        matches!(self, TAtomic::Resource(_))
641    }
642
643    #[inline]
644    #[must_use]
645    pub const fn is_closed_resource(&self) -> bool {
646        matches!(self, TAtomic::Resource(resource) if resource.is_closed())
647    }
648
649    #[inline]
650    #[must_use]
651    pub const fn is_open_resource(&self) -> bool {
652        matches!(self, TAtomic::Resource(resource) if resource.is_open())
653    }
654
655    #[inline]
656    #[must_use]
657    pub const fn is_literal(&self) -> bool {
658        match self {
659            TAtomic::Scalar(scalar) => scalar.is_literal_value(),
660            TAtomic::Null => true,
661            _ => false,
662        }
663    }
664
665    #[inline]
666    #[must_use]
667    pub const fn is_callable(&self) -> bool {
668        matches!(self, TAtomic::Callable(_))
669    }
670
671    #[inline]
672    #[must_use]
673    pub const fn is_conditional(&self) -> bool {
674        matches!(self, TAtomic::Conditional(_))
675    }
676
677    #[inline]
678    #[must_use]
679    pub const fn is_generic_parameter(&self) -> bool {
680        matches!(self, TAtomic::GenericParameter(_))
681    }
682
683    #[inline]
684    #[must_use]
685    pub const fn get_generic_parameter_name(&self) -> Option<Atom> {
686        match self {
687            TAtomic::GenericParameter(parameter) => Some(parameter.parameter_name),
688            _ => None,
689        }
690    }
691
692    /// Is this a type that could potentially be callable at runtime?
693    #[inline]
694    #[must_use]
695    pub const fn can_be_callable(&self) -> bool {
696        matches!(
697            self,
698            TAtomic::Callable(_)
699                | TAtomic::Scalar(TScalar::String(_))
700                | TAtomic::Array(TArray::List(_) | TArray::Keyed(_))
701                | TAtomic::Object(TObject::Named(_))
702        )
703    }
704
705    #[must_use]
706    pub fn is_truthy(&self) -> bool {
707        match &self {
708            TAtomic::Scalar(scalar) => scalar.is_truthy(),
709            TAtomic::Array(array) => array.is_truthy(),
710            TAtomic::Mixed(mixed) => mixed.is_truthy(),
711            TAtomic::Object(_) | TAtomic::Callable(_) => true,
712            _ => false,
713        }
714    }
715
716    #[must_use]
717    pub fn is_falsy(&self) -> bool {
718        match &self {
719            TAtomic::Scalar(scalar) if scalar.is_falsy() => true,
720            TAtomic::Array(array) if array.is_falsy() => true,
721            TAtomic::Mixed(mixed) if mixed.is_falsy() => true,
722            TAtomic::Null | TAtomic::Void => true,
723            _ => false,
724        }
725    }
726
727    #[must_use]
728    pub fn is_array_accessible_with_string_key(&self) -> bool {
729        matches!(self, TAtomic::Array(array) if array.is_keyed())
730    }
731
732    #[must_use]
733    pub fn is_array_accessible_with_int_or_string_key(&self) -> bool {
734        matches!(self, TAtomic::Array(_))
735    }
736
737    #[must_use]
738    pub fn is_derived(&self) -> bool {
739        matches!(self, TAtomic::Derived(_))
740    }
741
742    #[must_use]
743    pub fn clone_without_intersection_types(&self) -> TAtomic {
744        let mut clone = self.clone();
745        match &mut clone {
746            TAtomic::Object(TObject::Named(named_object)) => {
747                named_object.intersection_types = None;
748            }
749            TAtomic::GenericParameter(parameter) => {
750                parameter.intersection_types = None;
751            }
752            TAtomic::Iterable(iterable) => {
753                iterable.intersection_types = None;
754            }
755            TAtomic::Reference(TReference::Symbol { intersection_types, .. }) => {
756                *intersection_types = None;
757            }
758            _ => {}
759        }
760
761        clone
762    }
763
764    pub fn remove_placeholders(&mut self) {
765        match self {
766            TAtomic::Array(array) => {
767                array.remove_placeholders();
768            }
769            TAtomic::Object(TObject::Named(named_object)) => {
770                let name = named_object.get_name();
771                if let Some(type_parameters) = named_object.get_type_parameters_mut() {
772                    if name.eq_ignore_ascii_case("Traversable") {
773                        let has_kv_pair = type_parameters.len() == 2;
774
775                        if let Some(key_or_value_param) = type_parameters.get_mut(0)
776                            && let TAtomic::Placeholder = key_or_value_param.get_single()
777                        {
778                            *key_or_value_param = if has_kv_pair { get_arraykey() } else { get_mixed() };
779                        }
780
781                        if has_kv_pair
782                            && let Some(value_param) = type_parameters.get_mut(1)
783                            && let TAtomic::Placeholder = value_param.get_single()
784                        {
785                            *value_param = get_mixed();
786                        }
787                    } else {
788                        for type_param in type_parameters {
789                            if let TAtomic::Placeholder = type_param.get_single() {
790                                *type_param = get_mixed();
791                            }
792                        }
793                    }
794                }
795            }
796            _ => {}
797        }
798    }
799
800    #[must_use]
801    pub fn get_literal_string_value(&self) -> Option<&str> {
802        match self {
803            TAtomic::Scalar(scalar) => scalar.get_known_literal_string_value(),
804            _ => None,
805        }
806    }
807
808    #[must_use]
809    pub fn get_class_string_value(&self) -> Option<Atom> {
810        match self {
811            TAtomic::Scalar(scalar) => scalar.get_literal_class_string_value(),
812            _ => None,
813        }
814    }
815
816    #[must_use]
817    pub fn get_integer(&self) -> Option<TInteger> {
818        match self {
819            TAtomic::Scalar(TScalar::Integer(integer)) => Some(*integer),
820            _ => None,
821        }
822    }
823
824    #[must_use]
825    pub fn get_literal_int_value(&self) -> Option<i64> {
826        match self {
827            TAtomic::Scalar(scalar) => scalar.get_literal_int_value(),
828            _ => None,
829        }
830    }
831
832    #[must_use]
833    pub fn get_maximum_int_value(&self) -> Option<i64> {
834        match self {
835            TAtomic::Scalar(scalar) => scalar.get_maximum_int_value(),
836            _ => None,
837        }
838    }
839
840    #[must_use]
841    pub fn get_minimum_int_value(&self) -> Option<i64> {
842        match self {
843            TAtomic::Scalar(scalar) => scalar.get_minimum_int_value(),
844            _ => None,
845        }
846    }
847
848    #[must_use]
849    pub fn get_literal_float_value(&self) -> Option<f64> {
850        match self {
851            TAtomic::Scalar(scalar) => scalar.get_literal_float_value(),
852            _ => None,
853        }
854    }
855}
856
857impl TType for TAtomic {
858    fn get_child_nodes(&self) -> Vec<TypeRef<'_>> {
859        match self {
860            TAtomic::Array(ttype) => ttype.get_child_nodes(),
861            TAtomic::Callable(ttype) => ttype.get_child_nodes(),
862            TAtomic::Conditional(ttype) => ttype.get_child_nodes(),
863            TAtomic::Derived(ttype) => ttype.get_child_nodes(),
864            TAtomic::GenericParameter(ttype) => ttype.get_child_nodes(),
865            TAtomic::Iterable(ttype) => ttype.get_child_nodes(),
866            TAtomic::Mixed(ttype) => ttype.get_child_nodes(),
867            TAtomic::Object(ttype) => ttype.get_child_nodes(),
868            TAtomic::Reference(ttype) => ttype.get_child_nodes(),
869            TAtomic::Resource(ttype) => ttype.get_child_nodes(),
870            TAtomic::Scalar(ttype) => ttype.get_child_nodes(),
871            TAtomic::Alias(ttype) => ttype.get_child_nodes(),
872            _ => vec![],
873        }
874    }
875
876    fn can_be_intersected(&self) -> bool {
877        match self {
878            TAtomic::Object(ttype) => ttype.can_be_intersected(),
879            TAtomic::Reference(ttype) => ttype.can_be_intersected(),
880            TAtomic::GenericParameter(ttype) => ttype.can_be_intersected(),
881            TAtomic::Iterable(ttype) => ttype.can_be_intersected(),
882            TAtomic::Array(ttype) => ttype.can_be_intersected(),
883            TAtomic::Callable(ttype) => ttype.can_be_intersected(),
884            TAtomic::Mixed(ttype) => ttype.can_be_intersected(),
885            TAtomic::Scalar(ttype) => ttype.can_be_intersected(),
886            TAtomic::Resource(ttype) => ttype.can_be_intersected(),
887            TAtomic::Conditional(ttype) => ttype.can_be_intersected(),
888            TAtomic::Derived(ttype) => ttype.can_be_intersected(),
889            TAtomic::Alias(ttype) => ttype.can_be_intersected(),
890            _ => false,
891        }
892    }
893
894    fn get_intersection_types(&self) -> Option<&[TAtomic]> {
895        match self {
896            TAtomic::Object(ttype) => ttype.get_intersection_types(),
897            TAtomic::Reference(ttype) => ttype.get_intersection_types(),
898            TAtomic::GenericParameter(ttype) => ttype.get_intersection_types(),
899            TAtomic::Iterable(ttype) => ttype.get_intersection_types(),
900            TAtomic::Array(ttype) => ttype.get_intersection_types(),
901            TAtomic::Callable(ttype) => ttype.get_intersection_types(),
902            TAtomic::Mixed(ttype) => ttype.get_intersection_types(),
903            TAtomic::Scalar(ttype) => ttype.get_intersection_types(),
904            TAtomic::Resource(ttype) => ttype.get_intersection_types(),
905            TAtomic::Conditional(ttype) => ttype.get_intersection_types(),
906            TAtomic::Derived(ttype) => ttype.get_intersection_types(),
907            TAtomic::Alias(ttype) => ttype.get_intersection_types(),
908            _ => None,
909        }
910    }
911
912    fn get_intersection_types_mut(&mut self) -> Option<&mut Vec<TAtomic>> {
913        match self {
914            TAtomic::Object(ttype) => ttype.get_intersection_types_mut(),
915            TAtomic::Reference(ttype) => ttype.get_intersection_types_mut(),
916            TAtomic::GenericParameter(ttype) => ttype.get_intersection_types_mut(),
917            TAtomic::Iterable(ttype) => ttype.get_intersection_types_mut(),
918            TAtomic::Array(ttype) => ttype.get_intersection_types_mut(),
919            TAtomic::Callable(ttype) => ttype.get_intersection_types_mut(),
920            TAtomic::Mixed(ttype) => ttype.get_intersection_types_mut(),
921            TAtomic::Scalar(ttype) => ttype.get_intersection_types_mut(),
922            TAtomic::Resource(ttype) => ttype.get_intersection_types_mut(),
923            TAtomic::Conditional(ttype) => ttype.get_intersection_types_mut(),
924            TAtomic::Derived(ttype) => ttype.get_intersection_types_mut(),
925            TAtomic::Alias(ttype) => ttype.get_intersection_types_mut(),
926            _ => None,
927        }
928    }
929
930    fn has_intersection_types(&self) -> bool {
931        match self {
932            TAtomic::Object(ttype) => ttype.has_intersection_types(),
933            TAtomic::Reference(ttype) => ttype.has_intersection_types(),
934            TAtomic::GenericParameter(ttype) => ttype.has_intersection_types(),
935            TAtomic::Iterable(ttype) => ttype.has_intersection_types(),
936            TAtomic::Array(ttype) => ttype.has_intersection_types(),
937            TAtomic::Callable(ttype) => ttype.has_intersection_types(),
938            TAtomic::Mixed(ttype) => ttype.has_intersection_types(),
939            TAtomic::Scalar(ttype) => ttype.has_intersection_types(),
940            TAtomic::Resource(ttype) => ttype.has_intersection_types(),
941            TAtomic::Conditional(ttype) => ttype.has_intersection_types(),
942            TAtomic::Derived(ttype) => ttype.has_intersection_types(),
943            TAtomic::Alias(ttype) => ttype.has_intersection_types(),
944            _ => false,
945        }
946    }
947
948    fn add_intersection_type(&mut self, intersection_type: TAtomic) -> bool {
949        match self {
950            TAtomic::Object(ttype) => ttype.add_intersection_type(intersection_type),
951            TAtomic::Reference(ttype) => ttype.add_intersection_type(intersection_type),
952            TAtomic::GenericParameter(ttype) => ttype.add_intersection_type(intersection_type),
953            TAtomic::Iterable(ttype) => ttype.add_intersection_type(intersection_type),
954            TAtomic::Array(ttype) => ttype.add_intersection_type(intersection_type),
955            TAtomic::Callable(ttype) => ttype.add_intersection_type(intersection_type),
956            TAtomic::Mixed(ttype) => ttype.add_intersection_type(intersection_type),
957            TAtomic::Scalar(ttype) => ttype.add_intersection_type(intersection_type),
958            TAtomic::Resource(ttype) => ttype.add_intersection_type(intersection_type),
959            TAtomic::Conditional(ttype) => ttype.add_intersection_type(intersection_type),
960            TAtomic::Derived(ttype) => ttype.add_intersection_type(intersection_type),
961            TAtomic::Alias(ttype) => ttype.add_intersection_type(intersection_type),
962            _ => false,
963        }
964    }
965
966    fn needs_population(&self) -> bool {
967        if let Some(intersection) = self.get_intersection_types() {
968            for intersection_type in intersection {
969                if intersection_type.needs_population() {
970                    return true;
971                }
972            }
973        }
974
975        match self {
976            TAtomic::Object(ttype) => ttype.needs_population(),
977            TAtomic::Reference(ttype) => ttype.needs_population(),
978            TAtomic::GenericParameter(ttype) => ttype.needs_population(),
979            TAtomic::Iterable(ttype) => ttype.needs_population(),
980            TAtomic::Array(ttype) => ttype.needs_population(),
981            TAtomic::Callable(ttype) => ttype.needs_population(),
982            TAtomic::Conditional(ttype) => ttype.needs_population(),
983            TAtomic::Derived(ttype) => ttype.needs_population(),
984            TAtomic::Scalar(ttype) => ttype.needs_population(),
985            TAtomic::Mixed(ttype) => ttype.needs_population(),
986            TAtomic::Resource(ttype) => ttype.needs_population(),
987            TAtomic::Alias(ttype) => ttype.needs_population(),
988            _ => false,
989        }
990    }
991
992    fn is_expandable(&self) -> bool {
993        if let Some(intersection) = self.get_intersection_types() {
994            for intersection_type in intersection {
995                if intersection_type.is_expandable() {
996                    return true;
997                }
998            }
999        }
1000
1001        match self {
1002            TAtomic::Object(ttype) => ttype.is_expandable(),
1003            TAtomic::Reference(ttype) => ttype.is_expandable(),
1004            TAtomic::GenericParameter(ttype) => ttype.is_expandable(),
1005            TAtomic::Iterable(ttype) => ttype.is_expandable(),
1006            TAtomic::Array(ttype) => ttype.is_expandable(),
1007            TAtomic::Callable(ttype) => ttype.is_expandable(),
1008            TAtomic::Conditional(ttype) => ttype.is_expandable(),
1009            TAtomic::Derived(ttype) => ttype.is_expandable(),
1010            TAtomic::Scalar(ttype) => ttype.is_expandable(),
1011            TAtomic::Mixed(ttype) => ttype.is_expandable(),
1012            TAtomic::Resource(ttype) => ttype.is_expandable(),
1013            TAtomic::Alias(ttype) => ttype.is_expandable(),
1014            _ => false,
1015        }
1016    }
1017
1018    fn is_complex(&self) -> bool {
1019        if let Some(intersection) = self.get_intersection_types() {
1020            for intersection_type in intersection {
1021                if intersection_type.is_complex() {
1022                    return true;
1023                }
1024            }
1025        }
1026
1027        match self {
1028            TAtomic::Object(ttype) => ttype.is_complex(),
1029            TAtomic::Reference(ttype) => ttype.is_complex(),
1030            TAtomic::GenericParameter(ttype) => ttype.is_complex(),
1031            TAtomic::Iterable(ttype) => ttype.is_complex(),
1032            TAtomic::Array(ttype) => ttype.is_complex(),
1033            TAtomic::Callable(ttype) => ttype.is_complex(),
1034            TAtomic::Conditional(ttype) => ttype.is_complex(),
1035            TAtomic::Derived(ttype) => ttype.is_complex(),
1036            TAtomic::Scalar(ttype) => ttype.is_complex(),
1037            TAtomic::Mixed(ttype) => ttype.is_complex(),
1038            TAtomic::Resource(ttype) => ttype.is_complex(),
1039            TAtomic::Alias(ttype) => ttype.is_complex(),
1040            _ => false,
1041        }
1042    }
1043
1044    fn get_id(&self) -> Atom {
1045        match self {
1046            TAtomic::Scalar(scalar) => scalar.get_id(),
1047            TAtomic::Array(array) => array.get_id(),
1048            TAtomic::Callable(callable) => callable.get_id(),
1049            TAtomic::Object(object) => object.get_id(),
1050            TAtomic::Reference(reference) => reference.get_id(),
1051            TAtomic::Mixed(mixed) => mixed.get_id(),
1052            TAtomic::Resource(resource) => resource.get_id(),
1053            TAtomic::Iterable(iterable) => iterable.get_id(),
1054            TAtomic::GenericParameter(parameter) => parameter.get_id(),
1055            TAtomic::Conditional(conditional) => conditional.get_id(),
1056            TAtomic::Alias(alias) => alias.get_id(),
1057            TAtomic::Derived(derived) => derived.get_id(),
1058            TAtomic::Variable(name) => *name,
1059            TAtomic::Never => atom("never"),
1060            TAtomic::Null => atom("null"),
1061            TAtomic::Void => atom("void"),
1062            TAtomic::Placeholder => atom("_"),
1063        }
1064    }
1065
1066    fn get_pretty_id_with_indent(&self, indent: usize) -> Atom {
1067        match self {
1068            TAtomic::Scalar(scalar) => scalar.get_pretty_id_with_indent(indent),
1069            TAtomic::Array(array) => array.get_pretty_id_with_indent(indent),
1070            TAtomic::Callable(callable) => callable.get_pretty_id_with_indent(indent),
1071            TAtomic::Object(object) => object.get_pretty_id_with_indent(indent),
1072            TAtomic::Reference(reference) => reference.get_pretty_id_with_indent(indent),
1073            TAtomic::Mixed(mixed) => mixed.get_pretty_id_with_indent(indent),
1074            TAtomic::Resource(resource) => resource.get_pretty_id_with_indent(indent),
1075            TAtomic::Iterable(iterable) => iterable.get_pretty_id_with_indent(indent),
1076            TAtomic::GenericParameter(parameter) => parameter.get_pretty_id_with_indent(indent),
1077            TAtomic::Conditional(conditional) => conditional.get_pretty_id_with_indent(indent),
1078            TAtomic::Alias(alias) => alias.get_pretty_id_with_indent(indent),
1079            TAtomic::Derived(derived) => derived.get_pretty_id_with_indent(indent),
1080            TAtomic::Variable(name) => *name,
1081            TAtomic::Never => atom("never"),
1082            TAtomic::Null => atom("null"),
1083            TAtomic::Void => atom("void"),
1084            TAtomic::Placeholder => atom("_"),
1085        }
1086    }
1087}
1088
1089pub fn populate_atomic_type(
1090    unpopulated_atomic: &mut TAtomic,
1091    codebase_symbols: &Symbols,
1092    reference_source: Option<&ReferenceSource>,
1093    symbol_references: &mut SymbolReferences,
1094    force: bool,
1095) {
1096    match unpopulated_atomic {
1097        TAtomic::Array(array) => match array {
1098            TArray::List(list) => {
1099                populate_union_type(
1100                    list.element_type.as_mut(),
1101                    codebase_symbols,
1102                    reference_source,
1103                    symbol_references,
1104                    force,
1105                );
1106
1107                if let Some(known_elements) = list.known_elements.as_mut() {
1108                    for (_, element_type) in known_elements.values_mut() {
1109                        populate_union_type(element_type, codebase_symbols, reference_source, symbol_references, force);
1110                    }
1111                }
1112            }
1113            TArray::Keyed(keyed_array) => {
1114                if let Some(known_items) = keyed_array.known_items.as_mut() {
1115                    for (_, item_type) in known_items.values_mut() {
1116                        populate_union_type(item_type, codebase_symbols, reference_source, symbol_references, force);
1117                    }
1118                }
1119
1120                if let Some(parameters) = &mut keyed_array.parameters {
1121                    populate_union_type(
1122                        parameters.0.as_mut(),
1123                        codebase_symbols,
1124                        reference_source,
1125                        symbol_references,
1126                        force,
1127                    );
1128
1129                    populate_union_type(
1130                        parameters.1.as_mut(),
1131                        codebase_symbols,
1132                        reference_source,
1133                        symbol_references,
1134                        force,
1135                    );
1136                }
1137            }
1138        },
1139        TAtomic::Callable(TCallable::Signature(signature)) => {
1140            if let Some(return_type) = signature.get_return_type_mut() {
1141                populate_union_type(return_type, codebase_symbols, reference_source, symbol_references, force);
1142            }
1143
1144            for param in signature.get_parameters_mut() {
1145                if let Some(param_type) = param.get_type_signature_mut() {
1146                    populate_union_type(param_type, codebase_symbols, reference_source, symbol_references, force);
1147                }
1148            }
1149        }
1150        TAtomic::Object(TObject::Named(named_object)) => {
1151            let name = named_object.get_name();
1152
1153            if !named_object.is_intersection()
1154                && !named_object.has_type_parameters()
1155                && codebase_symbols.contains_enum(&name)
1156            {
1157                *unpopulated_atomic = TAtomic::Object(TObject::new_enum(name));
1158            } else {
1159                if let Some(type_parameters) = named_object.get_type_parameters_mut() {
1160                    for parameter in type_parameters {
1161                        populate_union_type(parameter, codebase_symbols, reference_source, symbol_references, force);
1162                    }
1163                }
1164
1165                if let Some(intersection_types) = named_object.get_intersection_types_mut() {
1166                    for intersection_type in intersection_types {
1167                        populate_atomic_type(
1168                            intersection_type,
1169                            codebase_symbols,
1170                            reference_source,
1171                            symbol_references,
1172                            force,
1173                        );
1174                    }
1175                }
1176            }
1177
1178            if let Some(reference_source) = reference_source {
1179                match reference_source {
1180                    ReferenceSource::Symbol(in_signature, a) => {
1181                        symbol_references.add_symbol_reference_to_symbol(*a, name, *in_signature);
1182                    }
1183                    ReferenceSource::ClassLikeMember(in_signature, a, b) => {
1184                        symbol_references.add_class_member_reference_to_symbol((*a, *b), name, *in_signature);
1185                    }
1186                }
1187            }
1188        }
1189        TAtomic::Object(TObject::WithProperties(keyed_array)) => {
1190            for (_, item_type) in keyed_array.known_properties.values_mut() {
1191                populate_union_type(item_type, codebase_symbols, reference_source, symbol_references, force);
1192            }
1193        }
1194        TAtomic::Iterable(iterable) => {
1195            populate_union_type(
1196                iterable.get_key_type_mut(),
1197                codebase_symbols,
1198                reference_source,
1199                symbol_references,
1200                force,
1201            );
1202
1203            populate_union_type(
1204                iterable.get_value_type_mut(),
1205                codebase_symbols,
1206                reference_source,
1207                symbol_references,
1208                force,
1209            );
1210
1211            if let Some(intersection_types) = iterable.get_intersection_types_mut() {
1212                for intersection_type in intersection_types {
1213                    populate_atomic_type(
1214                        intersection_type,
1215                        codebase_symbols,
1216                        reference_source,
1217                        symbol_references,
1218                        force,
1219                    );
1220                }
1221            }
1222        }
1223        TAtomic::Reference(reference) => match reference {
1224            TReference::Symbol { name, parameters, intersection_types } => {
1225                if let Some(parameters) = parameters {
1226                    for parameter in parameters {
1227                        populate_union_type(parameter, codebase_symbols, reference_source, symbol_references, force);
1228                    }
1229                }
1230
1231                if let Some(reference_source) = reference_source {
1232                    match reference_source {
1233                        ReferenceSource::Symbol(in_signature, a) => {
1234                            symbol_references.add_symbol_reference_to_symbol(*a, *name, *in_signature);
1235                        }
1236                        ReferenceSource::ClassLikeMember(in_signature, a, b) => {
1237                            symbol_references.add_class_member_reference_to_symbol((*a, *b), *name, *in_signature);
1238                        }
1239                    }
1240                }
1241
1242                if let Some(symbol_kind) = codebase_symbols.get_kind(&ascii_lowercase_atom(name)) {
1243                    if symbol_kind == SymbolKind::Enum {
1244                        *unpopulated_atomic = TAtomic::Object(TObject::new_enum(*name));
1245                    } else {
1246                        let intersection_types = intersection_types.take().map(|intersection_types| {
1247                            intersection_types
1248                                .into_iter()
1249                                .map(|mut intersection_type| {
1250                                    populate_atomic_type(
1251                                        &mut intersection_type,
1252                                        codebase_symbols,
1253                                        reference_source,
1254                                        symbol_references,
1255                                        force,
1256                                    );
1257
1258                                    intersection_type
1259                                })
1260                                .collect::<Vec<_>>()
1261                        });
1262
1263                        let mut named_object = TNamedObject::new(*name).with_type_parameters(parameters.clone());
1264                        if let Some(intersection_types) = intersection_types {
1265                            for intersection_type in intersection_types {
1266                                named_object.add_intersection_type(intersection_type);
1267                            }
1268                        }
1269
1270                        *unpopulated_atomic = TAtomic::Object(TObject::Named(named_object));
1271                    }
1272                }
1273            }
1274            TReference::Member { class_like_name, member_selector } => {
1275                if let TReferenceMemberSelector::Identifier(member_name) = member_selector
1276                    && let Some(reference_source) = reference_source
1277                {
1278                    match reference_source {
1279                        ReferenceSource::Symbol(in_signature, a) => symbol_references
1280                            .add_symbol_reference_to_class_member(*a, (*class_like_name, *member_name), *in_signature),
1281                        ReferenceSource::ClassLikeMember(in_signature, a, b) => symbol_references
1282                            .add_class_member_reference_to_class_member(
1283                                (*a, *b),
1284                                (*class_like_name, *member_name),
1285                                *in_signature,
1286                            ),
1287                    }
1288                }
1289            }
1290        },
1291        TAtomic::GenericParameter(TGenericParameter { constraint, intersection_types, .. }) => {
1292            populate_union_type(constraint.as_mut(), codebase_symbols, reference_source, symbol_references, force);
1293
1294            if let Some(intersection_types) = intersection_types.as_mut() {
1295                for intersection_type in intersection_types {
1296                    populate_atomic_type(
1297                        intersection_type,
1298                        codebase_symbols,
1299                        reference_source,
1300                        symbol_references,
1301                        force,
1302                    );
1303                }
1304            }
1305        }
1306        TAtomic::Scalar(TScalar::ClassLikeString(
1307            TClassLikeString::OfType { constraint, .. } | TClassLikeString::Generic { constraint, .. },
1308        )) => {
1309            populate_atomic_type(constraint.as_mut(), codebase_symbols, reference_source, symbol_references, force);
1310        }
1311        TAtomic::Conditional(conditional) => {
1312            populate_union_type(
1313                conditional.get_subject_mut(),
1314                codebase_symbols,
1315                reference_source,
1316                symbol_references,
1317                force,
1318            );
1319
1320            populate_union_type(
1321                conditional.get_target_mut(),
1322                codebase_symbols,
1323                reference_source,
1324                symbol_references,
1325                force,
1326            );
1327
1328            populate_union_type(
1329                conditional.get_then_mut(),
1330                codebase_symbols,
1331                reference_source,
1332                symbol_references,
1333                force,
1334            );
1335
1336            populate_union_type(
1337                conditional.get_otherwise_mut(),
1338                codebase_symbols,
1339                reference_source,
1340                symbol_references,
1341                force,
1342            );
1343        }
1344        TAtomic::Derived(derived) => match derived {
1345            TDerived::IntMask(int_mask) => {
1346                for value in int_mask.get_values_mut() {
1347                    populate_union_type(value, codebase_symbols, reference_source, symbol_references, force);
1348                }
1349            }
1350            TDerived::IndexAccess(index_access) => {
1351                populate_union_type(
1352                    index_access.get_target_type_mut(),
1353                    codebase_symbols,
1354                    reference_source,
1355                    symbol_references,
1356                    force,
1357                );
1358                populate_union_type(
1359                    index_access.get_index_type_mut(),
1360                    codebase_symbols,
1361                    reference_source,
1362                    symbol_references,
1363                    force,
1364                );
1365            }
1366            _ => {
1367                if let Some(target) = derived.get_target_type_mut() {
1368                    populate_union_type(target, codebase_symbols, reference_source, symbol_references, force);
1369                }
1370            }
1371        },
1372        _ => {}
1373    }
1374}