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