1use std::borrow::Cow;
2use std::collections::BTreeMap;
3use std::sync::Arc;
4
5use mago_atom::Atom;
6use mago_atom::ascii_lowercase_atom;
7use mago_atom::atom;
8use mago_atom::concat_atom;
9use mago_atom::i64_atom;
10use mago_names::kind::NameKind;
11use mago_names::scope::NamespaceScope;
12use mago_span::HasSpan;
13use mago_span::Span;
14use mago_type_syntax;
15use mago_type_syntax::ast::AliasName;
16use mago_type_syntax::ast::ArrayType;
17use mago_type_syntax::ast::AssociativeArrayType;
18use mago_type_syntax::ast::CallableType;
19use mago_type_syntax::ast::GenericParameters;
20use mago_type_syntax::ast::GlobalWildcardSelector;
21use mago_type_syntax::ast::Identifier;
22use mago_type_syntax::ast::IntOrKeyword;
23use mago_type_syntax::ast::LiteralIntOrFloatType;
24use mago_type_syntax::ast::MemberReferenceSelector;
25use mago_type_syntax::ast::PropertiesOfFilter;
26use mago_type_syntax::ast::ShapeKey;
27use mago_type_syntax::ast::ShapeType;
28use mago_type_syntax::ast::SingleGenericParameter;
29use mago_type_syntax::ast::Type;
30use mago_type_syntax::ast::UnionType;
31use mago_type_syntax::ast::object::ObjectType;
32
33use crate::ttype::TType;
34use crate::ttype::atomic::TAtomic;
35use crate::ttype::atomic::alias::TAlias;
36use crate::ttype::atomic::array::TArray;
37use crate::ttype::atomic::array::key::ArrayKey;
38use crate::ttype::atomic::array::keyed::TKeyedArray;
39use crate::ttype::atomic::array::list::TList;
40use crate::ttype::atomic::callable::TCallable;
41use crate::ttype::atomic::callable::TCallableSignature;
42use crate::ttype::atomic::callable::parameter::TCallableParameter;
43use crate::ttype::atomic::conditional::TConditional;
44use crate::ttype::atomic::derived::TDerived;
45use crate::ttype::atomic::derived::index_access::TIndexAccess;
46use crate::ttype::atomic::derived::int_mask::TIntMask;
47use crate::ttype::atomic::derived::int_mask_of::TIntMaskOf;
48use crate::ttype::atomic::derived::key_of::TKeyOf;
49use crate::ttype::atomic::derived::new::TNew;
50use crate::ttype::atomic::derived::properties_of::TPropertiesOf;
51use crate::ttype::atomic::derived::template_type::TTemplateType;
52use crate::ttype::atomic::derived::value_of::TValueOf;
53use crate::ttype::atomic::generic::TGenericParameter;
54use crate::ttype::atomic::iterable::TIterable;
55use crate::ttype::atomic::object::TObject;
56use crate::ttype::atomic::object::named::TNamedObject;
57use crate::ttype::atomic::reference::TGlobalReferenceSelector;
58use crate::ttype::atomic::reference::TReference;
59use crate::ttype::atomic::reference::TReferenceMemberSelector;
60use crate::ttype::atomic::scalar::TScalar;
61use crate::ttype::atomic::scalar::class_like_string::TClassLikeString;
62use crate::ttype::atomic::scalar::class_like_string::TClassLikeStringKind;
63use crate::ttype::atomic::scalar::int::TInteger;
64use crate::ttype::atomic::scalar::string::TStringCasing;
65use crate::ttype::error::TypeError;
66use crate::ttype::get_arraykey;
67use crate::ttype::get_bool;
68use crate::ttype::get_callable_string;
69use crate::ttype::get_closed_resource;
70use crate::ttype::get_false;
71use crate::ttype::get_float;
72use crate::ttype::get_int;
73use crate::ttype::get_literal_float;
74use crate::ttype::get_literal_int;
75use crate::ttype::get_literal_string;
76use crate::ttype::get_lowercase_string;
77use crate::ttype::get_mixed;
78use crate::ttype::get_negative_int;
79use crate::ttype::get_never;
80use crate::ttype::get_non_empty_lowercase_string;
81use crate::ttype::get_non_empty_string;
82use crate::ttype::get_non_empty_unspecified_literal_string;
83use crate::ttype::get_non_empty_uppercase_string;
84use crate::ttype::get_non_negative_int;
85use crate::ttype::get_non_positive_int;
86use crate::ttype::get_non_zero_int;
87use crate::ttype::get_null;
88use crate::ttype::get_nullable_float;
89use crate::ttype::get_nullable_int;
90use crate::ttype::get_nullable_object;
91use crate::ttype::get_nullable_scalar;
92use crate::ttype::get_nullable_string;
93use crate::ttype::get_numeric;
94use crate::ttype::get_numeric_string;
95use crate::ttype::get_open_resource;
96use crate::ttype::get_positive_int;
97use crate::ttype::get_resource;
98use crate::ttype::get_scalar;
99use crate::ttype::get_string;
100use crate::ttype::get_string_with_props;
101use crate::ttype::get_true;
102use crate::ttype::get_truthy_mixed;
103use crate::ttype::get_truthy_string;
104use crate::ttype::get_unspecified_literal_float;
105use crate::ttype::get_unspecified_literal_int;
106use crate::ttype::get_unspecified_literal_string;
107use crate::ttype::get_uppercase_string;
108use crate::ttype::get_void;
109use crate::ttype::resolution::TypeResolutionContext;
110use crate::ttype::template::GenericTemplate;
111use crate::ttype::union::TUnion;
112use crate::ttype::wrap_atomic;
113
114pub fn get_type_from_string(
151 type_string: &str,
152 span: Span,
153 scope: &NamespaceScope,
154 type_context: &TypeResolutionContext,
155 classname: Option<Atom>,
156) -> Result<TUnion, TypeError> {
157 let ast = mago_type_syntax::parse_str(span, type_string)?;
158
159 get_union_from_type_ast(&ast, scope, type_context, classname)
160}
161
162#[inline]
172pub fn get_union_from_type_ast(
173 ttype: &Type<'_>,
174 scope: &NamespaceScope,
175 type_context: &TypeResolutionContext,
176 classname: Option<Atom>,
177) -> Result<TUnion, TypeError> {
178 Ok(match ttype {
179 Type::Parenthesized(parenthesized_type) => {
180 get_union_from_type_ast(&parenthesized_type.inner, scope, type_context, classname)?
181 }
182 Type::Nullable(nullable_type) => match nullable_type.inner.as_ref() {
183 Type::Null(_) => get_null(),
184 Type::String(_) => get_nullable_string(),
185 Type::Int(_) => get_nullable_int(),
186 Type::Float(_) => get_nullable_float(),
187 Type::Object(_) => get_nullable_object(),
188 Type::Scalar(_) => get_nullable_scalar(),
189 _ => get_union_from_type_ast(&nullable_type.inner, scope, type_context, classname)?.as_nullable(),
190 },
191 Type::Union(UnionType { left, right, .. }) if matches!(left.as_ref(), Type::Null(_)) => match right.as_ref() {
192 Type::Null(_) => get_null(),
193 Type::String(_) => get_nullable_string(),
194 Type::Int(_) => get_nullable_int(),
195 Type::Float(_) => get_nullable_float(),
196 Type::Object(_) => get_nullable_object(),
197 Type::Scalar(_) => get_nullable_scalar(),
198 _ => get_union_from_type_ast(right, scope, type_context, classname)?.as_nullable(),
199 },
200 Type::Union(UnionType { left, right, .. }) if matches!(right.as_ref(), Type::Null(_)) => match left.as_ref() {
201 Type::Null(_) => get_null(),
202 Type::String(_) => get_nullable_string(),
203 Type::Int(_) => get_nullable_int(),
204 Type::Float(_) => get_nullable_float(),
205 Type::Object(_) => get_nullable_object(),
206 Type::Scalar(_) => get_nullable_scalar(),
207 _ => get_union_from_type_ast(left, scope, type_context, classname)?.as_nullable(),
208 },
209 Type::Union(union_type) => {
210 let left = get_union_from_type_ast(&union_type.left, scope, type_context, classname)?;
211 let right = get_union_from_type_ast(&union_type.right, scope, type_context, classname)?;
212
213 let combined_types: Vec<TAtomic> = left.types.iter().chain(right.types.iter()).cloned().collect();
214
215 TUnion::from_vec(combined_types)
216 }
217 Type::Intersection(intersection) => {
218 if matches!(intersection.left.as_ref(), Type::NonEmptyString(_)) {
219 match intersection.right.as_ref() {
220 Type::String(_) => return Ok(get_non_empty_string()),
221 Type::NonEmptyString(_) => return Ok(get_non_empty_string()),
222 Type::LowercaseString(_) => return Ok(get_non_empty_lowercase_string()),
223 Type::NonEmptyLowercaseString(_) => return Ok(get_non_empty_lowercase_string()),
224 Type::UppercaseString(_) => return Ok(get_non_empty_uppercase_string()),
225 Type::NonEmptyUppercaseString(_) => return Ok(get_non_empty_uppercase_string()),
226 _ => {}
227 }
228 }
229
230 if matches!(intersection.right.as_ref(), Type::NonEmptyString(_)) {
231 match intersection.left.as_ref() {
232 Type::String(_) => return Ok(get_non_empty_string()),
233 Type::NonEmptyString(_) => return Ok(get_non_empty_string()),
234 Type::LowercaseString(_) => return Ok(get_non_empty_lowercase_string()),
235 Type::NonEmptyLowercaseString(_) => return Ok(get_non_empty_lowercase_string()),
236 Type::UppercaseString(_) => return Ok(get_non_empty_uppercase_string()),
237 Type::NonEmptyUppercaseString(_) => return Ok(get_non_empty_uppercase_string()),
238 _ => {}
239 }
240 }
241
242 let left = get_union_from_type_ast(&intersection.left, scope, type_context, classname)?;
243 let right = get_union_from_type_ast(&intersection.right, scope, type_context, classname)?;
244
245 let left_str = left.get_id();
246 let right_str = right.get_id();
247
248 let left_types = left.types.into_owned();
249 let right_types = right.types.into_owned();
250 let mut intersection_types = vec![];
251 for left_type in left_types {
252 if !left_type.can_be_intersected() {
253 return Err(TypeError::InvalidType(
254 ttype.to_string(),
255 format!(
256 "Type `{}` used in intersection cannot be intersected with another type ( `{}` )",
257 left_type.get_id(),
258 right_str,
259 ),
260 ttype.span(),
261 ));
262 }
263
264 for right_type in &right_types {
265 let mut intersection = left_type.clone();
266
267 if !intersection.add_intersection_type(right_type.clone()) {
268 return Err(TypeError::InvalidType(
269 ttype.to_string(),
270 format!(
271 "Type `{}` used in intersection cannot be intersected with another type ( `{}` )",
272 right_type.get_id(),
273 left_str,
274 ),
275 ttype.span(),
276 ));
277 }
278
279 intersection_types.push(intersection);
280 }
281 }
282
283 TUnion::from_vec(intersection_types)
284 }
285 Type::Slice(slice) => wrap_atomic(get_array_type_from_ast(
286 None,
287 Some(slice.inner.as_ref()),
288 false,
289 scope,
290 type_context,
291 classname,
292 )?),
293 Type::Array(ArrayType { parameters, .. }) | Type::AssociativeArray(AssociativeArrayType { parameters, .. }) => {
294 let (key, value) = match parameters {
295 Some(parameters) => {
296 let key = parameters.entries.first().map(|g| &g.inner);
297 let value = parameters.entries.get(1).map(|g| &g.inner);
298
299 (key, value)
300 }
301 None => (None, None),
302 };
303
304 wrap_atomic(get_array_type_from_ast(key, value, false, scope, type_context, classname)?)
305 }
306 Type::NonEmptyArray(non_empty_array) => {
307 let (key, value) = match &non_empty_array.parameters {
308 Some(parameters) => {
309 let key = parameters.entries.first().map(|g| &g.inner);
310 let value = parameters.entries.get(1).map(|g| &g.inner);
311
312 (key, value)
313 }
314 None => (None, None),
315 };
316
317 wrap_atomic(get_array_type_from_ast(key, value, true, scope, type_context, classname)?)
318 }
319 Type::List(list_type) => {
320 let value = list_type.parameters.as_ref().and_then(|p| p.entries.first().map(|g| &g.inner));
321
322 wrap_atomic(get_list_type_from_ast(value, false, scope, type_context, classname)?)
323 }
324 Type::NonEmptyList(non_empty_list_type) => {
325 let value = non_empty_list_type.parameters.as_ref().and_then(|p| p.entries.first().map(|g| &g.inner));
326
327 wrap_atomic(get_list_type_from_ast(value, true, scope, type_context, classname)?)
328 }
329 Type::ClassString(class_string_type) => get_class_string_type_from_ast(
330 class_string_type.span(),
331 TClassLikeStringKind::Class,
332 &class_string_type.parameter,
333 scope,
334 type_context,
335 classname,
336 )?,
337 Type::InterfaceString(interface_string_type) => get_class_string_type_from_ast(
338 interface_string_type.span(),
339 TClassLikeStringKind::Interface,
340 &interface_string_type.parameter,
341 scope,
342 type_context,
343 classname,
344 )?,
345 Type::EnumString(enum_string_type) => get_class_string_type_from_ast(
346 enum_string_type.span(),
347 TClassLikeStringKind::Enum,
348 &enum_string_type.parameter,
349 scope,
350 type_context,
351 classname,
352 )?,
353 Type::TraitString(trait_string_type) => get_class_string_type_from_ast(
354 trait_string_type.span(),
355 TClassLikeStringKind::Trait,
356 &trait_string_type.parameter,
357 scope,
358 type_context,
359 classname,
360 )?,
361 Type::MemberReference(member_reference) => {
362 let class_like_name = if member_reference.class.value.eq_ignore_ascii_case("self")
363 || member_reference.class.value.eq_ignore_ascii_case("static")
364 || member_reference.class.value.eq("this")
365 || member_reference.class.value.eq("$this")
366 {
367 let Some(classname) = classname else {
368 return Err(TypeError::InvalidType(
369 ttype.to_string(),
370 "Cannot resolve `self` type reference outside of a class context".to_string(),
371 member_reference.span(),
372 ));
373 };
374
375 classname
376 } else if member_reference.class.value.eq_ignore_ascii_case("parent") {
377 atom("parent")
378 } else {
379 let (class_like_name, _) = scope.resolve(NameKind::Default, member_reference.class.value);
380
381 atom(&class_like_name)
382 };
383
384 let member_selector = match member_reference.member {
385 MemberReferenceSelector::Wildcard(_) => TReferenceMemberSelector::Wildcard,
386 MemberReferenceSelector::Identifier(identifier) => {
387 TReferenceMemberSelector::Identifier(atom(identifier.value))
388 }
389 MemberReferenceSelector::StartsWith(identifier, _) => {
390 TReferenceMemberSelector::StartsWith(atom(identifier.value))
391 }
392 MemberReferenceSelector::EndsWith(_, identifier) => {
393 TReferenceMemberSelector::EndsWith(atom(identifier.value))
394 }
395 };
396
397 wrap_atomic(TAtomic::Reference(TReference::Member { class_like_name, member_selector }))
398 }
399 Type::GlobalWildcardReference(global_wildcard) => {
400 let selector = match global_wildcard.selector {
401 GlobalWildcardSelector::StartsWith(identifier, _) => {
402 TGlobalReferenceSelector::StartsWith(atom(identifier.value))
403 }
404 GlobalWildcardSelector::EndsWith(_, identifier) => {
405 TGlobalReferenceSelector::EndsWith(atom(identifier.value))
406 }
407 };
408
409 wrap_atomic(TAtomic::Reference(TReference::Global { selector }))
410 }
411 Type::AliasReference(alias_reference) => {
412 let class_like_name = if alias_reference.class.value.eq_ignore_ascii_case("self")
413 || alias_reference.class.value.eq_ignore_ascii_case("static")
414 || alias_reference.class.value.eq("this")
415 || alias_reference.class.value.eq("$this")
416 {
417 let Some(classname) = classname else {
418 return Err(TypeError::InvalidType(
419 ttype.to_string(),
420 "Cannot resolve `self` type reference outside of a class context".to_string(),
421 alias_reference.span(),
422 ));
423 };
424
425 classname
426 } else if alias_reference.class.value.eq_ignore_ascii_case("parent") {
427 atom("parent")
428 } else {
429 let (class_like_name, _) = scope.resolve(NameKind::Default, alias_reference.class.value);
430
431 ascii_lowercase_atom(&class_like_name)
432 };
433
434 let alias_name = match alias_reference.alias {
435 AliasName::Identifier(identifier) => atom(identifier.value),
436 AliasName::Keyword(keyword) => atom(keyword.value),
437 };
438
439 wrap_atomic(TAtomic::Alias(TAlias::new(class_like_name, alias_name)))
440 }
441 Type::Object(object_type) => wrap_atomic(get_object_from_ast(object_type, scope, type_context, classname)?),
442 Type::Shape(shape_type) => wrap_atomic(get_shape_from_ast(shape_type, scope, type_context, classname)?),
443 Type::Callable(callable_type) => {
444 wrap_atomic(get_callable_from_ast(callable_type, scope, type_context, classname)?)
445 }
446 Type::Reference(reference_type) => {
447 let reference_name_atom = atom(reference_type.identifier.value);
448
449 if let Some((source_class, original_name)) = type_context.get_imported_type_alias(reference_name_atom) {
450 return Ok(wrap_atomic(TAtomic::Alias(TAlias::new(*source_class, *original_name))));
451 }
452
453 if type_context.has_type_alias(reference_name_atom)
454 && let Some(class_name) = classname
455 {
456 return Ok(wrap_atomic(TAtomic::Alias(TAlias::new(class_name, reference_name_atom))));
457 }
458
459 wrap_atomic(get_reference_from_ast(
460 &reference_type.identifier,
461 reference_type.parameters.as_ref(),
462 scope,
463 type_context,
464 classname,
465 )?)
466 }
467 Type::Mixed(_) | Type::Wildcard(_) => get_mixed(),
468 Type::NonEmptyMixed(_) => get_truthy_mixed(),
469 Type::Null(_) => get_null(),
470 Type::Void(_) => get_void(),
471 Type::Never(_) => get_never(),
472 Type::Resource(_) => get_resource(),
473 Type::ClosedResource(_) => get_closed_resource(),
474 Type::OpenResource(_) => get_open_resource(),
475 Type::True(_) => get_true(),
476 Type::False(_) => get_false(),
477 Type::Bool(_) => get_bool(),
478 Type::Float(_) => get_float(),
479 Type::Int(_) => get_int(),
480 Type::String(_) => get_string(),
481 Type::ArrayKey(_) => get_arraykey(),
482 Type::Numeric(_) => get_numeric(),
483 Type::Scalar(_) => get_scalar(),
484 Type::CallableString(_) => get_callable_string(),
485 Type::LowercaseCallableString(_) => get_string_with_props(false, false, false, true, TStringCasing::Lowercase),
486 Type::UppercaseCallableString(_) => get_string_with_props(false, false, false, true, TStringCasing::Uppercase),
487 Type::NumericString(_) => get_numeric_string(),
488 Type::NonEmptyString(_) => get_non_empty_string(),
489 Type::TruthyString(_) | Type::NonFalsyString(_) => get_truthy_string(),
490 Type::UnspecifiedLiteralString(_) => get_unspecified_literal_string(),
491 Type::NonEmptyUnspecifiedLiteralString(_) => get_non_empty_unspecified_literal_string(),
492 Type::NonEmptyLowercaseString(_) => get_non_empty_lowercase_string(),
493 Type::LowercaseString(_) => get_lowercase_string(),
494 Type::NonEmptyUppercaseString(_) => get_non_empty_uppercase_string(),
495 Type::UppercaseString(_) => get_uppercase_string(),
496 Type::UnspecifiedLiteralInt(_) => get_unspecified_literal_int(),
497 Type::UnspecifiedLiteralFloat(_) => get_unspecified_literal_float(),
498 Type::LiteralFloat(lit) => get_literal_float(*lit.value),
499 Type::LiteralInt(lit) => get_literal_int(lit.value as i64),
500 Type::LiteralString(lit) => get_literal_string(atom(lit.value)),
501 Type::Negated(negated) => match negated.number {
502 LiteralIntOrFloatType::Int(lit) => get_literal_int(-(lit.value as i64)),
503 LiteralIntOrFloatType::Float(lit) => get_literal_float(-(*lit.value)),
504 },
505 Type::Posited(posited) => match posited.number {
506 LiteralIntOrFloatType::Int(lit) => get_literal_int(lit.value as i64),
507 LiteralIntOrFloatType::Float(lit) => get_literal_float(*lit.value),
508 },
509 Type::Iterable(iterable) => match iterable.parameters.as_ref() {
510 Some(parameters) => match parameters.entries.len() {
511 0 => wrap_atomic(TAtomic::Iterable(TIterable::mixed())),
512 1 => {
513 let value_type =
514 get_union_from_type_ast(¶meters.entries[0].inner, scope, type_context, classname)?;
515
516 wrap_atomic(TAtomic::Iterable(TIterable::of_value(Arc::new(value_type))))
517 }
518 _ => {
519 let key_type =
520 get_union_from_type_ast(¶meters.entries[0].inner, scope, type_context, classname)?;
521
522 let value_type =
523 get_union_from_type_ast(¶meters.entries[1].inner, scope, type_context, classname)?;
524
525 wrap_atomic(TAtomic::Iterable(TIterable::new(Arc::new(key_type), Arc::new(value_type))))
526 }
527 },
528 None => wrap_atomic(TAtomic::Iterable(TIterable::mixed())),
529 },
530 Type::PositiveInt(_) => get_positive_int(),
531 Type::NegativeInt(_) => get_negative_int(),
532 Type::NonPositiveInt(_) => get_non_positive_int(),
533 Type::NonNegativeInt(_) => get_non_negative_int(),
534 Type::NonZeroInt(_) => get_non_zero_int(),
535 Type::TrailingPipe(trailing) => get_union_from_type_ast(&trailing.inner, scope, type_context, classname)?,
536 Type::IntRange(range) => {
537 let min = match range.min {
538 IntOrKeyword::NegativeInt { int, .. } => Some(-(int.value as i64)),
539 IntOrKeyword::Int(literal_int_type) => Some(literal_int_type.value as i64),
540 IntOrKeyword::Keyword(_) => None,
541 };
542
543 let max = match range.max {
544 IntOrKeyword::NegativeInt { int, .. } => Some(-(int.value as i64)),
545 IntOrKeyword::Int(literal_int_type) => Some(literal_int_type.value as i64),
546 IntOrKeyword::Keyword(_) => None,
547 };
548
549 if let (Some(min_value), Some(max_value)) = (min, max)
550 && min_value > max_value
551 {
552 return Err(TypeError::InvalidType(
553 ttype.to_string(),
554 "Minimum value of an int range cannot be greater than maximum value".to_string(),
555 ttype.span(),
556 ));
557 }
558
559 TUnion::from_single(Cow::Owned(TAtomic::Scalar(TScalar::Integer(TInteger::from_bounds(min, max)))))
560 }
561 Type::Conditional(conditional) => TUnion::from_single(Cow::Owned(TAtomic::Conditional(TConditional::new(
562 Arc::new(get_union_from_type_ast(&conditional.subject, scope, type_context, classname)?),
563 Arc::new(get_union_from_type_ast(&conditional.target, scope, type_context, classname)?),
564 Arc::new(get_union_from_type_ast(&conditional.then, scope, type_context, classname)?),
565 Arc::new(get_union_from_type_ast(&conditional.otherwise, scope, type_context, classname)?),
566 conditional.is_negated(),
567 )))),
568 Type::Variable(variable_type) => {
569 if variable_type.value == "$this" {
570 TUnion::from_single(Cow::Owned(TAtomic::Object(TObject::Named(TNamedObject::new_this(atom("$this"))))))
571 } else {
572 TUnion::from_single(Cow::Owned(TAtomic::Variable(atom(variable_type.value))))
573 }
574 }
575 Type::KeyOf(key_of_type) => TUnion::from_atomic(TAtomic::Derived(TDerived::KeyOf(TKeyOf::new(Arc::new(
576 get_union_from_type_ast(&key_of_type.parameter.entry.inner, scope, type_context, classname)?,
577 ))))),
578 Type::ValueOf(value_of_type) => TUnion::from_atomic(TAtomic::Derived(TDerived::ValueOf(TValueOf::new(
579 Arc::new(get_union_from_type_ast(&value_of_type.parameter.entry.inner, scope, type_context, classname)?),
580 )))),
581 Type::IntMask(int_mask_type) => {
582 let mut values = Vec::new();
583 for entry in &int_mask_type.parameters.entries {
584 values.push(get_union_from_type_ast(&entry.inner, scope, type_context, classname)?);
585 }
586 TUnion::from_atomic(TAtomic::Derived(TDerived::IntMask(TIntMask::new(values))))
587 }
588 Type::IntMaskOf(int_mask_of_type) => {
589 TUnion::from_atomic(TAtomic::Derived(TDerived::IntMaskOf(TIntMaskOf::new(Arc::new(
590 get_union_from_type_ast(&int_mask_of_type.parameter.entry.inner, scope, type_context, classname)?,
591 )))))
592 }
593 Type::New(new_type) => TUnion::from_atomic(TAtomic::Derived(TDerived::New(TNew::new(Arc::new(
594 get_union_from_type_ast(&new_type.parameter.entry.inner, scope, type_context, classname)?,
595 ))))),
596 Type::TemplateType(template_type_type) => {
597 let entries = &template_type_type.parameters.entries;
598 if entries.len() != 3 {
599 return Err(TypeError::InvalidType(
600 template_type_type.to_string(),
601 format!(
602 "`template-type<O, C, T>` expects exactly 3 parameters (object, class-name, template-name), got {}",
603 entries.len()
604 ),
605 template_type_type.span(),
606 ));
607 }
608
609 let object = Arc::new(get_union_from_type_ast(&entries[0].inner, scope, type_context, classname)?);
610 let class_name = Arc::new(get_union_from_type_ast(&entries[1].inner, scope, type_context, classname)?);
611 let template_name = Arc::new(get_union_from_type_ast(&entries[2].inner, scope, type_context, classname)?);
612
613 TUnion::from_atomic(TAtomic::Derived(TDerived::TemplateType(TTemplateType::new(
614 object,
615 class_name,
616 template_name,
617 ))))
618 }
619 Type::PropertiesOf(properties_of_type) => {
620 TUnion::from_atomic(TAtomic::Derived(TDerived::PropertiesOf(match properties_of_type.filter {
621 PropertiesOfFilter::All => TPropertiesOf::new(Arc::new(get_union_from_type_ast(
622 &properties_of_type.parameter.entry.inner,
623 scope,
624 type_context,
625 classname,
626 )?)),
627 PropertiesOfFilter::Public => TPropertiesOf::public(Arc::new(get_union_from_type_ast(
628 &properties_of_type.parameter.entry.inner,
629 scope,
630 type_context,
631 classname,
632 )?)),
633 PropertiesOfFilter::Protected => TPropertiesOf::protected(Arc::new(get_union_from_type_ast(
634 &properties_of_type.parameter.entry.inner,
635 scope,
636 type_context,
637 classname,
638 )?)),
639 PropertiesOfFilter::Private => TPropertiesOf::private(Arc::new(get_union_from_type_ast(
640 &properties_of_type.parameter.entry.inner,
641 scope,
642 type_context,
643 classname,
644 )?)),
645 })))
646 }
647 Type::IndexAccess(index_access_type) => {
648 TUnion::from_atomic(TAtomic::Derived(TDerived::IndexAccess(TIndexAccess::new(
649 get_union_from_type_ast(&index_access_type.target, scope, type_context, classname)?,
650 get_union_from_type_ast(&index_access_type.index, scope, type_context, classname)?,
651 ))))
652 }
653 _ => {
654 return Err(TypeError::UnsupportedType(ttype.to_string(), ttype.span()));
655 }
656 })
657}
658
659#[inline]
660fn get_object_from_ast(
661 object: &ObjectType<'_>,
662 scope: &NamespaceScope,
663 type_context: &TypeResolutionContext,
664 classname: Option<Atom>,
665) -> Result<TAtomic, TypeError> {
666 let Some(properties) = object.properties.as_ref() else {
667 return Ok(TAtomic::Object(TObject::Any));
668 };
669
670 let mut known_properties = BTreeMap::new();
671 for property in &properties.fields {
672 let property_is_optional = property.is_optional();
673
674 let Some(field_key) = property.key.as_ref() else {
675 continue;
676 };
677
678 let key = match field_key.key {
679 ShapeKey::String { value, .. } => atom(value),
680 ShapeKey::Integer { value, .. } => i64_atom(value),
681 ShapeKey::ClassLikeConstant { ref class_name, ref constant_name, .. } => {
682 concat_atom!(class_name.value, "::", constant_name.value)
683 }
684 };
685
686 let property_type = get_union_from_type_ast(&property.value, scope, type_context, classname)?;
687
688 known_properties.insert(key, (property_is_optional, property_type));
689 }
690
691 Ok(TAtomic::Object(TObject::new_with_properties(properties.ellipsis.is_none(), known_properties)))
692}
693
694#[inline]
695fn get_shape_from_ast(
696 shape: &ShapeType<'_>,
697 scope: &NamespaceScope,
698 type_context: &TypeResolutionContext,
699 classname: Option<Atom>,
700) -> Result<TAtomic, TypeError> {
701 if shape.kind.is_list() {
702 let mut list = TList::new(match &shape.additional_fields {
703 Some(additional_fields) => match &additional_fields.parameters {
704 Some(parameters) => Arc::new(if let Some(k) = parameters.entries.first().map(|g| &g.inner) {
705 get_union_from_type_ast(k, scope, type_context, classname)?
706 } else {
707 get_mixed()
708 }),
709 None => Arc::new(get_mixed()),
710 },
711 None => Arc::new(get_never()),
712 });
713
714 list.known_elements = Some({
715 let mut tree = BTreeMap::new();
716 let mut next_offset: usize = 0;
717
718 for field in &shape.fields {
719 let field_is_optional = field.is_optional();
720
721 let offset = if let Some(field_key) = field.key.as_ref() {
722 let array_key = match field_key.key {
723 ShapeKey::String { value, .. } => ArrayKey::String(atom(value)),
724 ShapeKey::Integer { value, .. } => ArrayKey::Integer(value),
725 ShapeKey::ClassLikeConstant { ref class_name, ref constant_name, .. } => {
726 let class_like_name = if class_name.value.eq_ignore_ascii_case("self")
727 || class_name.value.eq_ignore_ascii_case("static")
728 || class_name.value.eq("this")
729 || class_name.value.eq("$this")
730 {
731 classname.unwrap_or_else(|| atom(class_name.value))
732 } else if class_name.value.eq_ignore_ascii_case("parent") {
733 atom("parent")
734 } else {
735 let (resolved, _) = scope.resolve(NameKind::Default, class_name.value);
736 atom(&resolved)
737 };
738
739 ArrayKey::ClassLikeConstant { class_like_name, constant_name: atom(constant_name.value) }
740 }
741 };
742
743 if let ArrayKey::Integer(offset) = array_key {
744 if offset > 0 && (offset as usize) == next_offset {
745 next_offset += 1;
746
747 offset as usize
748 } else {
749 return Err(TypeError::InvalidType(
750 shape.to_string(),
751 "List shape keys must be sequential".to_string(),
752 field_key.span(),
753 ));
754 }
755 } else {
756 return Err(TypeError::InvalidType(
757 shape.to_string(),
758 "List shape keys are expected to be integers".to_string(),
759 field_key.span(),
760 ));
761 }
762 } else {
763 let offset = next_offset;
764
765 next_offset += 1;
766
767 offset
768 };
769
770 let mut field_value_type = get_union_from_type_ast(&field.value, scope, type_context, classname)?;
771 if field_is_optional {
772 field_value_type.set_possibly_undefined(true, None);
773 }
774
775 tree.insert(offset, (field_is_optional, field_value_type));
776 }
777
778 tree
779 });
780
781 list.non_empty = shape.has_non_optional_fields() || shape.kind.is_non_empty();
782
783 Ok(TAtomic::Array(TArray::List(list)))
784 } else {
785 let mut keyed_array = TKeyedArray::new();
786
787 keyed_array.parameters = match &shape.additional_fields {
788 Some(additional_fields) => Some(match &additional_fields.parameters {
789 Some(parameters) => (
790 Arc::new(if let Some(k) = parameters.entries.first().map(|g| &g.inner) {
791 get_union_from_type_ast(k, scope, type_context, classname)?
792 } else {
793 get_mixed()
794 }),
795 Arc::new(if let Some(v) = parameters.entries.get(1).map(|g| &g.inner) {
796 get_union_from_type_ast(v, scope, type_context, classname)?
797 } else {
798 get_mixed()
799 }),
800 ),
801 None => (Arc::new(get_arraykey()), Arc::new(get_mixed())),
802 }),
803 None => None,
804 };
805
806 keyed_array.known_items = Some({
807 let mut tree = BTreeMap::new();
808 let mut next_offset = 0;
809
810 for field in &shape.fields {
811 let field_is_optional = field.is_optional();
812
813 let array_key = if let Some(field_key) = field.key.as_ref() {
814 let array_key = match field_key.key {
815 ShapeKey::String { value, .. } => ArrayKey::String(atom(value)),
816 ShapeKey::Integer { value, .. } => ArrayKey::Integer(value),
817 ShapeKey::ClassLikeConstant { ref class_name, ref constant_name, .. } => {
818 let class_like_name = if class_name.value.eq_ignore_ascii_case("self")
819 || class_name.value.eq_ignore_ascii_case("static")
820 || class_name.value.eq("this")
821 || class_name.value.eq("$this")
822 {
823 classname.unwrap_or_else(|| atom(class_name.value))
824 } else if class_name.value.eq_ignore_ascii_case("parent") {
825 atom("parent")
826 } else {
827 let (resolved, _) = scope.resolve(NameKind::Default, class_name.value);
828 atom(&resolved)
829 };
830
831 ArrayKey::ClassLikeConstant { class_like_name, constant_name: atom(constant_name.value) }
832 }
833 };
834
835 if let ArrayKey::Integer(offset) = array_key
836 && offset >= next_offset
837 {
838 next_offset = offset + 1;
839 }
840
841 array_key
842 } else {
843 let array_key = ArrayKey::Integer(next_offset);
844
845 next_offset += 1;
846
847 array_key
848 };
849
850 let mut field_value_type = get_union_from_type_ast(&field.value, scope, type_context, classname)?;
851 if field_is_optional {
852 field_value_type.set_possibly_undefined(true, None);
853 }
854
855 tree.insert(array_key, (field_is_optional, field_value_type));
856 }
857
858 tree
859 });
860
861 keyed_array.non_empty = shape.has_non_optional_fields() || shape.kind.is_non_empty();
862
863 Ok(TAtomic::Array(TArray::Keyed(keyed_array)))
864 }
865}
866
867#[inline]
868fn get_callable_from_ast(
869 callable: &CallableType<'_>,
870 scope: &NamespaceScope,
871 type_context: &TypeResolutionContext,
872 classname: Option<Atom>,
873) -> Result<TAtomic, TypeError> {
874 let mut parameters = vec![];
875 let mut return_type = None;
876
877 if let Some(specification) = &callable.specification {
878 for parameter_ast in &specification.parameters.entries {
879 let parameter_type = if let Some(parameter_type) = ¶meter_ast.parameter_type {
880 get_union_from_type_ast(parameter_type, scope, type_context, classname)?
881 } else {
882 get_mixed()
883 };
884
885 parameters.push(TCallableParameter::new(
886 Some(Arc::new(parameter_type)),
887 false,
888 parameter_ast.is_variadic(),
889 parameter_ast.is_optional(),
890 ));
891 }
892
893 if let Some(ret) = specification.return_type.as_ref() {
894 return_type = Some(get_union_from_type_ast(&ret.return_type, scope, type_context, classname)?);
895 }
896 } else {
897 parameters.push(TCallableParameter::new(Some(Arc::new(get_mixed())), false, true, false));
900 return_type = Some(get_mixed());
901 }
902
903 Ok(TAtomic::Callable(TCallable::Signature(
904 TCallableSignature::new(callable.kind.is_pure(), callable.kind.is_closure())
905 .with_parameters(parameters)
906 .with_return_type(return_type.map(Arc::new)),
907 )))
908}
909
910#[inline]
911fn get_reference_from_ast<'i>(
912 reference_identifier: &Identifier<'i>,
913 generics: Option<&GenericParameters<'i>>,
914 scope: &NamespaceScope,
915 type_context: &TypeResolutionContext,
916 classname: Option<Atom>,
917) -> Result<TAtomic, TypeError> {
918 let reference_name = reference_identifier.value;
919
920 let mut is_this = false;
921 let mut is_static = false;
922 let mut is_named_object = false;
923 let fq_reference_name_id = if reference_name == "this" || reference_name == "static" || reference_name == "self" {
924 is_named_object = true;
925 is_this = reference_name == "this";
926 is_static = reference_name != "self";
927
928 classname.unwrap_or_else(|| atom("static"))
929 } else if reference_name == "parent" {
930 is_named_object = true;
931
932 atom("parent")
933 } else {
934 let reference_name_atom = atom(reference_name);
935 if let Some(defining_entities) = type_context.get_template_definition(reference_name_atom)
936 && generics.is_none()
937 {
938 return Ok(get_template_atomic(defining_entities, reference_name_atom));
939 }
940
941 let (fq_reference_name, _) = scope.resolve(NameKind::Default, reference_name);
942
943 if fq_reference_name.eq_ignore_ascii_case("Closure") && generics.is_none() {
945 return Ok(TAtomic::Callable(TCallable::Signature(
946 TCallableSignature::new(false, true)
947 .with_parameters(vec![TCallableParameter::new(Some(Arc::new(get_mixed())), false, true, false)])
948 .with_return_type(Some(Arc::new(get_mixed()))),
949 )));
950 }
951
952 atom(&fq_reference_name)
953 };
954
955 let mut type_parameters = None;
956 if let Some(generics) = generics {
957 let mut parameters = vec![];
958 for generic in &generics.entries {
959 let generic_type = get_union_from_type_ast(&generic.inner, scope, type_context, classname)?;
960
961 parameters.push(generic_type);
962 }
963
964 type_parameters = Some(parameters);
965 }
966
967 let is_generator = fq_reference_name_id.eq_ignore_ascii_case("Generator");
968
969 let is_iterator = is_generator
970 || fq_reference_name_id.eq_ignore_ascii_case("Iterator")
971 || fq_reference_name_id.eq_ignore_ascii_case("IteratorAggregate")
972 || fq_reference_name_id.eq_ignore_ascii_case("Traversable");
973
974 'iterator: {
975 if !is_iterator {
976 break 'iterator;
977 }
978
979 let Some(type_parameters) = &mut type_parameters else {
980 type_parameters = Some(vec![get_mixed(), get_mixed()]);
981
982 break 'iterator;
983 };
984
985 if type_parameters.len() == 1 {
986 type_parameters.insert(0, get_mixed());
987 } else if type_parameters.is_empty() {
988 type_parameters.push(get_mixed());
989 type_parameters.push(get_mixed());
990 }
991
992 if !is_generator {
993 break 'iterator;
994 }
995
996 while type_parameters.len() < 4 {
997 type_parameters.push(get_mixed());
998 }
999 }
1000
1001 if is_named_object {
1002 Ok(TAtomic::Object(TObject::Named(TNamedObject {
1003 name: fq_reference_name_id,
1004 type_parameters,
1005 intersection_types: None,
1006 is_static,
1007 is_this,
1008 remapped_parameters: false,
1009 })))
1010 } else {
1011 Ok(TAtomic::Reference(TReference::Symbol {
1012 name: fq_reference_name_id,
1013 parameters: type_parameters,
1014 intersection_types: None,
1015 }))
1016 }
1017}
1018
1019#[inline]
1020fn get_array_type_from_ast<'i, 'p>(
1021 mut key: Option<&'p Type<'i>>,
1022 mut value: Option<&'p Type<'i>>,
1023 non_empty: bool,
1024 scope: &NamespaceScope,
1025 type_context: &TypeResolutionContext,
1026 classname: Option<Atom>,
1027) -> Result<TAtomic, TypeError> {
1028 if key.is_some() && value.is_none() {
1029 std::mem::swap(&mut key, &mut value);
1030 }
1031
1032 let mut array = TKeyedArray::new_with_parameters(
1033 Arc::new(if let Some(k) = key {
1034 get_union_from_type_ast(k, scope, type_context, classname)?
1035 } else {
1036 get_arraykey()
1037 }),
1038 Arc::new(if let Some(v) = value {
1039 get_union_from_type_ast(v, scope, type_context, classname)?
1040 } else {
1041 get_mixed()
1042 }),
1043 );
1044
1045 array.non_empty = non_empty;
1046
1047 Ok(TAtomic::Array(TArray::Keyed(array)))
1048}
1049
1050#[inline]
1051fn get_list_type_from_ast(
1052 value: Option<&Type<'_>>,
1053 non_empty: bool,
1054 scope: &NamespaceScope,
1055 type_context: &TypeResolutionContext,
1056 classname: Option<Atom>,
1057) -> Result<TAtomic, TypeError> {
1058 Ok(TAtomic::Array(TArray::List(TList {
1059 element_type: Arc::new(if let Some(v) = value {
1060 get_union_from_type_ast(v, scope, type_context, classname)?
1061 } else {
1062 get_mixed()
1063 }),
1064 known_count: None,
1065 known_elements: None,
1066 non_empty,
1067 })))
1068}
1069
1070#[inline]
1071fn get_class_string_type_from_ast(
1072 span: Span,
1073 kind: TClassLikeStringKind,
1074 parameter: &Option<SingleGenericParameter<'_>>,
1075 scope: &NamespaceScope,
1076 type_context: &TypeResolutionContext,
1077 classname: Option<Atom>,
1078) -> Result<TUnion, TypeError> {
1079 Ok(match parameter {
1080 Some(parameter) => {
1081 let constraint_union = get_union_from_type_ast(¶meter.entry.inner, scope, type_context, classname)?;
1082
1083 let mut class_strings = vec![];
1084 for constraint in constraint_union.types.into_owned() {
1085 match constraint {
1086 TAtomic::Object(TObject::Named(_) | TObject::Enum(_))
1087 | TAtomic::Reference(TReference::Symbol { .. })
1088 | TAtomic::Alias(_) => class_strings
1089 .push(TAtomic::Scalar(TScalar::ClassLikeString(TClassLikeString::of_type(kind, constraint)))),
1090 TAtomic::GenericParameter(TGenericParameter {
1091 parameter_name,
1092 defining_entity,
1093 constraint,
1094 ..
1095 }) => {
1096 for constraint_atomic in Arc::unwrap_or_clone(constraint).types.into_owned() {
1097 class_strings.push(TAtomic::Scalar(TScalar::ClassLikeString(TClassLikeString::generic(
1098 kind,
1099 parameter_name,
1100 defining_entity,
1101 constraint_atomic,
1102 ))));
1103 }
1104 }
1105 _ => {
1106 return Err(TypeError::InvalidType(
1107 kind.to_string(),
1108 format!(
1109 "class string parameter must target an object type, found `{}`.",
1110 constraint.get_id()
1111 ),
1112 span,
1113 ));
1114 }
1115 }
1116 }
1117
1118 TUnion::from_vec(class_strings)
1119 }
1120 None => wrap_atomic(TAtomic::Scalar(TScalar::ClassLikeString(TClassLikeString::any(kind)))),
1121 })
1122}
1123
1124#[inline]
1125fn get_template_atomic(defining_entities: &[GenericTemplate], parameter_name: Atom) -> TAtomic {
1126 let GenericTemplate { defining_entity: template_source, constraint: template_type } = &defining_entities[0];
1127
1128 TAtomic::GenericParameter(TGenericParameter {
1129 parameter_name,
1130 constraint: Arc::new(template_type.clone()),
1131 defining_entity: *template_source,
1132 intersection_types: None,
1133 })
1134}