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