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