Skip to main content

mago_codex/ttype/atomic/
mod.rs

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