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