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