Skip to main content

mago_codex/ttype/atomic/
mod.rs

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