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