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            TAtomic::Scalar(TScalar::ClassLikeString(TClassLikeString::Literal { value })) => {
475                Some(ArrayKey::String(*value))
476            }
477            _ => None,
478        }
479    }
480
481    #[must_use]
482    pub fn get_array_key_type(&self) -> Option<TUnion> {
483        match self {
484            TAtomic::Array(array) => array.get_key_type(),
485            _ => None,
486        }
487    }
488
489    #[must_use]
490    pub fn get_array_value_type(&self) -> Option<TUnion> {
491        match self {
492            TAtomic::Array(array) => array.get_value_type(),
493            _ => None,
494        }
495    }
496
497    #[inline]
498    #[must_use]
499    pub const fn is_generic_scalar(&self) -> bool {
500        matches!(self, TAtomic::Scalar(TScalar::Generic))
501    }
502
503    #[inline]
504    #[must_use]
505    pub const fn is_some_scalar(&self) -> bool {
506        matches!(self, TAtomic::Scalar(_))
507    }
508
509    #[inline]
510    #[must_use]
511    pub const fn is_boring_scalar(&self) -> bool {
512        matches!(
513            self,
514            TAtomic::Scalar(scalar) if scalar.is_boring()
515        )
516    }
517
518    #[inline]
519    #[must_use]
520    pub const fn is_any_string(&self) -> bool {
521        matches!(
522            self,
523            TAtomic::Scalar(scalar) if scalar.is_any_string()
524        )
525    }
526
527    #[inline]
528    #[must_use]
529    pub const fn is_string(&self) -> bool {
530        matches!(
531            self,
532            TAtomic::Scalar(scalar) if scalar.is_string()
533        )
534    }
535
536    #[inline]
537    #[must_use]
538    pub const fn is_string_of_literal_origin(&self) -> bool {
539        matches!(
540            self,
541            TAtomic::Scalar(scalar) if scalar.is_literal_origin_string()
542        )
543    }
544
545    #[inline]
546    #[must_use]
547    pub const fn is_non_empty_string(&self) -> bool {
548        matches!(
549            self,
550            TAtomic::Scalar(scalar) if scalar.is_non_empty_string()
551        )
552    }
553
554    #[inline]
555    #[must_use]
556    pub const fn is_known_literal_string(&self) -> bool {
557        matches!(
558            self,
559            TAtomic::Scalar(scalar) if scalar.is_known_literal_string()
560        )
561    }
562
563    #[inline]
564    #[must_use]
565    pub const fn is_literal_class_string(&self) -> bool {
566        matches!(
567            self,
568            TAtomic::Scalar(scalar) if scalar.is_literal_class_string()
569        )
570    }
571
572    #[must_use]
573    pub const fn is_string_subtype(&self) -> bool {
574        matches!(
575            self,
576            TAtomic::Scalar(scalar) if scalar.is_non_boring_string()
577        )
578    }
579
580    #[inline]
581    #[must_use]
582    pub const fn is_array_key(&self) -> bool {
583        matches!(
584            self,
585            TAtomic::Scalar(scalar) if scalar.is_array_key()
586        )
587    }
588
589    #[inline]
590    #[must_use]
591    pub const fn is_int(&self) -> bool {
592        matches!(
593            self,
594            TAtomic::Scalar(scalar) if scalar.is_int()
595        )
596    }
597
598    #[inline]
599    #[must_use]
600    pub const fn is_literal_int(&self) -> bool {
601        matches!(
602            self,
603            TAtomic::Scalar(scalar) if scalar.is_literal_int()
604        )
605    }
606
607    #[inline]
608    #[must_use]
609    pub const fn is_float(&self) -> bool {
610        matches!(
611            self,
612            TAtomic::Scalar(scalar) if scalar.is_float()
613        )
614    }
615
616    #[inline]
617    #[must_use]
618    pub const fn is_literal_float(&self) -> bool {
619        matches!(
620            self,
621            TAtomic::Scalar(scalar) if scalar.is_literal_float()
622        )
623    }
624
625    #[inline]
626    #[must_use]
627    pub const fn is_null(&self) -> bool {
628        matches!(self, TAtomic::Null)
629    }
630
631    #[inline]
632    #[must_use]
633    pub const fn is_void(&self) -> bool {
634        matches!(self, TAtomic::Void)
635    }
636
637    #[inline]
638    #[must_use]
639    pub const fn is_bool(&self) -> bool {
640        matches!(
641            self,
642            TAtomic::Scalar(scalar) if scalar.is_bool()
643        )
644    }
645
646    #[inline]
647    #[must_use]
648    pub const fn is_general_bool(&self) -> bool {
649        matches!(
650            self,
651            TAtomic::Scalar(scalar) if scalar.is_general_bool()
652        )
653    }
654
655    #[inline]
656    #[must_use]
657    pub const fn is_general_string(&self) -> bool {
658        matches!(
659            self,
660            TAtomic::Scalar(scalar) if scalar.is_general_string()
661        )
662    }
663
664    #[inline]
665    #[must_use]
666    pub const fn is_true(&self) -> bool {
667        matches!(
668            self,
669            TAtomic::Scalar(scalar) if scalar.is_true()
670        )
671    }
672
673    #[inline]
674    #[must_use]
675    pub const fn is_false(&self) -> bool {
676        matches!(
677            self,
678            TAtomic::Scalar(scalar) if scalar.is_false()
679        )
680    }
681
682    #[inline]
683    #[must_use]
684    pub const fn is_falsable(&self) -> bool {
685        matches!(
686            self,
687            TAtomic::Scalar(scalar) if scalar.is_false() || scalar.is_general_bool() || scalar.is_generic()
688        )
689    }
690
691    #[inline]
692    #[must_use]
693    pub const fn is_resource(&self) -> bool {
694        matches!(self, TAtomic::Resource(_))
695    }
696
697    #[inline]
698    #[must_use]
699    pub const fn is_closed_resource(&self) -> bool {
700        matches!(self, TAtomic::Resource(resource) if resource.is_closed())
701    }
702
703    #[inline]
704    #[must_use]
705    pub const fn is_open_resource(&self) -> bool {
706        matches!(self, TAtomic::Resource(resource) if resource.is_open())
707    }
708
709    #[inline]
710    #[must_use]
711    pub const fn is_literal(&self) -> bool {
712        match self {
713            TAtomic::Scalar(scalar) => scalar.is_literal_value(),
714            TAtomic::Null => true,
715            _ => false,
716        }
717    }
718
719    #[inline]
720    #[must_use]
721    pub const fn is_callable(&self) -> bool {
722        matches!(self, TAtomic::Callable(_))
723    }
724
725    #[inline]
726    #[must_use]
727    pub const fn is_conditional(&self) -> bool {
728        matches!(self, TAtomic::Conditional(_))
729    }
730
731    #[inline]
732    #[must_use]
733    pub const fn is_generic_parameter(&self) -> bool {
734        matches!(self, TAtomic::GenericParameter(_))
735    }
736
737    #[inline]
738    #[must_use]
739    pub const fn get_generic_parameter_name(&self) -> Option<Word> {
740        match self {
741            TAtomic::GenericParameter(parameter) => Some(parameter.parameter_name),
742            _ => None,
743        }
744    }
745
746    /// Is this a type that could potentially be callable at runtime?
747    #[inline]
748    #[must_use]
749    pub const fn can_be_callable(&self) -> bool {
750        matches!(
751            self,
752            TAtomic::Callable(_)
753                | TAtomic::Scalar(TScalar::String(_))
754                | TAtomic::Array(TArray::List(_) | TArray::Keyed(_))
755                | TAtomic::Object(TObject::Named(_))
756        )
757    }
758
759    #[must_use]
760    pub fn is_truthy(&self) -> bool {
761        match &self {
762            TAtomic::Scalar(scalar) => scalar.is_truthy(),
763            TAtomic::Array(array) => array.is_truthy(),
764            TAtomic::Mixed(mixed) => mixed.is_truthy(),
765            TAtomic::Resource(resource) => resource.closed.is_none_or(|closed| !closed),
766            TAtomic::Object(_) | TAtomic::Callable(_) => true,
767            _ => false,
768        }
769    }
770
771    #[must_use]
772    pub fn is_falsy(&self) -> bool {
773        match &self {
774            TAtomic::Scalar(scalar) if scalar.is_falsy() => true,
775            TAtomic::Array(array) if array.is_falsy() => true,
776            TAtomic::Mixed(mixed) if mixed.is_falsy() => true,
777            TAtomic::Resource(resource) => resource.closed.is_some_and(|closed| closed),
778            TAtomic::Null | TAtomic::Void => true,
779            _ => false,
780        }
781    }
782
783    #[must_use]
784    pub fn is_array_accessible_with_string_key(&self) -> bool {
785        matches!(self, TAtomic::Array(array) if array.is_keyed())
786    }
787
788    #[must_use]
789    pub fn is_array_accessible_with_int_or_string_key(&self) -> bool {
790        matches!(self, TAtomic::Array(_))
791    }
792
793    #[must_use]
794    pub fn is_derived(&self) -> bool {
795        matches!(self, TAtomic::Derived(_))
796    }
797
798    #[must_use]
799    pub fn clone_without_intersection_types(&self) -> TAtomic {
800        let mut clone = self.clone();
801        match &mut clone {
802            TAtomic::Object(TObject::Named(named_object)) => {
803                named_object.intersection_types = None;
804            }
805            TAtomic::GenericParameter(parameter) => {
806                parameter.intersection_types = None;
807            }
808            TAtomic::Iterable(iterable) => {
809                iterable.intersection_types = None;
810            }
811            TAtomic::Reference(TReference::Symbol { intersection_types, .. }) => {
812                *intersection_types = None;
813            }
814            _ => {}
815        }
816
817        clone
818    }
819
820    pub fn remove_placeholders(&mut self) {
821        match self {
822            TAtomic::Array(array) => {
823                array.remove_placeholders();
824            }
825            TAtomic::Object(TObject::Named(named_object)) => {
826                let name = named_object.get_name();
827                if let Some(type_parameters) = named_object.get_type_parameters_mut() {
828                    if name.as_bytes().eq_ignore_ascii_case(b"Traversable") {
829                        let has_kv_pair = type_parameters.len() == 2;
830
831                        if let Some(key_or_value_param) = type_parameters.get_mut(0)
832                            && matches!(key_or_value_param.get_single(), TAtomic::Placeholder)
833                        {
834                            *key_or_value_param = if has_kv_pair { get_arraykey() } else { get_mixed() };
835                        }
836
837                        if has_kv_pair
838                            && let Some(value_param) = type_parameters.get_mut(1)
839                            && matches!(value_param.get_single(), TAtomic::Placeholder)
840                        {
841                            *value_param = get_mixed();
842                        }
843                    } else {
844                        for type_param in type_parameters {
845                            if matches!(type_param.get_single(), TAtomic::Placeholder) {
846                                *type_param = get_mixed();
847                            }
848                        }
849                    }
850                }
851            }
852            _ => {}
853        }
854    }
855
856    #[must_use]
857    pub fn get_literal_string_value(&self) -> Option<&[u8]> {
858        match self {
859            TAtomic::Scalar(scalar) => scalar.get_known_literal_string_value(),
860            _ => None,
861        }
862    }
863
864    #[must_use]
865    pub fn get_class_string_value(&self) -> Option<Word> {
866        match self {
867            TAtomic::Scalar(scalar) => scalar.get_literal_class_string_value(),
868            _ => None,
869        }
870    }
871
872    #[must_use]
873    pub fn get_integer(&self) -> Option<TInteger> {
874        match self {
875            TAtomic::Scalar(TScalar::Integer(integer)) => Some(*integer),
876            _ => None,
877        }
878    }
879
880    #[must_use]
881    pub fn get_literal_int_value(&self) -> Option<i64> {
882        match self {
883            TAtomic::Scalar(scalar) => scalar.get_literal_int_value(),
884            _ => None,
885        }
886    }
887
888    #[must_use]
889    pub fn get_maximum_int_value(&self) -> Option<i64> {
890        match self {
891            TAtomic::Scalar(scalar) => scalar.get_maximum_int_value(),
892            _ => None,
893        }
894    }
895
896    #[must_use]
897    pub fn get_minimum_int_value(&self) -> Option<i64> {
898        match self {
899            TAtomic::Scalar(scalar) => scalar.get_minimum_int_value(),
900            _ => None,
901        }
902    }
903
904    #[must_use]
905    pub fn get_literal_float_value(&self) -> Option<f64> {
906        match self {
907            TAtomic::Scalar(scalar) => scalar.get_literal_float_value(),
908            _ => None,
909        }
910    }
911}
912
913impl TType for TAtomic {
914    fn get_child_nodes(&self) -> Vec<TypeRef<'_>> {
915        match self {
916            TAtomic::Array(ttype) => ttype.get_child_nodes(),
917            TAtomic::Callable(ttype) => ttype.get_child_nodes(),
918            TAtomic::Conditional(ttype) => ttype.get_child_nodes(),
919            TAtomic::Derived(ttype) => ttype.get_child_nodes(),
920            TAtomic::GenericParameter(ttype) => ttype.get_child_nodes(),
921            TAtomic::Iterable(ttype) => ttype.get_child_nodes(),
922            TAtomic::Mixed(ttype) => ttype.get_child_nodes(),
923            TAtomic::Object(ttype) => ttype.get_child_nodes(),
924            TAtomic::Reference(ttype) => ttype.get_child_nodes(),
925            TAtomic::Resource(ttype) => ttype.get_child_nodes(),
926            TAtomic::Scalar(ttype) => ttype.get_child_nodes(),
927            TAtomic::Alias(ttype) => ttype.get_child_nodes(),
928            _ => vec![],
929        }
930    }
931
932    fn can_be_intersected(&self) -> bool {
933        match self {
934            TAtomic::Object(ttype) => ttype.can_be_intersected(),
935            TAtomic::Reference(ttype) => ttype.can_be_intersected(),
936            TAtomic::GenericParameter(ttype) => ttype.can_be_intersected(),
937            TAtomic::Iterable(ttype) => ttype.can_be_intersected(),
938            TAtomic::Array(ttype) => ttype.can_be_intersected(),
939            TAtomic::Callable(ttype) => ttype.can_be_intersected(),
940            TAtomic::Mixed(ttype) => ttype.can_be_intersected(),
941            TAtomic::Scalar(ttype) => ttype.can_be_intersected(),
942            TAtomic::Resource(ttype) => ttype.can_be_intersected(),
943            TAtomic::Conditional(ttype) => ttype.can_be_intersected(),
944            TAtomic::Derived(ttype) => ttype.can_be_intersected(),
945            TAtomic::Alias(ttype) => ttype.can_be_intersected(),
946            _ => false,
947        }
948    }
949
950    fn get_intersection_types(&self) -> Option<&[TAtomic]> {
951        match self {
952            TAtomic::Object(ttype) => ttype.get_intersection_types(),
953            TAtomic::Reference(ttype) => ttype.get_intersection_types(),
954            TAtomic::GenericParameter(ttype) => ttype.get_intersection_types(),
955            TAtomic::Iterable(ttype) => ttype.get_intersection_types(),
956            TAtomic::Array(ttype) => ttype.get_intersection_types(),
957            TAtomic::Callable(ttype) => ttype.get_intersection_types(),
958            TAtomic::Mixed(ttype) => ttype.get_intersection_types(),
959            TAtomic::Scalar(ttype) => ttype.get_intersection_types(),
960            TAtomic::Resource(ttype) => ttype.get_intersection_types(),
961            TAtomic::Conditional(ttype) => ttype.get_intersection_types(),
962            TAtomic::Derived(ttype) => ttype.get_intersection_types(),
963            TAtomic::Alias(ttype) => ttype.get_intersection_types(),
964            _ => None,
965        }
966    }
967
968    fn get_intersection_types_mut(&mut self) -> Option<&mut Vec<TAtomic>> {
969        match self {
970            TAtomic::Object(ttype) => ttype.get_intersection_types_mut(),
971            TAtomic::Reference(ttype) => ttype.get_intersection_types_mut(),
972            TAtomic::GenericParameter(ttype) => ttype.get_intersection_types_mut(),
973            TAtomic::Iterable(ttype) => ttype.get_intersection_types_mut(),
974            TAtomic::Array(ttype) => ttype.get_intersection_types_mut(),
975            TAtomic::Callable(ttype) => ttype.get_intersection_types_mut(),
976            TAtomic::Mixed(ttype) => ttype.get_intersection_types_mut(),
977            TAtomic::Scalar(ttype) => ttype.get_intersection_types_mut(),
978            TAtomic::Resource(ttype) => ttype.get_intersection_types_mut(),
979            TAtomic::Conditional(ttype) => ttype.get_intersection_types_mut(),
980            TAtomic::Derived(ttype) => ttype.get_intersection_types_mut(),
981            TAtomic::Alias(ttype) => ttype.get_intersection_types_mut(),
982            _ => None,
983        }
984    }
985
986    fn has_intersection_types(&self) -> bool {
987        match self {
988            TAtomic::Object(ttype) => ttype.has_intersection_types(),
989            TAtomic::Reference(ttype) => ttype.has_intersection_types(),
990            TAtomic::GenericParameter(ttype) => ttype.has_intersection_types(),
991            TAtomic::Iterable(ttype) => ttype.has_intersection_types(),
992            TAtomic::Array(ttype) => ttype.has_intersection_types(),
993            TAtomic::Callable(ttype) => ttype.has_intersection_types(),
994            TAtomic::Mixed(ttype) => ttype.has_intersection_types(),
995            TAtomic::Scalar(ttype) => ttype.has_intersection_types(),
996            TAtomic::Resource(ttype) => ttype.has_intersection_types(),
997            TAtomic::Conditional(ttype) => ttype.has_intersection_types(),
998            TAtomic::Derived(ttype) => ttype.has_intersection_types(),
999            TAtomic::Alias(ttype) => ttype.has_intersection_types(),
1000            _ => false,
1001        }
1002    }
1003
1004    fn add_intersection_type(&mut self, intersection_type: TAtomic) -> bool {
1005        match self {
1006            TAtomic::Object(ttype) => ttype.add_intersection_type(intersection_type),
1007            TAtomic::Reference(ttype) => ttype.add_intersection_type(intersection_type),
1008            TAtomic::GenericParameter(ttype) => ttype.add_intersection_type(intersection_type),
1009            TAtomic::Iterable(ttype) => ttype.add_intersection_type(intersection_type),
1010            TAtomic::Array(ttype) => ttype.add_intersection_type(intersection_type),
1011            TAtomic::Callable(ttype) => ttype.add_intersection_type(intersection_type),
1012            TAtomic::Mixed(ttype) => ttype.add_intersection_type(intersection_type),
1013            TAtomic::Scalar(ttype) => ttype.add_intersection_type(intersection_type),
1014            TAtomic::Resource(ttype) => ttype.add_intersection_type(intersection_type),
1015            TAtomic::Conditional(ttype) => ttype.add_intersection_type(intersection_type),
1016            TAtomic::Derived(ttype) => ttype.add_intersection_type(intersection_type),
1017            TAtomic::Alias(ttype) => ttype.add_intersection_type(intersection_type),
1018            _ => false,
1019        }
1020    }
1021
1022    fn needs_population(&self) -> bool {
1023        if let Some(intersection) = self.get_intersection_types() {
1024            for intersection_type in intersection {
1025                if intersection_type.needs_population() {
1026                    return true;
1027                }
1028            }
1029        }
1030
1031        match self {
1032            TAtomic::Object(ttype) => ttype.needs_population(),
1033            TAtomic::Reference(ttype) => ttype.needs_population(),
1034            TAtomic::GenericParameter(ttype) => ttype.needs_population(),
1035            TAtomic::Iterable(ttype) => ttype.needs_population(),
1036            TAtomic::Array(ttype) => ttype.needs_population(),
1037            TAtomic::Callable(ttype) => ttype.needs_population(),
1038            TAtomic::Conditional(ttype) => ttype.needs_population(),
1039            TAtomic::Derived(ttype) => ttype.needs_population(),
1040            TAtomic::Scalar(ttype) => ttype.needs_population(),
1041            TAtomic::Mixed(ttype) => ttype.needs_population(),
1042            TAtomic::Resource(ttype) => ttype.needs_population(),
1043            TAtomic::Alias(ttype) => ttype.needs_population(),
1044            _ => false,
1045        }
1046    }
1047
1048    #[inline]
1049    fn is_expandable(&self) -> bool {
1050        if let Some(intersection) = self.get_intersection_types() {
1051            for intersection_type in intersection {
1052                if intersection_type.is_expandable() {
1053                    return true;
1054                }
1055            }
1056        }
1057
1058        match self {
1059            TAtomic::Object(ttype) => ttype.is_expandable(),
1060            TAtomic::Reference(ttype) => ttype.is_expandable(),
1061            TAtomic::GenericParameter(ttype) => ttype.is_expandable(),
1062            TAtomic::Iterable(ttype) => ttype.is_expandable(),
1063            TAtomic::Array(ttype) => ttype.is_expandable(),
1064            TAtomic::Callable(ttype) => ttype.is_expandable(),
1065            TAtomic::Conditional(ttype) => ttype.is_expandable(),
1066            TAtomic::Derived(ttype) => ttype.is_expandable(),
1067            TAtomic::Scalar(ttype) => ttype.is_expandable(),
1068            TAtomic::Mixed(ttype) => ttype.is_expandable(),
1069            TAtomic::Resource(ttype) => ttype.is_expandable(),
1070            TAtomic::Alias(ttype) => ttype.is_expandable(),
1071            _ => false,
1072        }
1073    }
1074
1075    fn is_complex(&self) -> bool {
1076        if let Some(intersection) = self.get_intersection_types() {
1077            for intersection_type in intersection {
1078                if intersection_type.is_complex() {
1079                    return true;
1080                }
1081            }
1082        }
1083
1084        match self {
1085            TAtomic::Object(ttype) => ttype.is_complex(),
1086            TAtomic::Reference(ttype) => ttype.is_complex(),
1087            TAtomic::GenericParameter(ttype) => ttype.is_complex(),
1088            TAtomic::Iterable(ttype) => ttype.is_complex(),
1089            TAtomic::Array(ttype) => ttype.is_complex(),
1090            TAtomic::Callable(ttype) => ttype.is_complex(),
1091            TAtomic::Conditional(ttype) => ttype.is_complex(),
1092            TAtomic::Derived(ttype) => ttype.is_complex(),
1093            TAtomic::Scalar(ttype) => ttype.is_complex(),
1094            TAtomic::Mixed(ttype) => ttype.is_complex(),
1095            TAtomic::Resource(ttype) => ttype.is_complex(),
1096            TAtomic::Alias(ttype) => ttype.is_complex(),
1097            _ => false,
1098        }
1099    }
1100
1101    fn get_id(&self) -> Word {
1102        match self {
1103            TAtomic::Scalar(scalar) => scalar.get_id(),
1104            TAtomic::Array(array) => array.get_id(),
1105            TAtomic::Callable(callable) => callable.get_id(),
1106            TAtomic::Object(object) => object.get_id(),
1107            TAtomic::Reference(reference) => reference.get_id(),
1108            TAtomic::Mixed(mixed) => mixed.get_id(),
1109            TAtomic::Resource(resource) => resource.get_id(),
1110            TAtomic::Iterable(iterable) => iterable.get_id(),
1111            TAtomic::GenericParameter(parameter) => parameter.get_id(),
1112            TAtomic::Conditional(conditional) => conditional.get_id(),
1113            TAtomic::Alias(alias) => alias.get_id(),
1114            TAtomic::Derived(derived) => derived.get_id(),
1115            TAtomic::Variable(name) => *name,
1116            TAtomic::Never => word("never"),
1117            TAtomic::Null => word("null"),
1118            TAtomic::Void => word("void"),
1119            TAtomic::Placeholder => word("_"),
1120        }
1121    }
1122
1123    fn get_pretty_id_with_indent(&self, indent: usize) -> Word {
1124        match self {
1125            TAtomic::Scalar(scalar) => scalar.get_pretty_id_with_indent(indent),
1126            TAtomic::Array(array) => array.get_pretty_id_with_indent(indent),
1127            TAtomic::Callable(callable) => callable.get_pretty_id_with_indent(indent),
1128            TAtomic::Object(object) => object.get_pretty_id_with_indent(indent),
1129            TAtomic::Reference(reference) => reference.get_pretty_id_with_indent(indent),
1130            TAtomic::Mixed(mixed) => mixed.get_pretty_id_with_indent(indent),
1131            TAtomic::Resource(resource) => resource.get_pretty_id_with_indent(indent),
1132            TAtomic::Iterable(iterable) => iterable.get_pretty_id_with_indent(indent),
1133            TAtomic::GenericParameter(parameter) => parameter.get_pretty_id_with_indent(indent),
1134            TAtomic::Conditional(conditional) => conditional.get_pretty_id_with_indent(indent),
1135            TAtomic::Alias(alias) => alias.get_pretty_id_with_indent(indent),
1136            TAtomic::Derived(derived) => derived.get_pretty_id_with_indent(indent),
1137            TAtomic::Variable(name) => *name,
1138            TAtomic::Never => word("never"),
1139            TAtomic::Null => word("null"),
1140            TAtomic::Void => word("void"),
1141            TAtomic::Placeholder => word("_"),
1142        }
1143    }
1144}
1145
1146pub fn populate_atomic_type(
1147    unpopulated_atomic: &mut TAtomic,
1148    codebase_symbols: &Symbols,
1149    reference_source: Option<&ReferenceSource>,
1150    symbol_references: &mut SymbolReferences,
1151    force: bool,
1152) {
1153    match unpopulated_atomic {
1154        TAtomic::Array(array) => match array {
1155            TArray::List(list) => {
1156                populate_union_type(
1157                    Arc::make_mut(&mut list.element_type),
1158                    codebase_symbols,
1159                    reference_source,
1160                    symbol_references,
1161                    force,
1162                );
1163
1164                if let Some(known_elements) = list.known_elements.as_mut() {
1165                    for (_, element_type) in known_elements.values_mut() {
1166                        populate_union_type(element_type, codebase_symbols, reference_source, symbol_references, force);
1167                    }
1168                }
1169            }
1170            TArray::Keyed(keyed_array) => {
1171                if let Some(known_items) = keyed_array.known_items.as_mut() {
1172                    for (_, item_type) in known_items.values_mut() {
1173                        populate_union_type(item_type, codebase_symbols, reference_source, symbol_references, force);
1174                    }
1175                }
1176
1177                if let Some(parameters) = &mut keyed_array.parameters {
1178                    populate_union_type(
1179                        Arc::make_mut(&mut parameters.0),
1180                        codebase_symbols,
1181                        reference_source,
1182                        symbol_references,
1183                        force,
1184                    );
1185
1186                    populate_union_type(
1187                        Arc::make_mut(&mut parameters.1),
1188                        codebase_symbols,
1189                        reference_source,
1190                        symbol_references,
1191                        force,
1192                    );
1193                }
1194            }
1195        },
1196        TAtomic::Callable(TCallable::Signature(signature)) => {
1197            if let Some(return_type) = signature.get_return_type_mut() {
1198                populate_union_type(return_type, codebase_symbols, reference_source, symbol_references, force);
1199            }
1200
1201            for param in signature.get_parameters_mut() {
1202                if let Some(param_type) = param.get_type_signature_mut() {
1203                    populate_union_type(param_type, codebase_symbols, reference_source, symbol_references, force);
1204                }
1205            }
1206        }
1207        TAtomic::Object(TObject::Named(named_object)) => {
1208            let name = named_object.get_name();
1209
1210            if !named_object.is_intersection()
1211                && !named_object.has_type_parameters()
1212                && codebase_symbols.contains_enum(name)
1213            {
1214                *unpopulated_atomic = TAtomic::Object(TObject::new_enum(name));
1215            } else {
1216                if let Some(type_parameters) = named_object.get_type_parameters_mut() {
1217                    for parameter in type_parameters {
1218                        populate_union_type(parameter, codebase_symbols, reference_source, symbol_references, force);
1219                    }
1220                }
1221
1222                if let Some(intersection_types) = named_object.get_intersection_types_mut() {
1223                    for intersection_type in intersection_types {
1224                        populate_atomic_type(
1225                            intersection_type,
1226                            codebase_symbols,
1227                            reference_source,
1228                            symbol_references,
1229                            force,
1230                        );
1231                    }
1232                }
1233            }
1234
1235            if let Some(reference_source) = reference_source {
1236                match reference_source {
1237                    ReferenceSource::Symbol(in_signature, a) => {
1238                        symbol_references.add_symbol_reference_to_symbol(*a, name, *in_signature);
1239                    }
1240                    ReferenceSource::ClassLikeMember(in_signature, a, b) => {
1241                        symbol_references.add_class_member_reference_to_symbol((*a, *b), name, *in_signature);
1242                    }
1243                }
1244            }
1245        }
1246        TAtomic::Object(TObject::WithProperties(keyed_array)) => {
1247            for (_, item_type) in keyed_array.known_properties.values_mut() {
1248                populate_union_type(item_type, codebase_symbols, reference_source, symbol_references, force);
1249            }
1250        }
1251        TAtomic::Iterable(iterable) => {
1252            populate_union_type(
1253                iterable.get_key_type_mut(),
1254                codebase_symbols,
1255                reference_source,
1256                symbol_references,
1257                force,
1258            );
1259
1260            populate_union_type(
1261                iterable.get_value_type_mut(),
1262                codebase_symbols,
1263                reference_source,
1264                symbol_references,
1265                force,
1266            );
1267
1268            if let Some(intersection_types) = iterable.get_intersection_types_mut() {
1269                for intersection_type in intersection_types {
1270                    populate_atomic_type(
1271                        intersection_type,
1272                        codebase_symbols,
1273                        reference_source,
1274                        symbol_references,
1275                        force,
1276                    );
1277                }
1278            }
1279        }
1280        TAtomic::Reference(reference) => match reference {
1281            TReference::Symbol { name, parameters, variances, intersection_types } => {
1282                if let Some(parameters) = parameters {
1283                    for parameter in parameters {
1284                        populate_union_type(parameter, codebase_symbols, reference_source, symbol_references, force);
1285                    }
1286                }
1287
1288                if let Some(reference_source) = reference_source {
1289                    match reference_source {
1290                        ReferenceSource::Symbol(in_signature, a) => {
1291                            symbol_references.add_symbol_reference_to_symbol(*a, *name, *in_signature);
1292                        }
1293                        ReferenceSource::ClassLikeMember(in_signature, a, b) => {
1294                            symbol_references.add_class_member_reference_to_symbol((*a, *b), *name, *in_signature);
1295                        }
1296                    }
1297                }
1298
1299                if let Some(symbol_kind) = codebase_symbols.get_kind(ascii_lowercase_word(name.as_bytes())) {
1300                    if symbol_kind == SymbolKind::Enum {
1301                        *unpopulated_atomic = TAtomic::Object(TObject::new_enum(*name));
1302                    } else {
1303                        let intersection_types = intersection_types.take().map(|intersection_types| {
1304                            intersection_types
1305                                .into_iter()
1306                                .map(|mut intersection_type| {
1307                                    populate_atomic_type(
1308                                        &mut intersection_type,
1309                                        codebase_symbols,
1310                                        reference_source,
1311                                        symbol_references,
1312                                        force,
1313                                    );
1314
1315                                    intersection_type
1316                                })
1317                                .collect::<Vec<_>>()
1318                        });
1319
1320                        let mut named_object = TNamedObject::new(*name)
1321                            .with_type_parameters(parameters.clone())
1322                            .with_variances(variances.clone());
1323                        if let Some(intersection_types) = intersection_types {
1324                            for intersection_type in intersection_types {
1325                                named_object.add_intersection_type(intersection_type);
1326                            }
1327                        }
1328
1329                        *unpopulated_atomic = TAtomic::Object(TObject::Named(named_object));
1330                    }
1331                }
1332            }
1333            TReference::Member { class_like_name, member_selector } => {
1334                if let TReferenceMemberSelector::Identifier(member_name) = member_selector
1335                    && let Some(reference_source) = reference_source
1336                {
1337                    match reference_source {
1338                        ReferenceSource::Symbol(in_signature, a) => symbol_references
1339                            .add_symbol_reference_to_class_member(*a, (*class_like_name, *member_name), *in_signature),
1340                        ReferenceSource::ClassLikeMember(in_signature, a, b) => symbol_references
1341                            .add_class_member_reference_to_class_member(
1342                                (*a, *b),
1343                                (*class_like_name, *member_name),
1344                                *in_signature,
1345                            ),
1346                    }
1347                }
1348            }
1349            TReference::Global { .. } => {
1350                // Global-constant wildcards are resolved at expansion time; nothing to populate.
1351            }
1352        },
1353        TAtomic::GenericParameter(TGenericParameter { constraint, intersection_types, .. }) => {
1354            populate_union_type(
1355                Arc::make_mut(constraint),
1356                codebase_symbols,
1357                reference_source,
1358                symbol_references,
1359                force,
1360            );
1361
1362            if let Some(intersection_types) = intersection_types.as_mut() {
1363                for intersection_type in intersection_types {
1364                    populate_atomic_type(
1365                        intersection_type,
1366                        codebase_symbols,
1367                        reference_source,
1368                        symbol_references,
1369                        force,
1370                    );
1371                }
1372            }
1373        }
1374        TAtomic::Scalar(TScalar::ClassLikeString(
1375            TClassLikeString::OfType { constraint, .. } | TClassLikeString::Generic { constraint, .. },
1376        )) => {
1377            populate_atomic_type(
1378                Arc::make_mut(constraint),
1379                codebase_symbols,
1380                reference_source,
1381                symbol_references,
1382                force,
1383            );
1384        }
1385        TAtomic::Conditional(conditional) => {
1386            populate_union_type(
1387                conditional.get_subject_mut(),
1388                codebase_symbols,
1389                reference_source,
1390                symbol_references,
1391                force,
1392            );
1393
1394            populate_union_type(
1395                conditional.get_target_mut(),
1396                codebase_symbols,
1397                reference_source,
1398                symbol_references,
1399                force,
1400            );
1401
1402            populate_union_type(
1403                conditional.get_then_mut(),
1404                codebase_symbols,
1405                reference_source,
1406                symbol_references,
1407                force,
1408            );
1409
1410            populate_union_type(
1411                conditional.get_otherwise_mut(),
1412                codebase_symbols,
1413                reference_source,
1414                symbol_references,
1415                force,
1416            );
1417        }
1418        TAtomic::Derived(derived) => match derived {
1419            TDerived::IntMask(int_mask) => {
1420                for value in int_mask.get_values_mut() {
1421                    populate_union_type(value, codebase_symbols, reference_source, symbol_references, force);
1422                }
1423            }
1424            TDerived::IndexAccess(index_access) => {
1425                populate_union_type(
1426                    index_access.get_target_type_mut(),
1427                    codebase_symbols,
1428                    reference_source,
1429                    symbol_references,
1430                    force,
1431                );
1432
1433                populate_union_type(
1434                    index_access.get_index_type_mut(),
1435                    codebase_symbols,
1436                    reference_source,
1437                    symbol_references,
1438                    force,
1439                );
1440            }
1441            TDerived::TemplateType(template_type) => {
1442                populate_union_type(
1443                    template_type.get_object_mut(),
1444                    codebase_symbols,
1445                    reference_source,
1446                    symbol_references,
1447                    force,
1448                );
1449
1450                populate_union_type(
1451                    template_type.get_class_name_mut(),
1452                    codebase_symbols,
1453                    reference_source,
1454                    symbol_references,
1455                    force,
1456                );
1457
1458                populate_union_type(
1459                    template_type.get_template_name_mut(),
1460                    codebase_symbols,
1461                    reference_source,
1462                    symbol_references,
1463                    force,
1464                );
1465            }
1466            _ => {
1467                if let Some(target) = derived.get_target_type_mut() {
1468                    populate_union_type(target, codebase_symbols, reference_source, symbol_references, force);
1469                }
1470            }
1471        },
1472        _ => {}
1473    }
1474}