Skip to main content

tsz_solver/
visitor_extract.rs

1//! Type data extraction helpers.
2//!
3//! Contains convenience functions for extracting specific data from `TypeData` variants
4//! using the visitor pattern. Each function takes a `TypeDatabase` and `TypeId` and returns
5//! the relevant data if the type matches the expected variant.
6
7use crate::def::DefId;
8use crate::types::{
9    CallableShapeId, ConditionalTypeId, FunctionShapeId, IntrinsicKind, LiteralValue, MappedTypeId,
10    ObjectShapeId, OrderedFloat, StringIntrinsicKind, TemplateLiteralId, TupleListId,
11    TypeApplicationId, TypeListId, TypeParamInfo,
12};
13use crate::visitor::TypeVisitor;
14use crate::{SymbolRef, TypeData, TypeDatabase, TypeId};
15use tsz_common::interner::Atom;
16
17struct TypeDataDataVisitor<F, T>
18where
19    F: Fn(&TypeData) -> Option<T>,
20{
21    extractor: F,
22}
23
24impl<F, T> TypeDataDataVisitor<F, T>
25where
26    F: Fn(&TypeData) -> Option<T>,
27{
28    const fn new(extractor: F) -> Self {
29        Self { extractor }
30    }
31}
32
33impl<F, T> TypeVisitor for TypeDataDataVisitor<F, T>
34where
35    F: Fn(&TypeData) -> Option<T>,
36{
37    type Output = Option<T>;
38
39    fn visit_type_key(&mut self, _types: &dyn TypeDatabase, type_key: &TypeData) -> Self::Output {
40        (self.extractor)(type_key)
41    }
42
43    fn visit_intrinsic(&mut self, _kind: IntrinsicKind) -> Self::Output {
44        Self::default_output()
45    }
46
47    fn visit_literal(&mut self, _value: &LiteralValue) -> Self::Output {
48        Self::default_output()
49    }
50
51    fn default_output() -> Self::Output {
52        None
53    }
54}
55
56fn extract_type_data<T, F>(types: &dyn TypeDatabase, type_id: TypeId, extractor: F) -> Option<T>
57where
58    F: Fn(&TypeData) -> Option<T>,
59{
60    let mut visitor = TypeDataDataVisitor::new(extractor);
61    visitor.visit_type(types, type_id)
62}
63
64/// Extract the union list id if this is a union type.
65pub fn union_list_id(types: &dyn TypeDatabase, type_id: TypeId) -> Option<TypeListId> {
66    extract_type_data(types, type_id, |key| match key {
67        TypeData::Union(list_id) => Some(*list_id),
68        _ => None,
69    })
70}
71
72/// Extract the intersection list id if this is an intersection type.
73pub fn intersection_list_id(types: &dyn TypeDatabase, type_id: TypeId) -> Option<TypeListId> {
74    extract_type_data(types, type_id, |key| match key {
75        TypeData::Intersection(list_id) => Some(*list_id),
76        _ => None,
77    })
78}
79
80/// Extract the object shape id if this is an object type.
81pub fn object_shape_id(types: &dyn TypeDatabase, type_id: TypeId) -> Option<ObjectShapeId> {
82    extract_type_data(types, type_id, |key| match key {
83        TypeData::Object(shape_id) => Some(*shape_id),
84        _ => None,
85    })
86}
87
88/// Extract the object-with-index shape id if this is an indexed object type.
89pub fn object_with_index_shape_id(
90    types: &dyn TypeDatabase,
91    type_id: TypeId,
92) -> Option<ObjectShapeId> {
93    extract_type_data(types, type_id, |key| match key {
94        TypeData::ObjectWithIndex(shape_id) => Some(*shape_id),
95        _ => None,
96    })
97}
98
99/// Extract the array element type if this is an array type.
100pub fn array_element_type(types: &dyn TypeDatabase, type_id: TypeId) -> Option<TypeId> {
101    extract_type_data(types, type_id, |key| match key {
102        TypeData::Array(element) => Some(*element),
103        _ => None,
104    })
105}
106
107/// Extract the tuple list id if this is a tuple type.
108pub fn tuple_list_id(types: &dyn TypeDatabase, type_id: TypeId) -> Option<TupleListId> {
109    extract_type_data(types, type_id, |key| match key {
110        TypeData::Tuple(list_id) => Some(*list_id),
111        _ => None,
112    })
113}
114
115/// Extract the intrinsic kind if this is an intrinsic type.
116pub fn intrinsic_kind(types: &dyn TypeDatabase, type_id: TypeId) -> Option<IntrinsicKind> {
117    extract_type_data(types, type_id, |key| match key {
118        TypeData::Intrinsic(kind) => Some(*kind),
119        _ => None,
120    })
121}
122
123/// Extract the literal value if this is a literal type.
124pub fn literal_value(types: &dyn TypeDatabase, type_id: TypeId) -> Option<LiteralValue> {
125    extract_type_data(types, type_id, |key| match key {
126        TypeData::Literal(value) => Some(value.clone()),
127        _ => None,
128    })
129}
130
131/// Extract the string literal atom if this is a string literal type.
132pub fn literal_string(types: &dyn TypeDatabase, type_id: TypeId) -> Option<Atom> {
133    match literal_value(types, type_id) {
134        Some(LiteralValue::String(atom)) => Some(atom),
135        _ => None,
136    }
137}
138
139/// Extract the numeric literal if this is a number literal type.
140pub fn literal_number(types: &dyn TypeDatabase, type_id: TypeId) -> Option<OrderedFloat> {
141    match literal_value(types, type_id) {
142        Some(LiteralValue::Number(value)) => Some(value),
143        _ => None,
144    }
145}
146
147/// Extract the template literal list id if this is a template literal type.
148pub fn template_literal_id(types: &dyn TypeDatabase, type_id: TypeId) -> Option<TemplateLiteralId> {
149    extract_type_data(types, type_id, |key| match key {
150        TypeData::TemplateLiteral(list_id) => Some(*list_id),
151        _ => None,
152    })
153}
154
155/// Extract the type parameter info if this is a type parameter or infer type.
156pub fn type_param_info(types: &dyn TypeDatabase, type_id: TypeId) -> Option<TypeParamInfo> {
157    extract_type_data(types, type_id, |key| match key {
158        TypeData::TypeParameter(info) | TypeData::Infer(info) => Some(info.clone()),
159        _ => None,
160    })
161}
162
163/// Extract the type reference symbol if this is a Ref type.
164pub fn ref_symbol(types: &dyn TypeDatabase, type_id: TypeId) -> Option<SymbolRef> {
165    extract_type_data(types, type_id, |key| match key {
166        TypeData::Lazy(_def_id) => {
167            // TypeData::Ref has been migrated to TypeData::Lazy(DefId)
168            // We can no longer extract SymbolRef from it
169            // Return None or handle as needed based on migration strategy
170            None
171        }
172        _ => None,
173    })
174}
175
176/// Extract the lazy `DefId` if this is a Lazy type.
177pub fn lazy_def_id(types: &dyn TypeDatabase, type_id: TypeId) -> Option<DefId> {
178    extract_type_data(types, type_id, |key| match key {
179        TypeData::Lazy(def_id) => Some(*def_id),
180        _ => None,
181    })
182}
183
184/// Extract the De Bruijn index if this is a bound type parameter.
185pub fn bound_parameter_index(types: &dyn TypeDatabase, type_id: TypeId) -> Option<u32> {
186    extract_type_data(types, type_id, |key| match key {
187        TypeData::BoundParameter(index) => Some(*index),
188        _ => None,
189    })
190}
191
192/// Extract the De Bruijn index if this is a recursive type reference.
193pub fn recursive_index(types: &dyn TypeDatabase, type_id: TypeId) -> Option<u32> {
194    extract_type_data(types, type_id, |key| match key {
195        TypeData::Recursive(index) => Some(*index),
196        _ => None,
197    })
198}
199
200/// Check if this is an Enum type.
201pub fn is_enum_type(types: &dyn TypeDatabase, type_id: TypeId) -> bool {
202    matches!(types.lookup(type_id), Some(TypeData::Enum(_, _)))
203}
204
205/// Extract the enum components (`DefId` and member type) if this is an Enum type.
206///
207/// Returns `Some((def_id, member_type))` where:
208/// - `def_id` is the unique identity of the enum for nominal checking
209/// - `member_type` is the structural union of member types (e.g., 0 | 1)
210pub fn enum_components(types: &dyn TypeDatabase, type_id: TypeId) -> Option<(DefId, TypeId)> {
211    extract_type_data(types, type_id, |key| match key {
212        TypeData::Enum(def_id, member_type) => Some((*def_id, *member_type)),
213        _ => None,
214    })
215}
216
217/// Extract the application id if this is a generic application type.
218pub fn application_id(types: &dyn TypeDatabase, type_id: TypeId) -> Option<TypeApplicationId> {
219    extract_type_data(types, type_id, |key| match key {
220        TypeData::Application(app_id) => Some(*app_id),
221        _ => None,
222    })
223}
224
225/// Extract the mapped type id if this is a mapped type.
226pub fn mapped_type_id(types: &dyn TypeDatabase, type_id: TypeId) -> Option<MappedTypeId> {
227    extract_type_data(types, type_id, |key| match key {
228        TypeData::Mapped(mapped_id) => Some(*mapped_id),
229        _ => None,
230    })
231}
232
233/// Extract the conditional type id if this is a conditional type.
234pub fn conditional_type_id(types: &dyn TypeDatabase, type_id: TypeId) -> Option<ConditionalTypeId> {
235    extract_type_data(types, type_id, |key| match key {
236        TypeData::Conditional(cond_id) => Some(*cond_id),
237        _ => None,
238    })
239}
240
241/// Extract index access components if this is an index access type.
242pub fn index_access_parts(types: &dyn TypeDatabase, type_id: TypeId) -> Option<(TypeId, TypeId)> {
243    extract_type_data(types, type_id, |key| match key {
244        TypeData::IndexAccess(object_type, index_type) => Some((*object_type, *index_type)),
245        _ => None,
246    })
247}
248
249/// Extract the type query symbol if this is a `TypeQuery`.
250pub fn type_query_symbol(types: &dyn TypeDatabase, type_id: TypeId) -> Option<SymbolRef> {
251    extract_type_data(types, type_id, |key| match key {
252        TypeData::TypeQuery(sym_ref) => Some(*sym_ref),
253        _ => None,
254    })
255}
256
257/// Extract the inner type if this is a keyof type.
258pub fn keyof_inner_type(types: &dyn TypeDatabase, type_id: TypeId) -> Option<TypeId> {
259    extract_type_data(types, type_id, |key| match key {
260        TypeData::KeyOf(inner) => Some(*inner),
261        _ => None,
262    })
263}
264
265/// Extract the inner type if this is a readonly type.
266pub fn readonly_inner_type(types: &dyn TypeDatabase, type_id: TypeId) -> Option<TypeId> {
267    extract_type_data(types, type_id, |key| match key {
268        TypeData::ReadonlyType(inner) => Some(*inner),
269        _ => None,
270    })
271}
272
273/// Extract the inner type if this is a `NoInfer` type.
274pub fn no_infer_inner_type(types: &dyn TypeDatabase, type_id: TypeId) -> Option<TypeId> {
275    extract_type_data(types, type_id, |key| match key {
276        TypeData::NoInfer(inner) => Some(*inner),
277        _ => None,
278    })
279}
280
281/// Extract string intrinsic components if this is a string intrinsic type.
282pub fn string_intrinsic_components(
283    types: &dyn TypeDatabase,
284    type_id: TypeId,
285) -> Option<(StringIntrinsicKind, TypeId)> {
286    extract_type_data(types, type_id, |key| match key {
287        TypeData::StringIntrinsic { kind, type_arg } => Some((*kind, *type_arg)),
288        _ => None,
289    })
290}
291
292/// Extract the unique symbol ref if this is a unique symbol type.
293pub fn unique_symbol_ref(types: &dyn TypeDatabase, type_id: TypeId) -> Option<SymbolRef> {
294    extract_type_data(types, type_id, |key| match key {
295        TypeData::UniqueSymbol(sym_ref) => Some(*sym_ref),
296        _ => None,
297    })
298}
299
300/// Extract the module namespace symbol ref if this is a module namespace type.
301pub fn module_namespace_symbol_ref(types: &dyn TypeDatabase, type_id: TypeId) -> Option<SymbolRef> {
302    extract_type_data(types, type_id, |key| match key {
303        TypeData::ModuleNamespace(sym_ref) => Some(*sym_ref),
304        _ => None,
305    })
306}
307
308/// Check if a type is the special `this` type.
309pub fn is_this_type(types: &dyn TypeDatabase, type_id: TypeId) -> bool {
310    extract_type_data(types, type_id, |key| match key {
311        TypeData::ThisType => Some(true),
312        _ => None,
313    })
314    .unwrap_or(false)
315}
316
317/// Check whether this is an explicit error type.
318pub fn is_error_type(types: &dyn TypeDatabase, type_id: TypeId) -> bool {
319    extract_type_data(types, type_id, |key| match key {
320        TypeData::Error => Some(true),
321        _ => None,
322    })
323    .unwrap_or(false)
324}
325
326/// Extract the function shape id if this is a function type.
327pub fn function_shape_id(types: &dyn TypeDatabase, type_id: TypeId) -> Option<FunctionShapeId> {
328    extract_type_data(types, type_id, |key| match key {
329        TypeData::Function(shape_id) => Some(*shape_id),
330        _ => None,
331    })
332}
333
334/// Extract the callable shape id if this is a callable type.
335pub fn callable_shape_id(types: &dyn TypeDatabase, type_id: TypeId) -> Option<CallableShapeId> {
336    extract_type_data(types, type_id, |key| match key {
337        TypeData::Callable(shape_id) => Some(*shape_id),
338        _ => None,
339    })
340}