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