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    #[inline]
440    pub const fn is_literal_class_string(&self) -> bool {
441        matches!(
442            self,
443            TAtomic::Scalar(scalar) if scalar.is_literal_class_string()
444        )
445    }
446
447    pub const fn is_string_subtype(&self) -> bool {
448        matches!(
449            self,
450            TAtomic::Scalar(scalar) if scalar.is_non_boring_string()
451        )
452    }
453
454    #[inline]
455    pub const fn is_array_key(&self) -> bool {
456        matches!(
457            self,
458            TAtomic::Scalar(scalar) if scalar.is_array_key()
459        )
460    }
461
462    #[inline]
463    pub const fn is_int(&self) -> bool {
464        matches!(
465            self,
466            TAtomic::Scalar(scalar) if scalar.is_int()
467        )
468    }
469
470    #[inline]
471    pub const fn is_literal_int(&self) -> bool {
472        matches!(
473            self,
474            TAtomic::Scalar(scalar) if scalar.is_literal_int()
475        )
476    }
477
478    #[inline]
479    pub const fn is_float(&self) -> bool {
480        matches!(
481            self,
482            TAtomic::Scalar(scalar) if scalar.is_float()
483        )
484    }
485
486    #[inline]
487    pub const fn is_literal_float(&self) -> bool {
488        matches!(
489            self,
490            TAtomic::Scalar(scalar) if scalar.is_literal_float()
491        )
492    }
493
494    #[inline]
495    pub const fn is_null(&self) -> bool {
496        matches!(self, TAtomic::Null)
497    }
498
499    #[inline]
500    pub const fn is_void(&self) -> bool {
501        matches!(self, TAtomic::Void)
502    }
503
504    #[inline]
505    pub const fn is_bool(&self) -> bool {
506        matches!(
507            self,
508            TAtomic::Scalar(scalar) if scalar.is_bool()
509        )
510    }
511
512    #[inline]
513    pub const fn is_general_bool(&self) -> bool {
514        matches!(
515            self,
516            TAtomic::Scalar(scalar) if scalar.is_general_bool()
517        )
518    }
519
520    #[inline]
521    pub const fn is_general_string(&self) -> bool {
522        matches!(
523            self,
524            TAtomic::Scalar(scalar) if scalar.is_general_string()
525        )
526    }
527
528    #[inline]
529    pub const fn is_true(&self) -> bool {
530        matches!(
531            self,
532            TAtomic::Scalar(scalar) if scalar.is_true()
533        )
534    }
535
536    #[inline]
537    pub const fn is_false(&self) -> bool {
538        matches!(
539            self,
540            TAtomic::Scalar(scalar) if scalar.is_false()
541        )
542    }
543
544    #[inline]
545    pub const fn is_falsable(&self) -> bool {
546        matches!(
547            self,
548            TAtomic::Scalar(scalar) if scalar.is_false() || scalar.is_general_bool() || scalar.is_generic()
549        )
550    }
551
552    #[inline]
553    pub const fn is_resource(&self) -> bool {
554        matches!(self, TAtomic::Resource(_))
555    }
556
557    #[inline]
558    pub const fn is_closed_resource(&self) -> bool {
559        matches!(self, TAtomic::Resource(resource) if resource.is_closed())
560    }
561
562    #[inline]
563    pub const fn is_open_resource(&self) -> bool {
564        matches!(self, TAtomic::Resource(resource) if resource.is_open())
565    }
566
567    #[inline]
568    pub const fn is_literal(&self) -> bool {
569        match self {
570            TAtomic::Scalar(scalar) => scalar.is_literal_value(),
571            TAtomic::Null => true,
572            _ => false,
573        }
574    }
575
576    #[inline]
577    pub const fn is_callable(&self) -> bool {
578        matches!(self, TAtomic::Callable(_))
579    }
580
581    #[inline]
582    pub const fn is_conditional(&self) -> bool {
583        matches!(self, TAtomic::Conditional(_))
584    }
585
586    #[inline]
587    pub const fn is_generic_parameter(&self) -> bool {
588        matches!(self, TAtomic::GenericParameter(_))
589    }
590
591    #[inline]
592    pub const fn get_generic_parameter_name(&self) -> Option<Atom> {
593        match self {
594            TAtomic::GenericParameter(parameter) => Some(parameter.parameter_name),
595            _ => None,
596        }
597    }
598
599    /// Is this a type that could potentially be callable at runtime?
600    #[inline]
601    pub const fn can_be_callable(&self) -> bool {
602        matches!(
603            self,
604            TAtomic::Callable(_)
605                | TAtomic::Scalar(TScalar::String(_))
606                | TAtomic::Array(TArray::List(_))
607                | TAtomic::Object(TObject::Named(_))
608        )
609    }
610
611    pub fn is_truthy(&self) -> bool {
612        match &self {
613            TAtomic::Scalar(scalar) => scalar.is_truthy(),
614            TAtomic::Array(array) => array.is_truthy(),
615            TAtomic::Mixed(mixed) => mixed.is_truthy(),
616            TAtomic::Object(_) | TAtomic::Callable(_) => true,
617            _ => false,
618        }
619    }
620
621    pub fn is_falsy(&self) -> bool {
622        match &self {
623            TAtomic::Scalar(scalar) if scalar.is_falsy() => true,
624            TAtomic::Array(array) if array.is_falsy() => true,
625            TAtomic::Mixed(mixed) if mixed.is_falsy() => true,
626            TAtomic::Null => true,
627            _ => false,
628        }
629    }
630
631    pub fn is_array_accessible_with_string_key(&self) -> bool {
632        matches!(self, TAtomic::Array(array) if array.is_keyed())
633    }
634
635    pub fn is_array_accessible_with_int_or_string_key(&self) -> bool {
636        matches!(self, TAtomic::Array(_))
637    }
638
639    pub fn is_derived(&self) -> bool {
640        matches!(self, TAtomic::Derived(_))
641    }
642
643    pub fn clone_without_intersection_types(&self) -> TAtomic {
644        let mut clone = self.clone();
645        match &mut clone {
646            TAtomic::Object(TObject::Named(named_object)) => {
647                named_object.intersection_types = None;
648            }
649            TAtomic::GenericParameter(parameter) => {
650                parameter.intersection_types = None;
651            }
652            TAtomic::Iterable(iterable) => {
653                iterable.intersection_types = None;
654            }
655            TAtomic::Reference(TReference::Symbol { intersection_types, .. }) => {
656                *intersection_types = None;
657            }
658            _ => {}
659        }
660
661        clone
662    }
663
664    pub fn remove_placeholders(&mut self) {
665        match self {
666            TAtomic::Array(array) => {
667                array.remove_placeholders();
668            }
669            TAtomic::Object(TObject::Named(named_object)) => {
670                let name = named_object.get_name();
671                if let Some(type_parameters) = named_object.get_type_parameters_mut() {
672                    if name.eq_ignore_ascii_case("Traversable") {
673                        let has_kv_pair = type_parameters.len() == 2;
674
675                        if let Some(key_or_value_param) = type_parameters.get_mut(0)
676                            && let TAtomic::Placeholder = key_or_value_param.get_single()
677                        {
678                            *key_or_value_param = if has_kv_pair { get_arraykey() } else { get_mixed() };
679                        }
680
681                        if has_kv_pair
682                            && let Some(value_param) = type_parameters.get_mut(1)
683                            && let TAtomic::Placeholder = value_param.get_single()
684                        {
685                            *value_param = get_mixed();
686                        }
687                    } else {
688                        for type_param in type_parameters {
689                            if let TAtomic::Placeholder = type_param.get_single() {
690                                *type_param = get_mixed();
691                            }
692                        }
693                    }
694                }
695            }
696            _ => {}
697        }
698    }
699
700    pub fn get_literal_string_value(&self) -> Option<&str> {
701        match self {
702            TAtomic::Scalar(scalar) => scalar.get_known_literal_string_value(),
703            _ => None,
704        }
705    }
706
707    pub fn get_class_string_value(&self) -> Option<Atom> {
708        match self {
709            TAtomic::Scalar(scalar) => scalar.get_literal_class_string_value(),
710            _ => None,
711        }
712    }
713
714    pub fn get_integer(&self) -> Option<TInteger> {
715        match self {
716            TAtomic::Scalar(TScalar::Integer(integer)) => Some(*integer),
717            _ => None,
718        }
719    }
720
721    pub fn get_literal_int_value(&self) -> Option<i64> {
722        match self {
723            TAtomic::Scalar(scalar) => scalar.get_literal_int_value(),
724            _ => None,
725        }
726    }
727
728    pub fn get_maximum_int_value(&self) -> Option<i64> {
729        match self {
730            TAtomic::Scalar(scalar) => scalar.get_maximum_int_value(),
731            _ => None,
732        }
733    }
734
735    pub fn get_minimum_int_value(&self) -> Option<i64> {
736        match self {
737            TAtomic::Scalar(scalar) => scalar.get_minimum_int_value(),
738            _ => None,
739        }
740    }
741
742    pub fn get_literal_float_value(&self) -> Option<f64> {
743        match self {
744            TAtomic::Scalar(scalar) => scalar.get_literal_float_value(),
745            _ => None,
746        }
747    }
748}
749
750impl TType for TAtomic {
751    fn get_child_nodes<'a>(&'a self) -> Vec<TypeRef<'a>> {
752        match self {
753            TAtomic::Array(ttype) => ttype.get_child_nodes(),
754            TAtomic::Callable(ttype) => ttype.get_child_nodes(),
755            TAtomic::Conditional(ttype) => ttype.get_child_nodes(),
756            TAtomic::Derived(ttype) => ttype.get_child_nodes(),
757            TAtomic::GenericParameter(ttype) => ttype.get_child_nodes(),
758            TAtomic::Iterable(ttype) => ttype.get_child_nodes(),
759            TAtomic::Mixed(ttype) => ttype.get_child_nodes(),
760            TAtomic::Object(ttype) => ttype.get_child_nodes(),
761            TAtomic::Reference(ttype) => ttype.get_child_nodes(),
762            TAtomic::Resource(ttype) => ttype.get_child_nodes(),
763            TAtomic::Scalar(ttype) => ttype.get_child_nodes(),
764            _ => vec![],
765        }
766    }
767
768    fn can_be_intersected(&self) -> bool {
769        match self {
770            TAtomic::Object(ttype) => ttype.can_be_intersected(),
771            TAtomic::Reference(ttype) => ttype.can_be_intersected(),
772            TAtomic::GenericParameter(ttype) => ttype.can_be_intersected(),
773            TAtomic::Iterable(ttype) => ttype.can_be_intersected(),
774            TAtomic::Array(ttype) => ttype.can_be_intersected(),
775            TAtomic::Callable(ttype) => ttype.can_be_intersected(),
776            TAtomic::Mixed(ttype) => ttype.can_be_intersected(),
777            TAtomic::Scalar(ttype) => ttype.can_be_intersected(),
778            TAtomic::Resource(ttype) => ttype.can_be_intersected(),
779            TAtomic::Conditional(ttype) => ttype.can_be_intersected(),
780            TAtomic::Derived(ttype) => ttype.can_be_intersected(),
781            _ => false,
782        }
783    }
784
785    fn get_intersection_types(&self) -> Option<&[TAtomic]> {
786        match self {
787            TAtomic::Object(ttype) => ttype.get_intersection_types(),
788            TAtomic::Reference(ttype) => ttype.get_intersection_types(),
789            TAtomic::GenericParameter(ttype) => ttype.get_intersection_types(),
790            TAtomic::Iterable(ttype) => ttype.get_intersection_types(),
791            TAtomic::Array(ttype) => ttype.get_intersection_types(),
792            TAtomic::Callable(ttype) => ttype.get_intersection_types(),
793            TAtomic::Mixed(ttype) => ttype.get_intersection_types(),
794            TAtomic::Scalar(ttype) => ttype.get_intersection_types(),
795            TAtomic::Resource(ttype) => ttype.get_intersection_types(),
796            TAtomic::Conditional(ttype) => ttype.get_intersection_types(),
797            TAtomic::Derived(ttype) => ttype.get_intersection_types(),
798            _ => None,
799        }
800    }
801
802    fn get_intersection_types_mut(&mut self) -> Option<&mut Vec<TAtomic>> {
803        match self {
804            TAtomic::Object(ttype) => ttype.get_intersection_types_mut(),
805            TAtomic::Reference(ttype) => ttype.get_intersection_types_mut(),
806            TAtomic::GenericParameter(ttype) => ttype.get_intersection_types_mut(),
807            TAtomic::Iterable(ttype) => ttype.get_intersection_types_mut(),
808            TAtomic::Array(ttype) => ttype.get_intersection_types_mut(),
809            TAtomic::Callable(ttype) => ttype.get_intersection_types_mut(),
810            TAtomic::Mixed(ttype) => ttype.get_intersection_types_mut(),
811            TAtomic::Scalar(ttype) => ttype.get_intersection_types_mut(),
812            TAtomic::Resource(ttype) => ttype.get_intersection_types_mut(),
813            TAtomic::Conditional(ttype) => ttype.get_intersection_types_mut(),
814            TAtomic::Derived(ttype) => ttype.get_intersection_types_mut(),
815            _ => None,
816        }
817    }
818
819    fn has_intersection_types(&self) -> bool {
820        match self {
821            TAtomic::Object(ttype) => ttype.has_intersection_types(),
822            TAtomic::Reference(ttype) => ttype.has_intersection_types(),
823            TAtomic::GenericParameter(ttype) => ttype.has_intersection_types(),
824            TAtomic::Iterable(ttype) => ttype.has_intersection_types(),
825            TAtomic::Array(ttype) => ttype.has_intersection_types(),
826            TAtomic::Callable(ttype) => ttype.has_intersection_types(),
827            TAtomic::Mixed(ttype) => ttype.has_intersection_types(),
828            TAtomic::Scalar(ttype) => ttype.has_intersection_types(),
829            TAtomic::Resource(ttype) => ttype.has_intersection_types(),
830            TAtomic::Conditional(ttype) => ttype.has_intersection_types(),
831            TAtomic::Derived(ttype) => ttype.has_intersection_types(),
832            _ => false,
833        }
834    }
835
836    fn add_intersection_type(&mut self, intersection_type: TAtomic) -> bool {
837        match self {
838            TAtomic::Object(ttype) => ttype.add_intersection_type(intersection_type),
839            TAtomic::Reference(ttype) => ttype.add_intersection_type(intersection_type),
840            TAtomic::GenericParameter(ttype) => ttype.add_intersection_type(intersection_type),
841            TAtomic::Iterable(ttype) => ttype.add_intersection_type(intersection_type),
842            TAtomic::Array(ttype) => ttype.add_intersection_type(intersection_type),
843            TAtomic::Callable(ttype) => ttype.add_intersection_type(intersection_type),
844            TAtomic::Mixed(ttype) => ttype.add_intersection_type(intersection_type),
845            TAtomic::Scalar(ttype) => ttype.add_intersection_type(intersection_type),
846            TAtomic::Resource(ttype) => ttype.add_intersection_type(intersection_type),
847            TAtomic::Conditional(ttype) => ttype.add_intersection_type(intersection_type),
848            TAtomic::Derived(ttype) => ttype.add_intersection_type(intersection_type),
849            _ => false,
850        }
851    }
852
853    fn needs_population(&self) -> bool {
854        if let Some(intersection) = self.get_intersection_types() {
855            for intersection_type in intersection {
856                if intersection_type.needs_population() {
857                    return true;
858                }
859            }
860        }
861
862        match self {
863            TAtomic::Object(ttype) => ttype.needs_population(),
864            TAtomic::Reference(ttype) => ttype.needs_population(),
865            TAtomic::GenericParameter(ttype) => ttype.needs_population(),
866            TAtomic::Iterable(ttype) => ttype.needs_population(),
867            TAtomic::Array(ttype) => ttype.needs_population(),
868            TAtomic::Callable(ttype) => ttype.needs_population(),
869            TAtomic::Conditional(ttype) => ttype.needs_population(),
870            TAtomic::Derived(ttype) => ttype.needs_population(),
871            TAtomic::Scalar(ttype) => ttype.needs_population(),
872            TAtomic::Mixed(ttype) => ttype.needs_population(),
873            TAtomic::Resource(ttype) => ttype.needs_population(),
874            _ => false,
875        }
876    }
877
878    fn is_expandable(&self) -> bool {
879        if let Some(intersection) = self.get_intersection_types() {
880            for intersection_type in intersection {
881                if intersection_type.is_expandable() {
882                    return true;
883                }
884            }
885        }
886
887        match self {
888            TAtomic::Object(ttype) => ttype.is_expandable(),
889            TAtomic::Reference(ttype) => ttype.is_expandable(),
890            TAtomic::GenericParameter(ttype) => ttype.is_expandable(),
891            TAtomic::Iterable(ttype) => ttype.is_expandable(),
892            TAtomic::Array(ttype) => ttype.is_expandable(),
893            TAtomic::Callable(ttype) => ttype.is_expandable(),
894            TAtomic::Conditional(ttype) => ttype.is_expandable(),
895            TAtomic::Derived(ttype) => ttype.is_expandable(),
896            TAtomic::Scalar(ttype) => ttype.is_expandable(),
897            TAtomic::Mixed(ttype) => ttype.is_expandable(),
898            TAtomic::Resource(ttype) => ttype.is_expandable(),
899            _ => false,
900        }
901    }
902
903    fn get_id(&self) -> Atom {
904        match self {
905            TAtomic::Scalar(scalar) => scalar.get_id(),
906            TAtomic::Array(array) => array.get_id(),
907            TAtomic::Callable(callable) => callable.get_id(),
908            TAtomic::Object(object) => object.get_id(),
909            TAtomic::Reference(reference) => reference.get_id(),
910            TAtomic::Mixed(mixed) => mixed.get_id(),
911            TAtomic::Resource(resource) => resource.get_id(),
912            TAtomic::Iterable(iterable) => iterable.get_id(),
913            TAtomic::GenericParameter(parameter) => parameter.get_id(),
914            TAtomic::Conditional(conditional) => conditional.get_id(),
915            TAtomic::Derived(derived) => derived.get_id(),
916            TAtomic::Variable(name) => *name,
917            TAtomic::Never => atom("never"),
918            TAtomic::Null => atom("null"),
919            TAtomic::Void => atom("void"),
920            TAtomic::Placeholder => atom("_"),
921        }
922    }
923}
924
925pub fn populate_atomic_type(
926    unpopulated_atomic: &mut TAtomic,
927    codebase_symbols: &Symbols,
928    reference_source: Option<&ReferenceSource>,
929    symbol_references: &mut SymbolReferences,
930    force: bool,
931) {
932    match unpopulated_atomic {
933        TAtomic::Array(array) => match array {
934            TArray::List(list) => {
935                populate_union_type(
936                    list.element_type.as_mut(),
937                    codebase_symbols,
938                    reference_source,
939                    symbol_references,
940                    force,
941                );
942
943                if let Some(known_elements) = list.known_elements.as_mut() {
944                    for (_, element_type) in known_elements.values_mut() {
945                        populate_union_type(element_type, codebase_symbols, reference_source, symbol_references, force);
946                    }
947                }
948            }
949            TArray::Keyed(keyed_array) => {
950                if let Some(known_items) = keyed_array.known_items.as_mut() {
951                    for (_, item_type) in known_items.values_mut() {
952                        populate_union_type(item_type, codebase_symbols, reference_source, symbol_references, force);
953                    }
954                }
955
956                if let Some(parameters) = &mut keyed_array.parameters {
957                    populate_union_type(
958                        parameters.0.as_mut(),
959                        codebase_symbols,
960                        reference_source,
961                        symbol_references,
962                        force,
963                    );
964
965                    populate_union_type(
966                        parameters.1.as_mut(),
967                        codebase_symbols,
968                        reference_source,
969                        symbol_references,
970                        force,
971                    );
972                }
973            }
974        },
975        TAtomic::Callable(TCallable::Signature(signature)) => {
976            if let Some(return_type) = signature.get_return_type_mut() {
977                populate_union_type(return_type, codebase_symbols, reference_source, symbol_references, force);
978            }
979
980            for param in signature.get_parameters_mut() {
981                if let Some(param_type) = param.get_type_signature_mut() {
982                    populate_union_type(param_type, codebase_symbols, reference_source, symbol_references, force);
983                }
984            }
985        }
986        TAtomic::Object(TObject::Named(named_object)) => {
987            let name = named_object.get_name();
988
989            if !named_object.is_intersection()
990                && !named_object.has_type_parameters()
991                && codebase_symbols.contains_enum(&name)
992            {
993                *unpopulated_atomic = TAtomic::Object(TObject::new_enum(name));
994            } else {
995                if let Some(type_parameters) = named_object.get_type_parameters_mut() {
996                    for parameter in type_parameters {
997                        populate_union_type(parameter, codebase_symbols, reference_source, symbol_references, force);
998                    }
999                }
1000
1001                if let Some(intersection_types) = named_object.get_intersection_types_mut() {
1002                    for intersection_type in intersection_types {
1003                        populate_atomic_type(
1004                            intersection_type,
1005                            codebase_symbols,
1006                            reference_source,
1007                            symbol_references,
1008                            force,
1009                        );
1010                    }
1011                }
1012            }
1013
1014            if let Some(reference_source) = reference_source {
1015                match reference_source {
1016                    ReferenceSource::Symbol(in_signature, a) => {
1017                        symbol_references.add_symbol_reference_to_symbol(*a, name, *in_signature)
1018                    }
1019                    ReferenceSource::ClassLikeMember(in_signature, a, b) => {
1020                        symbol_references.add_class_member_reference_to_symbol((*a, *b), name, *in_signature)
1021                    }
1022                }
1023            }
1024        }
1025        TAtomic::Iterable(iterable) => {
1026            populate_union_type(
1027                iterable.get_key_type_mut(),
1028                codebase_symbols,
1029                reference_source,
1030                symbol_references,
1031                force,
1032            );
1033
1034            populate_union_type(
1035                iterable.get_value_type_mut(),
1036                codebase_symbols,
1037                reference_source,
1038                symbol_references,
1039                force,
1040            );
1041
1042            if let Some(intersection_types) = iterable.get_intersection_types_mut() {
1043                for intersection_type in intersection_types {
1044                    populate_atomic_type(
1045                        intersection_type,
1046                        codebase_symbols,
1047                        reference_source,
1048                        symbol_references,
1049                        force,
1050                    );
1051                }
1052            }
1053        }
1054        TAtomic::Reference(reference) => match reference {
1055            TReference::Symbol { name, parameters, intersection_types } => {
1056                if let Some(parameters) = parameters {
1057                    for parameter in parameters {
1058                        populate_union_type(parameter, codebase_symbols, reference_source, symbol_references, force);
1059                    }
1060                }
1061
1062                if let Some(reference_source) = reference_source {
1063                    match reference_source {
1064                        ReferenceSource::Symbol(in_signature, a) => {
1065                            symbol_references.add_symbol_reference_to_symbol(*a, *name, *in_signature)
1066                        }
1067                        ReferenceSource::ClassLikeMember(in_signature, a, b) => {
1068                            symbol_references.add_class_member_reference_to_symbol((*a, *b), *name, *in_signature)
1069                        }
1070                    }
1071                }
1072
1073                if let Some(symbol_kind) = codebase_symbols.get_kind(&ascii_lowercase_atom(name)) {
1074                    match symbol_kind {
1075                        SymbolKind::Enum => {
1076                            *unpopulated_atomic = TAtomic::Object(TObject::new_enum(*name));
1077                        }
1078                        _ => {
1079                            let intersection_types = intersection_types.take().map(|intersection_types| {
1080                                intersection_types
1081                                    .into_iter()
1082                                    .map(|mut intersection_type| {
1083                                        populate_atomic_type(
1084                                            &mut intersection_type,
1085                                            codebase_symbols,
1086                                            reference_source,
1087                                            symbol_references,
1088                                            force,
1089                                        );
1090
1091                                        intersection_type
1092                                    })
1093                                    .collect::<Vec<_>>()
1094                            });
1095
1096                            let mut named_object = TNamedObject::new(*name).with_type_parameters(parameters.clone());
1097                            if let Some(intersection_types) = intersection_types {
1098                                for intersection_type in intersection_types {
1099                                    named_object.add_intersection_type(intersection_type);
1100                                }
1101                            }
1102
1103                            *unpopulated_atomic = TAtomic::Object(TObject::Named(named_object));
1104                        }
1105                    }
1106                }
1107            }
1108            TReference::Member { class_like_name, member_selector } => {
1109                if let TReferenceMemberSelector::Identifier(member_name) = member_selector
1110                    && let Some(reference_source) = reference_source
1111                {
1112                    match reference_source {
1113                        ReferenceSource::Symbol(in_signature, a) => symbol_references
1114                            .add_symbol_reference_to_class_member(*a, (*class_like_name, *member_name), *in_signature),
1115                        ReferenceSource::ClassLikeMember(in_signature, a, b) => symbol_references
1116                            .add_class_member_reference_to_class_member(
1117                                (*a, *b),
1118                                (*class_like_name, *member_name),
1119                                *in_signature,
1120                            ),
1121                    }
1122                }
1123            }
1124        },
1125        TAtomic::GenericParameter(TGenericParameter { constraint, intersection_types, .. }) => {
1126            populate_union_type(constraint.as_mut(), codebase_symbols, reference_source, symbol_references, force);
1127
1128            if let Some(intersection_types) = intersection_types.as_mut() {
1129                for intersection_type in intersection_types {
1130                    populate_atomic_type(
1131                        intersection_type,
1132                        codebase_symbols,
1133                        reference_source,
1134                        symbol_references,
1135                        force,
1136                    );
1137                }
1138            }
1139        }
1140        TAtomic::Scalar(TScalar::ClassLikeString(
1141            TClassLikeString::OfType { constraint, .. } | TClassLikeString::Generic { constraint, .. },
1142        )) => {
1143            populate_atomic_type(constraint.as_mut(), codebase_symbols, reference_source, symbol_references, force);
1144        }
1145        TAtomic::Conditional(conditional) => {
1146            populate_union_type(
1147                conditional.get_subject_mut(),
1148                codebase_symbols,
1149                reference_source,
1150                symbol_references,
1151                force,
1152            );
1153
1154            populate_union_type(
1155                conditional.get_target_mut(),
1156                codebase_symbols,
1157                reference_source,
1158                symbol_references,
1159                force,
1160            );
1161
1162            populate_union_type(
1163                conditional.get_then_mut(),
1164                codebase_symbols,
1165                reference_source,
1166                symbol_references,
1167                force,
1168            );
1169
1170            populate_union_type(
1171                conditional.get_otherwise_mut(),
1172                codebase_symbols,
1173                reference_source,
1174                symbol_references,
1175                force,
1176            );
1177        }
1178        TAtomic::Derived(derived) => {
1179            populate_atomic_type(
1180                derived.get_target_type_mut(),
1181                codebase_symbols,
1182                reference_source,
1183                symbol_references,
1184                force,
1185            );
1186        }
1187        _ => {}
1188    }
1189}