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