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