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