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