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