mago_codex/ttype/atomic/
mod.rs

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