Skip to main content

mago_codex/ttype/atomic/
mod.rs

1use std::sync::Arc;
2
3use serde::Deserialize;
4use serde::Serialize;
5
6use mago_word::Word;
7use mago_word::ascii_lowercase_word;
8use mago_word::word;
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(Word),
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<Word> {
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<Word> {
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.as_bytes().eq_ignore_ascii_case(b"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.as_bytes().eq_ignore_ascii_case(b"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.as_bytes().eq_ignore_ascii_case(b"Generator") {
278                break 'parameters None;
279            }
280
281            let parameters = named_object.get_type_parameters().unwrap_or_default();
282            match parameters {
283                [] => Some((get_mixed(), get_mixed(), get_mixed(), get_mixed())),
284                [a] => Some((get_mixed(), a.clone(), get_mixed(), get_mixed())),
285                [a, b] => Some((a.clone(), b.clone(), get_mixed(), get_mixed())),
286                [a, b, c] => Some((a.clone(), b.clone(), c.clone(), get_mixed())),
287                [a, b, c, d] => Some((a.clone(), b.clone(), c.clone(), d.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: &[u8]) -> 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.as_bytes() == interface {
406                return true;
407            }
408
409            if codebase.is_instance_of(object_name.as_bytes(), 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, b"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, b"Traversable")
444            || self.extends_or_implements(codebase, b"Iterator")
445            || self.extends_or_implements(codebase, b"IteratorAggregate")
446            || self.extends_or_implements(codebase, b"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<Word> {
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::Resource(resource) => resource.closed.is_none_or(|closed| !closed),
765            TAtomic::Object(_) | TAtomic::Callable(_) => true,
766            _ => false,
767        }
768    }
769
770    #[must_use]
771    pub fn is_falsy(&self) -> bool {
772        match &self {
773            TAtomic::Scalar(scalar) if scalar.is_falsy() => true,
774            TAtomic::Array(array) if array.is_falsy() => true,
775            TAtomic::Mixed(mixed) if mixed.is_falsy() => true,
776            TAtomic::Resource(resource) => resource.closed.is_some_and(|closed| closed),
777            TAtomic::Null | TAtomic::Void => true,
778            _ => false,
779        }
780    }
781
782    #[must_use]
783    pub fn is_array_accessible_with_string_key(&self) -> bool {
784        matches!(self, TAtomic::Array(array) if array.is_keyed())
785    }
786
787    #[must_use]
788    pub fn is_array_accessible_with_int_or_string_key(&self) -> bool {
789        matches!(self, TAtomic::Array(_))
790    }
791
792    #[must_use]
793    pub fn is_derived(&self) -> bool {
794        matches!(self, TAtomic::Derived(_))
795    }
796
797    #[must_use]
798    pub fn clone_without_intersection_types(&self) -> TAtomic {
799        let mut clone = self.clone();
800        match &mut clone {
801            TAtomic::Object(TObject::Named(named_object)) => {
802                named_object.intersection_types = None;
803            }
804            TAtomic::GenericParameter(parameter) => {
805                parameter.intersection_types = None;
806            }
807            TAtomic::Iterable(iterable) => {
808                iterable.intersection_types = None;
809            }
810            TAtomic::Reference(TReference::Symbol { intersection_types, .. }) => {
811                *intersection_types = None;
812            }
813            _ => {}
814        }
815
816        clone
817    }
818
819    pub fn remove_placeholders(&mut self) {
820        match self {
821            TAtomic::Array(array) => {
822                array.remove_placeholders();
823            }
824            TAtomic::Object(TObject::Named(named_object)) => {
825                let name = named_object.get_name();
826                if let Some(type_parameters) = named_object.get_type_parameters_mut() {
827                    if name.as_bytes().eq_ignore_ascii_case(b"Traversable") {
828                        let has_kv_pair = type_parameters.len() == 2;
829
830                        if let Some(key_or_value_param) = type_parameters.get_mut(0)
831                            && matches!(key_or_value_param.get_single(), TAtomic::Placeholder)
832                        {
833                            *key_or_value_param = if has_kv_pair { get_arraykey() } else { get_mixed() };
834                        }
835
836                        if has_kv_pair
837                            && let Some(value_param) = type_parameters.get_mut(1)
838                            && matches!(value_param.get_single(), TAtomic::Placeholder)
839                        {
840                            *value_param = get_mixed();
841                        }
842                    } else {
843                        for type_param in type_parameters {
844                            if matches!(type_param.get_single(), TAtomic::Placeholder) {
845                                *type_param = get_mixed();
846                            }
847                        }
848                    }
849                }
850            }
851            _ => {}
852        }
853    }
854
855    #[must_use]
856    pub fn get_literal_string_value(&self) -> Option<&[u8]> {
857        match self {
858            TAtomic::Scalar(scalar) => scalar.get_known_literal_string_value(),
859            _ => None,
860        }
861    }
862
863    #[must_use]
864    pub fn get_class_string_value(&self) -> Option<Word> {
865        match self {
866            TAtomic::Scalar(scalar) => scalar.get_literal_class_string_value(),
867            _ => None,
868        }
869    }
870
871    #[must_use]
872    pub fn get_integer(&self) -> Option<TInteger> {
873        match self {
874            TAtomic::Scalar(TScalar::Integer(integer)) => Some(*integer),
875            _ => None,
876        }
877    }
878
879    #[must_use]
880    pub fn get_literal_int_value(&self) -> Option<i64> {
881        match self {
882            TAtomic::Scalar(scalar) => scalar.get_literal_int_value(),
883            _ => None,
884        }
885    }
886
887    #[must_use]
888    pub fn get_maximum_int_value(&self) -> Option<i64> {
889        match self {
890            TAtomic::Scalar(scalar) => scalar.get_maximum_int_value(),
891            _ => None,
892        }
893    }
894
895    #[must_use]
896    pub fn get_minimum_int_value(&self) -> Option<i64> {
897        match self {
898            TAtomic::Scalar(scalar) => scalar.get_minimum_int_value(),
899            _ => None,
900        }
901    }
902
903    #[must_use]
904    pub fn get_literal_float_value(&self) -> Option<f64> {
905        match self {
906            TAtomic::Scalar(scalar) => scalar.get_literal_float_value(),
907            _ => None,
908        }
909    }
910}
911
912impl TType for TAtomic {
913    fn get_child_nodes(&self) -> Vec<TypeRef<'_>> {
914        match self {
915            TAtomic::Array(ttype) => ttype.get_child_nodes(),
916            TAtomic::Callable(ttype) => ttype.get_child_nodes(),
917            TAtomic::Conditional(ttype) => ttype.get_child_nodes(),
918            TAtomic::Derived(ttype) => ttype.get_child_nodes(),
919            TAtomic::GenericParameter(ttype) => ttype.get_child_nodes(),
920            TAtomic::Iterable(ttype) => ttype.get_child_nodes(),
921            TAtomic::Mixed(ttype) => ttype.get_child_nodes(),
922            TAtomic::Object(ttype) => ttype.get_child_nodes(),
923            TAtomic::Reference(ttype) => ttype.get_child_nodes(),
924            TAtomic::Resource(ttype) => ttype.get_child_nodes(),
925            TAtomic::Scalar(ttype) => ttype.get_child_nodes(),
926            TAtomic::Alias(ttype) => ttype.get_child_nodes(),
927            _ => vec![],
928        }
929    }
930
931    fn can_be_intersected(&self) -> bool {
932        match self {
933            TAtomic::Object(ttype) => ttype.can_be_intersected(),
934            TAtomic::Reference(ttype) => ttype.can_be_intersected(),
935            TAtomic::GenericParameter(ttype) => ttype.can_be_intersected(),
936            TAtomic::Iterable(ttype) => ttype.can_be_intersected(),
937            TAtomic::Array(ttype) => ttype.can_be_intersected(),
938            TAtomic::Callable(ttype) => ttype.can_be_intersected(),
939            TAtomic::Mixed(ttype) => ttype.can_be_intersected(),
940            TAtomic::Scalar(ttype) => ttype.can_be_intersected(),
941            TAtomic::Resource(ttype) => ttype.can_be_intersected(),
942            TAtomic::Conditional(ttype) => ttype.can_be_intersected(),
943            TAtomic::Derived(ttype) => ttype.can_be_intersected(),
944            TAtomic::Alias(ttype) => ttype.can_be_intersected(),
945            _ => false,
946        }
947    }
948
949    fn get_intersection_types(&self) -> Option<&[TAtomic]> {
950        match self {
951            TAtomic::Object(ttype) => ttype.get_intersection_types(),
952            TAtomic::Reference(ttype) => ttype.get_intersection_types(),
953            TAtomic::GenericParameter(ttype) => ttype.get_intersection_types(),
954            TAtomic::Iterable(ttype) => ttype.get_intersection_types(),
955            TAtomic::Array(ttype) => ttype.get_intersection_types(),
956            TAtomic::Callable(ttype) => ttype.get_intersection_types(),
957            TAtomic::Mixed(ttype) => ttype.get_intersection_types(),
958            TAtomic::Scalar(ttype) => ttype.get_intersection_types(),
959            TAtomic::Resource(ttype) => ttype.get_intersection_types(),
960            TAtomic::Conditional(ttype) => ttype.get_intersection_types(),
961            TAtomic::Derived(ttype) => ttype.get_intersection_types(),
962            TAtomic::Alias(ttype) => ttype.get_intersection_types(),
963            _ => None,
964        }
965    }
966
967    fn get_intersection_types_mut(&mut self) -> Option<&mut Vec<TAtomic>> {
968        match self {
969            TAtomic::Object(ttype) => ttype.get_intersection_types_mut(),
970            TAtomic::Reference(ttype) => ttype.get_intersection_types_mut(),
971            TAtomic::GenericParameter(ttype) => ttype.get_intersection_types_mut(),
972            TAtomic::Iterable(ttype) => ttype.get_intersection_types_mut(),
973            TAtomic::Array(ttype) => ttype.get_intersection_types_mut(),
974            TAtomic::Callable(ttype) => ttype.get_intersection_types_mut(),
975            TAtomic::Mixed(ttype) => ttype.get_intersection_types_mut(),
976            TAtomic::Scalar(ttype) => ttype.get_intersection_types_mut(),
977            TAtomic::Resource(ttype) => ttype.get_intersection_types_mut(),
978            TAtomic::Conditional(ttype) => ttype.get_intersection_types_mut(),
979            TAtomic::Derived(ttype) => ttype.get_intersection_types_mut(),
980            TAtomic::Alias(ttype) => ttype.get_intersection_types_mut(),
981            _ => None,
982        }
983    }
984
985    fn has_intersection_types(&self) -> bool {
986        match self {
987            TAtomic::Object(ttype) => ttype.has_intersection_types(),
988            TAtomic::Reference(ttype) => ttype.has_intersection_types(),
989            TAtomic::GenericParameter(ttype) => ttype.has_intersection_types(),
990            TAtomic::Iterable(ttype) => ttype.has_intersection_types(),
991            TAtomic::Array(ttype) => ttype.has_intersection_types(),
992            TAtomic::Callable(ttype) => ttype.has_intersection_types(),
993            TAtomic::Mixed(ttype) => ttype.has_intersection_types(),
994            TAtomic::Scalar(ttype) => ttype.has_intersection_types(),
995            TAtomic::Resource(ttype) => ttype.has_intersection_types(),
996            TAtomic::Conditional(ttype) => ttype.has_intersection_types(),
997            TAtomic::Derived(ttype) => ttype.has_intersection_types(),
998            TAtomic::Alias(ttype) => ttype.has_intersection_types(),
999            _ => false,
1000        }
1001    }
1002
1003    fn add_intersection_type(&mut self, intersection_type: TAtomic) -> bool {
1004        match self {
1005            TAtomic::Object(ttype) => ttype.add_intersection_type(intersection_type),
1006            TAtomic::Reference(ttype) => ttype.add_intersection_type(intersection_type),
1007            TAtomic::GenericParameter(ttype) => ttype.add_intersection_type(intersection_type),
1008            TAtomic::Iterable(ttype) => ttype.add_intersection_type(intersection_type),
1009            TAtomic::Array(ttype) => ttype.add_intersection_type(intersection_type),
1010            TAtomic::Callable(ttype) => ttype.add_intersection_type(intersection_type),
1011            TAtomic::Mixed(ttype) => ttype.add_intersection_type(intersection_type),
1012            TAtomic::Scalar(ttype) => ttype.add_intersection_type(intersection_type),
1013            TAtomic::Resource(ttype) => ttype.add_intersection_type(intersection_type),
1014            TAtomic::Conditional(ttype) => ttype.add_intersection_type(intersection_type),
1015            TAtomic::Derived(ttype) => ttype.add_intersection_type(intersection_type),
1016            TAtomic::Alias(ttype) => ttype.add_intersection_type(intersection_type),
1017            _ => false,
1018        }
1019    }
1020
1021    fn needs_population(&self) -> bool {
1022        if let Some(intersection) = self.get_intersection_types() {
1023            for intersection_type in intersection {
1024                if intersection_type.needs_population() {
1025                    return true;
1026                }
1027            }
1028        }
1029
1030        match self {
1031            TAtomic::Object(ttype) => ttype.needs_population(),
1032            TAtomic::Reference(ttype) => ttype.needs_population(),
1033            TAtomic::GenericParameter(ttype) => ttype.needs_population(),
1034            TAtomic::Iterable(ttype) => ttype.needs_population(),
1035            TAtomic::Array(ttype) => ttype.needs_population(),
1036            TAtomic::Callable(ttype) => ttype.needs_population(),
1037            TAtomic::Conditional(ttype) => ttype.needs_population(),
1038            TAtomic::Derived(ttype) => ttype.needs_population(),
1039            TAtomic::Scalar(ttype) => ttype.needs_population(),
1040            TAtomic::Mixed(ttype) => ttype.needs_population(),
1041            TAtomic::Resource(ttype) => ttype.needs_population(),
1042            TAtomic::Alias(ttype) => ttype.needs_population(),
1043            _ => false,
1044        }
1045    }
1046
1047    #[inline]
1048    fn is_expandable(&self) -> bool {
1049        if let Some(intersection) = self.get_intersection_types() {
1050            for intersection_type in intersection {
1051                if intersection_type.is_expandable() {
1052                    return true;
1053                }
1054            }
1055        }
1056
1057        match self {
1058            TAtomic::Object(ttype) => ttype.is_expandable(),
1059            TAtomic::Reference(ttype) => ttype.is_expandable(),
1060            TAtomic::GenericParameter(ttype) => ttype.is_expandable(),
1061            TAtomic::Iterable(ttype) => ttype.is_expandable(),
1062            TAtomic::Array(ttype) => ttype.is_expandable(),
1063            TAtomic::Callable(ttype) => ttype.is_expandable(),
1064            TAtomic::Conditional(ttype) => ttype.is_expandable(),
1065            TAtomic::Derived(ttype) => ttype.is_expandable(),
1066            TAtomic::Scalar(ttype) => ttype.is_expandable(),
1067            TAtomic::Mixed(ttype) => ttype.is_expandable(),
1068            TAtomic::Resource(ttype) => ttype.is_expandable(),
1069            TAtomic::Alias(ttype) => ttype.is_expandable(),
1070            _ => false,
1071        }
1072    }
1073
1074    fn is_complex(&self) -> bool {
1075        if let Some(intersection) = self.get_intersection_types() {
1076            for intersection_type in intersection {
1077                if intersection_type.is_complex() {
1078                    return true;
1079                }
1080            }
1081        }
1082
1083        match self {
1084            TAtomic::Object(ttype) => ttype.is_complex(),
1085            TAtomic::Reference(ttype) => ttype.is_complex(),
1086            TAtomic::GenericParameter(ttype) => ttype.is_complex(),
1087            TAtomic::Iterable(ttype) => ttype.is_complex(),
1088            TAtomic::Array(ttype) => ttype.is_complex(),
1089            TAtomic::Callable(ttype) => ttype.is_complex(),
1090            TAtomic::Conditional(ttype) => ttype.is_complex(),
1091            TAtomic::Derived(ttype) => ttype.is_complex(),
1092            TAtomic::Scalar(ttype) => ttype.is_complex(),
1093            TAtomic::Mixed(ttype) => ttype.is_complex(),
1094            TAtomic::Resource(ttype) => ttype.is_complex(),
1095            TAtomic::Alias(ttype) => ttype.is_complex(),
1096            _ => false,
1097        }
1098    }
1099
1100    fn get_id(&self) -> Word {
1101        match self {
1102            TAtomic::Scalar(scalar) => scalar.get_id(),
1103            TAtomic::Array(array) => array.get_id(),
1104            TAtomic::Callable(callable) => callable.get_id(),
1105            TAtomic::Object(object) => object.get_id(),
1106            TAtomic::Reference(reference) => reference.get_id(),
1107            TAtomic::Mixed(mixed) => mixed.get_id(),
1108            TAtomic::Resource(resource) => resource.get_id(),
1109            TAtomic::Iterable(iterable) => iterable.get_id(),
1110            TAtomic::GenericParameter(parameter) => parameter.get_id(),
1111            TAtomic::Conditional(conditional) => conditional.get_id(),
1112            TAtomic::Alias(alias) => alias.get_id(),
1113            TAtomic::Derived(derived) => derived.get_id(),
1114            TAtomic::Variable(name) => *name,
1115            TAtomic::Never => word("never"),
1116            TAtomic::Null => word("null"),
1117            TAtomic::Void => word("void"),
1118            TAtomic::Placeholder => word("_"),
1119        }
1120    }
1121
1122    fn get_pretty_id_with_indent(&self, indent: usize) -> Word {
1123        match self {
1124            TAtomic::Scalar(scalar) => scalar.get_pretty_id_with_indent(indent),
1125            TAtomic::Array(array) => array.get_pretty_id_with_indent(indent),
1126            TAtomic::Callable(callable) => callable.get_pretty_id_with_indent(indent),
1127            TAtomic::Object(object) => object.get_pretty_id_with_indent(indent),
1128            TAtomic::Reference(reference) => reference.get_pretty_id_with_indent(indent),
1129            TAtomic::Mixed(mixed) => mixed.get_pretty_id_with_indent(indent),
1130            TAtomic::Resource(resource) => resource.get_pretty_id_with_indent(indent),
1131            TAtomic::Iterable(iterable) => iterable.get_pretty_id_with_indent(indent),
1132            TAtomic::GenericParameter(parameter) => parameter.get_pretty_id_with_indent(indent),
1133            TAtomic::Conditional(conditional) => conditional.get_pretty_id_with_indent(indent),
1134            TAtomic::Alias(alias) => alias.get_pretty_id_with_indent(indent),
1135            TAtomic::Derived(derived) => derived.get_pretty_id_with_indent(indent),
1136            TAtomic::Variable(name) => *name,
1137            TAtomic::Never => word("never"),
1138            TAtomic::Null => word("null"),
1139            TAtomic::Void => word("void"),
1140            TAtomic::Placeholder => word("_"),
1141        }
1142    }
1143}
1144
1145pub fn populate_atomic_type(
1146    unpopulated_atomic: &mut TAtomic,
1147    codebase_symbols: &Symbols,
1148    reference_source: Option<&ReferenceSource>,
1149    symbol_references: &mut SymbolReferences,
1150    force: bool,
1151) {
1152    match unpopulated_atomic {
1153        TAtomic::Array(array) => match array {
1154            TArray::List(list) => {
1155                populate_union_type(
1156                    Arc::make_mut(&mut list.element_type),
1157                    codebase_symbols,
1158                    reference_source,
1159                    symbol_references,
1160                    force,
1161                );
1162
1163                if let Some(known_elements) = list.known_elements.as_mut() {
1164                    for (_, element_type) in known_elements.values_mut() {
1165                        populate_union_type(element_type, codebase_symbols, reference_source, symbol_references, force);
1166                    }
1167                }
1168            }
1169            TArray::Keyed(keyed_array) => {
1170                if let Some(known_items) = keyed_array.known_items.as_mut() {
1171                    for (_, item_type) in known_items.values_mut() {
1172                        populate_union_type(item_type, codebase_symbols, reference_source, symbol_references, force);
1173                    }
1174                }
1175
1176                if let Some(parameters) = &mut keyed_array.parameters {
1177                    populate_union_type(
1178                        Arc::make_mut(&mut parameters.0),
1179                        codebase_symbols,
1180                        reference_source,
1181                        symbol_references,
1182                        force,
1183                    );
1184
1185                    populate_union_type(
1186                        Arc::make_mut(&mut parameters.1),
1187                        codebase_symbols,
1188                        reference_source,
1189                        symbol_references,
1190                        force,
1191                    );
1192                }
1193            }
1194        },
1195        TAtomic::Callable(TCallable::Signature(signature)) => {
1196            if let Some(return_type) = signature.get_return_type_mut() {
1197                populate_union_type(return_type, codebase_symbols, reference_source, symbol_references, force);
1198            }
1199
1200            for param in signature.get_parameters_mut() {
1201                if let Some(param_type) = param.get_type_signature_mut() {
1202                    populate_union_type(param_type, codebase_symbols, reference_source, symbol_references, force);
1203                }
1204            }
1205        }
1206        TAtomic::Object(TObject::Named(named_object)) => {
1207            let name = named_object.get_name();
1208
1209            if !named_object.is_intersection()
1210                && !named_object.has_type_parameters()
1211                && codebase_symbols.contains_enum(name)
1212            {
1213                *unpopulated_atomic = TAtomic::Object(TObject::new_enum(name));
1214            } else {
1215                if let Some(type_parameters) = named_object.get_type_parameters_mut() {
1216                    for parameter in type_parameters {
1217                        populate_union_type(parameter, codebase_symbols, reference_source, symbol_references, force);
1218                    }
1219                }
1220
1221                if let Some(intersection_types) = named_object.get_intersection_types_mut() {
1222                    for intersection_type in intersection_types {
1223                        populate_atomic_type(
1224                            intersection_type,
1225                            codebase_symbols,
1226                            reference_source,
1227                            symbol_references,
1228                            force,
1229                        );
1230                    }
1231                }
1232            }
1233
1234            if let Some(reference_source) = reference_source {
1235                match reference_source {
1236                    ReferenceSource::Symbol(in_signature, a) => {
1237                        symbol_references.add_symbol_reference_to_symbol(*a, name, *in_signature);
1238                    }
1239                    ReferenceSource::ClassLikeMember(in_signature, a, b) => {
1240                        symbol_references.add_class_member_reference_to_symbol((*a, *b), name, *in_signature);
1241                    }
1242                }
1243            }
1244        }
1245        TAtomic::Object(TObject::WithProperties(keyed_array)) => {
1246            for (_, item_type) in keyed_array.known_properties.values_mut() {
1247                populate_union_type(item_type, codebase_symbols, reference_source, symbol_references, force);
1248            }
1249        }
1250        TAtomic::Iterable(iterable) => {
1251            populate_union_type(
1252                iterable.get_key_type_mut(),
1253                codebase_symbols,
1254                reference_source,
1255                symbol_references,
1256                force,
1257            );
1258
1259            populate_union_type(
1260                iterable.get_value_type_mut(),
1261                codebase_symbols,
1262                reference_source,
1263                symbol_references,
1264                force,
1265            );
1266
1267            if let Some(intersection_types) = iterable.get_intersection_types_mut() {
1268                for intersection_type in intersection_types {
1269                    populate_atomic_type(
1270                        intersection_type,
1271                        codebase_symbols,
1272                        reference_source,
1273                        symbol_references,
1274                        force,
1275                    );
1276                }
1277            }
1278        }
1279        TAtomic::Reference(reference) => match reference {
1280            TReference::Symbol { name, parameters, intersection_types } => {
1281                if let Some(parameters) = parameters {
1282                    for parameter in parameters {
1283                        populate_union_type(parameter, codebase_symbols, reference_source, symbol_references, force);
1284                    }
1285                }
1286
1287                if let Some(reference_source) = reference_source {
1288                    match reference_source {
1289                        ReferenceSource::Symbol(in_signature, a) => {
1290                            symbol_references.add_symbol_reference_to_symbol(*a, *name, *in_signature);
1291                        }
1292                        ReferenceSource::ClassLikeMember(in_signature, a, b) => {
1293                            symbol_references.add_class_member_reference_to_symbol((*a, *b), *name, *in_signature);
1294                        }
1295                    }
1296                }
1297
1298                if let Some(symbol_kind) = codebase_symbols.get_kind(ascii_lowercase_word(name.as_bytes())) {
1299                    if symbol_kind == SymbolKind::Enum {
1300                        *unpopulated_atomic = TAtomic::Object(TObject::new_enum(*name));
1301                    } else {
1302                        let intersection_types = intersection_types.take().map(|intersection_types| {
1303                            intersection_types
1304                                .into_iter()
1305                                .map(|mut intersection_type| {
1306                                    populate_atomic_type(
1307                                        &mut intersection_type,
1308                                        codebase_symbols,
1309                                        reference_source,
1310                                        symbol_references,
1311                                        force,
1312                                    );
1313
1314                                    intersection_type
1315                                })
1316                                .collect::<Vec<_>>()
1317                        });
1318
1319                        let mut named_object = TNamedObject::new(*name).with_type_parameters(parameters.clone());
1320                        if let Some(intersection_types) = intersection_types {
1321                            for intersection_type in intersection_types {
1322                                named_object.add_intersection_type(intersection_type);
1323                            }
1324                        }
1325
1326                        *unpopulated_atomic = TAtomic::Object(TObject::Named(named_object));
1327                    }
1328                }
1329            }
1330            TReference::Member { class_like_name, member_selector } => {
1331                if let TReferenceMemberSelector::Identifier(member_name) = member_selector
1332                    && let Some(reference_source) = reference_source
1333                {
1334                    match reference_source {
1335                        ReferenceSource::Symbol(in_signature, a) => symbol_references
1336                            .add_symbol_reference_to_class_member(*a, (*class_like_name, *member_name), *in_signature),
1337                        ReferenceSource::ClassLikeMember(in_signature, a, b) => symbol_references
1338                            .add_class_member_reference_to_class_member(
1339                                (*a, *b),
1340                                (*class_like_name, *member_name),
1341                                *in_signature,
1342                            ),
1343                    }
1344                }
1345            }
1346            TReference::Global { .. } => {
1347                // Global-constant wildcards are resolved at expansion time; nothing to populate.
1348            }
1349        },
1350        TAtomic::GenericParameter(TGenericParameter { constraint, intersection_types, .. }) => {
1351            populate_union_type(
1352                Arc::make_mut(constraint),
1353                codebase_symbols,
1354                reference_source,
1355                symbol_references,
1356                force,
1357            );
1358
1359            if let Some(intersection_types) = intersection_types.as_mut() {
1360                for intersection_type in intersection_types {
1361                    populate_atomic_type(
1362                        intersection_type,
1363                        codebase_symbols,
1364                        reference_source,
1365                        symbol_references,
1366                        force,
1367                    );
1368                }
1369            }
1370        }
1371        TAtomic::Scalar(TScalar::ClassLikeString(
1372            TClassLikeString::OfType { constraint, .. } | TClassLikeString::Generic { constraint, .. },
1373        )) => {
1374            populate_atomic_type(
1375                Arc::make_mut(constraint),
1376                codebase_symbols,
1377                reference_source,
1378                symbol_references,
1379                force,
1380            );
1381        }
1382        TAtomic::Conditional(conditional) => {
1383            populate_union_type(
1384                conditional.get_subject_mut(),
1385                codebase_symbols,
1386                reference_source,
1387                symbol_references,
1388                force,
1389            );
1390
1391            populate_union_type(
1392                conditional.get_target_mut(),
1393                codebase_symbols,
1394                reference_source,
1395                symbol_references,
1396                force,
1397            );
1398
1399            populate_union_type(
1400                conditional.get_then_mut(),
1401                codebase_symbols,
1402                reference_source,
1403                symbol_references,
1404                force,
1405            );
1406
1407            populate_union_type(
1408                conditional.get_otherwise_mut(),
1409                codebase_symbols,
1410                reference_source,
1411                symbol_references,
1412                force,
1413            );
1414        }
1415        TAtomic::Derived(derived) => match derived {
1416            TDerived::IntMask(int_mask) => {
1417                for value in int_mask.get_values_mut() {
1418                    populate_union_type(value, codebase_symbols, reference_source, symbol_references, force);
1419                }
1420            }
1421            TDerived::IndexAccess(index_access) => {
1422                populate_union_type(
1423                    index_access.get_target_type_mut(),
1424                    codebase_symbols,
1425                    reference_source,
1426                    symbol_references,
1427                    force,
1428                );
1429
1430                populate_union_type(
1431                    index_access.get_index_type_mut(),
1432                    codebase_symbols,
1433                    reference_source,
1434                    symbol_references,
1435                    force,
1436                );
1437            }
1438            TDerived::TemplateType(template_type) => {
1439                populate_union_type(
1440                    template_type.get_object_mut(),
1441                    codebase_symbols,
1442                    reference_source,
1443                    symbol_references,
1444                    force,
1445                );
1446
1447                populate_union_type(
1448                    template_type.get_class_name_mut(),
1449                    codebase_symbols,
1450                    reference_source,
1451                    symbol_references,
1452                    force,
1453                );
1454
1455                populate_union_type(
1456                    template_type.get_template_name_mut(),
1457                    codebase_symbols,
1458                    reference_source,
1459                    symbol_references,
1460                    force,
1461                );
1462            }
1463            _ => {
1464                if let Some(target) = derived.get_target_type_mut() {
1465                    populate_union_type(target, codebase_symbols, reference_source, symbol_references, force);
1466                }
1467            }
1468        },
1469        _ => {}
1470    }
1471}