1use std::borrow::Cow;
2use std::rc::Rc;
3use std::sync::Arc;
4
5use mago_atom::Atom;
6use mago_atom::atom;
7
8use crate::metadata::CodebaseMetadata;
9use crate::metadata::class_like::ClassLikeMetadata;
10use crate::misc::GenericParent;
11use crate::ttype::atomic::TAtomic;
12use crate::ttype::atomic::array::TArray;
13use crate::ttype::atomic::array::keyed::TKeyedArray;
14use crate::ttype::atomic::array::list::TList;
15use crate::ttype::atomic::generic::TGenericParameter;
16use crate::ttype::atomic::iterable::TIterable;
17use crate::ttype::atomic::object::TObject;
18use crate::ttype::atomic::object::named::TNamedObject;
19use crate::ttype::atomic::scalar::TScalar;
20use crate::ttype::atomic::scalar::class_like_string::TClassLikeString;
21use crate::ttype::atomic::scalar::class_like_string::TClassLikeStringKind;
22use crate::ttype::atomic::scalar::int::TInteger;
23use crate::ttype::atomic::scalar::string::TString;
24use crate::ttype::atomic::scalar::string::TStringCasing;
25use crate::ttype::atomic::scalar::string::TStringLiteral;
26use crate::ttype::comparator::ComparisonResult;
27use crate::ttype::comparator::union_comparator;
28use crate::ttype::expander::TypeExpansionOptions;
29use crate::ttype::resolution::TypeResolutionContext;
30use crate::ttype::shared::ARRAYKEY_ATOMIC;
31use crate::ttype::shared::BOOL_ATOMIC;
32use crate::ttype::shared::CALLABLE_STRING_ATOMIC;
33use crate::ttype::shared::CLASS_STRING_ATOMIC;
34use crate::ttype::shared::CLOSED_RESOURCE_ATOMIC;
35use crate::ttype::shared::EMPTY_KEYED_ARRAY_ATOMIC;
36use crate::ttype::shared::EMPTY_STRING_ATOMIC;
37use crate::ttype::shared::ENUM_STRING_ATOMIC;
38use crate::ttype::shared::FALSE_ATOMIC;
39use crate::ttype::shared::FLOAT_ATOMIC;
40use crate::ttype::shared::INT_ATOMIC;
41use crate::ttype::shared::INT_FLOAT_ATOMIC_SLICE;
42use crate::ttype::shared::INT_STRING_ATOMIC_SLICE;
43use crate::ttype::shared::INTERFACE_STRING_ATOMIC;
44use crate::ttype::shared::ISSET_FROM_LOOP_MIXED_ATOMIC;
45use crate::ttype::shared::LOWERCASE_CALLABLE_STRING_ATOMIC;
46use crate::ttype::shared::LOWERCASE_STRING_ATOMIC;
47use crate::ttype::shared::MINUS_ONE_INT_ATOMIC;
48use crate::ttype::shared::MIXED_ATOMIC;
49use crate::ttype::shared::MIXED_CALLABLE_ATOMIC;
50use crate::ttype::shared::MIXED_CLOSURE_ATOMIC;
51use crate::ttype::shared::MIXED_ITERABLE_ATOMIC;
52use crate::ttype::shared::NEGATIVE_INT_ATOMIC;
53use crate::ttype::shared::NEVER_ATOMIC;
54use crate::ttype::shared::NON_EMPTY_LOWERCASE_STRING_ATOMIC;
55use crate::ttype::shared::NON_EMPTY_STRING_ATOMIC;
56use crate::ttype::shared::NON_EMPTY_UNSPECIFIED_LITERAL_STRING_ATOMIC;
57use crate::ttype::shared::NON_EMPTY_UPPERCASE_STRING_ATOMIC;
58use crate::ttype::shared::NON_NEGATIVE_INT_ATOMIC;
59use crate::ttype::shared::NON_POSITIVE_INT_ATOMIC;
60use crate::ttype::shared::NULL_ATOMIC;
61use crate::ttype::shared::NULL_FLOAT_ATOMIC_SLICE;
62use crate::ttype::shared::NULL_INT_ATOMIC_SLICE;
63use crate::ttype::shared::NULL_OBJECT_ATOMIC_SLICE;
64use crate::ttype::shared::NULL_SCALAR_ATOMIC_SLICE;
65use crate::ttype::shared::NULL_STRING_ATOMIC_SLICE;
66use crate::ttype::shared::NUMERIC_ATOMIC;
67use crate::ttype::shared::NUMERIC_STRING_ATOMIC;
68use crate::ttype::shared::NUMERIC_TRUTHY_STRING_ATOMIC;
69use crate::ttype::shared::OBJECT_ATOMIC;
70use crate::ttype::shared::ONE_INT_ATOMIC;
71use crate::ttype::shared::OPEN_RESOURCE_ATOMIC;
72use crate::ttype::shared::PLACEHOLDER_ATOMIC;
73use crate::ttype::shared::POSITIVE_INT_ATOMIC;
74use crate::ttype::shared::RESOURCE_ATOMIC;
75use crate::ttype::shared::SCALAR_ATOMIC;
76use crate::ttype::shared::SIGNUM_RESULT_SLICE;
77use crate::ttype::shared::STRING_ATOMIC;
78use crate::ttype::shared::TRAIT_STRING_ATOMIC;
79use crate::ttype::shared::TRUE_ATOMIC;
80use crate::ttype::shared::TRUTHY_LOWERCASE_STRING_ATOMIC;
81use crate::ttype::shared::TRUTHY_MIXED_ATOMIC;
82use crate::ttype::shared::TRUTHY_STRING_ATOMIC;
83use crate::ttype::shared::TRUTHY_UPPERCASE_STRING_ATOMIC;
84use crate::ttype::shared::UNSPECIFIED_LITERAL_FLOAT_ATOMIC;
85use crate::ttype::shared::UNSPECIFIED_LITERAL_INT_ATOMIC;
86use crate::ttype::shared::UNSPECIFIED_LITERAL_STRING_ATOMIC;
87use crate::ttype::shared::UPPERCASE_CALLABLE_STRING_ATOMIC;
88use crate::ttype::shared::UPPERCASE_STRING_ATOMIC;
89use crate::ttype::shared::VOID_ATOMIC;
90use crate::ttype::shared::ZERO_INT_ATOMIC;
91use crate::ttype::template::TemplateResult;
92use crate::ttype::template::inferred_type_replacer;
93use crate::ttype::union::TUnion;
94
95pub mod atomic;
96pub mod builder;
97pub mod cast;
98pub mod combination;
99pub mod combiner;
100pub mod comparator;
101pub mod error;
102pub mod expander;
103pub mod flags;
104pub mod resolution;
105pub mod shared;
106pub mod template;
107pub mod union;
108
109#[derive(Clone, Copy, Debug)]
111pub enum TypeRef<'a> {
112 Union(&'a TUnion),
113 Atomic(&'a TAtomic),
114}
115
116pub trait TType {
118 fn get_child_nodes(&self) -> Vec<TypeRef<'_>> {
120 vec![]
121 }
122
123 fn get_all_child_nodes(&self) -> Vec<TypeRef<'_>> {
125 let mut child_nodes = self.get_child_nodes();
126 let mut all_child_nodes = Vec::with_capacity(16);
127
128 while let Some(child_node) = child_nodes.pop() {
129 let new_child_nodes = match child_node {
130 TypeRef::Union(union) => union.get_child_nodes(),
131 TypeRef::Atomic(atomic) => atomic.get_child_nodes(),
132 };
133
134 all_child_nodes.push(child_node);
135
136 child_nodes.extend(new_child_nodes);
137 }
138
139 all_child_nodes
140 }
141
142 fn can_be_intersected(&self) -> bool {
144 false
145 }
146
147 fn get_intersection_types(&self) -> Option<&[TAtomic]> {
149 None
150 }
151
152 fn get_intersection_types_mut(&mut self) -> Option<&mut Vec<TAtomic>> {
154 None
155 }
156
157 fn has_intersection_types(&self) -> bool {
159 false
160 }
161
162 fn add_intersection_type(&mut self, _intersection_type: TAtomic) -> bool {
167 false
168 }
169
170 fn needs_population(&self) -> bool;
171
172 fn is_expandable(&self) -> bool;
173
174 fn is_complex(&self) -> bool;
177
178 fn get_id(&self) -> Atom;
184
185 fn get_pretty_id(&self) -> Atom {
186 self.get_pretty_id_with_indent(0)
187 }
188
189 fn get_pretty_id_with_indent(&self, indent: usize) -> Atom;
190}
191
192impl<'a> TType for TypeRef<'a> {
194 fn get_child_nodes(&self) -> Vec<TypeRef<'a>> {
195 match self {
196 TypeRef::Union(ttype) => ttype.get_child_nodes(),
197 TypeRef::Atomic(ttype) => ttype.get_child_nodes(),
198 }
199 }
200
201 fn can_be_intersected(&self) -> bool {
202 match self {
203 TypeRef::Union(ttype) => ttype.can_be_intersected(),
204 TypeRef::Atomic(ttype) => ttype.can_be_intersected(),
205 }
206 }
207
208 fn get_intersection_types(&self) -> Option<&[TAtomic]> {
209 match self {
210 TypeRef::Union(ttype) => ttype.get_intersection_types(),
211 TypeRef::Atomic(ttype) => ttype.get_intersection_types(),
212 }
213 }
214
215 fn has_intersection_types(&self) -> bool {
216 match self {
217 TypeRef::Union(ttype) => ttype.has_intersection_types(),
218 TypeRef::Atomic(ttype) => ttype.has_intersection_types(),
219 }
220 }
221
222 fn needs_population(&self) -> bool {
223 match self {
224 TypeRef::Union(ttype) => ttype.needs_population(),
225 TypeRef::Atomic(ttype) => ttype.needs_population(),
226 }
227 }
228
229 fn is_expandable(&self) -> bool {
230 match self {
231 TypeRef::Union(ttype) => ttype.is_expandable(),
232 TypeRef::Atomic(ttype) => ttype.is_expandable(),
233 }
234 }
235
236 fn is_complex(&self) -> bool {
237 match self {
238 TypeRef::Union(ttype) => ttype.is_complex(),
239 TypeRef::Atomic(ttype) => ttype.is_complex(),
240 }
241 }
242
243 fn get_id(&self) -> Atom {
244 match self {
245 TypeRef::Union(ttype) => ttype.get_id(),
246 TypeRef::Atomic(ttype) => ttype.get_id(),
247 }
248 }
249
250 fn get_pretty_id_with_indent(&self, indent: usize) -> Atom {
251 match self {
252 TypeRef::Union(ttype) => ttype.get_pretty_id_with_indent(indent),
253 TypeRef::Atomic(ttype) => ttype.get_pretty_id_with_indent(indent),
254 }
255 }
256}
257
258impl<'a> From<&'a TUnion> for TypeRef<'a> {
259 fn from(reference: &'a TUnion) -> Self {
260 TypeRef::Union(reference)
261 }
262}
263
264impl<'a> From<&'a TAtomic> for TypeRef<'a> {
265 fn from(reference: &'a TAtomic) -> Self {
266 TypeRef::Atomic(reference)
267 }
268}
269
270#[must_use]
281pub fn get_union_from_integer(integer: &TInteger) -> TUnion {
282 if integer.is_unspecified() {
283 return get_int();
284 }
285
286 if integer.is_positive() {
287 return get_positive_int();
288 }
289
290 if integer.is_negative() {
291 return get_negative_int();
292 }
293
294 if integer.is_non_negative() {
295 return get_non_negative_int();
296 }
297
298 if integer.is_non_positive() {
299 return get_non_positive_int();
300 }
301
302 TUnion::from_single(Cow::Owned(TAtomic::Scalar(TScalar::Integer(*integer))))
303}
304
305#[inline]
306#[must_use]
307pub fn wrap_atomic(tinner: TAtomic) -> TUnion {
308 TUnion::from_single(Cow::Owned(tinner))
309}
310
311#[inline]
312#[must_use]
313pub fn get_int() -> TUnion {
314 TUnion::from_single(Cow::Borrowed(INT_ATOMIC))
315}
316
317#[inline]
318#[must_use]
319pub fn get_positive_int() -> TUnion {
320 TUnion::from_single(Cow::Borrowed(POSITIVE_INT_ATOMIC))
321}
322
323#[inline]
324#[must_use]
325pub fn get_negative_int() -> TUnion {
326 TUnion::from_single(Cow::Borrowed(NEGATIVE_INT_ATOMIC))
327}
328
329#[inline]
330#[must_use]
331pub fn get_non_positive_int() -> TUnion {
332 TUnion::from_single(Cow::Borrowed(NON_POSITIVE_INT_ATOMIC))
333}
334
335#[inline]
336#[must_use]
337pub fn get_non_negative_int() -> TUnion {
338 TUnion::from_single(Cow::Borrowed(NON_NEGATIVE_INT_ATOMIC))
339}
340
341#[inline]
342#[must_use]
343pub fn get_unspecified_literal_int() -> TUnion {
344 TUnion::from_single(Cow::Borrowed(UNSPECIFIED_LITERAL_INT_ATOMIC))
345}
346
347#[inline]
348#[must_use]
349pub fn get_unspecified_literal_float() -> TUnion {
350 TUnion::from_single(Cow::Borrowed(UNSPECIFIED_LITERAL_FLOAT_ATOMIC))
351}
352
353#[inline]
354#[must_use]
355pub fn get_int_range(from: Option<i64>, to: Option<i64>) -> TUnion {
356 let atomic = match (from, to) {
357 (Some(from), Some(to)) => TAtomic::Scalar(TScalar::Integer(TInteger::Range(from, to))),
358 (Some(from), None) => {
359 if 0 == from {
360 return get_non_negative_int();
361 }
362
363 if 1 == from {
364 return get_positive_int();
365 }
366
367 TAtomic::Scalar(TScalar::Integer(TInteger::From(from)))
368 }
369 (None, Some(to)) => {
370 if 0 == to {
371 return get_non_positive_int();
372 }
373
374 if -1 == to {
375 return get_negative_int();
376 }
377
378 TAtomic::Scalar(TScalar::Integer(TInteger::To(to)))
379 }
380 (None, None) => return get_int(),
381 };
382
383 TUnion::from_single(Cow::Owned(atomic))
384}
385
386#[inline]
388#[must_use]
389pub fn get_signum_result() -> TUnion {
390 TUnion::new(Cow::Borrowed(SIGNUM_RESULT_SLICE))
391}
392
393#[inline]
395#[must_use]
396pub fn get_one_int() -> TUnion {
397 TUnion::from_single(Cow::Borrowed(ONE_INT_ATOMIC))
398}
399
400#[inline]
402#[must_use]
403pub fn get_zero_int() -> TUnion {
404 TUnion::from_single(Cow::Borrowed(ZERO_INT_ATOMIC))
405}
406
407#[inline]
409#[must_use]
410pub fn get_minus_one_int() -> TUnion {
411 TUnion::from_single(Cow::Borrowed(MINUS_ONE_INT_ATOMIC))
412}
413
414#[inline]
415#[must_use]
416pub fn get_literal_int(value: i64) -> TUnion {
417 if value == 0 {
418 return get_zero_int();
419 }
420
421 if value == 1 {
422 return get_one_int();
423 }
424
425 if value == -1 {
426 return get_minus_one_int();
427 }
428
429 TUnion::from_single(Cow::Owned(TAtomic::Scalar(TScalar::literal_int(value))))
430}
431
432#[inline]
433#[must_use]
434pub fn get_int_or_float() -> TUnion {
435 TUnion::new(Cow::Borrowed(INT_FLOAT_ATOMIC_SLICE))
436}
437
438#[inline]
439#[must_use]
440pub fn get_int_or_string() -> TUnion {
441 TUnion::new(Cow::Borrowed(INT_STRING_ATOMIC_SLICE))
442}
443
444#[inline]
445#[must_use]
446pub fn get_nullable_int() -> TUnion {
447 TUnion::new(Cow::Borrowed(NULL_INT_ATOMIC_SLICE))
448}
449
450#[inline]
451#[must_use]
452pub fn get_nullable_float() -> TUnion {
453 TUnion::new(Cow::Borrowed(NULL_FLOAT_ATOMIC_SLICE))
454}
455
456#[inline]
457#[must_use]
458pub fn get_nullable_object() -> TUnion {
459 TUnion::new(Cow::Borrowed(NULL_OBJECT_ATOMIC_SLICE))
460}
461
462#[inline]
463#[must_use]
464pub fn get_nullable_string() -> TUnion {
465 TUnion::new(Cow::Borrowed(NULL_STRING_ATOMIC_SLICE))
466}
467
468#[inline]
469#[must_use]
470pub fn get_string() -> TUnion {
471 TUnion::from_single(Cow::Borrowed(STRING_ATOMIC))
472}
473
474#[must_use]
479pub fn get_string_with_props(
480 is_numeric: bool,
481 is_truthy: bool,
482 is_non_empty: bool,
483 is_callable: bool,
484 casing: TStringCasing,
485) -> TUnion {
486 if is_callable {
487 return match casing {
488 TStringCasing::Lowercase => TUnion::from_single(Cow::Borrowed(LOWERCASE_CALLABLE_STRING_ATOMIC)),
489 TStringCasing::Uppercase => TUnion::from_single(Cow::Borrowed(UPPERCASE_CALLABLE_STRING_ATOMIC)),
490 TStringCasing::Unspecified => TUnion::from_single(Cow::Borrowed(CALLABLE_STRING_ATOMIC)),
491 };
492 }
493
494 let atomic_ref = match (is_numeric, is_truthy, is_non_empty, casing) {
495 (true, true, _, _) => NUMERIC_TRUTHY_STRING_ATOMIC,
497 (true, false, _, _) => NUMERIC_STRING_ATOMIC,
498 (false, true, _, TStringCasing::Unspecified) => TRUTHY_STRING_ATOMIC,
500 (false, true, _, TStringCasing::Uppercase) => TRUTHY_UPPERCASE_STRING_ATOMIC,
501 (false, true, _, TStringCasing::Lowercase) => TRUTHY_LOWERCASE_STRING_ATOMIC,
502 (false, false, false, TStringCasing::Unspecified) => STRING_ATOMIC,
504 (false, false, false, TStringCasing::Uppercase) => UPPERCASE_STRING_ATOMIC,
505 (false, false, false, TStringCasing::Lowercase) => LOWERCASE_STRING_ATOMIC,
506 (false, false, true, TStringCasing::Unspecified) => NON_EMPTY_STRING_ATOMIC,
507 (false, false, true, TStringCasing::Uppercase) => NON_EMPTY_UPPERCASE_STRING_ATOMIC,
508 (false, false, true, TStringCasing::Lowercase) => NON_EMPTY_LOWERCASE_STRING_ATOMIC,
509 };
510
511 TUnion::from_single(Cow::Borrowed(atomic_ref))
512}
513
514#[inline]
515#[must_use]
516pub fn get_literal_class_string(value: Atom) -> TUnion {
517 TUnion::from_single(Cow::Owned(TAtomic::Scalar(TScalar::ClassLikeString(TClassLikeString::literal(value)))))
518}
519
520#[inline]
521#[must_use]
522pub fn get_class_string() -> TUnion {
523 TUnion::from_single(Cow::Borrowed(CLASS_STRING_ATOMIC))
524}
525
526#[inline]
527#[must_use]
528pub fn get_class_string_of_type(constraint: TAtomic) -> TUnion {
529 TUnion::from_single(Cow::Owned(TAtomic::Scalar(TScalar::ClassLikeString(TClassLikeString::class_string_of_type(
530 constraint,
531 )))))
532}
533
534#[inline]
535#[must_use]
536pub fn get_interface_string() -> TUnion {
537 TUnion::from_single(Cow::Borrowed(INTERFACE_STRING_ATOMIC))
538}
539
540#[inline]
541#[must_use]
542pub fn get_interface_string_of_type(constraint: TAtomic) -> TUnion {
543 TUnion::from_single(Cow::Owned(TAtomic::Scalar(TScalar::ClassLikeString(
544 TClassLikeString::interface_string_of_type(constraint),
545 ))))
546}
547
548#[inline]
549#[must_use]
550pub fn get_enum_string() -> TUnion {
551 TUnion::from_single(Cow::Borrowed(ENUM_STRING_ATOMIC))
552}
553
554#[inline]
555#[must_use]
556pub fn get_enum_string_of_type(constraint: TAtomic) -> TUnion {
557 TUnion::from_single(Cow::Owned(TAtomic::Scalar(TScalar::ClassLikeString(TClassLikeString::enum_string_of_type(
558 constraint,
559 )))))
560}
561
562#[inline]
563#[must_use]
564pub fn get_trait_string() -> TUnion {
565 TUnion::from_single(Cow::Borrowed(TRAIT_STRING_ATOMIC))
566}
567
568#[inline]
569#[must_use]
570pub fn get_trait_string_of_type(constraint: TAtomic) -> TUnion {
571 TUnion::from_single(Cow::Owned(TAtomic::Scalar(TScalar::ClassLikeString(TClassLikeString::trait_string_of_type(
572 constraint,
573 )))))
574}
575
576#[inline]
577#[must_use]
578pub fn get_literal_string(value: Atom) -> TUnion {
579 TUnion::from_single(Cow::Owned(TAtomic::Scalar(TScalar::literal_string(value))))
580}
581
582#[inline]
583#[must_use]
584pub fn get_float() -> TUnion {
585 TUnion::from_single(Cow::Borrowed(FLOAT_ATOMIC))
586}
587
588#[inline]
589#[must_use]
590pub fn get_literal_float(v: f64) -> TUnion {
591 TUnion::from_single(Cow::Owned(TAtomic::Scalar(TScalar::literal_float(v))))
592}
593
594#[inline]
595#[must_use]
596pub fn get_mixed() -> TUnion {
597 TUnion::from_single(Cow::Borrowed(MIXED_ATOMIC))
598}
599
600#[inline]
601#[must_use]
602pub fn get_truthy_mixed() -> TUnion {
603 TUnion::from_single(Cow::Borrowed(TRUTHY_MIXED_ATOMIC))
604}
605
606#[inline]
607#[must_use]
608pub fn get_isset_from_mixed_mixed() -> TUnion {
609 TUnion::from_single(Cow::Borrowed(ISSET_FROM_LOOP_MIXED_ATOMIC))
610}
611
612#[must_use]
613pub fn get_mixed_maybe_from_loop(from_loop_isset: bool) -> TUnion {
614 if from_loop_isset { get_isset_from_mixed_mixed() } else { get_mixed() }
615}
616
617#[inline]
618#[must_use]
619pub fn get_never() -> TUnion {
620 TUnion::from_single(Cow::Borrowed(NEVER_ATOMIC))
621}
622
623#[inline]
624#[must_use]
625pub fn get_resource() -> TUnion {
626 TUnion::from_single(Cow::Borrowed(RESOURCE_ATOMIC))
627}
628
629#[inline]
630#[must_use]
631pub fn get_closed_resource() -> TUnion {
632 TUnion::from_single(Cow::Borrowed(CLOSED_RESOURCE_ATOMIC))
633}
634
635#[inline]
636#[must_use]
637pub fn get_open_resource() -> TUnion {
638 TUnion::from_single(Cow::Borrowed(OPEN_RESOURCE_ATOMIC))
639}
640
641#[inline]
642#[must_use]
643pub fn get_placeholder() -> TUnion {
644 TUnion::from_single(Cow::Borrowed(PLACEHOLDER_ATOMIC))
645}
646
647#[inline]
648#[must_use]
649pub fn get_void() -> TUnion {
650 TUnion::from_single(Cow::Borrowed(VOID_ATOMIC))
651}
652
653#[inline]
654#[must_use]
655pub fn get_null() -> TUnion {
656 TUnion::from_single(Cow::Borrowed(NULL_ATOMIC))
657}
658
659#[inline]
660#[must_use]
661pub fn get_undefined_null() -> TUnion {
662 let mut null = TUnion::from_single(Cow::Borrowed(NULL_ATOMIC));
663 null.set_possibly_undefined(true, None);
664 null
665}
666
667#[inline]
668#[must_use]
669pub fn get_arraykey() -> TUnion {
670 TUnion::from_single(Cow::Borrowed(ARRAYKEY_ATOMIC))
671}
672
673#[inline]
674#[must_use]
675pub fn get_bool() -> TUnion {
676 TUnion::from_single(Cow::Borrowed(BOOL_ATOMIC))
677}
678
679#[inline]
680#[must_use]
681pub fn get_false() -> TUnion {
682 TUnion::from_single(Cow::Borrowed(FALSE_ATOMIC))
683}
684
685#[inline]
686#[must_use]
687pub fn get_true() -> TUnion {
688 TUnion::from_single(Cow::Borrowed(TRUE_ATOMIC))
689}
690
691#[inline]
692#[must_use]
693pub fn get_object() -> TUnion {
694 TUnion::from_single(Cow::Borrowed(OBJECT_ATOMIC))
695}
696
697#[inline]
698#[must_use]
699pub fn get_numeric() -> TUnion {
700 TUnion::from_single(Cow::Borrowed(NUMERIC_ATOMIC))
701}
702
703#[inline]
704#[must_use]
705pub fn get_callable_string() -> TUnion {
706 TUnion::from_single(Cow::Borrowed(CALLABLE_STRING_ATOMIC))
707}
708
709pub fn get_numeric_string() -> TUnion {
710 TUnion::from_single(Cow::Borrowed(NUMERIC_STRING_ATOMIC))
711}
712
713#[inline]
714#[must_use]
715pub fn get_lowercase_string() -> TUnion {
716 TUnion::from_single(Cow::Borrowed(LOWERCASE_STRING_ATOMIC))
717}
718
719#[inline]
720#[must_use]
721pub fn get_non_empty_lowercase_string() -> TUnion {
722 TUnion::from_single(Cow::Borrowed(NON_EMPTY_LOWERCASE_STRING_ATOMIC))
723}
724
725#[inline]
726#[must_use]
727pub fn get_uppercase_string() -> TUnion {
728 TUnion::from_single(Cow::Borrowed(UPPERCASE_STRING_ATOMIC))
729}
730
731#[inline]
732#[must_use]
733pub fn get_non_empty_uppercase_string() -> TUnion {
734 TUnion::from_single(Cow::Borrowed(NON_EMPTY_UPPERCASE_STRING_ATOMIC))
735}
736
737#[inline]
738#[must_use]
739pub fn get_non_empty_string() -> TUnion {
740 TUnion::from_single(Cow::Borrowed(NON_EMPTY_STRING_ATOMIC))
741}
742
743#[inline]
744#[must_use]
745pub fn get_empty_string() -> TUnion {
746 TUnion::from_single(Cow::Borrowed(&EMPTY_STRING_ATOMIC))
747}
748
749#[inline]
750#[must_use]
751pub fn get_truthy_string() -> TUnion {
752 TUnion::from_single(Cow::Borrowed(TRUTHY_STRING_ATOMIC))
753}
754
755#[inline]
756#[must_use]
757pub fn get_unspecified_literal_string() -> TUnion {
758 TUnion::from_single(Cow::Borrowed(UNSPECIFIED_LITERAL_STRING_ATOMIC))
759}
760
761#[inline]
762#[must_use]
763pub fn get_non_empty_unspecified_literal_string() -> TUnion {
764 TUnion::from_single(Cow::Borrowed(NON_EMPTY_UNSPECIFIED_LITERAL_STRING_ATOMIC))
765}
766
767#[inline]
768#[must_use]
769pub fn get_scalar() -> TUnion {
770 TUnion::from_single(Cow::Borrowed(SCALAR_ATOMIC))
771}
772
773#[inline]
774#[must_use]
775pub fn get_nullable_scalar() -> TUnion {
776 TUnion::new(Cow::Borrowed(NULL_SCALAR_ATOMIC_SLICE))
777}
778
779#[inline]
780#[must_use]
781pub fn get_mixed_iterable() -> TUnion {
782 TUnion::from_single(Cow::Borrowed(&MIXED_ITERABLE_ATOMIC))
783}
784
785#[inline]
786#[must_use]
787pub fn get_empty_keyed_array() -> TUnion {
788 TUnion::from_single(Cow::Borrowed(&EMPTY_KEYED_ARRAY_ATOMIC))
789}
790
791#[inline]
792#[must_use]
793pub fn get_mixed_list() -> TUnion {
794 get_list(get_mixed())
795}
796
797#[inline]
798#[must_use]
799pub fn get_mixed_keyed_array() -> TUnion {
800 get_keyed_array(get_arraykey(), get_mixed())
801}
802
803#[inline]
804#[must_use]
805pub fn get_mixed_callable() -> TUnion {
806 TUnion::from_single(Cow::Borrowed(&MIXED_CALLABLE_ATOMIC))
807}
808
809#[inline]
810#[must_use]
811pub fn get_mixed_closure() -> TUnion {
812 TUnion::from_single(Cow::Borrowed(&MIXED_CLOSURE_ATOMIC))
813}
814
815#[inline]
816#[must_use]
817pub fn get_named_object(name: Atom, type_resolution_context: Option<&TypeResolutionContext>) -> TUnion {
818 if let Some(type_resolution_context) = type_resolution_context
819 && let Some(defining_entities) = type_resolution_context.get_template_definition(name)
820 {
821 let first = &defining_entities[0];
822 return wrap_atomic(TAtomic::Scalar(TScalar::ClassLikeString(TClassLikeString::Generic {
823 kind: TClassLikeStringKind::Class,
824 parameter_name: name,
825 defining_entity: first.defining_entity,
826 constraint: Arc::new((*(first.constraint.get_single())).clone()),
827 })));
828 }
829
830 wrap_atomic(TAtomic::Object(TObject::Named(TNamedObject::new(name))))
831}
832
833#[inline]
834#[must_use]
835pub fn get_iterable(key_parameter: TUnion, value_parameter: TUnion) -> TUnion {
836 wrap_atomic(TAtomic::Iterable(TIterable::new(Arc::new(key_parameter), Arc::new(value_parameter))))
837}
838
839#[inline]
840#[must_use]
841pub fn get_list(element_type: TUnion) -> TUnion {
842 wrap_atomic(TAtomic::Array(TArray::List(TList::new(Arc::new(element_type)))))
843}
844
845#[inline]
846#[must_use]
847pub fn get_non_empty_list(element_type: TUnion) -> TUnion {
848 wrap_atomic(TAtomic::Array(TArray::List(TList::new_non_empty(Arc::new(element_type)))))
849}
850
851#[inline]
852#[must_use]
853pub fn get_keyed_array(key_parameter: TUnion, value_parameter: TUnion) -> TUnion {
854 wrap_atomic(TAtomic::Array(TArray::Keyed(TKeyedArray::new_with_parameters(
855 Arc::new(key_parameter),
856 Arc::new(value_parameter),
857 ))))
858}
859
860#[inline]
861#[must_use]
862pub fn add_optional_union_type(base_type: TUnion, maybe_type: Option<&TUnion>, codebase: &CodebaseMetadata) -> TUnion {
863 if let Some(type_2) = maybe_type {
864 add_union_type(base_type, type_2, codebase, combiner::CombinerOptions::default())
865 } else {
866 base_type
867 }
868}
869
870#[must_use]
872pub fn add_optional_union_type_rc(
873 base_type: &Rc<TUnion>,
874 maybe_type: Option<&TUnion>,
875 codebase: &CodebaseMetadata,
876) -> Rc<TUnion> {
877 match maybe_type {
878 Some(type_2) => {
879 Rc::new(add_union_type((**base_type).clone(), type_2, codebase, combiner::CombinerOptions::default()))
880 }
881 None => Rc::clone(base_type),
882 }
883}
884
885#[inline]
886#[must_use]
887pub fn combine_optional_union_types(
888 type_1: Option<&TUnion>,
889 type_2: Option<&TUnion>,
890 codebase: &CodebaseMetadata,
891) -> TUnion {
892 match (type_1, type_2) {
893 (Some(type_1), Some(type_2)) => {
894 combine_union_types(type_1, type_2, codebase, combiner::CombinerOptions::default())
895 }
896 (Some(type_1), None) => type_1.clone(),
897 (None, Some(type_2)) => type_2.clone(),
898 (None, None) => get_mixed(),
899 }
900}
901
902#[inline]
904#[must_use]
905pub fn combine_union_types_rc(
906 type_1: &Rc<TUnion>,
907 type_2: &Rc<TUnion>,
908 codebase: &CodebaseMetadata,
909 options: combiner::CombinerOptions,
910) -> Rc<TUnion> {
911 if Rc::ptr_eq(type_1, type_2) {
912 return Rc::clone(type_1);
913 }
914
915 Rc::new(combine_union_types(type_1, type_2, codebase, options))
916}
917
918#[inline]
919#[must_use]
920pub fn combine_union_types(
921 type_1: &TUnion,
922 type_2: &TUnion,
923 codebase: &CodebaseMetadata,
924 options: combiner::CombinerOptions,
925) -> TUnion {
926 if type_1 == type_2 {
927 return type_1.clone();
928 }
929
930 let mut combined_type = if type_1.is_never() || type_1.is_never_template() {
931 type_2.clone()
932 } else if type_2.is_never() || type_2.is_never_template() {
933 type_1.clone()
934 } else if type_1.is_vanilla_mixed() && type_2.is_vanilla_mixed() {
935 get_mixed()
936 } else {
937 let mut all_atomic_types = type_1.types.to_vec();
938 all_atomic_types.extend(type_2.types.iter().cloned());
939
940 let mut result = TUnion::from_vec(combiner::combine(all_atomic_types, codebase, options));
941
942 if type_1.had_template() && type_2.had_template() {
943 result.set_had_template(true);
944 }
945
946 if type_1.reference_free() && type_2.reference_free() {
947 result.set_reference_free(true);
948 }
949
950 result
951 };
952
953 if type_1.possibly_undefined() || type_2.possibly_undefined() {
954 combined_type.set_possibly_undefined(true, None);
955 }
956
957 if type_1.possibly_undefined_from_try() || type_2.possibly_undefined_from_try() {
958 combined_type.set_possibly_undefined_from_try(true);
959 }
960
961 if type_1.ignore_falsable_issues() || type_2.ignore_falsable_issues() {
962 combined_type.set_ignore_falsable_issues(true);
963 }
964
965 combined_type
966}
967
968#[inline]
969#[must_use]
970pub fn add_union_type(
971 mut base_type: TUnion,
972 other_type: &TUnion,
973 codebase: &CodebaseMetadata,
974 options: combiner::CombinerOptions,
975) -> TUnion {
976 if &base_type != other_type {
977 base_type.types = if base_type.is_vanilla_mixed() && other_type.is_vanilla_mixed() {
978 base_type.types
979 } else {
980 combine_union_types(&base_type, other_type, codebase, options).types
981 };
982
983 if !other_type.had_template() {
984 base_type.set_had_template(false);
985 }
986
987 if !other_type.reference_free() {
988 base_type.set_reference_free(false);
989 }
990 }
991
992 if other_type.possibly_undefined() {
993 base_type.set_possibly_undefined(true, None);
994 }
995 if other_type.possibly_undefined_from_try() {
996 base_type.set_possibly_undefined_from_try(true);
997 }
998 if other_type.ignore_falsable_issues() {
999 base_type.set_ignore_falsable_issues(true);
1000 }
1001 if other_type.ignore_nullable_issues() {
1002 base_type.set_ignore_nullable_issues(true);
1003 }
1004
1005 base_type
1006}
1007
1008#[must_use]
1009pub fn intersect_union_types(type_1: &TUnion, type_2: &TUnion, codebase: &CodebaseMetadata) -> Option<TUnion> {
1010 if type_1 == type_2 {
1011 return Some(type_1.clone());
1012 }
1013
1014 if type_1.is_never() || type_2.is_never() {
1015 return Some(get_never());
1016 }
1017
1018 let mut intersection_performed = false;
1019
1020 if type_1.is_mixed() {
1021 if type_2.is_mixed() {
1022 return Some(get_mixed());
1023 }
1024
1025 return Some(type_2.clone());
1026 } else if type_2.is_mixed() {
1027 return Some(type_1.clone());
1028 }
1029
1030 let mut intersected_atomic_types = vec![];
1031 for type_1_atomic in type_1.types.iter() {
1032 for type_2_atomic in type_2.types.iter() {
1033 if let Some(intersection_atomic) =
1034 intersect_atomic_types(type_1_atomic, type_2_atomic, codebase, &mut intersection_performed)
1035 {
1036 intersected_atomic_types.push(intersection_atomic);
1037 }
1038 }
1039 }
1040
1041 let mut combined_type: Option<TUnion> = None;
1042 if !intersected_atomic_types.is_empty() {
1043 let combined_vec = combiner::combine(intersected_atomic_types, codebase, combiner::CombinerOptions::default());
1044 if !combined_vec.is_empty() {
1045 combined_type = Some(TUnion::from_vec(combined_vec));
1046 }
1047 }
1048
1049 if !intersection_performed {
1051 if union_comparator::is_contained_by(
1052 codebase,
1053 type_1,
1054 type_2,
1055 false,
1056 false,
1057 false,
1058 &mut ComparisonResult::default(),
1059 ) {
1060 intersection_performed = true;
1061 combined_type = Some(type_1.clone());
1062 } else if union_comparator::is_contained_by(
1063 codebase,
1064 type_2,
1065 type_1,
1066 false,
1067 false,
1068 false,
1069 &mut ComparisonResult::default(),
1070 ) {
1071 intersection_performed = true;
1072 combined_type = Some(type_2.clone());
1073 }
1074 }
1075
1076 if let Some(mut final_type) = combined_type {
1077 final_type.set_possibly_undefined(
1078 type_1.possibly_undefined() && type_2.possibly_undefined(),
1079 Some(type_1.possibly_undefined_from_try() && type_2.possibly_undefined_from_try()),
1080 );
1081 final_type.set_ignore_falsable_issues(type_1.ignore_falsable_issues() && type_2.ignore_falsable_issues());
1082 final_type.set_ignore_nullable_issues(type_1.ignore_nullable_issues() && type_2.ignore_nullable_issues());
1083
1084 return Some(final_type);
1085 }
1086
1087 if !intersection_performed && type_1.get_id() != type_2.get_id() {
1088 return None;
1089 }
1090
1091 None
1092}
1093
1094fn intersect_atomic_types(
1096 type_1: &TAtomic,
1097 type_2: &TAtomic,
1098 codebase: &CodebaseMetadata,
1099 intersection_performed: &mut bool,
1100) -> Option<TAtomic> {
1101 if let (TAtomic::Scalar(TScalar::Integer(t1_int)), TAtomic::Scalar(TScalar::Integer(t2_int))) = (type_1, type_2) {
1102 let (min1, max1) = t1_int.get_bounds();
1103 let (min2, max2) = t2_int.get_bounds();
1104
1105 let new_min = match (min1, min2) {
1106 (Some(m1), Some(m2)) => Some(m1.max(m2)),
1107 (Some(m), None) | (None, Some(m)) => Some(m),
1108 (None, None) => None,
1109 };
1110
1111 let new_max = match (max1, max2) {
1112 (Some(m1), Some(m2)) => Some(m1.min(m2)),
1113 (Some(m), None) | (None, Some(m)) => Some(m),
1114 (None, None) => None,
1115 };
1116
1117 let intersected_int = if let (Some(min), Some(max)) = (new_min, new_max) {
1118 if min > max {
1119 return None;
1120 }
1121
1122 if min == max { TInteger::Literal(min) } else { TInteger::Range(min, max) }
1123 } else if let Some(min) = new_min {
1124 TInteger::From(min)
1125 } else if let Some(max) = new_max {
1126 TInteger::To(max)
1127 } else {
1128 TInteger::Unspecified
1129 };
1130
1131 *intersection_performed = true;
1132 return Some(TAtomic::Scalar(TScalar::Integer(intersected_int)));
1133 }
1134
1135 let t1_union = TUnion::from_atomic(type_1.clone());
1136 let t2_union = TUnion::from_atomic(type_2.clone());
1137
1138 let mut narrower_type = None;
1139 let mut wider_type = None;
1140
1141 if union_comparator::is_contained_by(
1142 codebase,
1143 &t2_union,
1144 &t1_union,
1145 false,
1146 false,
1147 false,
1148 &mut ComparisonResult::default(),
1149 ) {
1150 narrower_type = Some(type_2);
1151 wider_type = Some(type_1);
1152 } else if union_comparator::is_contained_by(
1153 codebase,
1154 &t1_union,
1155 &t2_union,
1156 false,
1157 false,
1158 false,
1159 &mut ComparisonResult::default(),
1160 ) {
1161 narrower_type = Some(type_1);
1162 wider_type = Some(type_2);
1163 }
1164
1165 if let (Some(narrower), Some(wider)) = (narrower_type, wider_type) {
1166 *intersection_performed = true;
1167 let mut result = narrower.clone();
1168
1169 if narrower.can_be_intersected() && wider.can_be_intersected() {
1170 let mut wider_clone = wider.clone();
1171 if let Some(types) = wider_clone.get_intersection_types_mut() {
1172 types.clear();
1173 }
1174 result.add_intersection_type(wider_clone);
1175
1176 if let Some(wider_intersections) = wider.get_intersection_types() {
1177 for i_type in wider_intersections {
1178 result.add_intersection_type(i_type.clone());
1179 }
1180 }
1181 }
1182 return Some(result);
1183 }
1184
1185 if let (TAtomic::Scalar(TScalar::String(s1)), TAtomic::Scalar(TScalar::String(s2))) = (type_1, type_2) {
1186 if let (Some(v1), Some(v2)) = (&s1.get_known_literal_value(), &s2.get_known_literal_value())
1187 && v1 != v2
1188 {
1189 return None;
1190 }
1191
1192 let combined = TAtomic::Scalar(TScalar::String(TString {
1193 is_numeric: s1.is_numeric || s2.is_numeric,
1194 is_truthy: s1.is_truthy || s2.is_truthy,
1195 is_non_empty: s1.is_non_empty || s2.is_non_empty,
1196 is_callable: false,
1197 casing: match (s1.casing, s2.casing) {
1198 (TStringCasing::Lowercase, TStringCasing::Lowercase) => TStringCasing::Lowercase,
1199 (TStringCasing::Uppercase, TStringCasing::Uppercase) => TStringCasing::Uppercase,
1200 _ => TStringCasing::Unspecified,
1201 },
1202 literal: if s1.is_literal_origin() && s2.is_literal_origin() {
1203 Some(TStringLiteral::Unspecified)
1204 } else {
1205 None
1206 },
1207 }));
1208 *intersection_performed = true;
1209 return Some(combined);
1210 }
1211
1212 if type_1.can_be_intersected() && type_2.can_be_intersected() {
1213 if let (TAtomic::Object(TObject::Named(n1)), TAtomic::Object(TObject::Named(n2))) = (type_1, type_2)
1214 && let (Some(c1), Some(c2)) = (codebase.get_class_like(&n1.name), codebase.get_class_like(&n2.name))
1215 && !c1.kind.is_interface()
1216 && !c1.kind.is_trait()
1217 && !c2.kind.is_interface()
1218 && !c2.kind.is_trait()
1219 {
1220 return None;
1221 }
1222
1223 let mut result = type_1.clone();
1224 result.add_intersection_type(type_2.clone());
1225 if let Some(intersections) = type_2.get_intersection_types() {
1226 for i in intersections {
1227 result.add_intersection_type(i.clone());
1228 }
1229 }
1230
1231 *intersection_performed = true;
1232 return Some(result);
1233 }
1234
1235 None
1236}
1237
1238pub fn get_iterable_parameters(atomic: &TAtomic, codebase: &CodebaseMetadata) -> Option<(TUnion, TUnion)> {
1239 if let Some(generator_parameters) = atomic.get_generator_parameters() {
1240 let mut key_type = generator_parameters.0;
1241 let mut value_type = generator_parameters.1;
1242
1243 expander::expand_union(codebase, &mut key_type, &TypeExpansionOptions::default());
1244 expander::expand_union(codebase, &mut value_type, &TypeExpansionOptions::default());
1245
1246 return Some((key_type, value_type));
1247 }
1248
1249 let parameters = 'parameters: {
1250 match atomic {
1251 TAtomic::Iterable(iterable) => {
1252 let mut key_type = iterable.get_key_type().clone();
1253 let mut value_type = iterable.get_value_type().clone();
1254
1255 expander::expand_union(codebase, &mut key_type, &TypeExpansionOptions::default());
1256 expander::expand_union(codebase, &mut value_type, &TypeExpansionOptions::default());
1257
1258 Some((key_type, value_type))
1259 }
1260 TAtomic::Array(array_type) => {
1261 let (mut key_type, mut value_type) = get_array_parameters(array_type, codebase);
1262
1263 expander::expand_union(codebase, &mut key_type, &TypeExpansionOptions::default());
1264 expander::expand_union(codebase, &mut value_type, &TypeExpansionOptions::default());
1265
1266 Some((key_type, value_type))
1267 }
1268 TAtomic::Object(object) => {
1269 let name = object.get_name()?;
1270 let traversable = atom("traversable");
1271 let iterator = atom("iterator");
1272 let iterator_aggregate = atom("iteratoraggregate");
1273
1274 let class_metadata = codebase.get_class_like(&name)?;
1275 if !codebase.is_instance_of(&class_metadata.name, &traversable) {
1276 break 'parameters None;
1277 }
1278
1279 let is_iterator_interface = name == iterator || name == traversable || name == iterator_aggregate;
1280 if !is_iterator_interface
1281 && codebase.is_instance_of(&class_metadata.name, &iterator)
1282 && let (Some(key_type), Some(value_type)) = (
1283 get_iterator_method_return_type(codebase, name, "key"),
1284 get_iterator_method_return_type(codebase, name, "current"),
1285 )
1286 {
1287 let contains_generic_param = |t: &TUnion| t.types.iter().any(atomic::TAtomic::is_generic_parameter);
1288
1289 if !key_type.is_mixed()
1290 && !value_type.is_mixed()
1291 && !contains_generic_param(&key_type)
1292 && !contains_generic_param(&value_type)
1293 {
1294 return Some((key_type, value_type));
1295 }
1296 }
1297
1298 let traversable_metadata = codebase.get_class_like(&traversable)?;
1299 let key_template = traversable_metadata.template_types.get_index(0).map(|(name, _)| *name)?;
1300 let value_template = traversable_metadata.template_types.get_index(1).map(|(name, _)| *name)?;
1301
1302 let key_type = get_specialized_template_type(
1303 codebase,
1304 key_template,
1305 traversable,
1306 class_metadata,
1307 object.get_type_parameters(),
1308 )
1309 .unwrap_or_else(get_mixed);
1310
1311 let value_type = get_specialized_template_type(
1312 codebase,
1313 value_template,
1314 traversable,
1315 class_metadata,
1316 object.get_type_parameters(),
1317 )
1318 .unwrap_or_else(get_mixed);
1319
1320 Some((key_type, value_type))
1321 }
1322 _ => None,
1323 }
1324 };
1325
1326 if let Some((key_type, value_type)) = parameters {
1327 return Some((key_type, value_type));
1328 }
1329
1330 if let Some(intersection_types) = atomic.get_intersection_types() {
1331 for intersection_type in intersection_types {
1332 if let Some((key_type, value_type)) = get_iterable_parameters(intersection_type, codebase) {
1333 return Some((key_type, value_type));
1334 }
1335 }
1336 }
1337
1338 None
1339}
1340
1341#[must_use]
1342pub fn get_array_parameters(array_type: &TArray, codebase: &CodebaseMetadata) -> (TUnion, TUnion) {
1343 match array_type {
1344 TArray::Keyed(keyed_data) => {
1345 let mut key_types = vec![];
1346 let mut value_param;
1347
1348 if let Some((key_param, value_p)) = &keyed_data.parameters {
1349 key_types.extend(key_param.types.iter().cloned());
1350 value_param = (**value_p).clone();
1351 } else {
1352 key_types.push(TAtomic::Never);
1353 value_param = get_never();
1354 }
1355
1356 if let Some(known_items) = &keyed_data.known_items {
1357 for (key, (_, item_type)) in known_items {
1358 key_types.push(key.to_atomic());
1359 value_param =
1360 add_union_type(value_param, item_type, codebase, combiner::CombinerOptions::default());
1361 }
1362 }
1363
1364 let combined_key_types = combiner::combine(key_types, codebase, combiner::CombinerOptions::default());
1365 let key_param_union = TUnion::from_vec(combined_key_types);
1366
1367 (key_param_union, value_param)
1368 }
1369 TArray::List(list_data) => {
1370 let mut key_types = vec![];
1371 let mut value_type = (*list_data.element_type).clone();
1372
1373 if let Some(known_elements) = &list_data.known_elements {
1374 for (key_idx, (_, element_type)) in known_elements {
1375 key_types.push(TAtomic::Scalar(TScalar::literal_int(*key_idx as i64)));
1376
1377 value_type =
1378 combine_union_types(element_type, &value_type, codebase, combiner::CombinerOptions::default());
1379 }
1380 }
1381
1382 if key_types.is_empty() || !value_type.is_never() {
1383 if value_type.is_never() {
1384 key_types.push(TAtomic::Never);
1385 } else {
1386 key_types.push(TAtomic::Scalar(TScalar::Integer(TInteger::non_negative())));
1387 }
1388 }
1389
1390 let key_type =
1391 TUnion::from_vec(combiner::combine(key_types, codebase, combiner::CombinerOptions::default()));
1392
1393 (key_type, value_type)
1394 }
1395 }
1396}
1397
1398#[must_use]
1399pub fn get_iterable_value_parameter(atomic: &TAtomic, codebase: &CodebaseMetadata) -> Option<TUnion> {
1400 if let Some(generator_parameters) = atomic.get_generator_parameters() {
1401 return Some(generator_parameters.1);
1402 }
1403
1404 let parameter = match atomic {
1405 TAtomic::Iterable(iterable) => Some(iterable.get_value_type().clone()),
1406 TAtomic::Array(array_type) => Some(get_array_value_parameter(array_type, codebase)),
1407 TAtomic::Object(object) => {
1408 let name = object.get_name()?;
1409 let traversable = atom("traversable");
1410
1411 let class_metadata = codebase.get_class_like(&name)?;
1412 if !codebase.is_instance_of(&class_metadata.name, &traversable) {
1413 return None;
1414 }
1415
1416 let traversable_metadata = codebase.get_class_like(&traversable)?;
1417 let value_template = traversable_metadata.template_types.get_index(1).map(|(name, _)| *name)?;
1418
1419 get_specialized_template_type(
1420 codebase,
1421 value_template,
1422 traversable,
1423 class_metadata,
1424 object.get_type_parameters(),
1425 )
1426 }
1427 _ => None,
1428 };
1429
1430 if let Some(value_param) = parameter {
1431 return Some(value_param);
1432 }
1433
1434 if let Some(intersection_types) = atomic.get_intersection_types() {
1435 for intersection_type in intersection_types {
1436 if let Some(value_param) = get_iterable_value_parameter(intersection_type, codebase) {
1437 return Some(value_param);
1438 }
1439 }
1440 }
1441
1442 None
1443}
1444
1445#[must_use]
1446pub fn get_array_value_parameter(array_type: &TArray, codebase: &CodebaseMetadata) -> TUnion {
1447 match array_type {
1448 TArray::Keyed(keyed_data) => {
1449 let mut value_param;
1450
1451 if let Some((_, value_p)) = &keyed_data.parameters {
1452 value_param = (**value_p).clone();
1453 } else {
1454 value_param = get_never();
1455 }
1456
1457 if let Some(known_items) = &keyed_data.known_items {
1458 for (_, item_type) in known_items.values() {
1459 value_param =
1460 combine_union_types(item_type, &value_param, codebase, combiner::CombinerOptions::default());
1461 }
1462 }
1463
1464 value_param
1465 }
1466 TArray::List(list_data) => {
1467 let mut value_param = (*list_data.element_type).clone();
1468
1469 if let Some(known_elements) = &list_data.known_elements {
1470 for (_, element_type) in known_elements.values() {
1471 value_param =
1472 combine_union_types(element_type, &value_param, codebase, combiner::CombinerOptions::default());
1473 }
1474 }
1475
1476 value_param
1477 }
1478 }
1479}
1480
1481#[must_use]
1486pub fn get_specialized_template_type(
1487 codebase: &CodebaseMetadata,
1488 template_name: Atom,
1489 template_defining_class_id: Atom,
1490 instantiated_class_metadata: &ClassLikeMetadata,
1491 instantiated_type_parameters: Option<&[TUnion]>,
1492) -> Option<TUnion> {
1493 let defining_class_metadata = codebase.get_class_like(&template_defining_class_id)?;
1494
1495 if defining_class_metadata.name == instantiated_class_metadata.name {
1496 let index = instantiated_class_metadata.get_template_index_for_name(template_name)?;
1497
1498 let Some(instantiated_type_parameters) = instantiated_type_parameters else {
1499 let template = instantiated_class_metadata.get_template_type(template_name)?;
1500 let mut result = template.constraint.clone();
1501
1502 expander::expand_union(codebase, &mut result, &TypeExpansionOptions::default());
1503
1504 return Some(result);
1505 };
1506
1507 let mut result = instantiated_type_parameters.get(index).cloned()?;
1508
1509 expander::expand_union(codebase, &mut result, &TypeExpansionOptions::default());
1510
1511 return Some(result);
1512 }
1513
1514 let template = defining_class_metadata.get_template_type(template_name)?;
1515 let template_union = wrap_atomic(TAtomic::GenericParameter(TGenericParameter {
1516 parameter_name: template_name,
1517 defining_entity: template.defining_entity,
1518 constraint: Arc::new(template.constraint.clone()),
1519 intersection_types: None,
1520 }));
1521
1522 let mut template_result = TemplateResult::default();
1523 for (defining_class, template_parameters) in &instantiated_class_metadata.template_extended_parameters {
1524 for (parameter_name, parameter_type) in template_parameters {
1525 template_result.add_lower_bound(
1526 *parameter_name,
1527 GenericParent::ClassLike(*defining_class),
1528 parameter_type.clone(),
1529 );
1530 }
1531 }
1532
1533 let mut template_type = inferred_type_replacer::replace(&template_union, &template_result, codebase);
1534 if let Some(type_parameters) = instantiated_type_parameters {
1535 let mut template_result = TemplateResult::default();
1536 for (i, parameter_type) in type_parameters.iter().enumerate() {
1537 if let Some(parameter_name) = instantiated_class_metadata.get_template_name_for_index(i) {
1538 template_result.add_lower_bound(
1539 parameter_name,
1540 GenericParent::ClassLike(instantiated_class_metadata.name),
1541 parameter_type.clone(),
1542 );
1543 }
1544 }
1545
1546 if !template_result.lower_bounds.is_empty() {
1547 template_type = inferred_type_replacer::replace(&template_type, &template_result, codebase);
1548 }
1549 }
1550
1551 expander::expand_union(codebase, &mut template_type, &TypeExpansionOptions::default());
1552
1553 Some(template_type)
1554}
1555
1556fn get_iterator_method_return_type(codebase: &CodebaseMetadata, class_name: Atom, method_name: &str) -> Option<TUnion> {
1557 let method = codebase.get_declaring_method(&class_name, method_name)?;
1558 let return_type_meta = method.return_type_metadata.as_ref()?;
1559 let mut return_type = return_type_meta.type_union.clone();
1560 expander::expand_union(codebase, &mut return_type, &TypeExpansionOptions::default());
1561 Some(return_type)
1562}