1use std::borrow::Cow;
2
3use mago_atom::Atom;
4use mago_atom::atom;
5
6use crate::get_class_like;
7use crate::is_instance_of;
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::resolution::TypeResolutionContext;
24use crate::ttype::shared::*;
25use crate::ttype::template::TemplateResult;
26use crate::ttype::template::inferred_type_replacer;
27use crate::ttype::union::TUnion;
28
29pub mod atomic;
30pub mod builder;
31pub mod cast;
32pub mod combination;
33pub mod combiner;
34pub mod comparator;
35pub mod error;
36pub mod expander;
37pub mod resolution;
38pub mod shared;
39pub mod template;
40pub mod union;
41
42#[derive(Clone, Copy, Debug)]
44pub enum TypeRef<'a> {
45 Union(&'a TUnion),
46 Atomic(&'a TAtomic),
47}
48
49pub trait TType {
51 fn get_child_nodes<'a>(&'a self) -> Vec<TypeRef<'a>> {
53 vec![]
54 }
55
56 fn get_all_child_nodes<'a>(&'a self) -> Vec<TypeRef<'a>> {
58 let mut child_nodes = self.get_child_nodes();
59 let mut all_child_nodes = vec![];
60
61 while let Some(child_node) = child_nodes.pop() {
62 let new_child_nodes = match child_node {
63 TypeRef::Union(union) => union.get_child_nodes(),
64 TypeRef::Atomic(atomic) => atomic.get_child_nodes(),
65 };
66
67 all_child_nodes.push(child_node);
68
69 child_nodes.extend(new_child_nodes);
70 }
71
72 all_child_nodes
73 }
74
75 fn can_be_intersected(&self) -> bool {
77 false
78 }
79
80 fn get_intersection_types(&self) -> Option<&[TAtomic]> {
82 None
83 }
84
85 fn get_intersection_types_mut(&mut self) -> Option<&mut Vec<TAtomic>> {
87 None
88 }
89
90 fn has_intersection_types(&self) -> bool {
92 false
93 }
94
95 fn add_intersection_type(&mut self, _intersection_type: TAtomic) -> bool {
100 false
101 }
102
103 fn needs_population(&self) -> bool;
104
105 fn is_expandable(&self) -> bool;
106
107 fn is_complex(&self) -> bool;
110
111 fn get_id(&self) -> Atom;
117
118 fn get_pretty_id(&self) -> Atom {
119 self.get_pretty_id_with_indent(0)
120 }
121
122 fn get_pretty_id_with_indent(&self, indent: usize) -> Atom;
123}
124
125impl<'a> TType for TypeRef<'a> {
127 fn get_child_nodes(&self) -> Vec<TypeRef<'a>> {
128 match self {
129 TypeRef::Union(ttype) => ttype.get_child_nodes(),
130 TypeRef::Atomic(ttype) => ttype.get_child_nodes(),
131 }
132 }
133
134 fn can_be_intersected(&self) -> bool {
135 match self {
136 TypeRef::Union(ttype) => ttype.can_be_intersected(),
137 TypeRef::Atomic(ttype) => ttype.can_be_intersected(),
138 }
139 }
140
141 fn get_intersection_types(&self) -> Option<&[TAtomic]> {
142 match self {
143 TypeRef::Union(ttype) => ttype.get_intersection_types(),
144 TypeRef::Atomic(ttype) => ttype.get_intersection_types(),
145 }
146 }
147
148 fn has_intersection_types(&self) -> bool {
149 match self {
150 TypeRef::Union(ttype) => ttype.has_intersection_types(),
151 TypeRef::Atomic(ttype) => ttype.has_intersection_types(),
152 }
153 }
154
155 fn needs_population(&self) -> bool {
156 match self {
157 TypeRef::Union(ttype) => ttype.needs_population(),
158 TypeRef::Atomic(ttype) => ttype.needs_population(),
159 }
160 }
161
162 fn is_expandable(&self) -> bool {
163 match self {
164 TypeRef::Union(ttype) => ttype.is_expandable(),
165 TypeRef::Atomic(ttype) => ttype.is_expandable(),
166 }
167 }
168
169 fn is_complex(&self) -> bool {
170 match self {
171 TypeRef::Union(ttype) => ttype.is_complex(),
172 TypeRef::Atomic(ttype) => ttype.is_complex(),
173 }
174 }
175
176 fn get_id(&self) -> Atom {
177 match self {
178 TypeRef::Union(ttype) => ttype.get_id(),
179 TypeRef::Atomic(ttype) => ttype.get_id(),
180 }
181 }
182
183 fn get_pretty_id_with_indent(&self, indent: usize) -> Atom {
184 match self {
185 TypeRef::Union(ttype) => ttype.get_pretty_id_with_indent(indent),
186 TypeRef::Atomic(ttype) => ttype.get_pretty_id_with_indent(indent),
187 }
188 }
189}
190
191impl<'a> From<&'a TUnion> for TypeRef<'a> {
192 fn from(reference: &'a TUnion) -> Self {
193 TypeRef::Union(reference)
194 }
195}
196
197impl<'a> From<&'a TAtomic> for TypeRef<'a> {
198 fn from(reference: &'a TAtomic) -> Self {
199 TypeRef::Atomic(reference)
200 }
201}
202
203pub fn get_union_from_integer(integer: &TInteger) -> TUnion {
214 if integer.is_unspecified() {
215 return get_int();
216 }
217
218 if integer.is_positive() {
219 return get_positive_int();
220 }
221
222 if integer.is_negative() {
223 return get_negative_int();
224 }
225
226 if integer.is_non_negative() {
227 return get_non_negative_int();
228 }
229
230 if integer.is_non_positive() {
231 return get_non_positive_int();
232 }
233
234 TUnion::from_single(Cow::Owned(TAtomic::Scalar(TScalar::Integer(*integer))))
235}
236
237#[inline]
238pub fn wrap_atomic(tinner: TAtomic) -> TUnion {
239 TUnion::from_single(Cow::Owned(tinner))
240}
241
242#[inline]
243pub fn get_int() -> TUnion {
244 TUnion::from_single(Cow::Borrowed(INT_ATOMIC))
245}
246
247#[inline]
248pub fn get_positive_int() -> TUnion {
249 TUnion::from_single(Cow::Borrowed(POSITIVE_INT_ATOMIC))
250}
251
252#[inline]
253pub fn get_negative_int() -> TUnion {
254 TUnion::from_single(Cow::Borrowed(NEGATIVE_INT_ATOMIC))
255}
256
257#[inline]
258pub fn get_non_positive_int() -> TUnion {
259 TUnion::from_single(Cow::Borrowed(NON_POSITIVE_INT_ATOMIC))
260}
261
262#[inline]
263pub fn get_non_negative_int() -> TUnion {
264 TUnion::from_single(Cow::Borrowed(NON_NEGATIVE_INT_ATOMIC))
265}
266
267#[inline]
268pub fn get_int_range(from: Option<i64>, to: Option<i64>) -> TUnion {
269 let atomic = match (from, to) {
270 (Some(from), Some(to)) => TAtomic::Scalar(TScalar::Integer(TInteger::Range(from, to))),
271 (Some(from), None) => {
272 if 0 == from {
273 return get_non_negative_int();
274 }
275
276 if 1 == from {
277 return get_positive_int();
278 }
279
280 TAtomic::Scalar(TScalar::Integer(TInteger::From(from)))
281 }
282 (None, Some(to)) => {
283 if 0 == to {
284 return get_non_positive_int();
285 }
286
287 if -1 == to {
288 return get_negative_int();
289 }
290
291 TAtomic::Scalar(TScalar::Integer(TInteger::To(to)))
292 }
293 (None, None) => return get_int(),
294 };
295
296 TUnion::from_single(Cow::Owned(atomic))
297}
298
299#[inline]
301pub fn get_signum_result() -> TUnion {
302 TUnion::new(Cow::Borrowed(SIGNUM_RESULT_SLICE))
303}
304
305#[inline]
307pub fn get_one_int() -> TUnion {
308 TUnion::from_single(Cow::Borrowed(ONE_INT_ATOMIC))
309}
310
311#[inline]
313pub fn get_zero_int() -> TUnion {
314 TUnion::from_single(Cow::Borrowed(ZERO_INT_ATOMIC))
315}
316
317#[inline]
319pub fn get_minus_one_int() -> TUnion {
320 TUnion::from_single(Cow::Borrowed(MINUS_ONE_INT_ATOMIC))
321}
322
323#[inline]
324pub fn get_literal_int(value: i64) -> TUnion {
325 if value == 0 {
326 return get_zero_int();
327 }
328
329 if value == 1 {
330 return get_one_int();
331 }
332
333 if value == -1 {
334 return get_minus_one_int();
335 }
336
337 TUnion::from_single(Cow::Owned(TAtomic::Scalar(TScalar::literal_int(value))))
338}
339
340#[inline]
341pub fn get_int_or_float() -> TUnion {
342 TUnion::new(Cow::Borrowed(INT_FLOAT_ATOMIC_SLICE))
343}
344
345#[inline]
346pub fn get_int_or_string() -> TUnion {
347 TUnion::new(Cow::Borrowed(INT_STRING_ATOMIC_SLICE))
348}
349
350#[inline]
351pub fn get_nullable_int() -> TUnion {
352 TUnion::new(Cow::Borrowed(NULL_INT_ATOMIC_SLICE))
353}
354
355#[inline]
356pub fn get_nullable_float() -> TUnion {
357 TUnion::new(Cow::Borrowed(NULL_FLOAT_ATOMIC_SLICE))
358}
359
360#[inline]
361pub fn get_nullable_object() -> TUnion {
362 TUnion::new(Cow::Borrowed(NULL_OBJECT_ATOMIC_SLICE))
363}
364
365#[inline]
366pub fn get_nullable_string() -> TUnion {
367 TUnion::new(Cow::Borrowed(NULL_STRING_ATOMIC_SLICE))
368}
369
370#[inline]
371pub fn get_string() -> TUnion {
372 TUnion::from_single(Cow::Borrowed(STRING_ATOMIC))
373}
374
375pub fn get_string_with_props(is_numeric: bool, is_truthy: bool, is_non_empty: bool, is_lowercase: bool) -> TUnion {
380 let atomic_ref = match (is_numeric, is_truthy, is_non_empty, is_lowercase) {
381 (true, true, _, _) => NUMERIC_TRUTHY_STRING_ATOMIC,
383 (true, false, _, _) => NUMERIC_STRING_ATOMIC,
384 (false, true, _, false) => TRUTHY_STRING_ATOMIC,
386 (false, true, _, true) => TRUTHY_LOWERCASE_STRING_ATOMIC,
387 (false, false, false, false) => STRING_ATOMIC,
389 (false, false, false, true) => LOWERCASE_STRING_ATOMIC,
390 (false, false, true, false) => NON_EMPTY_STRING_ATOMIC,
391 (false, false, true, true) => NON_EMPTY_LOWERCASE_STRING_ATOMIC,
392 };
393
394 TUnion::from_single(Cow::Borrowed(atomic_ref))
395}
396
397#[inline]
398pub fn get_literal_class_string(value: Atom) -> TUnion {
399 TUnion::from_single(Cow::Owned(TAtomic::Scalar(TScalar::ClassLikeString(TClassLikeString::literal(value)))))
400}
401
402#[inline]
403pub fn get_class_string() -> TUnion {
404 TUnion::from_single(Cow::Borrowed(CLASS_STRING_ATOMIC))
405}
406
407#[inline]
408pub fn get_class_string_of_type(constraint: TAtomic) -> TUnion {
409 TUnion::from_single(Cow::Owned(TAtomic::Scalar(TScalar::ClassLikeString(TClassLikeString::class_string_of_type(
410 constraint,
411 )))))
412}
413
414#[inline]
415pub fn get_interface_string() -> TUnion {
416 TUnion::from_single(Cow::Borrowed(INTERFACE_STRING_ATOMIC))
417}
418
419#[inline]
420pub fn get_interface_string_of_type(constraint: TAtomic) -> TUnion {
421 TUnion::from_single(Cow::Owned(TAtomic::Scalar(TScalar::ClassLikeString(
422 TClassLikeString::interface_string_of_type(constraint),
423 ))))
424}
425
426#[inline]
427pub fn get_enum_string() -> TUnion {
428 TUnion::from_single(Cow::Borrowed(ENUM_STRING_ATOMIC))
429}
430
431#[inline]
432pub fn get_enum_string_of_type(constraint: TAtomic) -> TUnion {
433 TUnion::from_single(Cow::Owned(TAtomic::Scalar(TScalar::ClassLikeString(TClassLikeString::enum_string_of_type(
434 constraint,
435 )))))
436}
437
438#[inline]
439pub fn get_trait_string() -> TUnion {
440 TUnion::from_single(Cow::Borrowed(TRAIT_STRING_ATOMIC))
441}
442
443#[inline]
444pub fn get_trait_string_of_type(constraint: TAtomic) -> TUnion {
445 TUnion::from_single(Cow::Owned(TAtomic::Scalar(TScalar::ClassLikeString(TClassLikeString::trait_string_of_type(
446 constraint,
447 )))))
448}
449
450#[inline]
451pub fn get_literal_string(value: Atom) -> TUnion {
452 TUnion::from_single(Cow::Owned(TAtomic::Scalar(TScalar::literal_string(value))))
453}
454
455#[inline]
456pub fn get_float() -> TUnion {
457 TUnion::from_single(Cow::Borrowed(FLOAT_ATOMIC))
458}
459
460#[inline]
461pub fn get_literal_float(v: f64) -> TUnion {
462 TUnion::from_single(Cow::Owned(TAtomic::Scalar(TScalar::literal_float(v))))
463}
464
465#[inline]
466pub fn get_mixed() -> TUnion {
467 TUnion::from_single(Cow::Borrowed(MIXED_ATOMIC))
468}
469
470#[inline]
471pub fn get_isset_from_mixed_mixed() -> TUnion {
472 TUnion::from_single(Cow::Borrowed(ISSET_FROM_LOOP_MIXED_ATOMIC))
473}
474
475pub fn get_mixed_maybe_from_loop(from_loop_isset: bool) -> TUnion {
476 if from_loop_isset { get_isset_from_mixed_mixed() } else { get_mixed() }
477}
478
479#[inline]
480pub fn get_never() -> TUnion {
481 TUnion::from_single(Cow::Borrowed(NEVER_ATOMIC))
482}
483
484#[inline]
485pub fn get_resource() -> TUnion {
486 TUnion::from_single(Cow::Borrowed(RESOURCE_ATOMIC))
487}
488
489#[inline]
490pub fn get_closed_resource() -> TUnion {
491 TUnion::from_single(Cow::Borrowed(CLOSED_RESOURCE_ATOMIC))
492}
493
494#[inline]
495pub fn get_open_resource() -> TUnion {
496 TUnion::from_single(Cow::Borrowed(OPEN_RESOURCE_ATOMIC))
497}
498
499#[inline]
500pub fn get_placeholder() -> TUnion {
501 TUnion::from_single(Cow::Borrowed(PLACEHOLDER_ATOMIC))
502}
503
504#[inline]
505pub fn get_void() -> TUnion {
506 TUnion::from_single(Cow::Borrowed(VOID_ATOMIC))
507}
508
509#[inline]
510pub fn get_null() -> TUnion {
511 TUnion::from_single(Cow::Borrowed(NULL_ATOMIC))
512}
513
514#[inline]
515pub fn get_arraykey() -> TUnion {
516 TUnion::from_single(Cow::Borrowed(ARRAYKEY_ATOMIC))
517}
518
519#[inline]
520pub fn get_bool() -> TUnion {
521 TUnion::from_single(Cow::Borrowed(BOOL_ATOMIC))
522}
523
524#[inline]
525pub fn get_false() -> TUnion {
526 TUnion::from_single(Cow::Borrowed(FALSE_ATOMIC))
527}
528
529#[inline]
530pub fn get_true() -> TUnion {
531 TUnion::from_single(Cow::Borrowed(TRUE_ATOMIC))
532}
533
534#[inline]
535pub fn get_object() -> TUnion {
536 TUnion::from_single(Cow::Borrowed(OBJECT_ATOMIC))
537}
538
539#[inline]
540pub fn get_numeric() -> TUnion {
541 TUnion::from_single(Cow::Borrowed(NUMERIC_ATOMIC))
542}
543
544#[inline]
545pub fn get_numeric_string() -> TUnion {
546 TUnion::from_single(Cow::Borrowed(NUMERIC_STRING_ATOMIC))
547}
548
549#[inline]
550pub fn get_lowercase_string() -> TUnion {
551 TUnion::from_single(Cow::Borrowed(LOWERCASE_STRING_ATOMIC))
552}
553
554#[inline]
555pub fn get_non_empty_lowercase_string() -> TUnion {
556 TUnion::from_single(Cow::Borrowed(NON_EMPTY_LOWERCASE_STRING_ATOMIC))
557}
558
559#[inline]
560pub fn get_non_empty_string() -> TUnion {
561 TUnion::from_single(Cow::Borrowed(NON_EMPTY_STRING_ATOMIC))
562}
563
564#[inline]
565pub fn get_empty_string() -> TUnion {
566 TUnion::from_single(Cow::Borrowed(&EMPTY_STRING_ATOMIC))
567}
568
569#[inline]
570pub fn get_truthy_string() -> TUnion {
571 TUnion::from_single(Cow::Borrowed(TRUTHY_STRING_ATOMIC))
572}
573
574#[inline]
575pub fn get_unspecified_literal_string() -> TUnion {
576 TUnion::from_single(Cow::Borrowed(UNSPECIFIED_LITERAL_STRING_ATOMIC))
577}
578
579#[inline]
580pub fn get_non_empty_unspecified_literal_string() -> TUnion {
581 TUnion::from_single(Cow::Borrowed(NON_EMPTY_UNSPECIFIED_LITERAL_STRING_ATOMIC))
582}
583
584#[inline]
585pub fn get_scalar() -> TUnion {
586 TUnion::from_single(Cow::Borrowed(SCALAR_ATOMIC))
587}
588
589#[inline]
590pub fn get_nullable_scalar() -> TUnion {
591 TUnion::new(Cow::Borrowed(NULL_SCALAR_ATOMIC_SLICE))
592}
593
594#[inline]
595pub fn get_mixed_iterable() -> TUnion {
596 TUnion::from_single(Cow::Borrowed(&MIXED_ITERABLE_ATOMIC))
597}
598
599#[inline]
600pub fn get_empty_keyed_array() -> TUnion {
601 TUnion::from_single(Cow::Borrowed(&EMPTY_KEYED_ARRAY_ATOMIC))
602}
603
604#[inline]
605pub fn get_mixed_list() -> TUnion {
606 get_list(get_mixed())
607}
608
609#[inline]
610pub fn get_mixed_keyed_array() -> TUnion {
611 get_keyed_array(get_arraykey(), get_mixed())
612}
613
614#[inline]
615pub fn get_mixed_callable() -> TUnion {
616 TUnion::from_single(Cow::Borrowed(&MIXED_CALLABLE_ATOMIC))
617}
618
619#[inline]
620pub fn get_mixed_closure() -> TUnion {
621 TUnion::from_single(Cow::Borrowed(&MIXED_CLOSURE_ATOMIC))
622}
623
624#[inline]
625pub fn get_named_object(name: Atom, type_resolution_context: Option<&TypeResolutionContext>) -> TUnion {
626 if let Some(type_resolution_context) = type_resolution_context
627 && let Some(defining_entities) = type_resolution_context.get_template_definition(&name)
628 {
629 return wrap_atomic(TAtomic::Scalar(TScalar::ClassLikeString(TClassLikeString::Generic {
630 kind: TClassLikeStringKind::Class,
631 parameter_name: name,
632 defining_entity: defining_entities[0].0,
633 constraint: Box::new((*(defining_entities[0].1.get_single())).clone()),
634 })));
635 }
636
637 wrap_atomic(TAtomic::Object(TObject::Named(TNamedObject::new(name))))
638}
639
640#[inline]
641pub fn get_iterable(key_parameter: TUnion, value_parameter: TUnion) -> TUnion {
642 wrap_atomic(TAtomic::Iterable(TIterable::new(Box::new(key_parameter), Box::new(value_parameter))))
643}
644
645#[inline]
646pub fn get_list(element_type: TUnion) -> TUnion {
647 wrap_atomic(TAtomic::Array(TArray::List(TList::new(Box::new(element_type)))))
648}
649
650#[inline]
651pub fn get_keyed_array(key_parameter: TUnion, value_parameter: TUnion) -> TUnion {
652 wrap_atomic(TAtomic::Array(TArray::Keyed(TKeyedArray::new_with_parameters(
653 Box::new(key_parameter),
654 Box::new(value_parameter),
655 ))))
656}
657
658#[inline]
659pub fn add_optional_union_type(base_type: TUnion, maybe_type: Option<&TUnion>, codebase: &CodebaseMetadata) -> TUnion {
660 if let Some(type_2) = maybe_type { add_union_type(base_type, type_2, codebase, false) } else { base_type }
661}
662
663#[inline]
664pub fn combine_optional_union_types(
665 type_1: Option<&TUnion>,
666 type_2: Option<&TUnion>,
667 codebase: &CodebaseMetadata,
668) -> TUnion {
669 match (type_1, type_2) {
670 (Some(type_1), Some(type_2)) => combine_union_types(type_1, type_2, codebase, false),
671 (Some(type_1), None) => type_1.clone(),
672 (None, Some(type_2)) => type_2.clone(),
673 (None, None) => get_mixed(),
674 }
675}
676
677#[inline]
678pub fn combine_union_types(
679 type_1: &TUnion,
680 type_2: &TUnion,
681 codebase: &CodebaseMetadata,
682 overwrite_empty_array: bool,
683) -> TUnion {
684 if type_1 == type_2 {
685 return type_1.clone();
686 }
687
688 let mut combined_type = if type_1.is_never() || type_1.is_never_template() {
689 type_2.clone()
690 } else if type_2.is_never() || type_2.is_never_template() {
691 type_1.clone()
692 } else if type_1.is_vanilla_mixed() && type_2.is_vanilla_mixed() {
693 get_mixed()
694 } else {
695 let mut all_atomic_types = type_1.types.clone().into_owned();
696 all_atomic_types.extend(type_2.types.clone().into_owned());
697
698 let mut result = TUnion::from_vec(combiner::combine(all_atomic_types, codebase, overwrite_empty_array));
699
700 if type_1.had_template && type_2.had_template {
701 result.had_template = true;
702 }
703
704 if type_1.reference_free && type_2.reference_free {
705 result.reference_free = true;
706 }
707
708 result
709 };
710
711 if type_1.possibly_undefined || type_2.possibly_undefined {
712 combined_type.possibly_undefined = true;
713 }
714
715 if type_1.possibly_undefined_from_try || type_2.possibly_undefined_from_try {
716 combined_type.possibly_undefined_from_try = true;
717 }
718
719 if type_1.ignore_falsable_issues || type_2.ignore_falsable_issues {
720 combined_type.ignore_falsable_issues = true;
721 }
722
723 combined_type
724}
725
726#[inline]
727pub fn add_union_type(
728 mut base_type: TUnion,
729 other_type: &TUnion,
730 codebase: &CodebaseMetadata,
731 overwrite_empty_array: bool,
732) -> TUnion {
733 if &base_type == other_type {
734 base_type.possibly_undefined |= other_type.possibly_undefined;
735 base_type.possibly_undefined_from_try |= other_type.possibly_undefined_from_try;
736 base_type.ignore_falsable_issues |= other_type.ignore_falsable_issues;
737 base_type.ignore_nullable_issues |= other_type.ignore_nullable_issues;
738
739 return base_type;
740 }
741
742 base_type.types = if base_type.is_vanilla_mixed() && other_type.is_vanilla_mixed() {
743 base_type.types
744 } else {
745 combine_union_types(&base_type, other_type, codebase, overwrite_empty_array).types
746 };
747
748 if !other_type.had_template {
749 base_type.had_template = false;
750 }
751
752 if !other_type.reference_free {
753 base_type.reference_free = false;
754 }
755
756 base_type.possibly_undefined |= other_type.possibly_undefined;
757 base_type.possibly_undefined_from_try |= other_type.possibly_undefined_from_try;
758 base_type.ignore_falsable_issues |= other_type.ignore_falsable_issues;
759 base_type.ignore_nullable_issues |= other_type.ignore_nullable_issues;
760
761 base_type
762}
763
764pub fn intersect_union_types(_type_1: &TUnion, _type_2: &TUnion, _codebase: &CodebaseMetadata) -> Option<TUnion> {
765 None
766}
767
768pub fn get_iterable_parameters(atomic: &TAtomic, codebase: &CodebaseMetadata) -> Option<(TUnion, TUnion)> {
769 if let Some(generator_parameters) = atomic.get_generator_parameters() {
770 return Some((generator_parameters.0, generator_parameters.1));
771 }
772
773 let parameters = 'parameters: {
774 match atomic {
775 TAtomic::Iterable(iterable) => Some((iterable.get_key_type().clone(), iterable.get_value_type().clone())),
776 TAtomic::Array(array_type) => Some(get_array_parameters(array_type, codebase)),
777 TAtomic::Object(object) => {
778 let name = object.get_name()?;
779 let traversable = atom("traversable");
780
781 let class_metadata = get_class_like(codebase, name)?;
782 if !is_instance_of(codebase, &class_metadata.name, &traversable) {
783 break 'parameters None;
784 }
785
786 let traversable_metadata = get_class_like(codebase, &traversable)?;
787 let key_template = traversable_metadata.template_types.first().map(|(name, _)| name)?;
788 let value_template = traversable_metadata.template_types.get(1).map(|(name, _)| name)?;
789
790 let key_type = get_specialized_template_type(
791 codebase,
792 key_template,
793 &traversable,
794 class_metadata,
795 object.get_type_parameters(),
796 )
797 .unwrap_or_else(get_mixed);
798
799 let value_type = get_specialized_template_type(
800 codebase,
801 value_template,
802 &traversable,
803 class_metadata,
804 object.get_type_parameters(),
805 )
806 .unwrap_or_else(get_mixed);
807
808 Some((key_type, value_type))
809 }
810 _ => None,
811 }
812 };
813
814 if let Some((key_type, value_type)) = parameters {
815 return Some((key_type, value_type));
816 }
817
818 if let Some(intersection_types) = atomic.get_intersection_types() {
819 for intersection_type in intersection_types {
820 if let Some((key_type, value_type)) = get_iterable_parameters(intersection_type, codebase) {
821 return Some((key_type, value_type));
822 }
823 }
824 }
825
826 None
827}
828
829pub fn get_array_parameters(array_type: &TArray, codebase: &CodebaseMetadata) -> (TUnion, TUnion) {
830 match array_type {
831 TArray::Keyed(keyed_data) => {
832 let mut key_types = vec![];
833 let mut value_param;
834
835 if let Some((key_param, value_p)) = &keyed_data.parameters {
836 key_types.extend(key_param.types.clone().into_owned());
837 value_param = (**value_p).clone();
838 } else {
839 key_types.push(TAtomic::Never);
840 value_param = get_never();
841 }
842
843 if let Some(known_items) = &keyed_data.known_items {
844 for (key, (_, item_type)) in known_items {
845 key_types.push(key.to_atomic());
846 value_param = add_union_type(value_param, item_type, codebase, false);
847 }
848 }
849
850 let combined_key_types = combiner::combine(key_types, codebase, false);
851 let key_param_union = TUnion::from_vec(combined_key_types);
852
853 (key_param_union, value_param)
854 }
855 TArray::List(list_data) => {
856 let mut key_types = vec![];
857 let mut value_type = (*list_data.element_type).clone();
858
859 if let Some(known_elements) = &list_data.known_elements {
860 for (key_idx, (_, element_type)) in known_elements {
861 key_types.push(TAtomic::Scalar(TScalar::literal_int(*key_idx as i64)));
862
863 value_type = combine_union_types(element_type, &value_type, codebase, false);
864 }
865 }
866
867 if key_types.is_empty() || !value_type.is_never() {
868 if value_type.is_never() {
869 key_types.push(TAtomic::Never);
870 } else {
871 key_types.push(TAtomic::Scalar(TScalar::Integer(TInteger::non_negative())));
872 }
873 }
874
875 let key_type = TUnion::from_vec(combiner::combine(key_types, codebase, false));
876
877 (key_type, value_type)
878 }
879 }
880}
881
882pub fn get_iterable_value_parameter(atomic: &TAtomic, codebase: &CodebaseMetadata) -> Option<TUnion> {
883 if let Some(generator_parameters) = atomic.get_generator_parameters() {
884 return Some(generator_parameters.1);
885 }
886
887 let parameter = match atomic {
888 TAtomic::Iterable(iterable) => Some(iterable.get_value_type().clone()),
889 TAtomic::Array(array_type) => Some(get_array_value_parameter(array_type, codebase)),
890 TAtomic::Object(object) => {
891 let name = object.get_name()?;
892 let traversable = atom("traversable");
893
894 let class_metadata = get_class_like(codebase, name)?;
895 if !is_instance_of(codebase, &class_metadata.name, &traversable) {
896 return None;
897 }
898
899 let traversable_metadata = get_class_like(codebase, &traversable)?;
900 let value_template = traversable_metadata.template_types.get(1).map(|(name, _)| name)?;
901
902 get_specialized_template_type(
903 codebase,
904 value_template,
905 &traversable,
906 class_metadata,
907 object.get_type_parameters(),
908 )
909 }
910 _ => None,
911 };
912
913 if let Some(value_param) = parameter {
914 return Some(value_param);
915 }
916
917 if let Some(intersection_types) = atomic.get_intersection_types() {
918 for intersection_type in intersection_types {
919 if let Some(value_param) = get_iterable_value_parameter(intersection_type, codebase) {
920 return Some(value_param);
921 }
922 }
923 }
924
925 None
926}
927
928pub fn get_array_value_parameter(array_type: &TArray, codebase: &CodebaseMetadata) -> TUnion {
929 match array_type {
930 TArray::Keyed(keyed_data) => {
931 let mut value_param;
932
933 if let Some((_, value_p)) = &keyed_data.parameters {
934 value_param = (**value_p).clone();
935 } else {
936 value_param = get_never();
937 }
938
939 if let Some(known_items) = &keyed_data.known_items {
940 for (_, item_type) in known_items.values() {
941 value_param = combine_union_types(item_type, &value_param, codebase, false);
942 }
943 }
944
945 value_param
946 }
947 TArray::List(list_data) => {
948 let mut value_param = (*list_data.element_type).clone();
949
950 if let Some(known_elements) = &list_data.known_elements {
951 for (_, element_type) in known_elements.values() {
952 value_param = combine_union_types(element_type, &value_param, codebase, false);
953 }
954 }
955
956 value_param
957 }
958 }
959}
960
961pub fn get_specialized_template_type(
966 codebase: &CodebaseMetadata,
967 template_name: &Atom,
968 template_defining_class_id: &Atom,
969 instantiated_class_metadata: &ClassLikeMetadata,
970 instantiated_type_parameters: Option<&[TUnion]>,
971) -> Option<TUnion> {
972 let defining_class_metadata = get_class_like(codebase, template_defining_class_id)?;
973
974 if defining_class_metadata.name == instantiated_class_metadata.name {
975 let index = instantiated_class_metadata.get_template_index_for_name(template_name)?;
976
977 let Some(instantiated_type_parameters) = instantiated_type_parameters else {
978 let type_map = instantiated_class_metadata.get_template_type(template_name)?;
979
980 return type_map.first().map(|(_, constraint)| constraint).cloned();
981 };
982
983 return instantiated_type_parameters.get(index).cloned();
984 }
985
986 let defining_template_type = defining_class_metadata.get_template_type(template_name)?;
987 let template_union = TUnion::from_vec(
988 defining_template_type
989 .iter()
990 .map(|(defining_entity, constraint)| {
991 TAtomic::GenericParameter(TGenericParameter {
992 parameter_name: *template_name,
993 defining_entity: *defining_entity,
994 constraint: Box::new(constraint.clone()),
995 intersection_types: None,
996 })
997 })
998 .collect::<Vec<_>>(),
999 );
1000
1001 let mut template_result = TemplateResult::default();
1002 for (defining_class, type_parameters_map) in &instantiated_class_metadata.template_extended_parameters {
1003 for (parameter_name, parameter_type) in type_parameters_map {
1004 template_result.add_lower_bound(
1005 *parameter_name,
1006 GenericParent::ClassLike(*defining_class),
1007 parameter_type.clone(),
1008 );
1009 }
1010 }
1011
1012 let mut template_type = inferred_type_replacer::replace(&template_union, &template_result, codebase);
1013 if let Some(type_parameters) = instantiated_type_parameters {
1014 let mut template_result = TemplateResult::default();
1015 for (i, parameter_type) in type_parameters.iter().enumerate() {
1016 if let Some(parameter_name) = instantiated_class_metadata.get_template_name_for_index(i) {
1017 template_result.add_lower_bound(
1018 parameter_name,
1019 GenericParent::ClassLike(instantiated_class_metadata.name),
1020 parameter_type.clone(),
1021 );
1022 }
1023 }
1024
1025 if !template_result.lower_bounds.is_empty() {
1026 template_type = inferred_type_replacer::replace(&template_type, &template_result, codebase);
1027 }
1028 }
1029
1030 Some(template_type)
1031}