Skip to main content

mago_codex/ttype/atomic/
mod.rs

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