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