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