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