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_non_zero_int() -> TUnion {
344 TUnion::from_vec(vec![
345 TAtomic::Scalar(TScalar::Integer(TInteger::negative())),
346 TAtomic::Scalar(TScalar::Integer(TInteger::positive())),
347 ])
348}
349
350#[inline]
351#[must_use]
352pub fn get_unspecified_literal_int() -> TUnion {
353 TUnion::from_single(Cow::Borrowed(UNSPECIFIED_LITERAL_INT_ATOMIC))
354}
355
356#[inline]
357#[must_use]
358pub fn get_unspecified_literal_float() -> TUnion {
359 TUnion::from_single(Cow::Borrowed(UNSPECIFIED_LITERAL_FLOAT_ATOMIC))
360}
361
362#[inline]
363#[must_use]
364pub fn get_int_range(from: Option<i64>, to: Option<i64>) -> TUnion {
365 let atomic = match (from, to) {
366 (Some(from), Some(to)) => TAtomic::Scalar(TScalar::Integer(TInteger::Range(from, to))),
367 (Some(from), None) => {
368 if 0 == from {
369 return get_non_negative_int();
370 }
371
372 if 1 == from {
373 return get_positive_int();
374 }
375
376 TAtomic::Scalar(TScalar::Integer(TInteger::From(from)))
377 }
378 (None, Some(to)) => {
379 if 0 == to {
380 return get_non_positive_int();
381 }
382
383 if -1 == to {
384 return get_negative_int();
385 }
386
387 TAtomic::Scalar(TScalar::Integer(TInteger::To(to)))
388 }
389 (None, None) => return get_int(),
390 };
391
392 TUnion::from_single(Cow::Owned(atomic))
393}
394
395#[inline]
397#[must_use]
398pub fn get_signum_result() -> TUnion {
399 TUnion::new(Cow::Borrowed(SIGNUM_RESULT_SLICE))
400}
401
402#[inline]
404#[must_use]
405pub fn get_one_int() -> TUnion {
406 TUnion::from_single(Cow::Borrowed(ONE_INT_ATOMIC))
407}
408
409#[inline]
411#[must_use]
412pub fn get_zero_int() -> TUnion {
413 TUnion::from_single(Cow::Borrowed(ZERO_INT_ATOMIC))
414}
415
416#[inline]
418#[must_use]
419pub fn get_minus_one_int() -> TUnion {
420 TUnion::from_single(Cow::Borrowed(MINUS_ONE_INT_ATOMIC))
421}
422
423#[inline]
424#[must_use]
425pub fn get_literal_int(value: i64) -> TUnion {
426 if value == 0 {
427 return get_zero_int();
428 }
429
430 if value == 1 {
431 return get_one_int();
432 }
433
434 if value == -1 {
435 return get_minus_one_int();
436 }
437
438 TUnion::from_single(Cow::Owned(TAtomic::Scalar(TScalar::literal_int(value))))
439}
440
441#[inline]
442#[must_use]
443pub fn get_int_or_float() -> TUnion {
444 TUnion::new(Cow::Borrowed(INT_FLOAT_ATOMIC_SLICE))
445}
446
447#[inline]
448#[must_use]
449pub fn get_int_or_string() -> TUnion {
450 TUnion::new(Cow::Borrowed(INT_STRING_ATOMIC_SLICE))
451}
452
453#[inline]
454#[must_use]
455pub fn get_nullable_int() -> TUnion {
456 TUnion::new(Cow::Borrowed(NULL_INT_ATOMIC_SLICE))
457}
458
459#[inline]
460#[must_use]
461pub fn get_nullable_float() -> TUnion {
462 TUnion::new(Cow::Borrowed(NULL_FLOAT_ATOMIC_SLICE))
463}
464
465#[inline]
466#[must_use]
467pub fn get_nullable_object() -> TUnion {
468 TUnion::new(Cow::Borrowed(NULL_OBJECT_ATOMIC_SLICE))
469}
470
471#[inline]
472#[must_use]
473pub fn get_nullable_string() -> TUnion {
474 TUnion::new(Cow::Borrowed(NULL_STRING_ATOMIC_SLICE))
475}
476
477#[inline]
478#[must_use]
479pub fn get_string() -> TUnion {
480 TUnion::from_single(Cow::Borrowed(STRING_ATOMIC))
481}
482
483#[must_use]
488pub fn get_string_with_props(
489 is_numeric: bool,
490 is_truthy: bool,
491 is_non_empty: bool,
492 is_callable: bool,
493 casing: TStringCasing,
494) -> TUnion {
495 if is_callable {
496 return match casing {
497 TStringCasing::Lowercase => TUnion::from_single(Cow::Borrowed(LOWERCASE_CALLABLE_STRING_ATOMIC)),
498 TStringCasing::Uppercase => TUnion::from_single(Cow::Borrowed(UPPERCASE_CALLABLE_STRING_ATOMIC)),
499 TStringCasing::Unspecified => TUnion::from_single(Cow::Borrowed(CALLABLE_STRING_ATOMIC)),
500 };
501 }
502
503 let atomic_ref = match (is_numeric, is_truthy, is_non_empty, casing) {
504 (true, true, _, _) => NUMERIC_TRUTHY_STRING_ATOMIC,
506 (true, false, _, _) => NUMERIC_STRING_ATOMIC,
507 (false, true, _, TStringCasing::Unspecified) => TRUTHY_STRING_ATOMIC,
509 (false, true, _, TStringCasing::Uppercase) => TRUTHY_UPPERCASE_STRING_ATOMIC,
510 (false, true, _, TStringCasing::Lowercase) => TRUTHY_LOWERCASE_STRING_ATOMIC,
511 (false, false, false, TStringCasing::Unspecified) => STRING_ATOMIC,
513 (false, false, false, TStringCasing::Uppercase) => UPPERCASE_STRING_ATOMIC,
514 (false, false, false, TStringCasing::Lowercase) => LOWERCASE_STRING_ATOMIC,
515 (false, false, true, TStringCasing::Unspecified) => NON_EMPTY_STRING_ATOMIC,
516 (false, false, true, TStringCasing::Uppercase) => NON_EMPTY_UPPERCASE_STRING_ATOMIC,
517 (false, false, true, TStringCasing::Lowercase) => NON_EMPTY_LOWERCASE_STRING_ATOMIC,
518 };
519
520 TUnion::from_single(Cow::Borrowed(atomic_ref))
521}
522
523#[inline]
524#[must_use]
525pub fn get_literal_class_string(value: Atom) -> TUnion {
526 TUnion::from_single(Cow::Owned(TAtomic::Scalar(TScalar::ClassLikeString(TClassLikeString::literal(value)))))
527}
528
529#[inline]
530#[must_use]
531pub fn get_class_string() -> TUnion {
532 TUnion::from_single(Cow::Borrowed(CLASS_STRING_ATOMIC))
533}
534
535#[inline]
536#[must_use]
537pub fn get_class_string_of_type(constraint: TAtomic) -> TUnion {
538 TUnion::from_single(Cow::Owned(TAtomic::Scalar(TScalar::ClassLikeString(TClassLikeString::class_string_of_type(
539 constraint,
540 )))))
541}
542
543#[inline]
544#[must_use]
545pub fn get_interface_string() -> TUnion {
546 TUnion::from_single(Cow::Borrowed(INTERFACE_STRING_ATOMIC))
547}
548
549#[inline]
550#[must_use]
551pub fn get_interface_string_of_type(constraint: TAtomic) -> TUnion {
552 TUnion::from_single(Cow::Owned(TAtomic::Scalar(TScalar::ClassLikeString(
553 TClassLikeString::interface_string_of_type(constraint),
554 ))))
555}
556
557#[inline]
558#[must_use]
559pub fn get_enum_string() -> TUnion {
560 TUnion::from_single(Cow::Borrowed(ENUM_STRING_ATOMIC))
561}
562
563#[inline]
564#[must_use]
565pub fn get_enum_string_of_type(constraint: TAtomic) -> TUnion {
566 TUnion::from_single(Cow::Owned(TAtomic::Scalar(TScalar::ClassLikeString(TClassLikeString::enum_string_of_type(
567 constraint,
568 )))))
569}
570
571#[inline]
572#[must_use]
573pub fn get_trait_string() -> TUnion {
574 TUnion::from_single(Cow::Borrowed(TRAIT_STRING_ATOMIC))
575}
576
577#[inline]
578#[must_use]
579pub fn get_trait_string_of_type(constraint: TAtomic) -> TUnion {
580 TUnion::from_single(Cow::Owned(TAtomic::Scalar(TScalar::ClassLikeString(TClassLikeString::trait_string_of_type(
581 constraint,
582 )))))
583}
584
585#[inline]
586#[must_use]
587pub fn get_literal_string(value: Atom) -> TUnion {
588 TUnion::from_single(Cow::Owned(TAtomic::Scalar(TScalar::literal_string(value))))
589}
590
591#[inline]
592#[must_use]
593pub fn get_float() -> TUnion {
594 TUnion::from_single(Cow::Borrowed(FLOAT_ATOMIC))
595}
596
597#[inline]
598#[must_use]
599pub fn get_literal_float(v: f64) -> TUnion {
600 TUnion::from_single(Cow::Owned(TAtomic::Scalar(TScalar::literal_float(v))))
601}
602
603#[inline]
604#[must_use]
605pub fn get_mixed() -> TUnion {
606 TUnion::from_single(Cow::Borrowed(MIXED_ATOMIC))
607}
608
609#[inline]
610#[must_use]
611pub fn get_truthy_mixed() -> TUnion {
612 TUnion::from_single(Cow::Borrowed(TRUTHY_MIXED_ATOMIC))
613}
614
615#[inline]
616#[must_use]
617pub fn get_isset_from_mixed_mixed() -> TUnion {
618 TUnion::from_single(Cow::Borrowed(ISSET_FROM_LOOP_MIXED_ATOMIC))
619}
620
621#[must_use]
622pub fn get_mixed_maybe_from_loop(from_loop_isset: bool) -> TUnion {
623 if from_loop_isset { get_isset_from_mixed_mixed() } else { get_mixed() }
624}
625
626#[inline]
627#[must_use]
628pub fn get_never() -> TUnion {
629 TUnion::from_single(Cow::Borrowed(NEVER_ATOMIC))
630}
631
632#[inline]
633#[must_use]
634pub fn get_resource() -> TUnion {
635 TUnion::from_single(Cow::Borrowed(RESOURCE_ATOMIC))
636}
637
638#[inline]
639#[must_use]
640pub fn get_closed_resource() -> TUnion {
641 TUnion::from_single(Cow::Borrowed(CLOSED_RESOURCE_ATOMIC))
642}
643
644#[inline]
645#[must_use]
646pub fn get_open_resource() -> TUnion {
647 TUnion::from_single(Cow::Borrowed(OPEN_RESOURCE_ATOMIC))
648}
649
650#[inline]
651#[must_use]
652pub fn get_placeholder() -> TUnion {
653 TUnion::from_single(Cow::Borrowed(PLACEHOLDER_ATOMIC))
654}
655
656#[inline]
657#[must_use]
658pub fn get_void() -> TUnion {
659 TUnion::from_single(Cow::Borrowed(VOID_ATOMIC))
660}
661
662#[inline]
663#[must_use]
664pub fn get_null() -> TUnion {
665 TUnion::from_single(Cow::Borrowed(NULL_ATOMIC))
666}
667
668#[inline]
669#[must_use]
670pub fn get_undefined_null() -> TUnion {
671 let mut null = TUnion::from_single(Cow::Borrowed(NULL_ATOMIC));
672 null.set_possibly_undefined(true, None);
673 null
674}
675
676#[inline]
677#[must_use]
678pub fn get_arraykey() -> TUnion {
679 TUnion::from_single(Cow::Borrowed(ARRAYKEY_ATOMIC))
680}
681
682#[inline]
683#[must_use]
684pub fn get_bool() -> TUnion {
685 TUnion::from_single(Cow::Borrowed(BOOL_ATOMIC))
686}
687
688#[inline]
689#[must_use]
690pub fn get_false() -> TUnion {
691 TUnion::from_single(Cow::Borrowed(FALSE_ATOMIC))
692}
693
694#[inline]
695#[must_use]
696pub fn get_true() -> TUnion {
697 TUnion::from_single(Cow::Borrowed(TRUE_ATOMIC))
698}
699
700#[inline]
701#[must_use]
702pub fn get_object() -> TUnion {
703 TUnion::from_single(Cow::Borrowed(OBJECT_ATOMIC))
704}
705
706#[inline]
707#[must_use]
708pub fn get_numeric() -> TUnion {
709 TUnion::from_single(Cow::Borrowed(NUMERIC_ATOMIC))
710}
711
712#[inline]
713#[must_use]
714pub fn get_callable_string() -> TUnion {
715 TUnion::from_single(Cow::Borrowed(CALLABLE_STRING_ATOMIC))
716}
717
718pub fn get_numeric_string() -> TUnion {
719 TUnion::from_single(Cow::Borrowed(NUMERIC_STRING_ATOMIC))
720}
721
722#[inline]
723#[must_use]
724pub fn get_lowercase_string() -> TUnion {
725 TUnion::from_single(Cow::Borrowed(LOWERCASE_STRING_ATOMIC))
726}
727
728#[inline]
729#[must_use]
730pub fn get_non_empty_lowercase_string() -> TUnion {
731 TUnion::from_single(Cow::Borrowed(NON_EMPTY_LOWERCASE_STRING_ATOMIC))
732}
733
734#[inline]
735#[must_use]
736pub fn get_uppercase_string() -> TUnion {
737 TUnion::from_single(Cow::Borrowed(UPPERCASE_STRING_ATOMIC))
738}
739
740#[inline]
741#[must_use]
742pub fn get_non_empty_uppercase_string() -> TUnion {
743 TUnion::from_single(Cow::Borrowed(NON_EMPTY_UPPERCASE_STRING_ATOMIC))
744}
745
746#[inline]
747#[must_use]
748pub fn get_non_empty_string() -> TUnion {
749 TUnion::from_single(Cow::Borrowed(NON_EMPTY_STRING_ATOMIC))
750}
751
752#[inline]
753#[must_use]
754pub fn get_empty_string() -> TUnion {
755 TUnion::from_single(Cow::Borrowed(&EMPTY_STRING_ATOMIC))
756}
757
758#[inline]
759#[must_use]
760pub fn get_truthy_string() -> TUnion {
761 TUnion::from_single(Cow::Borrowed(TRUTHY_STRING_ATOMIC))
762}
763
764#[inline]
765#[must_use]
766pub fn get_unspecified_literal_string() -> TUnion {
767 TUnion::from_single(Cow::Borrowed(UNSPECIFIED_LITERAL_STRING_ATOMIC))
768}
769
770#[inline]
771#[must_use]
772pub fn get_non_empty_unspecified_literal_string() -> TUnion {
773 TUnion::from_single(Cow::Borrowed(NON_EMPTY_UNSPECIFIED_LITERAL_STRING_ATOMIC))
774}
775
776#[inline]
777#[must_use]
778pub fn get_scalar() -> TUnion {
779 TUnion::from_single(Cow::Borrowed(SCALAR_ATOMIC))
780}
781
782#[inline]
783#[must_use]
784pub fn get_nullable_scalar() -> TUnion {
785 TUnion::new(Cow::Borrowed(NULL_SCALAR_ATOMIC_SLICE))
786}
787
788#[inline]
789#[must_use]
790pub fn get_mixed_iterable() -> TUnion {
791 TUnion::from_single(Cow::Borrowed(&MIXED_ITERABLE_ATOMIC))
792}
793
794#[inline]
795#[must_use]
796pub fn get_empty_keyed_array() -> TUnion {
797 TUnion::from_single(Cow::Borrowed(&EMPTY_KEYED_ARRAY_ATOMIC))
798}
799
800#[inline]
801#[must_use]
802pub fn get_mixed_list() -> TUnion {
803 get_list(get_mixed())
804}
805
806#[inline]
807#[must_use]
808pub fn get_mixed_keyed_array() -> TUnion {
809 get_keyed_array(get_arraykey(), get_mixed())
810}
811
812#[inline]
813#[must_use]
814pub fn get_mixed_callable() -> TUnion {
815 TUnion::from_single(Cow::Borrowed(&MIXED_CALLABLE_ATOMIC))
816}
817
818#[inline]
819#[must_use]
820pub fn get_mixed_closure() -> TUnion {
821 TUnion::from_single(Cow::Borrowed(&MIXED_CLOSURE_ATOMIC))
822}
823
824#[inline]
825#[must_use]
826pub fn get_named_object(name: Atom, type_resolution_context: Option<&TypeResolutionContext>) -> TUnion {
827 if let Some(type_resolution_context) = type_resolution_context
828 && let Some(defining_entities) = type_resolution_context.get_template_definition(name)
829 {
830 let first = &defining_entities[0];
831 return wrap_atomic(TAtomic::Scalar(TScalar::ClassLikeString(TClassLikeString::Generic {
832 kind: TClassLikeStringKind::Class,
833 parameter_name: name,
834 defining_entity: first.defining_entity,
835 constraint: Arc::new((*(first.constraint.get_single())).clone()),
836 })));
837 }
838
839 wrap_atomic(TAtomic::Object(TObject::Named(TNamedObject::new(name))))
840}
841
842#[inline]
843#[must_use]
844pub fn get_iterable(key_parameter: TUnion, value_parameter: TUnion) -> TUnion {
845 wrap_atomic(TAtomic::Iterable(TIterable::new(Arc::new(key_parameter), Arc::new(value_parameter))))
846}
847
848#[inline]
849#[must_use]
850pub fn get_list(element_type: TUnion) -> TUnion {
851 wrap_atomic(TAtomic::Array(TArray::List(TList::new(Arc::new(element_type)))))
852}
853
854#[inline]
855#[must_use]
856pub fn get_non_empty_list(element_type: TUnion) -> TUnion {
857 wrap_atomic(TAtomic::Array(TArray::List(TList::new_non_empty(Arc::new(element_type)))))
858}
859
860#[inline]
861#[must_use]
862pub fn get_keyed_array(key_parameter: TUnion, value_parameter: TUnion) -> TUnion {
863 wrap_atomic(TAtomic::Array(TArray::Keyed(TKeyedArray::new_with_parameters(
864 Arc::new(key_parameter),
865 Arc::new(value_parameter),
866 ))))
867}
868
869#[inline]
870#[must_use]
871pub fn add_optional_union_type(base_type: TUnion, maybe_type: Option<&TUnion>, codebase: &CodebaseMetadata) -> TUnion {
872 if let Some(type_2) = maybe_type {
873 add_union_type(base_type, type_2, codebase, combiner::CombinerOptions::default())
874 } else {
875 base_type
876 }
877}
878
879#[must_use]
881pub fn add_optional_union_type_rc(
882 base_type: &Rc<TUnion>,
883 maybe_type: Option<&TUnion>,
884 codebase: &CodebaseMetadata,
885) -> Rc<TUnion> {
886 match maybe_type {
887 Some(type_2) => {
888 Rc::new(add_union_type((**base_type).clone(), type_2, codebase, combiner::CombinerOptions::default()))
889 }
890 None => Rc::clone(base_type),
891 }
892}
893
894#[inline]
895#[must_use]
896pub fn combine_optional_union_types(
897 type_1: Option<&TUnion>,
898 type_2: Option<&TUnion>,
899 codebase: &CodebaseMetadata,
900) -> TUnion {
901 match (type_1, type_2) {
902 (Some(type_1), Some(type_2)) => {
903 combine_union_types(type_1, type_2, codebase, combiner::CombinerOptions::default())
904 }
905 (Some(type_1), None) => type_1.clone(),
906 (None, Some(type_2)) => type_2.clone(),
907 (None, None) => get_mixed(),
908 }
909}
910
911#[inline]
913#[must_use]
914pub fn combine_union_types_rc(
915 type_1: &Rc<TUnion>,
916 type_2: &Rc<TUnion>,
917 codebase: &CodebaseMetadata,
918 options: combiner::CombinerOptions,
919) -> Rc<TUnion> {
920 if Rc::ptr_eq(type_1, type_2) {
921 return Rc::clone(type_1);
922 }
923
924 Rc::new(combine_union_types(type_1, type_2, codebase, options))
925}
926
927#[inline]
928#[must_use]
929pub fn combine_union_types(
930 type_1: &TUnion,
931 type_2: &TUnion,
932 codebase: &CodebaseMetadata,
933 options: combiner::CombinerOptions,
934) -> TUnion {
935 if type_1 == type_2 {
936 return type_1.clone();
937 }
938
939 let mut combined_type = if type_1.is_never() || type_1.is_never_template() {
940 type_2.clone()
941 } else if type_2.is_never() || type_2.is_never_template() {
942 type_1.clone()
943 } else if type_1.is_vanilla_mixed() && type_2.is_vanilla_mixed() {
944 get_mixed()
945 } else {
946 let mut all_atomic_types = type_1.types.to_vec();
947 all_atomic_types.extend(type_2.types.iter().cloned());
948
949 let mut result = TUnion::from_vec(combiner::combine(all_atomic_types, codebase, options));
950
951 if type_1.had_template() && type_2.had_template() {
952 result.set_had_template(true);
953 }
954
955 if type_1.reference_free() && type_2.reference_free() {
956 result.set_reference_free(true);
957 }
958
959 result
960 };
961
962 if type_1.possibly_undefined() || type_2.possibly_undefined() {
963 combined_type.set_possibly_undefined(true, None);
964 }
965
966 if type_1.possibly_undefined_from_try() || type_2.possibly_undefined_from_try() {
967 combined_type.set_possibly_undefined_from_try(true);
968 }
969
970 if type_1.ignore_falsable_issues() || type_2.ignore_falsable_issues() {
971 combined_type.set_ignore_falsable_issues(true);
972 }
973
974 combined_type
975}
976
977#[inline]
978#[must_use]
979pub fn add_union_type(
980 mut base_type: TUnion,
981 other_type: &TUnion,
982 codebase: &CodebaseMetadata,
983 options: combiner::CombinerOptions,
984) -> TUnion {
985 if &base_type != other_type {
986 base_type.types = if base_type.is_vanilla_mixed() && other_type.is_vanilla_mixed() {
987 base_type.types
988 } else {
989 combine_union_types(&base_type, other_type, codebase, options).types
990 };
991
992 if !other_type.had_template() {
993 base_type.set_had_template(false);
994 }
995
996 if !other_type.reference_free() {
997 base_type.set_reference_free(false);
998 }
999 }
1000
1001 if other_type.possibly_undefined() {
1002 base_type.set_possibly_undefined(true, None);
1003 }
1004 if other_type.possibly_undefined_from_try() {
1005 base_type.set_possibly_undefined_from_try(true);
1006 }
1007 if other_type.ignore_falsable_issues() {
1008 base_type.set_ignore_falsable_issues(true);
1009 }
1010 if other_type.ignore_nullable_issues() {
1011 base_type.set_ignore_nullable_issues(true);
1012 }
1013
1014 base_type
1015}
1016
1017#[must_use]
1018pub fn intersect_union_types(type_1: &TUnion, type_2: &TUnion, codebase: &CodebaseMetadata) -> Option<TUnion> {
1019 if type_1 == type_2 {
1020 return Some(type_1.clone());
1021 }
1022
1023 if type_1.is_never() || type_2.is_never() {
1024 return Some(get_never());
1025 }
1026
1027 let mut intersection_performed = false;
1028
1029 if type_1.is_mixed() {
1030 if type_2.is_mixed() {
1031 return Some(get_mixed());
1032 }
1033
1034 return Some(type_2.clone());
1035 } else if type_2.is_mixed() {
1036 return Some(type_1.clone());
1037 }
1038
1039 let mut intersected_atomic_types = vec![];
1040 for type_1_atomic in type_1.types.iter() {
1041 for type_2_atomic in type_2.types.iter() {
1042 if let Some(intersection_atomic) =
1043 intersect_atomic_types(type_1_atomic, type_2_atomic, codebase, &mut intersection_performed)
1044 {
1045 intersected_atomic_types.push(intersection_atomic);
1046 }
1047 }
1048 }
1049
1050 let mut combined_type: Option<TUnion> = None;
1051 if !intersected_atomic_types.is_empty() {
1052 let combined_vec = combiner::combine(intersected_atomic_types, codebase, combiner::CombinerOptions::default());
1053 if !combined_vec.is_empty() {
1054 combined_type = Some(TUnion::from_vec(combined_vec));
1055 }
1056 }
1057
1058 if !intersection_performed {
1060 if union_comparator::is_contained_by(
1061 codebase,
1062 type_1,
1063 type_2,
1064 false,
1065 false,
1066 false,
1067 &mut ComparisonResult::default(),
1068 ) {
1069 intersection_performed = true;
1070 combined_type = Some(type_1.clone());
1071 } else if union_comparator::is_contained_by(
1072 codebase,
1073 type_2,
1074 type_1,
1075 false,
1076 false,
1077 false,
1078 &mut ComparisonResult::default(),
1079 ) {
1080 intersection_performed = true;
1081 combined_type = Some(type_2.clone());
1082 }
1083 }
1084
1085 if let Some(mut final_type) = combined_type {
1086 final_type.set_possibly_undefined(
1087 type_1.possibly_undefined() && type_2.possibly_undefined(),
1088 Some(type_1.possibly_undefined_from_try() && type_2.possibly_undefined_from_try()),
1089 );
1090 final_type.set_ignore_falsable_issues(type_1.ignore_falsable_issues() && type_2.ignore_falsable_issues());
1091 final_type.set_ignore_nullable_issues(type_1.ignore_nullable_issues() && type_2.ignore_nullable_issues());
1092
1093 return Some(final_type);
1094 }
1095
1096 if !intersection_performed && type_1.get_id() != type_2.get_id() {
1097 return None;
1098 }
1099
1100 None
1101}
1102
1103fn intersect_atomic_types(
1105 type_1: &TAtomic,
1106 type_2: &TAtomic,
1107 codebase: &CodebaseMetadata,
1108 intersection_performed: &mut bool,
1109) -> Option<TAtomic> {
1110 if let (TAtomic::Scalar(TScalar::Integer(t1_int)), TAtomic::Scalar(TScalar::Integer(t2_int))) = (type_1, type_2) {
1111 let (min1, max1) = t1_int.get_bounds();
1112 let (min2, max2) = t2_int.get_bounds();
1113
1114 let new_min = match (min1, min2) {
1115 (Some(m1), Some(m2)) => Some(m1.max(m2)),
1116 (Some(m), None) | (None, Some(m)) => Some(m),
1117 (None, None) => None,
1118 };
1119
1120 let new_max = match (max1, max2) {
1121 (Some(m1), Some(m2)) => Some(m1.min(m2)),
1122 (Some(m), None) | (None, Some(m)) => Some(m),
1123 (None, None) => None,
1124 };
1125
1126 let intersected_int = if let (Some(min), Some(max)) = (new_min, new_max) {
1127 if min > max {
1128 return None;
1129 }
1130
1131 if min == max { TInteger::Literal(min) } else { TInteger::Range(min, max) }
1132 } else if let Some(min) = new_min {
1133 TInteger::From(min)
1134 } else if let Some(max) = new_max {
1135 TInteger::To(max)
1136 } else {
1137 TInteger::Unspecified
1138 };
1139
1140 *intersection_performed = true;
1141 return Some(TAtomic::Scalar(TScalar::Integer(intersected_int)));
1142 }
1143
1144 let t1_union = TUnion::from_atomic(type_1.clone());
1145 let t2_union = TUnion::from_atomic(type_2.clone());
1146
1147 let mut narrower_type = None;
1148 let mut wider_type = None;
1149
1150 if union_comparator::is_contained_by(
1151 codebase,
1152 &t2_union,
1153 &t1_union,
1154 false,
1155 false,
1156 false,
1157 &mut ComparisonResult::default(),
1158 ) {
1159 narrower_type = Some(type_2);
1160 wider_type = Some(type_1);
1161 } else if union_comparator::is_contained_by(
1162 codebase,
1163 &t1_union,
1164 &t2_union,
1165 false,
1166 false,
1167 false,
1168 &mut ComparisonResult::default(),
1169 ) {
1170 narrower_type = Some(type_1);
1171 wider_type = Some(type_2);
1172 }
1173
1174 if let (Some(narrower), Some(wider)) = (narrower_type, wider_type) {
1175 *intersection_performed = true;
1176 let mut result = narrower.clone();
1177
1178 if narrower.can_be_intersected() && wider.can_be_intersected() {
1179 let mut wider_clone = wider.clone();
1180 if let Some(types) = wider_clone.get_intersection_types_mut() {
1181 types.clear();
1182 }
1183 result.add_intersection_type(wider_clone);
1184
1185 if let Some(wider_intersections) = wider.get_intersection_types() {
1186 for i_type in wider_intersections {
1187 result.add_intersection_type(i_type.clone());
1188 }
1189 }
1190 }
1191 return Some(result);
1192 }
1193
1194 if let (TAtomic::Scalar(TScalar::String(s1)), TAtomic::Scalar(TScalar::String(s2))) = (type_1, type_2) {
1195 if let (Some(v1), Some(v2)) = (&s1.get_known_literal_value(), &s2.get_known_literal_value())
1196 && v1 != v2
1197 {
1198 return None;
1199 }
1200
1201 let combined = TAtomic::Scalar(TScalar::String(TString {
1202 is_numeric: s1.is_numeric || s2.is_numeric,
1203 is_truthy: s1.is_truthy || s2.is_truthy,
1204 is_non_empty: s1.is_non_empty || s2.is_non_empty,
1205 is_callable: false,
1206 casing: match (s1.casing, s2.casing) {
1207 (TStringCasing::Lowercase, TStringCasing::Lowercase) => TStringCasing::Lowercase,
1208 (TStringCasing::Uppercase, TStringCasing::Uppercase) => TStringCasing::Uppercase,
1209 _ => TStringCasing::Unspecified,
1210 },
1211 literal: if s1.is_literal_origin() && s2.is_literal_origin() {
1212 Some(TStringLiteral::Unspecified)
1213 } else {
1214 None
1215 },
1216 }));
1217 *intersection_performed = true;
1218 return Some(combined);
1219 }
1220
1221 if type_1.can_be_intersected() && type_2.can_be_intersected() {
1222 if let (TAtomic::Object(TObject::Named(n1)), TAtomic::Object(TObject::Named(n2))) = (type_1, type_2)
1223 && let (Some(c1), Some(c2)) = (codebase.get_class_like(&n1.name), codebase.get_class_like(&n2.name))
1224 && !c1.kind.is_interface()
1225 && !c1.kind.is_trait()
1226 && !c2.kind.is_interface()
1227 && !c2.kind.is_trait()
1228 {
1229 return None;
1230 }
1231
1232 let mut result = type_1.clone();
1233 result.add_intersection_type(type_2.clone());
1234 if let Some(intersections) = type_2.get_intersection_types() {
1235 for i in intersections {
1236 result.add_intersection_type(i.clone());
1237 }
1238 }
1239
1240 *intersection_performed = true;
1241 return Some(result);
1242 }
1243
1244 None
1245}
1246
1247pub fn get_iterable_parameters(atomic: &TAtomic, codebase: &CodebaseMetadata) -> Option<(TUnion, TUnion)> {
1248 if let Some(generator_parameters) = atomic.get_generator_parameters() {
1249 let mut key_type = generator_parameters.0;
1250 let mut value_type = generator_parameters.1;
1251
1252 expander::expand_union(codebase, &mut key_type, &TypeExpansionOptions::default());
1253 expander::expand_union(codebase, &mut value_type, &TypeExpansionOptions::default());
1254
1255 return Some((key_type, value_type));
1256 }
1257
1258 let parameters = 'parameters: {
1259 match atomic {
1260 TAtomic::Iterable(iterable) => {
1261 let mut key_type = iterable.get_key_type().clone();
1262 let mut value_type = iterable.get_value_type().clone();
1263
1264 expander::expand_union(codebase, &mut key_type, &TypeExpansionOptions::default());
1265 expander::expand_union(codebase, &mut value_type, &TypeExpansionOptions::default());
1266
1267 Some((key_type, value_type))
1268 }
1269 TAtomic::Array(array_type) => {
1270 let (mut key_type, mut value_type) = get_array_parameters(array_type, codebase);
1271
1272 expander::expand_union(codebase, &mut key_type, &TypeExpansionOptions::default());
1273 expander::expand_union(codebase, &mut value_type, &TypeExpansionOptions::default());
1274
1275 Some((key_type, value_type))
1276 }
1277 TAtomic::Object(object) => {
1278 let name = object.get_name()?;
1279 let traversable = atom("traversable");
1280 let iterator = atom("iterator");
1281 let iterator_aggregate = atom("iteratoraggregate");
1282
1283 let class_metadata = codebase.get_class_like(&name)?;
1284 if !codebase.is_instance_of(&class_metadata.name, &traversable) {
1285 break 'parameters None;
1286 }
1287
1288 let is_iterator_interface = name == iterator || name == traversable || name == iterator_aggregate;
1289 if !is_iterator_interface
1290 && codebase.is_instance_of(&class_metadata.name, &iterator)
1291 && let (Some(key_type), Some(value_type)) = (
1292 get_iterator_method_return_type(codebase, name, "key"),
1293 get_iterator_method_return_type(codebase, name, "current"),
1294 )
1295 {
1296 let contains_generic_param = |t: &TUnion| t.types.iter().any(atomic::TAtomic::is_generic_parameter);
1297
1298 if !key_type.is_mixed()
1299 && !value_type.is_mixed()
1300 && !contains_generic_param(&key_type)
1301 && !contains_generic_param(&value_type)
1302 {
1303 return Some((key_type, value_type));
1304 }
1305 }
1306
1307 let traversable_metadata = codebase.get_class_like(&traversable)?;
1308 let key_template = traversable_metadata.template_types.get_index(0).map(|(name, _)| *name)?;
1309 let value_template = traversable_metadata.template_types.get_index(1).map(|(name, _)| *name)?;
1310
1311 let key_type = get_specialized_template_type(
1312 codebase,
1313 key_template,
1314 traversable,
1315 class_metadata,
1316 object.get_type_parameters(),
1317 )
1318 .unwrap_or_else(get_mixed);
1319
1320 let value_type = get_specialized_template_type(
1321 codebase,
1322 value_template,
1323 traversable,
1324 class_metadata,
1325 object.get_type_parameters(),
1326 )
1327 .unwrap_or_else(get_mixed);
1328
1329 Some((key_type, value_type))
1330 }
1331 _ => None,
1332 }
1333 };
1334
1335 if let Some((key_type, value_type)) = parameters {
1336 return Some((key_type, value_type));
1337 }
1338
1339 if let Some(intersection_types) = atomic.get_intersection_types() {
1340 for intersection_type in intersection_types {
1341 if let Some((key_type, value_type)) = get_iterable_parameters(intersection_type, codebase) {
1342 return Some((key_type, value_type));
1343 }
1344 }
1345 }
1346
1347 None
1348}
1349
1350#[must_use]
1351pub fn get_array_parameters(array_type: &TArray, codebase: &CodebaseMetadata) -> (TUnion, TUnion) {
1352 match array_type {
1353 TArray::Keyed(keyed_data) => {
1354 let mut key_types = vec![];
1355 let mut value_param;
1356
1357 if let Some((key_param, value_p)) = &keyed_data.parameters {
1358 key_types.extend(key_param.types.iter().cloned());
1359 value_param = (**value_p).clone();
1360 } else {
1361 key_types.push(TAtomic::Never);
1362 value_param = get_never();
1363 }
1364
1365 if let Some(known_items) = &keyed_data.known_items {
1366 for (key, (_, item_type)) in known_items {
1367 key_types.push(key.to_atomic());
1368 value_param =
1369 add_union_type(value_param, item_type, codebase, combiner::CombinerOptions::default());
1370 }
1371 }
1372
1373 if key_types.is_empty() {
1374 key_types.push(TAtomic::Never);
1375 }
1376
1377 let combined_key_types = combiner::combine(key_types, codebase, combiner::CombinerOptions::default());
1378 let key_param_union = TUnion::from_vec(combined_key_types);
1379
1380 (key_param_union, value_param)
1381 }
1382 TArray::List(list_data) => {
1383 let mut key_types = vec![];
1384 let mut value_type = (*list_data.element_type).clone();
1385
1386 if let Some(known_elements) = &list_data.known_elements {
1387 for (key_idx, (_, element_type)) in known_elements {
1388 key_types.push(TAtomic::Scalar(TScalar::literal_int(*key_idx as i64)));
1389
1390 value_type =
1391 combine_union_types(element_type, &value_type, codebase, combiner::CombinerOptions::default());
1392 }
1393 }
1394
1395 if key_types.is_empty() || !value_type.is_never() {
1396 if value_type.is_never() {
1397 key_types.push(TAtomic::Never);
1398 } else {
1399 key_types.push(TAtomic::Scalar(TScalar::Integer(TInteger::non_negative())));
1400 }
1401 }
1402
1403 let key_type =
1404 TUnion::from_vec(combiner::combine(key_types, codebase, combiner::CombinerOptions::default()));
1405
1406 (key_type, value_type)
1407 }
1408 }
1409}
1410
1411#[must_use]
1412pub fn get_iterable_value_parameter(atomic: &TAtomic, codebase: &CodebaseMetadata) -> Option<TUnion> {
1413 if let Some(generator_parameters) = atomic.get_generator_parameters() {
1414 return Some(generator_parameters.1);
1415 }
1416
1417 let parameter = match atomic {
1418 TAtomic::Iterable(iterable) => Some(iterable.get_value_type().clone()),
1419 TAtomic::Array(array_type) => Some(get_array_value_parameter(array_type, codebase)),
1420 TAtomic::Object(object) => {
1421 let name = object.get_name()?;
1422 let traversable = atom("traversable");
1423
1424 let class_metadata = codebase.get_class_like(&name)?;
1425 if !codebase.is_instance_of(&class_metadata.name, &traversable) {
1426 return None;
1427 }
1428
1429 let traversable_metadata = codebase.get_class_like(&traversable)?;
1430 let value_template = traversable_metadata.template_types.get_index(1).map(|(name, _)| *name)?;
1431
1432 get_specialized_template_type(
1433 codebase,
1434 value_template,
1435 traversable,
1436 class_metadata,
1437 object.get_type_parameters(),
1438 )
1439 }
1440 _ => None,
1441 };
1442
1443 if let Some(value_param) = parameter {
1444 return Some(value_param);
1445 }
1446
1447 if let Some(intersection_types) = atomic.get_intersection_types() {
1448 for intersection_type in intersection_types {
1449 if let Some(value_param) = get_iterable_value_parameter(intersection_type, codebase) {
1450 return Some(value_param);
1451 }
1452 }
1453 }
1454
1455 None
1456}
1457
1458#[must_use]
1459pub fn get_array_value_parameter(array_type: &TArray, codebase: &CodebaseMetadata) -> TUnion {
1460 match array_type {
1461 TArray::Keyed(keyed_data) => {
1462 let mut value_param;
1463
1464 if let Some((_, value_p)) = &keyed_data.parameters {
1465 value_param = (**value_p).clone();
1466 } else {
1467 value_param = get_never();
1468 }
1469
1470 if let Some(known_items) = &keyed_data.known_items {
1471 for (_, item_type) in known_items.values() {
1472 value_param =
1473 combine_union_types(item_type, &value_param, codebase, combiner::CombinerOptions::default());
1474 }
1475 }
1476
1477 value_param
1478 }
1479 TArray::List(list_data) => {
1480 let mut value_param = (*list_data.element_type).clone();
1481
1482 if let Some(known_elements) = &list_data.known_elements {
1483 for (_, element_type) in known_elements.values() {
1484 value_param =
1485 combine_union_types(element_type, &value_param, codebase, combiner::CombinerOptions::default());
1486 }
1487 }
1488
1489 value_param
1490 }
1491 }
1492}
1493
1494#[must_use]
1499pub fn get_specialized_template_type(
1500 codebase: &CodebaseMetadata,
1501 template_name: Atom,
1502 template_defining_class_id: Atom,
1503 instantiated_class_metadata: &ClassLikeMetadata,
1504 instantiated_type_parameters: Option<&[TUnion]>,
1505) -> Option<TUnion> {
1506 let defining_class_metadata = codebase.get_class_like(&template_defining_class_id)?;
1507
1508 if defining_class_metadata.name == instantiated_class_metadata.name {
1509 let index = instantiated_class_metadata.get_template_index_for_name(template_name)?;
1510
1511 let Some(instantiated_type_parameters) = instantiated_type_parameters else {
1512 let template = instantiated_class_metadata.get_template_type(template_name)?;
1513 let mut result = template.constraint.clone();
1514
1515 expander::expand_union(codebase, &mut result, &TypeExpansionOptions::default());
1516
1517 return Some(result);
1518 };
1519
1520 let mut result = instantiated_type_parameters.get(index).cloned()?;
1521
1522 expander::expand_union(codebase, &mut result, &TypeExpansionOptions::default());
1523
1524 return Some(result);
1525 }
1526
1527 let template = defining_class_metadata.get_template_type(template_name)?;
1528 let template_union = wrap_atomic(TAtomic::GenericParameter(TGenericParameter {
1529 parameter_name: template_name,
1530 defining_entity: template.defining_entity,
1531 constraint: Arc::new(template.constraint.clone()),
1532 intersection_types: None,
1533 }));
1534
1535 let mut template_result = TemplateResult::default();
1536 for (defining_class, template_parameters) in &instantiated_class_metadata.template_extended_parameters {
1537 for (parameter_name, parameter_type) in template_parameters {
1538 template_result.add_lower_bound(
1539 *parameter_name,
1540 GenericParent::ClassLike(*defining_class),
1541 parameter_type.clone(),
1542 );
1543 }
1544 }
1545
1546 let mut template_type = inferred_type_replacer::replace(&template_union, &template_result, codebase);
1547 if let Some(type_parameters) = instantiated_type_parameters {
1548 let mut template_result = TemplateResult::default();
1549 for (i, parameter_type) in type_parameters.iter().enumerate() {
1550 if let Some(parameter_name) = instantiated_class_metadata.get_template_name_for_index(i) {
1551 template_result.add_lower_bound(
1552 parameter_name,
1553 GenericParent::ClassLike(instantiated_class_metadata.name),
1554 parameter_type.clone(),
1555 );
1556 }
1557 }
1558
1559 if !template_result.lower_bounds.is_empty() {
1560 template_type = inferred_type_replacer::replace(&template_type, &template_result, codebase);
1561 }
1562 }
1563
1564 expander::expand_union(codebase, &mut template_type, &TypeExpansionOptions::default());
1565
1566 Some(template_type)
1567}
1568
1569fn get_iterator_method_return_type(codebase: &CodebaseMetadata, class_name: Atom, method_name: &str) -> Option<TUnion> {
1570 let method = codebase.get_declaring_method(&class_name, method_name)?;
1571 let return_type_meta = method.return_type_metadata.as_ref()?;
1572 let mut return_type = return_type_meta.type_union.clone();
1573 expander::expand_union(codebase, &mut return_type, &TypeExpansionOptions::default());
1574 Some(return_type)
1575}