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