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