mago_codex/ttype/atomic/
mod.rs

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