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::new()))
267}
268
269pub fn get_mixed_maybe_from_loop(from_loop_isset: bool) -> TUnion {
270 wrap_atomic(TAtomic::Mixed(TMixed::maybe_isset_from_loop(from_loop_isset)))
271}
272
273#[inline]
274pub fn get_never() -> TUnion {
275 wrap_atomic(TAtomic::Never)
276}
277
278#[inline]
279pub fn get_resource() -> TUnion {
280 wrap_atomic(TAtomic::Resource(TResource::new(None)))
281}
282
283#[inline]
284pub fn get_closed_resource() -> TUnion {
285 wrap_atomic(TAtomic::Resource(TResource::new(Some(true))))
286}
287
288#[inline]
289pub fn get_open_resource() -> TUnion {
290 wrap_atomic(TAtomic::Resource(TResource::new(Some(false))))
291}
292
293#[inline]
294pub fn get_placeholder() -> TUnion {
295 wrap_atomic(TAtomic::Placeholder)
296}
297
298#[inline]
299pub fn get_void() -> TUnion {
300 wrap_atomic(TAtomic::Void)
301}
302
303#[inline]
304pub fn get_null() -> TUnion {
305 wrap_atomic(TAtomic::Null)
306}
307
308#[inline]
309pub fn get_arraykey() -> TUnion {
310 wrap_atomic(TAtomic::Scalar(TScalar::ArrayKey))
311}
312
313#[inline]
314pub fn get_bool() -> TUnion {
315 wrap_atomic(TAtomic::Scalar(TScalar::bool()))
316}
317
318#[inline]
319pub fn get_false() -> TUnion {
320 wrap_atomic(TAtomic::Scalar(TScalar::r#false()))
321}
322
323#[inline]
324pub fn get_true() -> TUnion {
325 wrap_atomic(TAtomic::Scalar(TScalar::r#true()))
326}
327
328#[inline]
329pub fn get_object() -> TUnion {
330 wrap_atomic(TAtomic::Object(TObject::Any))
331}
332
333#[inline]
334pub fn get_numeric() -> TUnion {
335 TUnion::new(vec![TAtomic::Scalar(TScalar::Numeric)])
336}
337
338#[inline]
339pub fn get_numeric_string() -> TUnion {
340 wrap_atomic(TAtomic::Scalar(TScalar::String(TString::general_with_props(true, false, false, true))))
341}
342
343#[inline]
344pub fn get_lowercase_string() -> TUnion {
345 wrap_atomic(TAtomic::Scalar(TScalar::String(TString::general_with_props(false, false, false, true))))
346}
347
348#[inline]
349pub fn get_non_empty_lowercase_string() -> TUnion {
350 wrap_atomic(TAtomic::Scalar(TScalar::String(TString::general_with_props(false, false, true, true))))
351}
352
353#[inline]
354pub fn get_non_empty_string() -> TUnion {
355 wrap_atomic(TAtomic::Scalar(TScalar::String(TString::general_with_props(false, false, true, false))))
356}
357
358#[inline]
359pub fn get_truthy_string() -> TUnion {
360 wrap_atomic(TAtomic::Scalar(TScalar::String(TString::general_with_props(false, true, false, false))))
361}
362
363#[inline]
364pub fn get_unspecified_literal_string() -> TUnion {
365 wrap_atomic(TAtomic::Scalar(TScalar::String(TString::unspecified_literal())))
366}
367
368#[inline]
369pub fn get_non_empty_unspecified_literal_string() -> TUnion {
370 wrap_atomic(TAtomic::Scalar(TScalar::String(TString::unspecified_literal_with_props(false, false, true, false))))
371}
372
373#[inline]
374pub fn get_named_object(
375 interner: &ThreadedInterner,
376 name: StringIdentifier,
377 type_resolution_context: Option<&TypeResolutionContext>,
378) -> TUnion {
379 if let Some(type_resolution_context) = type_resolution_context {
380 let name_str = interner.lookup(&name);
381 if let Some(defining_entities) = type_resolution_context.get_template_definition(name_str) {
382 return wrap_atomic(TAtomic::Scalar(TScalar::ClassLikeString(TClassLikeString::Generic {
383 kind: TClassLikeStringKind::Class,
384 parameter_name: name,
385 defining_entity: defining_entities[0].0,
386 constraint: Box::new((*(defining_entities[0].1.get_single())).clone()),
387 })));
388 }
389 }
390
391 wrap_atomic(TAtomic::Object(TObject::Named(TNamedObject::new(name))))
392}
393
394#[inline]
395pub fn get_scalar() -> TUnion {
396 wrap_atomic(TAtomic::Scalar(TScalar::Generic))
397}
398
399#[inline]
400pub fn get_mixed_iterable() -> TUnion {
401 wrap_atomic(TAtomic::Iterable(TIterable::mixed()))
402}
403
404#[inline]
405pub fn get_iterable(key_parameter: TUnion, value_parameter: TUnion) -> TUnion {
406 wrap_atomic(TAtomic::Iterable(TIterable::new(Box::new(key_parameter), Box::new(value_parameter))))
407}
408
409#[inline]
410pub fn get_list(element_type: TUnion) -> TUnion {
411 wrap_atomic(TAtomic::Array(TArray::List(TList::new(Box::new(element_type)))))
412}
413
414#[inline]
415pub fn get_empty_keyed_array() -> TUnion {
416 wrap_atomic(TAtomic::Array(TArray::Keyed(TKeyedArray::new())))
417}
418
419#[inline]
420pub fn get_keyed_array(key_parameter: TUnion, value_parameter: TUnion) -> TUnion {
421 wrap_atomic(TAtomic::Array(TArray::Keyed(TKeyedArray::new_with_parameters(
422 Box::new(key_parameter),
423 Box::new(value_parameter),
424 ))))
425}
426
427#[inline]
428pub fn get_mixed_list() -> TUnion {
429 get_list(get_mixed())
430}
431
432#[inline]
433pub fn get_mixed_keyed_array() -> TUnion {
434 get_keyed_array(get_arraykey(), get_mixed())
435}
436
437#[inline]
438pub fn get_mixed_callable() -> TUnion {
439 wrap_atomic(TAtomic::Callable(TCallable::Signature(TCallableSignature::mixed(false))))
440}
441
442#[inline]
443pub fn get_mixed_closure() -> TUnion {
444 wrap_atomic(TAtomic::Callable(TCallable::Signature(TCallableSignature::mixed(true))))
445}
446
447#[inline]
448pub fn add_optional_union_type(
449 base_type: TUnion,
450 maybe_type: Option<&TUnion>,
451 codebase: &CodebaseMetadata,
452 interner: &ThreadedInterner,
453) -> TUnion {
454 if let Some(type_2) = maybe_type { add_union_type(base_type, type_2, codebase, interner, false) } else { base_type }
455}
456
457#[inline]
458pub fn combine_optional_union_types(
459 type_1: Option<&TUnion>,
460 type_2: Option<&TUnion>,
461 codebase: &CodebaseMetadata,
462 interner: &ThreadedInterner,
463) -> TUnion {
464 match (type_1, type_2) {
465 (Some(type_1), Some(type_2)) => combine_union_types(type_1, type_2, codebase, interner, false),
466 (Some(type_1), None) => type_1.clone(),
467 (None, Some(type_2)) => type_2.clone(),
468 (None, None) => get_mixed(),
469 }
470}
471
472#[inline]
473pub fn combine_union_types(
474 type_1: &TUnion,
475 type_2: &TUnion,
476 codebase: &CodebaseMetadata,
477 interner: &ThreadedInterner,
478 overwrite_empty_array: bool,
479) -> TUnion {
480 if type_1 == type_2 {
481 return type_1.clone();
482 }
483
484 let mut combined_type = if type_1.is_never() || type_1.is_never_template() {
485 type_2.clone()
486 } else if type_2.is_never() || type_2.is_never_template() {
487 type_1.clone()
488 } else if type_1.is_vanilla_mixed() && type_2.is_vanilla_mixed() {
489 get_mixed()
490 } else {
491 let mut all_atomic_types = type_1.types.clone();
492 all_atomic_types.extend(type_2.types.clone());
493
494 let mut result = TUnion::new(combiner::combine(all_atomic_types, codebase, interner, overwrite_empty_array));
495
496 if type_1.had_template && type_2.had_template {
497 result.had_template = true;
498 }
499
500 if type_1.reference_free && type_2.reference_free {
501 result.reference_free = true;
502 }
503
504 result
505 };
506
507 if type_1.possibly_undefined || type_2.possibly_undefined {
508 combined_type.possibly_undefined = true;
509 }
510
511 if type_1.possibly_undefined_from_try || type_2.possibly_undefined_from_try {
512 combined_type.possibly_undefined_from_try = true;
513 }
514
515 if type_1.ignore_falsable_issues || type_2.ignore_falsable_issues {
516 combined_type.ignore_falsable_issues = true;
517 }
518
519 combined_type
520}
521
522#[inline]
523pub fn add_union_type(
524 mut base_type: TUnion,
525 other_type: &TUnion,
526 codebase: &CodebaseMetadata,
527 interner: &ThreadedInterner,
528 overwrite_empty_array: bool,
529) -> TUnion {
530 if &base_type == other_type {
531 base_type.possibly_undefined |= other_type.possibly_undefined;
532 base_type.possibly_undefined_from_try |= other_type.possibly_undefined_from_try;
533 base_type.ignore_falsable_issues |= other_type.ignore_falsable_issues;
534 base_type.ignore_nullable_issues |= other_type.ignore_nullable_issues;
535
536 return base_type;
537 }
538
539 base_type.types = if base_type.is_vanilla_mixed() && other_type.is_vanilla_mixed() {
540 base_type.types
541 } else {
542 let mut all_atomic_types = base_type.types.clone();
543 all_atomic_types.extend(other_type.types.clone());
544
545 combiner::combine(all_atomic_types, codebase, interner, overwrite_empty_array)
546 };
547
548 if !other_type.had_template {
549 base_type.had_template = false;
550 }
551
552 if !other_type.reference_free {
553 base_type.reference_free = false;
554 }
555
556 base_type.possibly_undefined |= other_type.possibly_undefined;
557 base_type.possibly_undefined_from_try |= other_type.possibly_undefined_from_try;
558 base_type.ignore_falsable_issues |= other_type.ignore_falsable_issues;
559 base_type.ignore_nullable_issues |= other_type.ignore_nullable_issues;
560
561 base_type
562}
563
564pub fn intersect_union_types(_type_1: &TUnion, _type_2: &TUnion, _codebase: &CodebaseMetadata) -> Option<TUnion> {
565 None
566}
567
568pub fn get_iterable_parameters(
569 atomic: &TAtomic,
570 codebase: &CodebaseMetadata,
571 interner: &ThreadedInterner,
572) -> Option<(TUnion, TUnion)> {
573 if let Some(generator_parameters) = atomic.get_generator_parameters(interner) {
574 return Some((generator_parameters.0, generator_parameters.1));
575 }
576
577 let parameters = 'parameters: {
578 match atomic {
579 TAtomic::Iterable(iterable) => Some((iterable.get_key_type().clone(), iterable.get_value_type().clone())),
580 TAtomic::Array(array_type) => Some(get_array_parameters(array_type, codebase, interner)),
581 TAtomic::Object(object) => {
582 let name = object.get_name()?;
583 let traversable = interner.intern("traversable");
584
585 let class_metadata = get_class_like(codebase, interner, name)?;
586 if !is_instance_of(codebase, interner, &class_metadata.name, &traversable) {
587 break 'parameters None;
588 }
589
590 let traversable_metadata = get_class_like(codebase, interner, &traversable)?;
591 let key_template = traversable_metadata.template_types.first().map(|(name, _)| name)?;
592 let value_template = traversable_metadata.template_types.get(1).map(|(name, _)| name)?;
593
594 let key_type = get_specialized_template_type(
595 codebase,
596 interner,
597 key_template,
598 &traversable,
599 class_metadata,
600 object.get_type_parameters(),
601 )
602 .unwrap_or_else(get_mixed);
603
604 let value_type = get_specialized_template_type(
605 codebase,
606 interner,
607 value_template,
608 &traversable,
609 class_metadata,
610 object.get_type_parameters(),
611 )
612 .unwrap_or_else(get_mixed);
613
614 Some((key_type, value_type))
615 }
616 _ => None,
617 }
618 };
619
620 if let Some((key_type, value_type)) = parameters {
621 return Some((key_type, value_type));
622 }
623
624 if let Some(intersection_types) = atomic.get_intersection_types() {
625 for intersection_type in intersection_types {
626 if let Some((key_type, value_type)) = get_iterable_parameters(intersection_type, codebase, interner) {
627 return Some((key_type, value_type));
628 }
629 }
630 }
631
632 None
633}
634
635pub fn get_array_parameters(
636 array_type: &TArray,
637 codebase: &CodebaseMetadata,
638 interner: &ThreadedInterner,
639) -> (TUnion, TUnion) {
640 match array_type {
641 TArray::Keyed(keyed_data) => {
642 let mut key_types = vec![];
643 let mut value_param;
644
645 if let Some((key_param, value_p)) = &keyed_data.parameters {
646 key_types.extend(key_param.types.clone());
647 value_param = (**value_p).clone();
648 } else {
649 key_types.push(TAtomic::Never);
650 value_param = get_never();
651 }
652
653 if let Some(known_items) = &keyed_data.known_items {
654 for (key, (_, item_type)) in known_items {
655 key_types.push(key.to_atomic());
656 value_param = add_union_type(value_param, item_type, codebase, interner, false);
657 }
658 }
659
660 let combined_key_types = combiner::combine(key_types, codebase, interner, false);
661 let key_param_union = TUnion::new(combined_key_types);
662
663 (key_param_union, value_param)
664 }
665 TArray::List(list_data) => {
666 let mut key_types = vec![];
667 let mut value_type = (*list_data.element_type).clone();
668
669 if let Some(known_elements) = &list_data.known_elements {
670 for (key_idx, (_, element_type)) in known_elements {
671 key_types.push(TAtomic::Scalar(TScalar::literal_int(*key_idx as i64)));
672
673 value_type = combine_union_types(element_type, &value_type, codebase, interner, false);
674 }
675 }
676
677 if key_types.is_empty() || !value_type.is_never() {
678 if value_type.is_never() {
679 key_types.push(TAtomic::Never);
680 } else {
681 key_types.push(TAtomic::Scalar(TScalar::Integer(TInteger::non_negative())));
682 }
683 }
684
685 let key_type = TUnion::new(combiner::combine(key_types, codebase, interner, false));
686
687 (key_type, value_type)
688 }
689 }
690}
691
692pub fn get_iterable_value_parameter(
693 atomic: &TAtomic,
694 codebase: &CodebaseMetadata,
695 interner: &ThreadedInterner,
696) -> Option<TUnion> {
697 if let Some(generator_parameters) = atomic.get_generator_parameters(interner) {
698 return Some(generator_parameters.1);
699 }
700
701 let parameter = match atomic {
702 TAtomic::Iterable(iterable) => Some(iterable.get_value_type().clone()),
703 TAtomic::Array(array_type) => Some(get_array_value_parameter(array_type, codebase, interner)),
704 TAtomic::Object(object) => {
705 let name = object.get_name()?;
706 let traversable = interner.intern("traversable");
707
708 let class_metadata = get_class_like(codebase, interner, name)?;
709 if !is_instance_of(codebase, interner, &class_metadata.name, &traversable) {
710 return None;
711 }
712
713 let traversable_metadata = get_class_like(codebase, interner, &traversable)?;
714 let value_template = traversable_metadata.template_types.get(1).map(|(name, _)| name)?;
715
716 get_specialized_template_type(
717 codebase,
718 interner,
719 value_template,
720 &traversable,
721 class_metadata,
722 object.get_type_parameters(),
723 )
724 }
725 _ => None,
726 };
727
728 if let Some(value_param) = parameter {
729 return Some(value_param);
730 }
731
732 if let Some(intersection_types) = atomic.get_intersection_types() {
733 for intersection_type in intersection_types {
734 if let Some(value_param) = get_iterable_value_parameter(intersection_type, codebase, interner) {
735 return Some(value_param);
736 }
737 }
738 }
739
740 None
741}
742
743pub fn get_array_value_parameter(
744 array_type: &TArray,
745 codebase: &CodebaseMetadata,
746 interner: &ThreadedInterner,
747) -> TUnion {
748 match array_type {
749 TArray::Keyed(keyed_data) => {
750 let mut value_param;
751
752 if let Some((_, value_p)) = &keyed_data.parameters {
753 value_param = (**value_p).clone();
754 } else {
755 value_param = get_never();
756 }
757
758 if let Some(known_items) = &keyed_data.known_items {
759 for (_, item_type) in known_items.values() {
760 value_param = combine_union_types(item_type, &value_param, codebase, interner, false);
761 }
762 }
763
764 value_param
765 }
766 TArray::List(list_data) => {
767 let mut value_param = (*list_data.element_type).clone();
768
769 if let Some(known_elements) = &list_data.known_elements {
770 for (_, element_type) in known_elements.values() {
771 value_param = combine_union_types(element_type, &value_param, codebase, interner, false);
772 }
773 }
774
775 value_param
776 }
777 }
778}
779
780pub fn get_specialized_template_type(
785 codebase: &CodebaseMetadata,
786 interner: &ThreadedInterner,
787 template_name: &StringIdentifier,
788 template_defining_class_id: &StringIdentifier,
789 instantiated_class_metadata: &ClassLikeMetadata,
790 instantiated_type_parameters: Option<&[TUnion]>,
791) -> Option<TUnion> {
792 let defining_class_metadata = get_class_like(codebase, interner, template_defining_class_id)?;
793
794 if defining_class_metadata.name == instantiated_class_metadata.name {
795 let index = instantiated_class_metadata.get_template_index_for_name(template_name)?;
796
797 let Some(instantiated_type_parameters) = instantiated_type_parameters else {
798 let type_map = instantiated_class_metadata.get_template_type(template_name)?;
799
800 return type_map.first().map(|(_, constraint)| constraint).cloned();
801 };
802
803 return instantiated_type_parameters.get(index).cloned();
804 }
805
806 let defining_template_type = defining_class_metadata.get_template_type(template_name)?;
807 let template_union = TUnion::new(
808 defining_template_type
809 .iter()
810 .map(|(defining_entity, constraint)| {
811 TAtomic::GenericParameter(TGenericParameter {
812 parameter_name: *template_name,
813 defining_entity: *defining_entity,
814 constraint: Box::new(constraint.clone()),
815 intersection_types: None,
816 })
817 })
818 .collect::<Vec<_>>(),
819 );
820
821 let mut template_result = TemplateResult::default();
822 for (defining_class, type_parameters_map) in &instantiated_class_metadata.template_extended_parameters {
823 for (parameter_name, parameter_type) in type_parameters_map {
824 template_result.add_lower_bound(
825 *parameter_name,
826 GenericParent::ClassLike(*defining_class),
827 parameter_type.clone(),
828 );
829 }
830 }
831
832 let mut template_type = inferred_type_replacer::replace(&template_union, &template_result, codebase, interner);
833 if let Some(type_parameters) = instantiated_type_parameters {
834 let mut template_result = TemplateResult::default();
835 for (i, parameter_type) in type_parameters.iter().enumerate() {
836 if let Some(parameter_name) = instantiated_class_metadata.get_template_name_for_index(i) {
837 template_result.add_lower_bound(
838 parameter_name,
839 GenericParent::ClassLike(instantiated_class_metadata.name),
840 parameter_type.clone(),
841 );
842 }
843 }
844
845 if !template_result.lower_bounds.is_empty() {
846 template_type = inferred_type_replacer::replace(&template_type, &template_result, codebase, interner);
847 }
848 }
849
850 Some(template_type)
851}