1use mago_interner::StringIdentifier;
2use mago_interner::ThreadedInterner;
3
4use crate::get_class_like;
5use crate::is_instance_of;
6use crate::metadata::CodebaseMetadata;
7use crate::metadata::class_like::ClassLikeMetadata;
8use crate::misc::GenericParent;
9use crate::ttype::atomic::TAtomic;
10use crate::ttype::atomic::array::TArray;
11use crate::ttype::atomic::array::keyed::TKeyedArray;
12use crate::ttype::atomic::array::list::TList;
13use crate::ttype::atomic::callable::TCallable;
14use crate::ttype::atomic::callable::TCallableSignature;
15use crate::ttype::atomic::generic::TGenericParameter;
16use crate::ttype::atomic::iterable::TIterable;
17use crate::ttype::atomic::mixed::TMixed;
18use crate::ttype::atomic::object::TObject;
19use crate::ttype::atomic::object::named::TNamedObject;
20use crate::ttype::atomic::resource::TResource;
21use crate::ttype::atomic::scalar::TScalar;
22use crate::ttype::atomic::scalar::class_like_string::TClassLikeString;
23use crate::ttype::atomic::scalar::class_like_string::TClassLikeStringKind;
24use crate::ttype::atomic::scalar::int::TInteger;
25use crate::ttype::atomic::scalar::string::TString;
26use crate::ttype::resolution::TypeResolutionContext;
27use crate::ttype::template::TemplateResult;
28use crate::ttype::template::inferred_type_replacer;
29use crate::ttype::union::TUnion;
30
31pub mod atomic;
32pub mod builder;
33pub mod cast;
34pub mod combination;
35pub mod combiner;
36pub mod comparator;
37pub mod error;
38pub mod expander;
39pub mod resolution;
40pub mod template;
41pub mod union;
42
43#[derive(Clone, Copy, Debug)]
45pub enum TypeRef<'a> {
46 Union(&'a TUnion),
47 Atomic(&'a TAtomic),
48}
49
50pub trait TType {
52 fn get_child_nodes<'a>(&'a self) -> Vec<TypeRef<'a>> {
54 vec![]
55 }
56
57 fn get_all_child_nodes<'a>(&'a self) -> Vec<TypeRef<'a>> {
59 let mut child_nodes = self.get_child_nodes();
60 let mut all_child_nodes = vec![];
61
62 while let Some(child_node) = child_nodes.pop() {
63 let new_child_nodes = match child_node {
64 TypeRef::Union(union) => union.get_child_nodes(),
65 TypeRef::Atomic(atomic) => atomic.get_child_nodes(),
66 };
67
68 all_child_nodes.push(child_node);
69
70 child_nodes.extend(new_child_nodes);
71 }
72
73 all_child_nodes
74 }
75
76 fn can_be_intersected(&self) -> bool {
78 false
79 }
80
81 fn get_intersection_types(&self) -> Option<&[TAtomic]> {
83 None
84 }
85
86 fn get_intersection_types_mut(&mut self) -> Option<&mut Vec<TAtomic>> {
88 None
89 }
90
91 fn has_intersection_types(&self) -> bool {
93 false
94 }
95
96 fn add_intersection_type(&mut self, _intersection_type: TAtomic) -> bool {
101 false
102 }
103
104 fn get_id(&self, interner: Option<&ThreadedInterner>) -> String;
113}
114
115impl<'a> TType for TypeRef<'a> {
117 fn get_child_nodes(&self) -> Vec<TypeRef<'a>> {
118 match self {
119 TypeRef::Union(ttype) => ttype.get_child_nodes(),
120 TypeRef::Atomic(ttype) => ttype.get_child_nodes(),
121 }
122 }
123
124 fn can_be_intersected(&self) -> bool {
125 match self {
126 TypeRef::Union(ttype) => ttype.can_be_intersected(),
127 TypeRef::Atomic(ttype) => ttype.can_be_intersected(),
128 }
129 }
130
131 fn get_intersection_types(&self) -> Option<&[TAtomic]> {
132 match self {
133 TypeRef::Union(ttype) => ttype.get_intersection_types(),
134 TypeRef::Atomic(ttype) => ttype.get_intersection_types(),
135 }
136 }
137
138 fn has_intersection_types(&self) -> bool {
139 match self {
140 TypeRef::Union(ttype) => ttype.has_intersection_types(),
141 TypeRef::Atomic(ttype) => ttype.has_intersection_types(),
142 }
143 }
144
145 fn get_id(&self, interner: Option<&ThreadedInterner>) -> String {
146 match self {
147 TypeRef::Union(ttype) => ttype.get_id(interner),
148 TypeRef::Atomic(ttype) => ttype.get_id(interner),
149 }
150 }
151}
152
153impl<'a> From<&'a TUnion> for TypeRef<'a> {
154 fn from(reference: &'a TUnion) -> Self {
155 TypeRef::Union(reference)
156 }
157}
158
159impl<'a> From<&'a TAtomic> for TypeRef<'a> {
160 fn from(reference: &'a TAtomic) -> Self {
161 TypeRef::Atomic(reference)
162 }
163}
164
165#[inline]
166pub fn wrap_atomic(tinner: TAtomic) -> TUnion {
167 TUnion::new(vec![tinner])
168}
169
170#[inline]
171pub fn get_int() -> TUnion {
172 wrap_atomic(TAtomic::Scalar(TScalar::int()))
173}
174
175#[inline]
176pub fn get_int_range(from: Option<i64>, to: Option<i64>) -> TUnion {
177 match (from, to) {
178 (Some(from), Some(to)) => wrap_atomic(TAtomic::Scalar(TScalar::Integer(TInteger::Range(from, to)))),
179 (Some(from), None) => wrap_atomic(TAtomic::Scalar(TScalar::Integer(TInteger::From(from)))),
180 (None, Some(to)) => wrap_atomic(TAtomic::Scalar(TScalar::Integer(TInteger::To(to)))),
181 (None, None) => get_int(),
182 }
183}
184
185#[inline]
186pub fn get_literal_int(value: i64) -> TUnion {
187 wrap_atomic(TAtomic::Scalar(TScalar::literal_int(value)))
188}
189
190#[inline]
191pub fn get_string() -> TUnion {
192 wrap_atomic(TAtomic::Scalar(TScalar::string()))
193}
194
195pub fn get_string_with_props(is_numeric: bool, is_truthy: bool, is_non_empty: bool, is_lowercase: bool) -> TUnion {
196 wrap_atomic(TAtomic::Scalar(TScalar::String(TString::general_with_props(
197 is_numeric,
198 is_truthy,
199 is_non_empty,
200 is_lowercase,
201 ))))
202}
203
204#[inline]
205pub fn get_literal_class_string(value: StringIdentifier) -> TUnion {
206 wrap_atomic(TAtomic::Scalar(TScalar::ClassLikeString(TClassLikeString::literal(value))))
207}
208
209#[inline]
210pub fn get_class_string() -> TUnion {
211 wrap_atomic(TAtomic::Scalar(TScalar::ClassLikeString(TClassLikeString::class_string())))
212}
213
214#[inline]
215pub fn get_class_string_of_type(constraint: TAtomic) -> TUnion {
216 wrap_atomic(TAtomic::Scalar(TScalar::ClassLikeString(TClassLikeString::class_string_of_type(constraint))))
217}
218
219#[inline]
220pub fn get_interface_string() -> TUnion {
221 wrap_atomic(TAtomic::Scalar(TScalar::ClassLikeString(TClassLikeString::interface_string())))
222}
223
224#[inline]
225pub fn get_interface_string_of_type(constraint: TAtomic) -> TUnion {
226 wrap_atomic(TAtomic::Scalar(TScalar::ClassLikeString(TClassLikeString::interface_string_of_type(constraint))))
227}
228
229#[inline]
230pub fn get_enum_string() -> TUnion {
231 wrap_atomic(TAtomic::Scalar(TScalar::ClassLikeString(TClassLikeString::enum_string())))
232}
233
234#[inline]
235pub fn get_enum_string_of_type(constraint: TAtomic) -> TUnion {
236 wrap_atomic(TAtomic::Scalar(TScalar::ClassLikeString(TClassLikeString::enum_string_of_type(constraint))))
237}
238
239#[inline]
240pub fn get_trait_string() -> TUnion {
241 wrap_atomic(TAtomic::Scalar(TScalar::ClassLikeString(TClassLikeString::trait_string())))
242}
243
244#[inline]
245pub fn get_trait_string_of_type(constraint: TAtomic) -> TUnion {
246 wrap_atomic(TAtomic::Scalar(TScalar::ClassLikeString(TClassLikeString::trait_string_of_type(constraint))))
247}
248
249#[inline]
250pub fn get_literal_string(value: String) -> TUnion {
251 wrap_atomic(TAtomic::Scalar(TScalar::literal_string(value)))
252}
253
254#[inline]
255pub fn get_float() -> TUnion {
256 wrap_atomic(TAtomic::Scalar(TScalar::float()))
257}
258
259#[inline]
260pub fn get_literal_float(v: f64) -> TUnion {
261 wrap_atomic(TAtomic::Scalar(TScalar::literal_float(v)))
262}
263
264#[inline]
265pub fn get_mixed() -> TUnion {
266 wrap_atomic(TAtomic::Mixed(TMixed::vanilla()))
267}
268
269#[inline]
270pub fn get_mixed_any() -> TUnion {
271 wrap_atomic(TAtomic::Mixed(TMixed::any()))
272}
273
274pub fn get_mixed_maybe_from_loop(from_loop_isset: bool) -> TUnion {
275 wrap_atomic(TAtomic::Mixed(TMixed::maybe_isset_from_loop(from_loop_isset)))
276}
277
278#[inline]
279pub fn get_never() -> TUnion {
280 wrap_atomic(TAtomic::Never)
281}
282
283#[inline]
284pub fn get_resource() -> TUnion {
285 wrap_atomic(TAtomic::Resource(TResource::new(None)))
286}
287
288#[inline]
289pub fn get_closed_resource() -> TUnion {
290 wrap_atomic(TAtomic::Resource(TResource::new(Some(true))))
291}
292
293#[inline]
294pub fn get_open_resource() -> TUnion {
295 wrap_atomic(TAtomic::Resource(TResource::new(Some(false))))
296}
297
298#[inline]
299pub fn get_placeholder() -> TUnion {
300 wrap_atomic(TAtomic::Placeholder)
301}
302
303#[inline]
304pub fn get_void() -> TUnion {
305 wrap_atomic(TAtomic::Void)
306}
307
308#[inline]
309pub fn get_null() -> TUnion {
310 wrap_atomic(TAtomic::Null)
311}
312
313#[inline]
314pub fn get_arraykey() -> TUnion {
315 wrap_atomic(TAtomic::Scalar(TScalar::ArrayKey))
316}
317
318#[inline]
319pub fn get_bool() -> TUnion {
320 wrap_atomic(TAtomic::Scalar(TScalar::bool()))
321}
322
323#[inline]
324pub fn get_false() -> TUnion {
325 wrap_atomic(TAtomic::Scalar(TScalar::r#false()))
326}
327
328#[inline]
329pub fn get_true() -> TUnion {
330 wrap_atomic(TAtomic::Scalar(TScalar::r#true()))
331}
332
333#[inline]
334pub fn get_object() -> TUnion {
335 wrap_atomic(TAtomic::Object(TObject::Any))
336}
337
338#[inline]
339pub fn get_numeric() -> TUnion {
340 TUnion::new(vec![TAtomic::Scalar(TScalar::Numeric)])
341}
342
343#[inline]
344pub fn get_numeric_string() -> TUnion {
345 wrap_atomic(TAtomic::Scalar(TScalar::String(TString::general_with_props(true, false, false, true))))
346}
347
348#[inline]
349pub fn get_lowercase_string() -> TUnion {
350 wrap_atomic(TAtomic::Scalar(TScalar::String(TString::general_with_props(false, false, false, true))))
351}
352
353#[inline]
354pub fn get_non_empty_lowercase_string() -> TUnion {
355 wrap_atomic(TAtomic::Scalar(TScalar::String(TString::general_with_props(false, false, true, true))))
356}
357
358#[inline]
359pub fn get_non_empty_string() -> TUnion {
360 wrap_atomic(TAtomic::Scalar(TScalar::String(TString::general_with_props(false, false, true, false))))
361}
362
363#[inline]
364pub fn get_truthy_string() -> TUnion {
365 wrap_atomic(TAtomic::Scalar(TScalar::String(TString::general_with_props(false, true, false, false))))
366}
367
368#[inline]
369pub fn get_unspecified_literal_string() -> TUnion {
370 wrap_atomic(TAtomic::Scalar(TScalar::String(TString::unspecified_literal())))
371}
372
373#[inline]
374pub fn get_non_empty_unspecified_literal_string() -> TUnion {
375 wrap_atomic(TAtomic::Scalar(TScalar::String(TString::unspecified_literal_with_props(false, false, true, false))))
376}
377
378#[inline]
379pub fn get_named_object(
380 interner: &ThreadedInterner,
381 name: StringIdentifier,
382 type_resolution_context: Option<&TypeResolutionContext>,
383) -> TUnion {
384 if let Some(type_resolution_context) = type_resolution_context {
385 let name_str = interner.lookup(&name);
386 if let Some(defining_entities) = type_resolution_context.get_template_definition(name_str) {
387 return wrap_atomic(TAtomic::Scalar(TScalar::ClassLikeString(TClassLikeString::Generic {
388 kind: TClassLikeStringKind::Class,
389 parameter_name: name,
390 defining_entity: defining_entities[0].0,
391 constraint: Box::new((*(defining_entities[0].1.get_single())).clone()),
392 })));
393 }
394 }
395
396 wrap_atomic(TAtomic::Object(TObject::Named(TNamedObject::new(name))))
397}
398
399#[inline]
400pub fn get_scalar() -> TUnion {
401 wrap_atomic(TAtomic::Scalar(TScalar::Generic))
402}
403
404#[inline]
405pub fn get_mixed_iterable() -> TUnion {
406 wrap_atomic(TAtomic::Iterable(TIterable::mixed()))
407}
408
409#[inline]
410pub fn get_iterable(key_parameter: TUnion, value_parameter: TUnion) -> TUnion {
411 wrap_atomic(TAtomic::Iterable(TIterable::new(Box::new(key_parameter), Box::new(value_parameter))))
412}
413
414#[inline]
415pub fn get_list(element_type: TUnion) -> TUnion {
416 wrap_atomic(TAtomic::Array(TArray::List(TList::new(Box::new(element_type)))))
417}
418
419#[inline]
420pub fn get_empty_keyed_array() -> TUnion {
421 wrap_atomic(TAtomic::Array(TArray::Keyed(TKeyedArray::new())))
422}
423
424#[inline]
425pub fn get_keyed_array(key_parameter: TUnion, value_parameter: TUnion) -> TUnion {
426 wrap_atomic(TAtomic::Array(TArray::Keyed(TKeyedArray::new_with_parameters(
427 Box::new(key_parameter),
428 Box::new(value_parameter),
429 ))))
430}
431
432#[inline]
433pub fn get_mixed_list() -> TUnion {
434 get_list(get_mixed())
435}
436
437#[inline]
438pub fn get_mixed_keyed_array() -> TUnion {
439 get_keyed_array(get_arraykey(), get_mixed())
440}
441
442#[inline]
443pub fn get_mixed_callable() -> TUnion {
444 wrap_atomic(TAtomic::Callable(TCallable::Signature(TCallableSignature::mixed(false))))
445}
446
447#[inline]
448pub fn get_mixed_closure() -> TUnion {
449 wrap_atomic(TAtomic::Callable(TCallable::Signature(TCallableSignature::mixed(true))))
450}
451
452#[inline]
453pub fn add_optional_union_type(
454 base_type: TUnion,
455 maybe_type: Option<&TUnion>,
456 codebase: &CodebaseMetadata,
457 interner: &ThreadedInterner,
458) -> TUnion {
459 if let Some(type_2) = maybe_type { add_union_type(base_type, type_2, codebase, interner, false) } else { base_type }
460}
461
462#[inline]
463pub fn combine_optional_union_types(
464 type_1: Option<&TUnion>,
465 type_2: Option<&TUnion>,
466 codebase: &CodebaseMetadata,
467 interner: &ThreadedInterner,
468) -> TUnion {
469 match (type_1, type_2) {
470 (Some(type_1), Some(type_2)) => combine_union_types(type_1, type_2, codebase, interner, false),
471 (Some(type_1), None) => type_1.clone(),
472 (None, Some(type_2)) => type_2.clone(),
473 (None, None) => get_mixed_any(),
474 }
475}
476
477#[inline]
478pub fn combine_union_types(
479 type_1: &TUnion,
480 type_2: &TUnion,
481 codebase: &CodebaseMetadata,
482 interner: &ThreadedInterner,
483 overwrite_empty_array: bool,
484) -> TUnion {
485 if type_1 == type_2 {
486 return type_1.clone();
487 }
488
489 let mut combined_type = if type_1.is_never() || type_1.is_never_template() {
490 type_2.clone()
491 } else if type_2.is_never() || type_2.is_never_template() {
492 type_1.clone()
493 } else if type_1.is_vanilla_mixed() && type_2.is_vanilla_mixed() {
494 get_mixed()
495 } else {
496 let mut all_atomic_types = type_1.types.clone();
497 all_atomic_types.extend(type_2.types.clone());
498
499 let mut result = TUnion::new(combiner::combine(all_atomic_types, codebase, interner, overwrite_empty_array));
500
501 if type_1.had_template && type_2.had_template {
502 result.had_template = true;
503 }
504
505 if type_1.reference_free && type_2.reference_free {
506 result.reference_free = true;
507 }
508
509 result
510 };
511
512 if type_1.possibly_undefined || type_2.possibly_undefined {
513 combined_type.possibly_undefined = true;
514 }
515
516 if type_1.possibly_undefined_from_try || type_2.possibly_undefined_from_try {
517 combined_type.possibly_undefined_from_try = true;
518 }
519
520 if type_1.ignore_falsable_issues || type_2.ignore_falsable_issues {
521 combined_type.ignore_falsable_issues = true;
522 }
523
524 combined_type
525}
526
527#[inline]
528pub fn add_union_type(
529 mut base_type: TUnion,
530 other_type: &TUnion,
531 codebase: &CodebaseMetadata,
532 interner: &ThreadedInterner,
533 overwrite_empty_array: bool,
534) -> TUnion {
535 if &base_type == other_type {
536 base_type.possibly_undefined |= other_type.possibly_undefined;
537 base_type.possibly_undefined_from_try |= other_type.possibly_undefined_from_try;
538 base_type.ignore_falsable_issues |= other_type.ignore_falsable_issues;
539 base_type.ignore_nullable_issues |= other_type.ignore_nullable_issues;
540
541 return base_type;
542 }
543
544 base_type.types = if base_type.is_vanilla_mixed() && other_type.is_vanilla_mixed() {
545 base_type.types
546 } else {
547 let mut all_atomic_types = base_type.types.clone();
548 all_atomic_types.extend(other_type.types.clone());
549
550 combiner::combine(all_atomic_types, codebase, interner, overwrite_empty_array)
551 };
552
553 if !other_type.had_template {
554 base_type.had_template = false;
555 }
556
557 if !other_type.reference_free {
558 base_type.reference_free = false;
559 }
560
561 base_type.possibly_undefined |= other_type.possibly_undefined;
562 base_type.possibly_undefined_from_try |= other_type.possibly_undefined_from_try;
563 base_type.ignore_falsable_issues |= other_type.ignore_falsable_issues;
564 base_type.ignore_nullable_issues |= other_type.ignore_nullable_issues;
565
566 base_type
567}
568
569pub fn intersect_union_types(_type_1: &TUnion, _type_2: &TUnion, _codebase: &CodebaseMetadata) -> Option<TUnion> {
570 None
571}
572
573pub fn get_iterable_parameters(
574 atomic: &TAtomic,
575 codebase: &CodebaseMetadata,
576 interner: &ThreadedInterner,
577) -> Option<(TUnion, TUnion)> {
578 if let Some(generator_parameters) = atomic.get_generator_parameters(interner) {
579 return Some((generator_parameters.0, generator_parameters.1));
580 }
581
582 let parameters = 'parameters: {
583 match atomic {
584 TAtomic::Iterable(iterable) => Some((iterable.get_key_type().clone(), iterable.get_value_type().clone())),
585 TAtomic::Array(array_type) => Some(get_array_parameters(array_type, codebase, interner)),
586 TAtomic::Object(object) => {
587 let name = object.get_name()?;
588 let traversable = interner.intern("traversable");
589
590 let class_metadata = get_class_like(codebase, interner, name)?;
591 if !is_instance_of(codebase, interner, &class_metadata.name, &traversable) {
592 break 'parameters None;
593 }
594
595 let traversable_metadata = get_class_like(codebase, interner, &traversable)?;
596 let key_template = traversable_metadata.template_types.first().map(|(name, _)| name)?;
597 let value_template = traversable_metadata.template_types.get(1).map(|(name, _)| name)?;
598
599 let key_type = get_specialized_template_type(
600 codebase,
601 interner,
602 key_template,
603 &traversable,
604 class_metadata,
605 object.get_type_parameters(),
606 )
607 .unwrap_or_else(get_mixed);
608
609 let value_type = get_specialized_template_type(
610 codebase,
611 interner,
612 value_template,
613 &traversable,
614 class_metadata,
615 object.get_type_parameters(),
616 )
617 .unwrap_or_else(get_mixed);
618
619 Some((key_type, value_type))
620 }
621 _ => None,
622 }
623 };
624
625 if let Some((key_type, value_type)) = parameters {
626 return Some((key_type, value_type));
627 }
628
629 if let Some(intersection_types) = atomic.get_intersection_types() {
630 for intersection_type in intersection_types {
631 if let Some((key_type, value_type)) = get_iterable_parameters(intersection_type, codebase, interner) {
632 return Some((key_type, value_type));
633 }
634 }
635 }
636
637 None
638}
639
640pub fn get_array_parameters(
641 array_type: &TArray,
642 codebase: &CodebaseMetadata,
643 interner: &ThreadedInterner,
644) -> (TUnion, TUnion) {
645 match array_type {
646 TArray::Keyed(keyed_data) => {
647 let mut key_types = vec![];
648 let mut value_param;
649
650 if let Some((key_param, value_p)) = &keyed_data.parameters {
651 key_types.extend(key_param.types.clone());
652 value_param = (**value_p).clone();
653 } else {
654 key_types.push(TAtomic::Never);
655 value_param = get_never();
656 }
657
658 if let Some(known_items) = &keyed_data.known_items {
659 for (key, (_, item_type)) in known_items {
660 key_types.push(key.to_atomic());
661 value_param = add_union_type(value_param, item_type, codebase, interner, false);
662 }
663 }
664
665 let combined_key_types = combiner::combine(key_types, codebase, interner, false);
666 let key_param_union = TUnion::new(combined_key_types);
667
668 (key_param_union, value_param)
669 }
670 TArray::List(list_data) => {
671 let mut key_types = vec![];
672 let mut value_type = (*list_data.element_type).clone();
673
674 if let Some(known_elements) = &list_data.known_elements {
675 for (key_idx, (_, element_type)) in known_elements {
676 key_types.push(TAtomic::Scalar(TScalar::literal_int(*key_idx as i64)));
677
678 value_type = combine_union_types(element_type, &value_type, codebase, interner, false);
679 }
680 }
681
682 if key_types.is_empty() || !value_type.is_never() {
683 if value_type.is_never() {
684 key_types.push(TAtomic::Never);
685 } else {
686 key_types.push(TAtomic::Scalar(TScalar::Integer(TInteger::non_negative())));
687 }
688 }
689
690 let key_type = TUnion::new(combiner::combine(key_types, codebase, interner, false));
691
692 (key_type, value_type)
693 }
694 }
695}
696
697pub fn get_iterable_value_parameter(
698 atomic: &TAtomic,
699 codebase: &CodebaseMetadata,
700 interner: &ThreadedInterner,
701) -> Option<TUnion> {
702 if let Some(generator_parameters) = atomic.get_generator_parameters(interner) {
703 return Some(generator_parameters.1);
704 }
705
706 let parameter = match atomic {
707 TAtomic::Iterable(iterable) => Some(iterable.get_value_type().clone()),
708 TAtomic::Array(array_type) => Some(get_array_value_parameter(array_type, codebase, interner)),
709 TAtomic::Object(object) => {
710 let name = object.get_name()?;
711 let traversable = interner.intern("traversable");
712
713 let class_metadata = get_class_like(codebase, interner, name)?;
714 if !is_instance_of(codebase, interner, &class_metadata.name, &traversable) {
715 return None;
716 }
717
718 let traversable_metadata = get_class_like(codebase, interner, &traversable)?;
719 let value_template = traversable_metadata.template_types.get(1).map(|(name, _)| name)?;
720
721 get_specialized_template_type(
722 codebase,
723 interner,
724 value_template,
725 &traversable,
726 class_metadata,
727 object.get_type_parameters(),
728 )
729 }
730 _ => None,
731 };
732
733 if let Some(value_param) = parameter {
734 return Some(value_param);
735 }
736
737 if let Some(intersection_types) = atomic.get_intersection_types() {
738 for intersection_type in intersection_types {
739 if let Some(value_param) = get_iterable_value_parameter(intersection_type, codebase, interner) {
740 return Some(value_param);
741 }
742 }
743 }
744
745 None
746}
747
748pub fn get_array_value_parameter(
749 array_type: &TArray,
750 codebase: &CodebaseMetadata,
751 interner: &ThreadedInterner,
752) -> TUnion {
753 match array_type {
754 TArray::Keyed(keyed_data) => {
755 let mut value_param;
756
757 if let Some((_, value_p)) = &keyed_data.parameters {
758 value_param = (**value_p).clone();
759 } else {
760 value_param = get_never();
761 }
762
763 if let Some(known_items) = &keyed_data.known_items {
764 for (_, item_type) in known_items.values() {
765 value_param = combine_union_types(item_type, &value_param, codebase, interner, false);
766 }
767 }
768
769 value_param
770 }
771 TArray::List(list_data) => {
772 let mut value_param = (*list_data.element_type).clone();
773
774 if let Some(known_elements) = &list_data.known_elements {
775 for (_, element_type) in known_elements.values() {
776 value_param = combine_union_types(element_type, &value_param, codebase, interner, false);
777 }
778 }
779
780 value_param
781 }
782 }
783}
784
785pub fn get_specialized_template_type(
790 codebase: &CodebaseMetadata,
791 interner: &ThreadedInterner,
792 template_name: &StringIdentifier,
793 template_defining_class_id: &StringIdentifier,
794 instantiated_class_metadata: &ClassLikeMetadata,
795 instantiated_type_parameters: Option<&[TUnion]>,
796) -> Option<TUnion> {
797 let defining_class_metadata = get_class_like(codebase, interner, template_defining_class_id)?;
798
799 if defining_class_metadata.name == instantiated_class_metadata.name {
800 let index = instantiated_class_metadata.get_template_index_for_name(template_name)?;
801
802 let Some(instantiated_type_parameters) = instantiated_type_parameters else {
803 let type_map = instantiated_class_metadata.get_template_type(template_name)?;
804
805 return type_map.first().map(|(_, constraint)| constraint).cloned();
806 };
807
808 return instantiated_type_parameters.get(index).cloned();
809 }
810
811 let defining_template_type = defining_class_metadata.get_template_type(template_name)?;
812 let template_union = TUnion::new(
813 defining_template_type
814 .iter()
815 .map(|(defining_entity, constraint)| {
816 TAtomic::GenericParameter(TGenericParameter {
817 parameter_name: *template_name,
818 defining_entity: *defining_entity,
819 constraint: Box::new(constraint.clone()),
820 intersection_types: None,
821 })
822 })
823 .collect::<Vec<_>>(),
824 );
825
826 let mut template_result = TemplateResult::default();
827 for (defining_class, type_parameters_map) in &instantiated_class_metadata.template_extended_parameters {
828 for (parameter_name, parameter_type) in type_parameters_map {
829 template_result.add_lower_bound(
830 *parameter_name,
831 GenericParent::ClassLike(*defining_class),
832 parameter_type.clone(),
833 );
834 }
835 }
836
837 let mut template_type = inferred_type_replacer::replace(&template_union, &template_result, codebase, interner);
838 if let Some(type_parameters) = instantiated_type_parameters {
839 let mut template_result = TemplateResult::default();
840 for (i, parameter_type) in type_parameters.iter().enumerate() {
841 if let Some(parameter_name) = instantiated_class_metadata.get_template_name_for_index(i) {
842 template_result.add_lower_bound(
843 parameter_name,
844 GenericParent::ClassLike(instantiated_class_metadata.name),
845 parameter_type.clone(),
846 );
847 }
848 }
849
850 if !template_result.lower_bounds.is_empty() {
851 template_type = inferred_type_replacer::replace(&template_type, &template_result, codebase, interner);
852 }
853 }
854
855 Some(template_type)
856}