1use crate::def::DefId;
11use crate::{TypeData, TypeDatabase, TypeId};
12use rustc_hash::FxHashSet;
13
14#[derive(Debug, Clone)]
21pub enum LiteralTypeKind {
22 String(tsz_common::interner::Atom),
24 Number(f64),
26 BigInt(tsz_common::interner::Atom),
28 Boolean(bool),
30 NotLiteral,
32}
33
34pub fn classify_literal_type(db: &dyn TypeDatabase, type_id: TypeId) -> LiteralTypeKind {
42 let Some(key) = db.lookup(type_id) else {
43 return LiteralTypeKind::NotLiteral;
44 };
45
46 match key {
47 TypeData::Literal(crate::LiteralValue::String(atom)) => LiteralTypeKind::String(atom),
48 TypeData::Literal(crate::LiteralValue::Number(ordered_float)) => {
49 LiteralTypeKind::Number(ordered_float.0)
50 }
51 TypeData::Literal(crate::LiteralValue::BigInt(atom)) => LiteralTypeKind::BigInt(atom),
52 TypeData::Literal(crate::LiteralValue::Boolean(value)) => LiteralTypeKind::Boolean(value),
53 _ => LiteralTypeKind::NotLiteral,
54 }
55}
56
57pub fn is_string_literal(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
59 matches!(
60 classify_literal_type(db, type_id),
61 LiteralTypeKind::String(_)
62 )
63}
64
65pub fn is_number_literal(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
67 matches!(
68 classify_literal_type(db, type_id),
69 LiteralTypeKind::Number(_)
70 )
71}
72
73pub fn is_boolean_literal(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
75 matches!(
76 classify_literal_type(db, type_id),
77 LiteralTypeKind::Boolean(_)
78 )
79}
80
81pub fn get_string_literal_atom(
83 db: &dyn TypeDatabase,
84 type_id: TypeId,
85) -> Option<tsz_common::interner::Atom> {
86 match classify_literal_type(db, type_id) {
87 LiteralTypeKind::String(atom) => Some(atom),
88 _ => None,
89 }
90}
91
92pub fn get_number_literal_value(db: &dyn TypeDatabase, type_id: TypeId) -> Option<f64> {
94 match classify_literal_type(db, type_id) {
95 LiteralTypeKind::Number(value) => Some(value),
96 _ => None,
97 }
98}
99
100pub fn get_boolean_literal_value(db: &dyn TypeDatabase, type_id: TypeId) -> Option<bool> {
102 match classify_literal_type(db, type_id) {
103 LiteralTypeKind::Boolean(value) => Some(value),
104 _ => None,
105 }
106}
107
108pub fn is_invalid_index_type(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
116 let mut visited = FxHashSet::default();
117 is_invalid_index_type_inner(db, type_id, &mut visited)
118}
119
120fn is_invalid_index_type_inner(
121 db: &dyn TypeDatabase,
122 type_id: TypeId,
123 visited: &mut FxHashSet<TypeId>,
124) -> bool {
125 if !visited.insert(type_id) {
126 return false;
127 }
128
129 if matches!(
130 type_id,
131 TypeId::ANY | TypeId::UNKNOWN | TypeId::ERROR | TypeId::NEVER
132 ) {
133 return false;
134 }
135
136 match db.lookup(type_id) {
137 Some(TypeData::Intrinsic(kind)) => matches!(
138 kind,
139 crate::IntrinsicKind::Void
140 | crate::IntrinsicKind::Null
141 | crate::IntrinsicKind::Undefined
142 | crate::IntrinsicKind::Boolean
143 | crate::IntrinsicKind::Bigint
144 | crate::IntrinsicKind::Object
145 | crate::IntrinsicKind::Function
146 ),
147 Some(TypeData::Literal(value)) => matches!(
148 value,
149 crate::LiteralValue::Boolean(_) | crate::LiteralValue::BigInt(_)
150 ),
151 Some(
152 TypeData::Array(_)
153 | TypeData::Tuple(_)
154 | TypeData::Object(_)
155 | TypeData::ObjectWithIndex(_)
156 | TypeData::Function(_)
157 | TypeData::Callable(_)
158 | TypeData::Lazy(_),
159 ) => true,
160 Some(TypeData::Union(list_id) | TypeData::Intersection(list_id)) => db
161 .type_list(list_id)
162 .iter()
163 .any(|&member| is_invalid_index_type_inner(db, member, visited)),
164 Some(TypeData::TypeParameter(info)) => info
165 .constraint
166 .is_some_and(|constraint| is_invalid_index_type_inner(db, constraint, visited)),
167 _ => false,
168 }
169}
170
171#[derive(Debug, Clone)]
180pub enum SpreadTypeKind {
181 Array(TypeId),
183 Tuple(crate::types::TupleListId),
185 Object(crate::types::ObjectShapeId),
187 ObjectWithIndex(crate::types::ObjectShapeId),
189 StringLiteral(tsz_common::interner::Atom),
191 Lazy(DefId),
193 Other,
195 NotSpreadable,
197}
198
199pub fn classify_spread_type(db: &dyn TypeDatabase, type_id: TypeId) -> SpreadTypeKind {
204 if type_id.is_any() || type_id == TypeId::STRING {
206 return SpreadTypeKind::Other;
207 }
208 if type_id.is_unknown() {
209 return SpreadTypeKind::NotSpreadable;
210 }
211
212 let Some(key) = db.lookup(type_id) else {
213 return SpreadTypeKind::NotSpreadable;
214 };
215
216 match key {
217 TypeData::Array(element_type) => SpreadTypeKind::Array(element_type),
218 TypeData::Tuple(tuple_id) => SpreadTypeKind::Tuple(tuple_id),
219 TypeData::Object(shape_id) => SpreadTypeKind::Object(shape_id),
220 TypeData::ObjectWithIndex(shape_id) => SpreadTypeKind::ObjectWithIndex(shape_id),
221 TypeData::Literal(crate::LiteralValue::String(atom)) => SpreadTypeKind::StringLiteral(atom),
222 TypeData::Lazy(def_id) => SpreadTypeKind::Lazy(def_id),
223 _ => SpreadTypeKind::Other,
224 }
225}
226
227pub fn is_iterable_type_kind(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
231 if type_id == TypeId::STRING {
233 return true;
234 }
235
236 let Some(key) = db.lookup(type_id) else {
237 return false;
238 };
239
240 match key {
241 TypeData::Array(_)
242 | TypeData::Tuple(_)
243 | TypeData::Literal(crate::LiteralValue::String(_)) => true,
244 TypeData::Object(shape_id) => {
245 let shape = db.object_shape(shape_id);
247 shape.properties.iter().any(|prop| {
248 let prop_name = db.resolve_atom_ref(prop.name);
249 (prop_name.as_ref() == "[Symbol.iterator]" || prop_name.as_ref() == "next")
250 && prop.is_method
251 })
252 }
253 _ => false,
254 }
255}
256
257pub fn get_iterable_element_type_from_db(db: &dyn TypeDatabase, type_id: TypeId) -> Option<TypeId> {
259 if type_id == TypeId::STRING {
261 return Some(TypeId::STRING);
262 }
263
264 let key = db.lookup(type_id)?;
265
266 match key {
267 TypeData::Array(elem_type) => Some(elem_type),
268 TypeData::Tuple(tuple_list_id) => {
269 let elements = db.tuple_list(tuple_list_id);
270 if elements.is_empty() {
271 Some(TypeId::NEVER)
272 } else {
273 let types: Vec<TypeId> = elements.iter().map(|e| e.type_id).collect();
274 Some(db.union(types))
275 }
276 }
277 TypeData::Literal(crate::LiteralValue::String(_)) => Some(TypeId::STRING),
278 TypeData::Object(shape_id) => {
279 let shape = db.object_shape(shape_id);
282 let has_iterator = shape.properties.iter().any(|prop| {
283 let prop_name = db.resolve_atom_ref(prop.name);
284 (prop_name.as_ref() == "[Symbol.iterator]" || prop_name.as_ref() == "next")
285 && prop.is_method
286 });
287 has_iterator.then_some(TypeId::ANY)
288 }
289 _ => None,
290 }
291}
292
293#[derive(Debug, Clone)]
302pub enum TypeParameterKind {
303 TypeParameter(crate::types::TypeParamInfo),
305 Infer(crate::types::TypeParamInfo),
307 Application(crate::types::TypeApplicationId),
309 Union(Vec<TypeId>),
311 Intersection(Vec<TypeId>),
313 Callable(crate::types::CallableShapeId),
315 NotTypeParameter,
317}
318
319pub fn classify_type_parameter(db: &dyn TypeDatabase, type_id: TypeId) -> TypeParameterKind {
323 let Some(key) = db.lookup(type_id) else {
324 return TypeParameterKind::NotTypeParameter;
325 };
326
327 match key {
328 TypeData::TypeParameter(info) => TypeParameterKind::TypeParameter(info),
329 TypeData::Infer(info) => TypeParameterKind::Infer(info),
330 TypeData::Application(app_id) => TypeParameterKind::Application(app_id),
331 TypeData::Union(list_id) => {
332 let members = db.type_list(list_id);
333 TypeParameterKind::Union(members.to_vec())
334 }
335 TypeData::Intersection(list_id) => {
336 let members = db.type_list(list_id);
337 TypeParameterKind::Intersection(members.to_vec())
338 }
339 TypeData::Callable(shape_id) => TypeParameterKind::Callable(shape_id),
340 _ => TypeParameterKind::NotTypeParameter,
341 }
342}
343
344pub fn is_direct_type_parameter(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
346 matches!(
347 classify_type_parameter(db, type_id),
348 TypeParameterKind::TypeParameter(_) | TypeParameterKind::Infer(_)
349 )
350}
351
352pub fn get_type_param_default(db: &dyn TypeDatabase, type_id: TypeId) -> Option<TypeId> {
354 match db.lookup(type_id) {
355 Some(TypeData::TypeParameter(info) | TypeData::Infer(info)) => info.default,
356 _ => None,
357 }
358}
359
360pub fn get_callable_type_param_count(db: &dyn TypeDatabase, type_id: TypeId) -> usize {
362 match db.lookup(type_id) {
363 Some(TypeData::Callable(shape_id)) => {
364 let shape = db.callable_shape(shape_id);
365 shape
366 .call_signatures
367 .iter()
368 .map(|sig| sig.type_params.len())
369 .max()
370 .unwrap_or(0)
371 }
372 _ => 0,
373 }
374}
375
376#[derive(Debug, Clone)]
385pub enum PromiseTypeKind {
386 Application {
388 app_id: crate::types::TypeApplicationId,
389 base: TypeId,
390 args: Vec<TypeId>,
391 },
392 Lazy(crate::def::DefId),
394 Object(crate::types::ObjectShapeId),
396 Union(Vec<TypeId>),
398 NotPromise,
400}
401
402pub fn classify_promise_type(db: &dyn TypeDatabase, type_id: TypeId) -> PromiseTypeKind {
407 let Some(key) = db.lookup(type_id) else {
408 return PromiseTypeKind::NotPromise;
409 };
410
411 match key {
412 TypeData::Application(app_id) => {
413 let app = db.type_application(app_id);
414 PromiseTypeKind::Application {
415 app_id,
416 base: app.base,
417 args: app.args.clone(),
418 }
419 }
420 TypeData::Lazy(def_id) => PromiseTypeKind::Lazy(def_id),
421 TypeData::Object(shape_id) => PromiseTypeKind::Object(shape_id),
422 TypeData::Union(list_id) => {
423 let members = db.type_list(list_id);
424 PromiseTypeKind::Union(members.to_vec())
425 }
426 _ => PromiseTypeKind::NotPromise,
427 }
428}
429
430#[derive(Debug, Clone)]
436pub enum NewExpressionTypeKind {
437 Callable(crate::types::CallableShapeId),
439 Function(crate::types::FunctionShapeId),
441 TypeQuery(crate::types::SymbolRef),
443 Intersection(Vec<TypeId>),
445 Union(Vec<TypeId>),
447 TypeParameter { constraint: Option<TypeId> },
449 NotConstructable,
451}
452
453pub fn classify_for_new_expression(
455 db: &dyn TypeDatabase,
456 type_id: TypeId,
457) -> NewExpressionTypeKind {
458 let Some(key) = db.lookup(type_id) else {
459 return NewExpressionTypeKind::NotConstructable;
460 };
461
462 match key {
463 TypeData::Callable(shape_id) => NewExpressionTypeKind::Callable(shape_id),
464 TypeData::Function(shape_id) => NewExpressionTypeKind::Function(shape_id),
465 TypeData::TypeQuery(sym_ref) => NewExpressionTypeKind::TypeQuery(sym_ref),
466 TypeData::Intersection(list_id) => {
467 let members = db.type_list(list_id);
468 NewExpressionTypeKind::Intersection(members.to_vec())
469 }
470 TypeData::Union(list_id) => {
471 let members = db.type_list(list_id);
472 NewExpressionTypeKind::Union(members.to_vec())
473 }
474 TypeData::TypeParameter(info) | TypeData::Infer(info) => {
475 NewExpressionTypeKind::TypeParameter {
476 constraint: info.constraint,
477 }
478 }
479 TypeData::Object(shape_id) | TypeData::ObjectWithIndex(shape_id) => {
480 let shape = db.object_shape(shape_id);
483 for prop in &shape.properties {
484 if let Some(TypeData::Callable(callable_shape_id)) = db.lookup(prop.type_id) {
486 let callable_shape = db.callable_shape(callable_shape_id);
487 if !callable_shape.construct_signatures.is_empty() {
488 return NewExpressionTypeKind::Callable(callable_shape_id);
490 }
491 }
492 }
493 NewExpressionTypeKind::NotConstructable
494 }
495 _ => NewExpressionTypeKind::NotConstructable,
496 }
497}
498
499#[derive(Debug, Clone)]
505pub enum AbstractClassCheckKind {
506 TypeQuery(crate::types::SymbolRef),
508 Union(Vec<TypeId>),
510 Intersection(Vec<TypeId>),
512 NotAbstract,
514}
515
516pub fn classify_for_abstract_check(
518 db: &dyn TypeDatabase,
519 type_id: TypeId,
520) -> AbstractClassCheckKind {
521 let Some(key) = db.lookup(type_id) else {
522 return AbstractClassCheckKind::NotAbstract;
523 };
524
525 match key {
526 TypeData::TypeQuery(sym_ref) => AbstractClassCheckKind::TypeQuery(sym_ref),
527 TypeData::Union(list_id) => {
528 let members = db.type_list(list_id);
529 AbstractClassCheckKind::Union(members.to_vec())
530 }
531 TypeData::Intersection(list_id) => {
532 let members = db.type_list(list_id);
533 AbstractClassCheckKind::Intersection(members.to_vec())
534 }
535 _ => AbstractClassCheckKind::NotAbstract,
536 }
537}
538
539#[derive(Debug, Clone)]
545pub enum ConstructSignatureKind {
546 Callable(crate::types::CallableShapeId),
548 Lazy(crate::def::DefId),
550 TypeQuery(crate::types::SymbolRef),
552 Application(crate::types::TypeApplicationId),
554 Union(Vec<TypeId>),
556 Intersection(Vec<TypeId>),
558 TypeParameter { constraint: Option<TypeId> },
560 Function(crate::types::FunctionShapeId),
562 NoConstruct,
564}
565
566pub fn classify_for_construct_signature(
568 db: &dyn TypeDatabase,
569 type_id: TypeId,
570) -> ConstructSignatureKind {
571 let Some(key) = db.lookup(type_id) else {
572 return ConstructSignatureKind::NoConstruct;
573 };
574
575 match key {
576 TypeData::Callable(shape_id) => ConstructSignatureKind::Callable(shape_id),
577 TypeData::Lazy(def_id) => ConstructSignatureKind::Lazy(def_id),
578 TypeData::TypeQuery(sym_ref) => ConstructSignatureKind::TypeQuery(sym_ref),
579 TypeData::Application(app_id) => ConstructSignatureKind::Application(app_id),
580 TypeData::Union(list_id) => {
581 let members = db.type_list(list_id);
582 ConstructSignatureKind::Union(members.to_vec())
583 }
584 TypeData::Intersection(list_id) => {
585 let members = db.type_list(list_id);
586 ConstructSignatureKind::Intersection(members.to_vec())
587 }
588 TypeData::TypeParameter(info) | TypeData::Infer(info) => {
589 ConstructSignatureKind::TypeParameter {
590 constraint: info.constraint,
591 }
592 }
593 TypeData::Function(shape_id) => ConstructSignatureKind::Function(shape_id),
594 _ => ConstructSignatureKind::NoConstruct,
595 }
596}
597
598#[derive(Debug, Clone)]
604pub enum KeyOfTypeKind {
605 Object(crate::types::ObjectShapeId),
607 NoKeys,
609}
610
611pub fn classify_for_keyof(db: &dyn TypeDatabase, type_id: TypeId) -> KeyOfTypeKind {
613 let Some(key) = db.lookup(type_id) else {
614 return KeyOfTypeKind::NoKeys;
615 };
616
617 match key {
618 TypeData::Object(shape_id) | TypeData::ObjectWithIndex(shape_id) => {
619 KeyOfTypeKind::Object(shape_id)
620 }
621 _ => KeyOfTypeKind::NoKeys,
622 }
623}
624
625#[derive(Debug, Clone)]
631pub enum StringLiteralKeyKind {
632 SingleString(tsz_common::interner::Atom),
634 Union(Vec<TypeId>),
636 NotStringLiteral,
638}
639
640pub fn classify_for_string_literal_keys(
642 db: &dyn TypeDatabase,
643 type_id: TypeId,
644) -> StringLiteralKeyKind {
645 let Some(key) = db.lookup(type_id) else {
646 return StringLiteralKeyKind::NotStringLiteral;
647 };
648
649 match key {
650 TypeData::Literal(crate::types::LiteralValue::String(name)) => {
651 StringLiteralKeyKind::SingleString(name)
652 }
653 TypeData::Union(list_id) => {
654 let members = db.type_list(list_id);
655 StringLiteralKeyKind::Union(members.to_vec())
656 }
657 _ => StringLiteralKeyKind::NotStringLiteral,
658 }
659}
660
661pub fn get_string_literal_value(
664 db: &dyn TypeDatabase,
665 type_id: TypeId,
666) -> Option<tsz_common::interner::Atom> {
667 match db.lookup(type_id) {
668 Some(TypeData::Literal(crate::types::LiteralValue::String(name))) => Some(name),
669 _ => None,
670 }
671}
672
673pub fn get_literal_property_name(
675 db: &dyn TypeDatabase,
676 type_id: TypeId,
677) -> Option<tsz_common::interner::Atom> {
678 match db.lookup(type_id) {
679 Some(TypeData::Literal(crate::types::LiteralValue::String(name))) => Some(name),
680 Some(TypeData::Literal(crate::types::LiteralValue::Number(num))) => {
681 let s = format!("{}", num.0);
683 Some(db.intern_string(&s))
684 }
685 Some(TypeData::UniqueSymbol(sym)) => {
686 let s = format!("__unique_{}", sym.0);
687 Some(db.intern_string(&s))
688 }
689 Some(TypeData::Enum(_, member_type)) => get_literal_property_name(db, member_type),
690 _ => None,
691 }
692}
693
694#[derive(Debug, Clone)]
700pub enum ClassDeclTypeKind {
701 Object(crate::types::ObjectShapeId),
703 Members(Vec<TypeId>),
705 NotObject,
707}
708
709pub fn classify_for_class_decl(db: &dyn TypeDatabase, type_id: TypeId) -> ClassDeclTypeKind {
711 let Some(key) = db.lookup(type_id) else {
712 return ClassDeclTypeKind::NotObject;
713 };
714
715 match key {
716 TypeData::Object(shape_id) | TypeData::ObjectWithIndex(shape_id) => {
717 ClassDeclTypeKind::Object(shape_id)
718 }
719 TypeData::Union(list_id) | TypeData::Intersection(list_id) => {
720 let members = db.type_list(list_id);
721 ClassDeclTypeKind::Members(members.to_vec())
722 }
723 _ => ClassDeclTypeKind::NotObject,
724 }
725}
726
727#[derive(Debug, Clone)]
733pub enum CallSignaturesKind {
734 Callable(crate::types::CallableShapeId),
736 MultipleSignatures(Vec<crate::CallSignature>),
738 NoSignatures,
740}
741
742pub fn classify_for_call_signatures(db: &dyn TypeDatabase, type_id: TypeId) -> CallSignaturesKind {
744 let Some(key) = db.lookup(type_id) else {
745 return CallSignaturesKind::NoSignatures;
746 };
747
748 match key {
749 TypeData::Callable(shape_id) => CallSignaturesKind::Callable(shape_id),
750 TypeData::Union(list_id) => {
751 let members = db.type_list(list_id);
753 let mut call_signatures = Vec::new();
754
755 for &member in members.iter() {
756 match db.lookup(member) {
757 Some(TypeData::Callable(shape_id)) => {
758 let shape = db.callable_shape(shape_id);
759 call_signatures.extend(shape.call_signatures.iter().cloned());
761 }
762 _ => continue,
763 }
764 }
765
766 if call_signatures.is_empty() {
767 CallSignaturesKind::NoSignatures
768 } else {
769 CallSignaturesKind::MultipleSignatures(call_signatures)
770 }
771 }
772 _ => CallSignaturesKind::NoSignatures,
773 }
774}
775
776pub fn get_application_info(
783 db: &dyn TypeDatabase,
784 type_id: TypeId,
785) -> Option<(TypeId, Vec<TypeId>)> {
786 match db.lookup(type_id) {
787 Some(TypeData::Application(app_id)) => {
788 let app = db.type_application(app_id);
789 Some((app.base, app.args.clone()))
790 }
791 _ => None,
792 }
793}
794
795#[derive(Debug, Clone)]
801pub enum TypeParameterContentKind {
802 IsTypeParameter,
804 Array(TypeId),
806 Tuple(crate::types::TupleListId),
808 Union(Vec<TypeId>),
810 Intersection(Vec<TypeId>),
812 Application { base: TypeId, args: Vec<TypeId> },
814 NotTypeParameter,
816}
817
818pub fn classify_for_type_parameter_content(
820 db: &dyn TypeDatabase,
821 type_id: TypeId,
822) -> TypeParameterContentKind {
823 let Some(key) = db.lookup(type_id) else {
824 return TypeParameterContentKind::NotTypeParameter;
825 };
826
827 match key {
828 TypeData::TypeParameter(_) | TypeData::Infer(_) => {
829 TypeParameterContentKind::IsTypeParameter
830 }
831 TypeData::Array(elem) => TypeParameterContentKind::Array(elem),
832 TypeData::Tuple(list_id) => TypeParameterContentKind::Tuple(list_id),
833 TypeData::Union(list_id) => {
834 let members = db.type_list(list_id);
835 TypeParameterContentKind::Union(members.to_vec())
836 }
837 TypeData::Intersection(list_id) => {
838 let members = db.type_list(list_id);
839 TypeParameterContentKind::Intersection(members.to_vec())
840 }
841 TypeData::Application(app_id) => {
842 let app = db.type_application(app_id);
843 TypeParameterContentKind::Application {
844 base: app.base,
845 args: app.args.clone(),
846 }
847 }
848 _ => TypeParameterContentKind::NotTypeParameter,
849 }
850}
851
852#[derive(Debug, Clone)]
858pub enum TypeDepthKind {
859 Array(TypeId),
861 Tuple(crate::types::TupleListId),
863 Members(Vec<TypeId>),
865 Application { base: TypeId, args: Vec<TypeId> },
867 Terminal,
869}
870
871pub fn classify_for_type_depth(db: &dyn TypeDatabase, type_id: TypeId) -> TypeDepthKind {
873 let Some(key) = db.lookup(type_id) else {
874 return TypeDepthKind::Terminal;
875 };
876
877 match key {
878 TypeData::Array(elem) => TypeDepthKind::Array(elem),
879 TypeData::Tuple(list_id) => TypeDepthKind::Tuple(list_id),
880 TypeData::Union(list_id) | TypeData::Intersection(list_id) => {
881 let members = db.type_list(list_id);
882 TypeDepthKind::Members(members.to_vec())
883 }
884 TypeData::Application(app_id) => {
885 let app = db.type_application(app_id);
886 TypeDepthKind::Application {
887 base: app.base,
888 args: app.args.clone(),
889 }
890 }
891 _ => TypeDepthKind::Terminal,
892 }
893}
894
895#[derive(Debug, Clone)]
901pub enum SpreadPropertyKind {
902 Object(crate::types::ObjectShapeId),
904 Callable(crate::types::CallableShapeId),
906 Intersection(Vec<TypeId>),
908 NoProperties,
910}
911
912pub fn classify_for_spread_properties(
914 db: &dyn TypeDatabase,
915 type_id: TypeId,
916) -> SpreadPropertyKind {
917 let Some(key) = db.lookup(type_id) else {
918 return SpreadPropertyKind::NoProperties;
919 };
920
921 match key {
922 TypeData::Object(shape_id) | TypeData::ObjectWithIndex(shape_id) => {
923 SpreadPropertyKind::Object(shape_id)
924 }
925 TypeData::Callable(shape_id) => SpreadPropertyKind::Callable(shape_id),
926 TypeData::Intersection(list_id) => {
927 let members = db.type_list(list_id);
928 SpreadPropertyKind::Intersection(members.to_vec())
929 }
930 _ => SpreadPropertyKind::NoProperties,
931 }
932}
933
934#[derive(Debug, Clone)]
940pub enum LazyTypeKind {
941 Lazy(crate::def::DefId),
943 NotLazy,
945}
946
947pub fn classify_for_lazy_resolution(db: &dyn TypeDatabase, type_id: TypeId) -> LazyTypeKind {
949 let Some(key) = db.lookup(type_id) else {
950 return LazyTypeKind::NotLazy;
951 };
952
953 match key {
954 TypeData::Lazy(def_id) => LazyTypeKind::Lazy(def_id),
955 _ => LazyTypeKind::NotLazy,
956 }
957}
958
959#[derive(Debug, Clone)]
965pub enum ConstructorCheckKind {
966 TypeParameter { constraint: Option<TypeId> },
968 Intersection(Vec<TypeId>),
970 Union(Vec<TypeId>),
972 Application { base: TypeId },
974 Lazy(crate::def::DefId),
976 TypeQuery(crate::types::SymbolRef),
978 Other,
980}
981
982pub fn classify_for_constructor_check(
984 db: &dyn TypeDatabase,
985 type_id: TypeId,
986) -> ConstructorCheckKind {
987 let Some(key) = db.lookup(type_id) else {
988 return ConstructorCheckKind::Other;
989 };
990
991 match key {
992 TypeData::TypeParameter(info) | TypeData::Infer(info) => {
993 ConstructorCheckKind::TypeParameter {
994 constraint: info.constraint,
995 }
996 }
997 TypeData::Intersection(members_id) => {
998 let members = db.type_list(members_id);
999 ConstructorCheckKind::Intersection(members.to_vec())
1000 }
1001 TypeData::Union(members_id) => {
1002 let members = db.type_list(members_id);
1003 ConstructorCheckKind::Union(members.to_vec())
1004 }
1005 TypeData::Application(app_id) => {
1006 let app = db.type_application(app_id);
1007 ConstructorCheckKind::Application { base: app.base }
1008 }
1009 TypeData::Lazy(def_id) => ConstructorCheckKind::Lazy(def_id),
1010 TypeData::TypeQuery(sym_ref) => ConstructorCheckKind::TypeQuery(sym_ref),
1011 _ => ConstructorCheckKind::Other,
1012 }
1013}
1014
1015pub fn is_narrowable_type_key(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
1017 matches!(
1022 db.lookup(type_id),
1023 Some(
1024 TypeData::Union(_)
1025 | TypeData::TypeParameter(_)
1026 | TypeData::Infer(_)
1027 | TypeData::Lazy(_)
1028 )
1029 )
1030}
1031
1032#[derive(Debug, Clone)]
1038pub enum PrivateBrandKind {
1039 Object(crate::types::ObjectShapeId),
1041 Callable(crate::types::CallableShapeId),
1043 None,
1045}
1046
1047pub fn classify_for_private_brand(db: &dyn TypeDatabase, type_id: TypeId) -> PrivateBrandKind {
1049 match db.lookup(type_id) {
1050 Some(TypeData::Object(shape_id) | TypeData::ObjectWithIndex(shape_id)) => {
1051 PrivateBrandKind::Object(shape_id)
1052 }
1053 Some(TypeData::Callable(shape_id)) => PrivateBrandKind::Callable(shape_id),
1054 _ => PrivateBrandKind::None,
1055 }
1056}
1057
1058pub fn get_widened_literal_type(db: &dyn TypeDatabase, type_id: TypeId) -> Option<TypeId> {
1060 match db.lookup(type_id) {
1061 Some(TypeData::Literal(crate::LiteralValue::String(_))) => Some(TypeId::STRING),
1062 Some(TypeData::Literal(crate::LiteralValue::Number(_))) => Some(TypeId::NUMBER),
1063 Some(TypeData::Literal(crate::LiteralValue::BigInt(_))) => Some(TypeId::BIGINT),
1064 Some(TypeData::Literal(crate::LiteralValue::Boolean(_))) => Some(TypeId::BOOLEAN),
1065 _ => None,
1066 }
1067}
1068
1069pub fn get_tuple_list_id(
1071 db: &dyn TypeDatabase,
1072 type_id: TypeId,
1073) -> Option<crate::types::TupleListId> {
1074 match db.lookup(type_id) {
1075 Some(TypeData::Tuple(list_id)) => Some(list_id),
1076 _ => None,
1077 }
1078}
1079
1080pub fn get_application_base(db: &dyn TypeDatabase, type_id: TypeId) -> Option<TypeId> {
1082 match db.lookup(type_id) {
1083 Some(TypeData::Application(app_id)) => Some(db.type_application(app_id).base),
1084 _ => None,
1085 }
1086}
1087
1088#[derive(Debug, Clone)]
1094pub enum LiteralKeyKind {
1095 StringLiteral(tsz_common::interner::Atom),
1096 NumberLiteral(f64),
1097 Union(Vec<TypeId>),
1098 Other,
1099}
1100
1101pub fn classify_literal_key(db: &dyn TypeDatabase, type_id: TypeId) -> LiteralKeyKind {
1103 match db.lookup(type_id) {
1104 Some(TypeData::Literal(crate::LiteralValue::String(atom))) => {
1105 LiteralKeyKind::StringLiteral(atom)
1106 }
1107 Some(TypeData::Literal(crate::LiteralValue::Number(num))) => {
1108 LiteralKeyKind::NumberLiteral(num.0)
1109 }
1110 Some(TypeData::Union(members_id)) => {
1111 LiteralKeyKind::Union(db.type_list(members_id).to_vec())
1112 }
1113 _ => LiteralKeyKind::Other,
1114 }
1115}
1116
1117pub fn get_literal_value(db: &dyn TypeDatabase, type_id: TypeId) -> Option<crate::LiteralValue> {
1119 match db.lookup(type_id) {
1120 Some(TypeData::Literal(value)) => Some(value),
1121 _ => None,
1122 }
1123}
1124
1125pub fn widen_literal_to_primitive(db: &dyn TypeDatabase, type_id: TypeId) -> TypeId {
1134 match db.lookup(type_id) {
1135 Some(TypeData::Literal(ref lit)) => match lit {
1136 crate::LiteralValue::String(_) => TypeId::STRING,
1137 crate::LiteralValue::Number(_) => TypeId::NUMBER,
1138 crate::LiteralValue::Boolean(_) => TypeId::BOOLEAN,
1139 crate::LiteralValue::BigInt(_) => TypeId::BIGINT,
1140 },
1141 _ => type_id,
1142 }
1143}
1144
1145pub fn get_object_property_type(
1149 db: &dyn TypeDatabase,
1150 type_id: TypeId,
1151 property_name: &str,
1152) -> Option<TypeId> {
1153 match db.lookup(type_id) {
1154 Some(TypeData::Object(shape_id) | TypeData::ObjectWithIndex(shape_id)) => {
1155 let shape = db.object_shape(shape_id);
1156 for prop in &shape.properties {
1157 let prop_name = db.resolve_atom_ref(prop.name);
1158 if prop_name.as_ref() == property_name {
1159 return Some(prop.type_id);
1160 }
1161 }
1162 None
1163 }
1164 _ => None,
1165 }
1166}
1167
1168pub fn get_function_return_type(db: &dyn TypeDatabase, type_id: TypeId) -> Option<TypeId> {
1172 match db.lookup(type_id) {
1173 Some(TypeData::Function(shape_id)) => {
1174 let shape = db.function_shape(shape_id);
1175 Some(shape.return_type)
1176 }
1177 _ => None,
1178 }
1179}
1180
1181pub fn is_object_with_index_type(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
1185 matches!(db.lookup(type_id), Some(TypeData::ObjectWithIndex(_)))
1186}
1187
1188#[derive(Debug, Clone)]
1194pub enum ArrayLikeKind {
1195 Array(TypeId),
1196 Tuple,
1197 Readonly(TypeId),
1198 Union(Vec<TypeId>),
1199 Intersection(Vec<TypeId>),
1200 Other,
1201}
1202
1203pub fn classify_array_like(db: &dyn TypeDatabase, type_id: TypeId) -> ArrayLikeKind {
1205 match db.lookup(type_id) {
1206 Some(TypeData::Array(elem)) => ArrayLikeKind::Array(elem),
1207 Some(TypeData::Tuple(_)) => ArrayLikeKind::Tuple,
1208 Some(TypeData::ReadonlyType(inner)) => ArrayLikeKind::Readonly(inner),
1209 Some(TypeData::TypeParameter(info) | TypeData::Infer(info)) => {
1210 info.constraint.map_or(ArrayLikeKind::Other, |constraint| {
1211 classify_array_like(db, constraint)
1212 })
1213 }
1214 Some(TypeData::Union(members_id)) => {
1215 ArrayLikeKind::Union(db.type_list(members_id).to_vec())
1216 }
1217 Some(TypeData::Intersection(members_id)) => {
1218 ArrayLikeKind::Intersection(db.type_list(members_id).to_vec())
1219 }
1220 _ => ArrayLikeKind::Other,
1221 }
1222}
1223
1224#[derive(Debug, Clone)]
1230pub enum IndexKeyKind {
1231 String,
1232 Number,
1233 StringLiteral,
1234 NumberLiteral,
1235 Union(Vec<TypeId>),
1236 Other,
1237}
1238
1239pub fn classify_index_key(db: &dyn TypeDatabase, type_id: TypeId) -> IndexKeyKind {
1241 match db.lookup(type_id) {
1242 Some(TypeData::Intrinsic(crate::IntrinsicKind::String)) => IndexKeyKind::String,
1243 Some(TypeData::Intrinsic(crate::IntrinsicKind::Number)) => IndexKeyKind::Number,
1244 Some(TypeData::Literal(crate::LiteralValue::String(_))) => IndexKeyKind::StringLiteral,
1245 Some(TypeData::Literal(crate::LiteralValue::Number(_))) => IndexKeyKind::NumberLiteral,
1246 Some(TypeData::Union(members_id)) => IndexKeyKind::Union(db.type_list(members_id).to_vec()),
1247 _ => IndexKeyKind::Other,
1248 }
1249}
1250
1251#[derive(Debug, Clone)]
1257pub enum ElementIndexableKind {
1258 Array,
1259 Tuple,
1260 ObjectWithIndex { has_string: bool, has_number: bool },
1261 Union(Vec<TypeId>),
1262 Intersection(Vec<TypeId>),
1263 StringLike,
1264 Other,
1265}
1266
1267pub fn classify_element_indexable(db: &dyn TypeDatabase, type_id: TypeId) -> ElementIndexableKind {
1269 match db.lookup(type_id) {
1270 Some(TypeData::Array(_)) => ElementIndexableKind::Array,
1271 Some(TypeData::Tuple(_)) => ElementIndexableKind::Tuple,
1272 Some(TypeData::ObjectWithIndex(shape_id)) => {
1273 let shape = db.object_shape(shape_id);
1274 ElementIndexableKind::ObjectWithIndex {
1275 has_string: shape.string_index.is_some(),
1276 has_number: shape.number_index.is_some(),
1277 }
1278 }
1279 Some(TypeData::Union(members_id)) => {
1280 ElementIndexableKind::Union(db.type_list(members_id).to_vec())
1281 }
1282 Some(TypeData::Intersection(members_id)) => {
1283 ElementIndexableKind::Intersection(db.type_list(members_id).to_vec())
1284 }
1285 Some(TypeData::Literal(crate::LiteralValue::String(_)))
1286 | Some(TypeData::Intrinsic(crate::IntrinsicKind::String)) => {
1287 ElementIndexableKind::StringLike
1288 }
1289 Some(TypeData::Enum(_, _)) => ElementIndexableKind::ObjectWithIndex {
1292 has_string: true,
1293 has_number: true,
1294 },
1295 _ => ElementIndexableKind::Other,
1296 }
1297}
1298
1299#[derive(Debug, Clone)]
1305pub enum TypeQueryKind {
1306 TypeQuery(crate::types::SymbolRef),
1307 ApplicationWithTypeQuery {
1308 base_sym_ref: crate::types::SymbolRef,
1309 args: Vec<TypeId>,
1310 },
1311 Application {
1312 app_id: crate::types::TypeApplicationId,
1313 },
1314 Other,
1315}
1316
1317pub fn classify_type_query(db: &dyn TypeDatabase, type_id: TypeId) -> TypeQueryKind {
1319 match db.lookup(type_id) {
1320 Some(TypeData::TypeQuery(sym_ref)) => TypeQueryKind::TypeQuery(sym_ref),
1321 Some(TypeData::Application(app_id)) => {
1322 let app = db.type_application(app_id);
1323 match db.lookup(app.base) {
1324 Some(TypeData::TypeQuery(base_sym_ref)) => {
1325 TypeQueryKind::ApplicationWithTypeQuery {
1326 base_sym_ref,
1327 args: app.args.clone(),
1328 }
1329 }
1330 _ => TypeQueryKind::Application { app_id },
1331 }
1332 }
1333 _ => TypeQueryKind::Other,
1334 }
1335}
1336
1337#[derive(Debug, Clone)]
1343pub enum SymbolRefKind {
1344 Lazy(crate::def::DefId),
1346 TypeQuery(crate::types::SymbolRef),
1347 Other,
1348}
1349
1350pub fn classify_symbol_ref(db: &dyn TypeDatabase, type_id: TypeId) -> SymbolRefKind {
1352 match db.lookup(type_id) {
1353 Some(TypeData::Lazy(def_id)) => SymbolRefKind::Lazy(def_id),
1354 Some(TypeData::TypeQuery(sym_ref)) => SymbolRefKind::TypeQuery(sym_ref),
1355 _ => SymbolRefKind::Other,
1356 }
1357}
1358
1359#[derive(Debug, Clone)]
1365pub enum TypeContainsKind {
1366 Array(TypeId),
1367 Tuple(crate::types::TupleListId),
1368 Members(Vec<TypeId>),
1369 Object(crate::types::ObjectShapeId),
1370 Function(crate::types::FunctionShapeId),
1371 Callable(crate::types::CallableShapeId),
1372 Application(crate::types::TypeApplicationId),
1373 Conditional(crate::types::ConditionalTypeId),
1374 Mapped(crate::types::MappedTypeId),
1375 IndexAccess {
1376 base: TypeId,
1377 index: TypeId,
1378 },
1379 TemplateLiteral(crate::types::TemplateLiteralId),
1380 Inner(TypeId),
1381 TypeParam {
1382 constraint: Option<TypeId>,
1383 default: Option<TypeId>,
1384 },
1385 Terminal,
1386}
1387
1388pub fn classify_for_contains_traversal(db: &dyn TypeDatabase, type_id: TypeId) -> TypeContainsKind {
1390 match db.lookup(type_id) {
1391 Some(TypeData::Array(elem)) => TypeContainsKind::Array(elem),
1392 Some(TypeData::Tuple(list_id)) => TypeContainsKind::Tuple(list_id),
1393 Some(TypeData::Union(list_id) | TypeData::Intersection(list_id)) => {
1394 TypeContainsKind::Members(db.type_list(list_id).to_vec())
1395 }
1396 Some(TypeData::Object(shape_id) | TypeData::ObjectWithIndex(shape_id)) => {
1397 TypeContainsKind::Object(shape_id)
1398 }
1399 Some(TypeData::Function(shape_id)) => TypeContainsKind::Function(shape_id),
1400 Some(TypeData::Callable(shape_id)) => TypeContainsKind::Callable(shape_id),
1401 Some(TypeData::Application(app_id)) => TypeContainsKind::Application(app_id),
1402 Some(TypeData::Conditional(cond_id)) => TypeContainsKind::Conditional(cond_id),
1403 Some(TypeData::Mapped(mapped_id)) => TypeContainsKind::Mapped(mapped_id),
1404 Some(TypeData::IndexAccess(base, index)) => TypeContainsKind::IndexAccess { base, index },
1405 Some(TypeData::TemplateLiteral(template_id)) => {
1406 TypeContainsKind::TemplateLiteral(template_id)
1407 }
1408 Some(TypeData::KeyOf(inner) | TypeData::ReadonlyType(inner)) => {
1409 TypeContainsKind::Inner(inner)
1410 }
1411 Some(TypeData::TypeParameter(info) | TypeData::Infer(info)) => {
1412 TypeContainsKind::TypeParam {
1413 constraint: info.constraint,
1414 default: info.default,
1415 }
1416 }
1417 _ => TypeContainsKind::Terminal,
1418 }
1419}
1420
1421#[derive(Debug, Clone)]
1427pub enum NamespaceMemberKind {
1428 Lazy(DefId),
1429 ModuleNamespace(crate::types::SymbolRef),
1430 Callable(crate::types::CallableShapeId),
1431 Enum(DefId),
1433 Other,
1434}
1435
1436pub fn classify_namespace_member(db: &dyn TypeDatabase, type_id: TypeId) -> NamespaceMemberKind {
1438 match db.lookup(type_id) {
1439 Some(TypeData::Callable(shape_id)) => NamespaceMemberKind::Callable(shape_id),
1440 Some(TypeData::Lazy(def_id)) => NamespaceMemberKind::Lazy(def_id),
1441 Some(TypeData::ModuleNamespace(sym_ref)) => NamespaceMemberKind::ModuleNamespace(sym_ref),
1442 Some(TypeData::Enum(def_id, _)) => NamespaceMemberKind::Enum(def_id),
1444 _ => NamespaceMemberKind::Other,
1445 }
1446}
1447
1448pub fn unwrap_readonly_for_lookup(db: &dyn TypeDatabase, type_id: TypeId) -> TypeId {
1450 match db.lookup(type_id) {
1451 Some(TypeData::ReadonlyType(inner)) => inner,
1452 _ => type_id,
1453 }
1454}
1455
1456pub fn create_string_literal_type(db: &dyn TypeDatabase, value: &str) -> TypeId {
1464 let atom = db.intern_string(value);
1465 db.literal_string_atom(atom)
1466}
1467
1468pub fn create_number_literal_type(db: &dyn TypeDatabase, value: f64) -> TypeId {
1472 db.literal_number(value)
1473}
1474
1475pub fn create_boolean_literal_type(db: &dyn TypeDatabase, value: bool) -> TypeId {
1479 db.literal_boolean(value)
1480}
1481
1482#[derive(Debug, Clone)]
1488pub enum InstanceTypeKind {
1489 Callable(crate::types::CallableShapeId),
1491 Function(crate::types::FunctionShapeId),
1493 Intersection(Vec<TypeId>),
1495 Union(Vec<TypeId>),
1497 Readonly(TypeId),
1499 TypeParameter { constraint: Option<TypeId> },
1501 SymbolRef(crate::types::SymbolRef),
1503 NeedsEvaluation,
1505 NotConstructor,
1507}
1508
1509pub fn classify_for_instance_type(db: &dyn TypeDatabase, type_id: TypeId) -> InstanceTypeKind {
1511 let Some(key) = db.lookup(type_id) else {
1512 return InstanceTypeKind::NotConstructor;
1513 };
1514
1515 match key {
1516 TypeData::Callable(shape_id) => InstanceTypeKind::Callable(shape_id),
1517 TypeData::Function(shape_id) => InstanceTypeKind::Function(shape_id),
1518 TypeData::Intersection(list_id) => {
1519 let members = db.type_list(list_id);
1520 InstanceTypeKind::Intersection(members.to_vec())
1521 }
1522 TypeData::Union(list_id) => {
1523 let members = db.type_list(list_id);
1524 InstanceTypeKind::Union(members.to_vec())
1525 }
1526 TypeData::ReadonlyType(inner) => InstanceTypeKind::Readonly(inner),
1527 TypeData::TypeParameter(info) | TypeData::Infer(info) => InstanceTypeKind::TypeParameter {
1528 constraint: info.constraint,
1529 },
1530 TypeData::TypeQuery(sym_ref) => InstanceTypeKind::SymbolRef(sym_ref),
1532 TypeData::Conditional(_)
1533 | TypeData::Mapped(_)
1534 | TypeData::IndexAccess(_, _)
1535 | TypeData::KeyOf(_)
1536 | TypeData::Application(_) => InstanceTypeKind::NeedsEvaluation,
1537 _ => InstanceTypeKind::NotConstructor,
1538 }
1539}
1540
1541#[derive(Debug, Clone)]
1547pub enum ConstructorReturnMergeKind {
1548 Callable(crate::types::CallableShapeId),
1550 Function(crate::types::FunctionShapeId),
1552 Intersection(Vec<TypeId>),
1554 Other,
1556}
1557
1558pub fn classify_for_constructor_return_merge(
1560 db: &dyn TypeDatabase,
1561 type_id: TypeId,
1562) -> ConstructorReturnMergeKind {
1563 let Some(key) = db.lookup(type_id) else {
1564 return ConstructorReturnMergeKind::Other;
1565 };
1566
1567 match key {
1568 TypeData::Callable(shape_id) => ConstructorReturnMergeKind::Callable(shape_id),
1569 TypeData::Function(shape_id) => ConstructorReturnMergeKind::Function(shape_id),
1570 TypeData::Intersection(list_id) => {
1571 let members = db.type_list(list_id);
1572 ConstructorReturnMergeKind::Intersection(members.to_vec())
1573 }
1574 _ => ConstructorReturnMergeKind::Other,
1575 }
1576}
1577
1578#[derive(Debug, Clone)]
1584pub enum AbstractConstructorKind {
1585 TypeQuery(crate::types::SymbolRef),
1587 Callable(crate::types::CallableShapeId),
1589 Application(crate::types::TypeApplicationId),
1591 NotAbstract,
1593}
1594
1595#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1597pub enum AbstractConstructorAnchor {
1598 TypeQuery(crate::types::SymbolRef),
1600 CallableType(TypeId),
1602 NotAbstract,
1604}
1605
1606pub fn classify_for_abstract_constructor(
1608 db: &dyn TypeDatabase,
1609 type_id: TypeId,
1610) -> AbstractConstructorKind {
1611 let Some(key) = db.lookup(type_id) else {
1612 return AbstractConstructorKind::NotAbstract;
1613 };
1614
1615 match key {
1616 TypeData::TypeQuery(sym_ref) => AbstractConstructorKind::TypeQuery(sym_ref),
1617 TypeData::Callable(shape_id) => AbstractConstructorKind::Callable(shape_id),
1618 TypeData::Application(app_id) => AbstractConstructorKind::Application(app_id),
1619 _ => AbstractConstructorKind::NotAbstract,
1620 }
1621}
1622
1623pub fn resolve_abstract_constructor_anchor(
1628 db: &dyn TypeDatabase,
1629 type_id: TypeId,
1630) -> AbstractConstructorAnchor {
1631 let mut current = type_id;
1632 let mut visited = FxHashSet::default();
1633
1634 while visited.insert(current) {
1635 match classify_for_abstract_constructor(db, current) {
1636 AbstractConstructorKind::TypeQuery(sym_ref) => {
1637 return AbstractConstructorAnchor::TypeQuery(sym_ref);
1638 }
1639 AbstractConstructorKind::Callable(_) => {
1640 return AbstractConstructorAnchor::CallableType(current);
1641 }
1642 AbstractConstructorKind::Application(app_id) => {
1643 let app = db.type_application(app_id);
1644 if app.base == current {
1645 break;
1646 }
1647 current = app.base;
1648 }
1649 AbstractConstructorKind::NotAbstract => break,
1650 }
1651 }
1652
1653 AbstractConstructorAnchor::NotAbstract
1654}
1655
1656#[derive(Debug, Clone)]
1662pub enum PropertyAccessResolutionKind {
1663 Lazy(DefId),
1665 TypeQuery(crate::types::SymbolRef),
1667 Application(crate::types::TypeApplicationId),
1669 TypeParameter { constraint: Option<TypeId> },
1671 NeedsEvaluation,
1673 Union(Vec<TypeId>),
1675 Intersection(Vec<TypeId>),
1677 Readonly(TypeId),
1679 FunctionLike,
1681 Resolved,
1683}
1684
1685pub fn classify_for_property_access_resolution(
1687 db: &dyn TypeDatabase,
1688 type_id: TypeId,
1689) -> PropertyAccessResolutionKind {
1690 let Some(key) = db.lookup(type_id) else {
1691 return PropertyAccessResolutionKind::Resolved;
1692 };
1693
1694 match key {
1695 TypeData::TypeQuery(sym_ref) => PropertyAccessResolutionKind::TypeQuery(sym_ref),
1696 TypeData::Lazy(def_id) => PropertyAccessResolutionKind::Lazy(def_id),
1697 TypeData::Application(app_id) => PropertyAccessResolutionKind::Application(app_id),
1698 TypeData::TypeParameter(info) | TypeData::Infer(info) => {
1699 PropertyAccessResolutionKind::TypeParameter {
1700 constraint: info.constraint,
1701 }
1702 }
1703 TypeData::Conditional(_)
1704 | TypeData::Mapped(_)
1705 | TypeData::IndexAccess(_, _)
1706 | TypeData::KeyOf(_) => PropertyAccessResolutionKind::NeedsEvaluation,
1707 TypeData::Union(list_id) => {
1708 let members = db.type_list(list_id);
1709 PropertyAccessResolutionKind::Union(members.to_vec())
1710 }
1711 TypeData::Intersection(list_id) => {
1712 let members = db.type_list(list_id);
1713 PropertyAccessResolutionKind::Intersection(members.to_vec())
1714 }
1715 TypeData::ReadonlyType(inner) => PropertyAccessResolutionKind::Readonly(inner),
1716 TypeData::Function(_) | TypeData::Callable(_) => PropertyAccessResolutionKind::FunctionLike,
1717 _ => PropertyAccessResolutionKind::Resolved,
1718 }
1719}
1720
1721#[derive(Debug, Clone)]
1727pub enum ContextualLiteralAllowKind {
1728 Members(Vec<TypeId>),
1730 TypeParameter { constraint: Option<TypeId> },
1732 Application,
1734 Mapped,
1736 TemplateLiteral,
1740 NotAllowed,
1742}
1743
1744pub fn classify_for_contextual_literal(
1746 db: &dyn TypeDatabase,
1747 type_id: TypeId,
1748) -> ContextualLiteralAllowKind {
1749 let Some(key) = db.lookup(type_id) else {
1750 return ContextualLiteralAllowKind::NotAllowed;
1751 };
1752
1753 match key {
1754 TypeData::Union(list_id) | TypeData::Intersection(list_id) => {
1755 let members = db.type_list(list_id);
1756 ContextualLiteralAllowKind::Members(members.to_vec())
1757 }
1758 TypeData::TypeParameter(info) | TypeData::Infer(info) => {
1759 ContextualLiteralAllowKind::TypeParameter {
1760 constraint: info.constraint,
1761 }
1762 }
1763 TypeData::Application(_) => ContextualLiteralAllowKind::Application,
1764 TypeData::Mapped(_) => ContextualLiteralAllowKind::Mapped,
1765 TypeData::TemplateLiteral(_) => ContextualLiteralAllowKind::TemplateLiteral,
1766 _ => ContextualLiteralAllowKind::NotAllowed,
1767 }
1768}
1769
1770#[derive(Debug, Clone)]
1776pub enum MappedConstraintKind {
1777 KeyOf(TypeId),
1779 Resolved,
1781 Other,
1783}
1784
1785pub fn classify_mapped_constraint(db: &dyn TypeDatabase, type_id: TypeId) -> MappedConstraintKind {
1787 let Some(key) = db.lookup(type_id) else {
1788 return MappedConstraintKind::Other;
1789 };
1790
1791 match key {
1792 TypeData::KeyOf(operand) => MappedConstraintKind::KeyOf(operand),
1793 TypeData::Union(_) | TypeData::Literal(_) => MappedConstraintKind::Resolved,
1794 _ => MappedConstraintKind::Other,
1795 }
1796}
1797
1798#[derive(Debug, Clone)]
1804pub enum TypeResolutionKind {
1805 Lazy(DefId),
1807 Application,
1809 Resolved,
1811}
1812
1813pub fn classify_for_type_resolution(db: &dyn TypeDatabase, type_id: TypeId) -> TypeResolutionKind {
1815 let Some(key) = db.lookup(type_id) else {
1816 return TypeResolutionKind::Resolved;
1817 };
1818
1819 match key {
1820 TypeData::Lazy(def_id) => TypeResolutionKind::Lazy(def_id),
1821 TypeData::Application(_) => TypeResolutionKind::Application,
1822 _ => TypeResolutionKind::Resolved,
1823 }
1824}
1825
1826#[derive(Debug, Clone)]
1832pub enum TypeArgumentExtractionKind {
1833 Function(crate::types::FunctionShapeId),
1835 Callable(crate::types::CallableShapeId),
1837 Other,
1839}
1840
1841pub fn classify_for_type_argument_extraction(
1843 db: &dyn TypeDatabase,
1844 type_id: TypeId,
1845) -> TypeArgumentExtractionKind {
1846 let Some(key) = db.lookup(type_id) else {
1847 return TypeArgumentExtractionKind::Other;
1848 };
1849
1850 match key {
1851 TypeData::Function(shape_id) => TypeArgumentExtractionKind::Function(shape_id),
1852 TypeData::Callable(shape_id) => TypeArgumentExtractionKind::Callable(shape_id),
1853 _ => TypeArgumentExtractionKind::Other,
1854 }
1855}
1856
1857#[derive(Debug, Clone)]
1863pub enum BaseInstanceMergeKind {
1864 Object(crate::types::ObjectShapeId),
1866 Intersection(Vec<TypeId>),
1868 Union(Vec<TypeId>),
1870 Other,
1872}
1873
1874pub fn classify_for_base_instance_merge(
1876 db: &dyn TypeDatabase,
1877 type_id: TypeId,
1878) -> BaseInstanceMergeKind {
1879 let Some(key) = db.lookup(type_id) else {
1880 return BaseInstanceMergeKind::Other;
1881 };
1882
1883 match key {
1884 TypeData::Object(shape_id) | TypeData::ObjectWithIndex(shape_id) => {
1885 BaseInstanceMergeKind::Object(shape_id)
1886 }
1887 TypeData::Intersection(list_id) => {
1888 let members = db.type_list(list_id);
1889 BaseInstanceMergeKind::Intersection(members.to_vec())
1890 }
1891 TypeData::Union(list_id) => {
1892 let members = db.type_list(list_id);
1893 BaseInstanceMergeKind::Union(members.to_vec())
1894 }
1895 _ => BaseInstanceMergeKind::Other,
1896 }
1897}