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 #[inline]
440 pub const fn is_literal_class_string(&self) -> bool {
441 matches!(
442 self,
443 TAtomic::Scalar(scalar) if scalar.is_literal_class_string()
444 )
445 }
446
447 pub const fn is_string_subtype(&self) -> bool {
448 matches!(
449 self,
450 TAtomic::Scalar(scalar) if scalar.is_non_boring_string()
451 )
452 }
453
454 #[inline]
455 pub const fn is_array_key(&self) -> bool {
456 matches!(
457 self,
458 TAtomic::Scalar(scalar) if scalar.is_array_key()
459 )
460 }
461
462 #[inline]
463 pub const fn is_int(&self) -> bool {
464 matches!(
465 self,
466 TAtomic::Scalar(scalar) if scalar.is_int()
467 )
468 }
469
470 #[inline]
471 pub const fn is_literal_int(&self) -> bool {
472 matches!(
473 self,
474 TAtomic::Scalar(scalar) if scalar.is_literal_int()
475 )
476 }
477
478 #[inline]
479 pub const fn is_float(&self) -> bool {
480 matches!(
481 self,
482 TAtomic::Scalar(scalar) if scalar.is_float()
483 )
484 }
485
486 #[inline]
487 pub const fn is_literal_float(&self) -> bool {
488 matches!(
489 self,
490 TAtomic::Scalar(scalar) if scalar.is_literal_float()
491 )
492 }
493
494 #[inline]
495 pub const fn is_null(&self) -> bool {
496 matches!(self, TAtomic::Null)
497 }
498
499 #[inline]
500 pub const fn is_void(&self) -> bool {
501 matches!(self, TAtomic::Void)
502 }
503
504 #[inline]
505 pub const fn is_bool(&self) -> bool {
506 matches!(
507 self,
508 TAtomic::Scalar(scalar) if scalar.is_bool()
509 )
510 }
511
512 #[inline]
513 pub const fn is_general_bool(&self) -> bool {
514 matches!(
515 self,
516 TAtomic::Scalar(scalar) if scalar.is_general_bool()
517 )
518 }
519
520 #[inline]
521 pub const fn is_general_string(&self) -> bool {
522 matches!(
523 self,
524 TAtomic::Scalar(scalar) if scalar.is_general_string()
525 )
526 }
527
528 #[inline]
529 pub const fn is_true(&self) -> bool {
530 matches!(
531 self,
532 TAtomic::Scalar(scalar) if scalar.is_true()
533 )
534 }
535
536 #[inline]
537 pub const fn is_false(&self) -> bool {
538 matches!(
539 self,
540 TAtomic::Scalar(scalar) if scalar.is_false()
541 )
542 }
543
544 #[inline]
545 pub const fn is_falsable(&self) -> bool {
546 matches!(
547 self,
548 TAtomic::Scalar(scalar) if scalar.is_false() || scalar.is_general_bool() || scalar.is_generic()
549 )
550 }
551
552 #[inline]
553 pub const fn is_resource(&self) -> bool {
554 matches!(self, TAtomic::Resource(_))
555 }
556
557 #[inline]
558 pub const fn is_closed_resource(&self) -> bool {
559 matches!(self, TAtomic::Resource(resource) if resource.is_closed())
560 }
561
562 #[inline]
563 pub const fn is_open_resource(&self) -> bool {
564 matches!(self, TAtomic::Resource(resource) if resource.is_open())
565 }
566
567 #[inline]
568 pub const fn is_literal(&self) -> bool {
569 match self {
570 TAtomic::Scalar(scalar) => scalar.is_literal_value(),
571 TAtomic::Null => true,
572 _ => false,
573 }
574 }
575
576 #[inline]
577 pub const fn is_callable(&self) -> bool {
578 matches!(self, TAtomic::Callable(_))
579 }
580
581 #[inline]
582 pub const fn is_conditional(&self) -> bool {
583 matches!(self, TAtomic::Conditional(_))
584 }
585
586 #[inline]
587 pub const fn is_generic_parameter(&self) -> bool {
588 matches!(self, TAtomic::GenericParameter(_))
589 }
590
591 #[inline]
592 pub const fn get_generic_parameter_name(&self) -> Option<Atom> {
593 match self {
594 TAtomic::GenericParameter(parameter) => Some(parameter.parameter_name),
595 _ => None,
596 }
597 }
598
599 #[inline]
601 pub const fn can_be_callable(&self) -> bool {
602 matches!(
603 self,
604 TAtomic::Callable(_)
605 | TAtomic::Scalar(TScalar::String(_))
606 | TAtomic::Array(TArray::List(_))
607 | TAtomic::Object(TObject::Named(_))
608 )
609 }
610
611 pub fn is_truthy(&self) -> bool {
612 match &self {
613 TAtomic::Scalar(scalar) => scalar.is_truthy(),
614 TAtomic::Array(array) => array.is_truthy(),
615 TAtomic::Mixed(mixed) => mixed.is_truthy(),
616 TAtomic::Object(_) | TAtomic::Callable(_) => true,
617 _ => false,
618 }
619 }
620
621 pub fn is_falsy(&self) -> bool {
622 match &self {
623 TAtomic::Scalar(scalar) if scalar.is_falsy() => true,
624 TAtomic::Array(array) if array.is_falsy() => true,
625 TAtomic::Mixed(mixed) if mixed.is_falsy() => true,
626 TAtomic::Null => true,
627 _ => false,
628 }
629 }
630
631 pub fn is_array_accessible_with_string_key(&self) -> bool {
632 matches!(self, TAtomic::Array(array) if array.is_keyed())
633 }
634
635 pub fn is_array_accessible_with_int_or_string_key(&self) -> bool {
636 matches!(self, TAtomic::Array(_))
637 }
638
639 pub fn is_derived(&self) -> bool {
640 matches!(self, TAtomic::Derived(_))
641 }
642
643 pub fn clone_without_intersection_types(&self) -> TAtomic {
644 let mut clone = self.clone();
645 match &mut clone {
646 TAtomic::Object(TObject::Named(named_object)) => {
647 named_object.intersection_types = None;
648 }
649 TAtomic::GenericParameter(parameter) => {
650 parameter.intersection_types = None;
651 }
652 TAtomic::Iterable(iterable) => {
653 iterable.intersection_types = None;
654 }
655 TAtomic::Reference(TReference::Symbol { intersection_types, .. }) => {
656 *intersection_types = None;
657 }
658 _ => {}
659 }
660
661 clone
662 }
663
664 pub fn remove_placeholders(&mut self) {
665 match self {
666 TAtomic::Array(array) => {
667 array.remove_placeholders();
668 }
669 TAtomic::Object(TObject::Named(named_object)) => {
670 let name = named_object.get_name();
671 if let Some(type_parameters) = named_object.get_type_parameters_mut() {
672 if name.eq_ignore_ascii_case("Traversable") {
673 let has_kv_pair = type_parameters.len() == 2;
674
675 if let Some(key_or_value_param) = type_parameters.get_mut(0)
676 && let TAtomic::Placeholder = key_or_value_param.get_single()
677 {
678 *key_or_value_param = if has_kv_pair { get_arraykey() } else { get_mixed() };
679 }
680
681 if has_kv_pair
682 && let Some(value_param) = type_parameters.get_mut(1)
683 && let TAtomic::Placeholder = value_param.get_single()
684 {
685 *value_param = get_mixed();
686 }
687 } else {
688 for type_param in type_parameters {
689 if let TAtomic::Placeholder = type_param.get_single() {
690 *type_param = get_mixed();
691 }
692 }
693 }
694 }
695 }
696 _ => {}
697 }
698 }
699
700 pub fn get_literal_string_value(&self) -> Option<&str> {
701 match self {
702 TAtomic::Scalar(scalar) => scalar.get_known_literal_string_value(),
703 _ => None,
704 }
705 }
706
707 pub fn get_class_string_value(&self) -> Option<Atom> {
708 match self {
709 TAtomic::Scalar(scalar) => scalar.get_literal_class_string_value(),
710 _ => None,
711 }
712 }
713
714 pub fn get_integer(&self) -> Option<TInteger> {
715 match self {
716 TAtomic::Scalar(TScalar::Integer(integer)) => Some(*integer),
717 _ => None,
718 }
719 }
720
721 pub fn get_literal_int_value(&self) -> Option<i64> {
722 match self {
723 TAtomic::Scalar(scalar) => scalar.get_literal_int_value(),
724 _ => None,
725 }
726 }
727
728 pub fn get_maximum_int_value(&self) -> Option<i64> {
729 match self {
730 TAtomic::Scalar(scalar) => scalar.get_maximum_int_value(),
731 _ => None,
732 }
733 }
734
735 pub fn get_minimum_int_value(&self) -> Option<i64> {
736 match self {
737 TAtomic::Scalar(scalar) => scalar.get_minimum_int_value(),
738 _ => None,
739 }
740 }
741
742 pub fn get_literal_float_value(&self) -> Option<f64> {
743 match self {
744 TAtomic::Scalar(scalar) => scalar.get_literal_float_value(),
745 _ => None,
746 }
747 }
748}
749
750impl TType for TAtomic {
751 fn get_child_nodes<'a>(&'a self) -> Vec<TypeRef<'a>> {
752 match self {
753 TAtomic::Array(ttype) => ttype.get_child_nodes(),
754 TAtomic::Callable(ttype) => ttype.get_child_nodes(),
755 TAtomic::Conditional(ttype) => ttype.get_child_nodes(),
756 TAtomic::Derived(ttype) => ttype.get_child_nodes(),
757 TAtomic::GenericParameter(ttype) => ttype.get_child_nodes(),
758 TAtomic::Iterable(ttype) => ttype.get_child_nodes(),
759 TAtomic::Mixed(ttype) => ttype.get_child_nodes(),
760 TAtomic::Object(ttype) => ttype.get_child_nodes(),
761 TAtomic::Reference(ttype) => ttype.get_child_nodes(),
762 TAtomic::Resource(ttype) => ttype.get_child_nodes(),
763 TAtomic::Scalar(ttype) => ttype.get_child_nodes(),
764 _ => vec![],
765 }
766 }
767
768 fn can_be_intersected(&self) -> bool {
769 match self {
770 TAtomic::Object(ttype) => ttype.can_be_intersected(),
771 TAtomic::Reference(ttype) => ttype.can_be_intersected(),
772 TAtomic::GenericParameter(ttype) => ttype.can_be_intersected(),
773 TAtomic::Iterable(ttype) => ttype.can_be_intersected(),
774 TAtomic::Array(ttype) => ttype.can_be_intersected(),
775 TAtomic::Callable(ttype) => ttype.can_be_intersected(),
776 TAtomic::Mixed(ttype) => ttype.can_be_intersected(),
777 TAtomic::Scalar(ttype) => ttype.can_be_intersected(),
778 TAtomic::Resource(ttype) => ttype.can_be_intersected(),
779 TAtomic::Conditional(ttype) => ttype.can_be_intersected(),
780 TAtomic::Derived(ttype) => ttype.can_be_intersected(),
781 _ => false,
782 }
783 }
784
785 fn get_intersection_types(&self) -> Option<&[TAtomic]> {
786 match self {
787 TAtomic::Object(ttype) => ttype.get_intersection_types(),
788 TAtomic::Reference(ttype) => ttype.get_intersection_types(),
789 TAtomic::GenericParameter(ttype) => ttype.get_intersection_types(),
790 TAtomic::Iterable(ttype) => ttype.get_intersection_types(),
791 TAtomic::Array(ttype) => ttype.get_intersection_types(),
792 TAtomic::Callable(ttype) => ttype.get_intersection_types(),
793 TAtomic::Mixed(ttype) => ttype.get_intersection_types(),
794 TAtomic::Scalar(ttype) => ttype.get_intersection_types(),
795 TAtomic::Resource(ttype) => ttype.get_intersection_types(),
796 TAtomic::Conditional(ttype) => ttype.get_intersection_types(),
797 TAtomic::Derived(ttype) => ttype.get_intersection_types(),
798 _ => None,
799 }
800 }
801
802 fn get_intersection_types_mut(&mut self) -> Option<&mut Vec<TAtomic>> {
803 match self {
804 TAtomic::Object(ttype) => ttype.get_intersection_types_mut(),
805 TAtomic::Reference(ttype) => ttype.get_intersection_types_mut(),
806 TAtomic::GenericParameter(ttype) => ttype.get_intersection_types_mut(),
807 TAtomic::Iterable(ttype) => ttype.get_intersection_types_mut(),
808 TAtomic::Array(ttype) => ttype.get_intersection_types_mut(),
809 TAtomic::Callable(ttype) => ttype.get_intersection_types_mut(),
810 TAtomic::Mixed(ttype) => ttype.get_intersection_types_mut(),
811 TAtomic::Scalar(ttype) => ttype.get_intersection_types_mut(),
812 TAtomic::Resource(ttype) => ttype.get_intersection_types_mut(),
813 TAtomic::Conditional(ttype) => ttype.get_intersection_types_mut(),
814 TAtomic::Derived(ttype) => ttype.get_intersection_types_mut(),
815 _ => None,
816 }
817 }
818
819 fn has_intersection_types(&self) -> bool {
820 match self {
821 TAtomic::Object(ttype) => ttype.has_intersection_types(),
822 TAtomic::Reference(ttype) => ttype.has_intersection_types(),
823 TAtomic::GenericParameter(ttype) => ttype.has_intersection_types(),
824 TAtomic::Iterable(ttype) => ttype.has_intersection_types(),
825 TAtomic::Array(ttype) => ttype.has_intersection_types(),
826 TAtomic::Callable(ttype) => ttype.has_intersection_types(),
827 TAtomic::Mixed(ttype) => ttype.has_intersection_types(),
828 TAtomic::Scalar(ttype) => ttype.has_intersection_types(),
829 TAtomic::Resource(ttype) => ttype.has_intersection_types(),
830 TAtomic::Conditional(ttype) => ttype.has_intersection_types(),
831 TAtomic::Derived(ttype) => ttype.has_intersection_types(),
832 _ => false,
833 }
834 }
835
836 fn add_intersection_type(&mut self, intersection_type: TAtomic) -> bool {
837 match self {
838 TAtomic::Object(ttype) => ttype.add_intersection_type(intersection_type),
839 TAtomic::Reference(ttype) => ttype.add_intersection_type(intersection_type),
840 TAtomic::GenericParameter(ttype) => ttype.add_intersection_type(intersection_type),
841 TAtomic::Iterable(ttype) => ttype.add_intersection_type(intersection_type),
842 TAtomic::Array(ttype) => ttype.add_intersection_type(intersection_type),
843 TAtomic::Callable(ttype) => ttype.add_intersection_type(intersection_type),
844 TAtomic::Mixed(ttype) => ttype.add_intersection_type(intersection_type),
845 TAtomic::Scalar(ttype) => ttype.add_intersection_type(intersection_type),
846 TAtomic::Resource(ttype) => ttype.add_intersection_type(intersection_type),
847 TAtomic::Conditional(ttype) => ttype.add_intersection_type(intersection_type),
848 TAtomic::Derived(ttype) => ttype.add_intersection_type(intersection_type),
849 _ => false,
850 }
851 }
852
853 fn needs_population(&self) -> bool {
854 if let Some(intersection) = self.get_intersection_types() {
855 for intersection_type in intersection {
856 if intersection_type.needs_population() {
857 return true;
858 }
859 }
860 }
861
862 match self {
863 TAtomic::Object(ttype) => ttype.needs_population(),
864 TAtomic::Reference(ttype) => ttype.needs_population(),
865 TAtomic::GenericParameter(ttype) => ttype.needs_population(),
866 TAtomic::Iterable(ttype) => ttype.needs_population(),
867 TAtomic::Array(ttype) => ttype.needs_population(),
868 TAtomic::Callable(ttype) => ttype.needs_population(),
869 TAtomic::Conditional(ttype) => ttype.needs_population(),
870 TAtomic::Derived(ttype) => ttype.needs_population(),
871 TAtomic::Scalar(ttype) => ttype.needs_population(),
872 TAtomic::Mixed(ttype) => ttype.needs_population(),
873 TAtomic::Resource(ttype) => ttype.needs_population(),
874 _ => false,
875 }
876 }
877
878 fn is_expandable(&self) -> bool {
879 if let Some(intersection) = self.get_intersection_types() {
880 for intersection_type in intersection {
881 if intersection_type.is_expandable() {
882 return true;
883 }
884 }
885 }
886
887 match self {
888 TAtomic::Object(ttype) => ttype.is_expandable(),
889 TAtomic::Reference(ttype) => ttype.is_expandable(),
890 TAtomic::GenericParameter(ttype) => ttype.is_expandable(),
891 TAtomic::Iterable(ttype) => ttype.is_expandable(),
892 TAtomic::Array(ttype) => ttype.is_expandable(),
893 TAtomic::Callable(ttype) => ttype.is_expandable(),
894 TAtomic::Conditional(ttype) => ttype.is_expandable(),
895 TAtomic::Derived(ttype) => ttype.is_expandable(),
896 TAtomic::Scalar(ttype) => ttype.is_expandable(),
897 TAtomic::Mixed(ttype) => ttype.is_expandable(),
898 TAtomic::Resource(ttype) => ttype.is_expandable(),
899 _ => false,
900 }
901 }
902
903 fn get_id(&self) -> Atom {
904 match self {
905 TAtomic::Scalar(scalar) => scalar.get_id(),
906 TAtomic::Array(array) => array.get_id(),
907 TAtomic::Callable(callable) => callable.get_id(),
908 TAtomic::Object(object) => object.get_id(),
909 TAtomic::Reference(reference) => reference.get_id(),
910 TAtomic::Mixed(mixed) => mixed.get_id(),
911 TAtomic::Resource(resource) => resource.get_id(),
912 TAtomic::Iterable(iterable) => iterable.get_id(),
913 TAtomic::GenericParameter(parameter) => parameter.get_id(),
914 TAtomic::Conditional(conditional) => conditional.get_id(),
915 TAtomic::Derived(derived) => derived.get_id(),
916 TAtomic::Variable(name) => *name,
917 TAtomic::Never => atom("never"),
918 TAtomic::Null => atom("null"),
919 TAtomic::Void => atom("void"),
920 TAtomic::Placeholder => atom("_"),
921 }
922 }
923}
924
925pub fn populate_atomic_type(
926 unpopulated_atomic: &mut TAtomic,
927 codebase_symbols: &Symbols,
928 reference_source: Option<&ReferenceSource>,
929 symbol_references: &mut SymbolReferences,
930 force: bool,
931) {
932 match unpopulated_atomic {
933 TAtomic::Array(array) => match array {
934 TArray::List(list) => {
935 populate_union_type(
936 list.element_type.as_mut(),
937 codebase_symbols,
938 reference_source,
939 symbol_references,
940 force,
941 );
942
943 if let Some(known_elements) = list.known_elements.as_mut() {
944 for (_, element_type) in known_elements.values_mut() {
945 populate_union_type(element_type, codebase_symbols, reference_source, symbol_references, force);
946 }
947 }
948 }
949 TArray::Keyed(keyed_array) => {
950 if let Some(known_items) = keyed_array.known_items.as_mut() {
951 for (_, item_type) in known_items.values_mut() {
952 populate_union_type(item_type, codebase_symbols, reference_source, symbol_references, force);
953 }
954 }
955
956 if let Some(parameters) = &mut keyed_array.parameters {
957 populate_union_type(
958 parameters.0.as_mut(),
959 codebase_symbols,
960 reference_source,
961 symbol_references,
962 force,
963 );
964
965 populate_union_type(
966 parameters.1.as_mut(),
967 codebase_symbols,
968 reference_source,
969 symbol_references,
970 force,
971 );
972 }
973 }
974 },
975 TAtomic::Callable(TCallable::Signature(signature)) => {
976 if let Some(return_type) = signature.get_return_type_mut() {
977 populate_union_type(return_type, codebase_symbols, reference_source, symbol_references, force);
978 }
979
980 for param in signature.get_parameters_mut() {
981 if let Some(param_type) = param.get_type_signature_mut() {
982 populate_union_type(param_type, codebase_symbols, reference_source, symbol_references, force);
983 }
984 }
985 }
986 TAtomic::Object(TObject::Named(named_object)) => {
987 let name = named_object.get_name();
988
989 if !named_object.is_intersection()
990 && !named_object.has_type_parameters()
991 && codebase_symbols.contains_enum(&name)
992 {
993 *unpopulated_atomic = TAtomic::Object(TObject::new_enum(name));
994 } else {
995 if let Some(type_parameters) = named_object.get_type_parameters_mut() {
996 for parameter in type_parameters {
997 populate_union_type(parameter, codebase_symbols, reference_source, symbol_references, force);
998 }
999 }
1000
1001 if let Some(intersection_types) = named_object.get_intersection_types_mut() {
1002 for intersection_type in intersection_types {
1003 populate_atomic_type(
1004 intersection_type,
1005 codebase_symbols,
1006 reference_source,
1007 symbol_references,
1008 force,
1009 );
1010 }
1011 }
1012 }
1013
1014 if let Some(reference_source) = reference_source {
1015 match reference_source {
1016 ReferenceSource::Symbol(in_signature, a) => {
1017 symbol_references.add_symbol_reference_to_symbol(*a, name, *in_signature)
1018 }
1019 ReferenceSource::ClassLikeMember(in_signature, a, b) => {
1020 symbol_references.add_class_member_reference_to_symbol((*a, *b), name, *in_signature)
1021 }
1022 }
1023 }
1024 }
1025 TAtomic::Iterable(iterable) => {
1026 populate_union_type(
1027 iterable.get_key_type_mut(),
1028 codebase_symbols,
1029 reference_source,
1030 symbol_references,
1031 force,
1032 );
1033
1034 populate_union_type(
1035 iterable.get_value_type_mut(),
1036 codebase_symbols,
1037 reference_source,
1038 symbol_references,
1039 force,
1040 );
1041
1042 if let Some(intersection_types) = iterable.get_intersection_types_mut() {
1043 for intersection_type in intersection_types {
1044 populate_atomic_type(
1045 intersection_type,
1046 codebase_symbols,
1047 reference_source,
1048 symbol_references,
1049 force,
1050 );
1051 }
1052 }
1053 }
1054 TAtomic::Reference(reference) => match reference {
1055 TReference::Symbol { name, parameters, intersection_types } => {
1056 if let Some(parameters) = parameters {
1057 for parameter in parameters {
1058 populate_union_type(parameter, codebase_symbols, reference_source, symbol_references, force);
1059 }
1060 }
1061
1062 if let Some(reference_source) = reference_source {
1063 match reference_source {
1064 ReferenceSource::Symbol(in_signature, a) => {
1065 symbol_references.add_symbol_reference_to_symbol(*a, *name, *in_signature)
1066 }
1067 ReferenceSource::ClassLikeMember(in_signature, a, b) => {
1068 symbol_references.add_class_member_reference_to_symbol((*a, *b), *name, *in_signature)
1069 }
1070 }
1071 }
1072
1073 if let Some(symbol_kind) = codebase_symbols.get_kind(&ascii_lowercase_atom(name)) {
1074 match symbol_kind {
1075 SymbolKind::Enum => {
1076 *unpopulated_atomic = TAtomic::Object(TObject::new_enum(*name));
1077 }
1078 _ => {
1079 let intersection_types = intersection_types.take().map(|intersection_types| {
1080 intersection_types
1081 .into_iter()
1082 .map(|mut intersection_type| {
1083 populate_atomic_type(
1084 &mut intersection_type,
1085 codebase_symbols,
1086 reference_source,
1087 symbol_references,
1088 force,
1089 );
1090
1091 intersection_type
1092 })
1093 .collect::<Vec<_>>()
1094 });
1095
1096 let mut named_object = TNamedObject::new(*name).with_type_parameters(parameters.clone());
1097 if let Some(intersection_types) = intersection_types {
1098 for intersection_type in intersection_types {
1099 named_object.add_intersection_type(intersection_type);
1100 }
1101 }
1102
1103 *unpopulated_atomic = TAtomic::Object(TObject::Named(named_object));
1104 }
1105 }
1106 }
1107 }
1108 TReference::Member { class_like_name, member_selector } => {
1109 if let TReferenceMemberSelector::Identifier(member_name) = member_selector
1110 && let Some(reference_source) = reference_source
1111 {
1112 match reference_source {
1113 ReferenceSource::Symbol(in_signature, a) => symbol_references
1114 .add_symbol_reference_to_class_member(*a, (*class_like_name, *member_name), *in_signature),
1115 ReferenceSource::ClassLikeMember(in_signature, a, b) => symbol_references
1116 .add_class_member_reference_to_class_member(
1117 (*a, *b),
1118 (*class_like_name, *member_name),
1119 *in_signature,
1120 ),
1121 }
1122 }
1123 }
1124 },
1125 TAtomic::GenericParameter(TGenericParameter { constraint, intersection_types, .. }) => {
1126 populate_union_type(constraint.as_mut(), codebase_symbols, reference_source, symbol_references, force);
1127
1128 if let Some(intersection_types) = intersection_types.as_mut() {
1129 for intersection_type in intersection_types {
1130 populate_atomic_type(
1131 intersection_type,
1132 codebase_symbols,
1133 reference_source,
1134 symbol_references,
1135 force,
1136 );
1137 }
1138 }
1139 }
1140 TAtomic::Scalar(TScalar::ClassLikeString(
1141 TClassLikeString::OfType { constraint, .. } | TClassLikeString::Generic { constraint, .. },
1142 )) => {
1143 populate_atomic_type(constraint.as_mut(), codebase_symbols, reference_source, symbol_references, force);
1144 }
1145 TAtomic::Conditional(conditional) => {
1146 populate_union_type(
1147 conditional.get_subject_mut(),
1148 codebase_symbols,
1149 reference_source,
1150 symbol_references,
1151 force,
1152 );
1153
1154 populate_union_type(
1155 conditional.get_target_mut(),
1156 codebase_symbols,
1157 reference_source,
1158 symbol_references,
1159 force,
1160 );
1161
1162 populate_union_type(
1163 conditional.get_then_mut(),
1164 codebase_symbols,
1165 reference_source,
1166 symbol_references,
1167 force,
1168 );
1169
1170 populate_union_type(
1171 conditional.get_otherwise_mut(),
1172 codebase_symbols,
1173 reference_source,
1174 symbol_references,
1175 force,
1176 );
1177 }
1178 TAtomic::Derived(derived) => {
1179 populate_atomic_type(
1180 derived.get_target_type_mut(),
1181 codebase_symbols,
1182 reference_source,
1183 symbol_references,
1184 force,
1185 );
1186 }
1187 _ => {}
1188 }
1189}