1use crate::def::DefId;
8use crate::types::{
9 CallableShapeId, ConditionalTypeId, FunctionShapeId, IntrinsicKind, LiteralValue, MappedTypeId,
10 ObjectShapeId, OrderedFloat, StringIntrinsicKind, TemplateLiteralId, TemplateSpan, TupleListId,
11 TypeApplicationId, TypeListId, TypeParamInfo,
12};
13use crate::visitor::TypeVisitor;
14use crate::{SymbolRef, TypeData, TypeDatabase, TypeId};
15use rustc_hash::FxHashSet;
16use tsz_common::interner::Atom;
17
18struct TypeDataDataVisitor<F, T>
19where
20 F: Fn(&TypeData) -> Option<T>,
21{
22 extractor: F,
23}
24
25impl<F, T> TypeDataDataVisitor<F, T>
26where
27 F: Fn(&TypeData) -> Option<T>,
28{
29 const fn new(extractor: F) -> Self {
30 Self { extractor }
31 }
32}
33
34impl<F, T> TypeVisitor for TypeDataDataVisitor<F, T>
35where
36 F: Fn(&TypeData) -> Option<T>,
37{
38 type Output = Option<T>;
39
40 fn visit_type_key(&mut self, _types: &dyn TypeDatabase, type_key: &TypeData) -> Self::Output {
41 (self.extractor)(type_key)
42 }
43
44 fn visit_intrinsic(&mut self, _kind: IntrinsicKind) -> Self::Output {
45 Self::default_output()
46 }
47
48 fn visit_literal(&mut self, _value: &LiteralValue) -> Self::Output {
49 Self::default_output()
50 }
51
52 fn default_output() -> Self::Output {
53 None
54 }
55}
56
57fn extract_type_data<T, F>(types: &dyn TypeDatabase, type_id: TypeId, extractor: F) -> Option<T>
58where
59 F: Fn(&TypeData) -> Option<T>,
60{
61 let mut visitor = TypeDataDataVisitor::new(extractor);
62 visitor.visit_type(types, type_id)
63}
64
65pub fn union_list_id(types: &dyn TypeDatabase, type_id: TypeId) -> Option<TypeListId> {
67 extract_type_data(types, type_id, |key| match key {
68 TypeData::Union(list_id) => Some(*list_id),
69 _ => None,
70 })
71}
72
73pub fn intersection_list_id(types: &dyn TypeDatabase, type_id: TypeId) -> Option<TypeListId> {
75 extract_type_data(types, type_id, |key| match key {
76 TypeData::Intersection(list_id) => Some(*list_id),
77 _ => None,
78 })
79}
80
81pub fn object_shape_id(types: &dyn TypeDatabase, type_id: TypeId) -> Option<ObjectShapeId> {
83 extract_type_data(types, type_id, |key| match key {
84 TypeData::Object(shape_id) => Some(*shape_id),
85 _ => None,
86 })
87}
88
89pub fn object_with_index_shape_id(
91 types: &dyn TypeDatabase,
92 type_id: TypeId,
93) -> Option<ObjectShapeId> {
94 extract_type_data(types, type_id, |key| match key {
95 TypeData::ObjectWithIndex(shape_id) => Some(*shape_id),
96 _ => None,
97 })
98}
99
100pub fn array_element_type(types: &dyn TypeDatabase, type_id: TypeId) -> Option<TypeId> {
102 extract_type_data(types, type_id, |key| match key {
103 TypeData::Array(element) => Some(*element),
104 _ => None,
105 })
106}
107
108pub fn tuple_list_id(types: &dyn TypeDatabase, type_id: TypeId) -> Option<TupleListId> {
110 extract_type_data(types, type_id, |key| match key {
111 TypeData::Tuple(list_id) => Some(*list_id),
112 _ => None,
113 })
114}
115
116pub fn intrinsic_kind(types: &dyn TypeDatabase, type_id: TypeId) -> Option<IntrinsicKind> {
118 extract_type_data(types, type_id, |key| match key {
119 TypeData::Intrinsic(kind) => Some(*kind),
120 _ => None,
121 })
122}
123
124pub fn literal_value(types: &dyn TypeDatabase, type_id: TypeId) -> Option<LiteralValue> {
126 extract_type_data(types, type_id, |key| match key {
127 TypeData::Literal(value) => Some(value.clone()),
128 _ => None,
129 })
130}
131
132pub fn literal_string(types: &dyn TypeDatabase, type_id: TypeId) -> Option<Atom> {
134 match literal_value(types, type_id) {
135 Some(LiteralValue::String(atom)) => Some(atom),
136 _ => None,
137 }
138}
139
140pub fn literal_number(types: &dyn TypeDatabase, type_id: TypeId) -> Option<OrderedFloat> {
142 match literal_value(types, type_id) {
143 Some(LiteralValue::Number(value)) => Some(value),
144 _ => None,
145 }
146}
147
148pub fn template_literal_id(types: &dyn TypeDatabase, type_id: TypeId) -> Option<TemplateLiteralId> {
150 extract_type_data(types, type_id, |key| match key {
151 TypeData::TemplateLiteral(list_id) => Some(*list_id),
152 _ => None,
153 })
154}
155
156pub fn type_param_info(types: &dyn TypeDatabase, type_id: TypeId) -> Option<TypeParamInfo> {
158 extract_type_data(types, type_id, |key| match key {
159 TypeData::TypeParameter(info) | TypeData::Infer(info) => Some(info.clone()),
160 _ => None,
161 })
162}
163
164pub fn ref_symbol(types: &dyn TypeDatabase, type_id: TypeId) -> Option<SymbolRef> {
166 extract_type_data(types, type_id, |key| match key {
167 TypeData::Lazy(_def_id) => {
168 None
172 }
173 _ => None,
174 })
175}
176
177pub fn lazy_def_id(types: &dyn TypeDatabase, type_id: TypeId) -> Option<DefId> {
179 extract_type_data(types, type_id, |key| match key {
180 TypeData::Lazy(def_id) => Some(*def_id),
181 _ => None,
182 })
183}
184
185pub fn bound_parameter_index(types: &dyn TypeDatabase, type_id: TypeId) -> Option<u32> {
187 extract_type_data(types, type_id, |key| match key {
188 TypeData::BoundParameter(index) => Some(*index),
189 _ => None,
190 })
191}
192
193pub fn recursive_index(types: &dyn TypeDatabase, type_id: TypeId) -> Option<u32> {
195 extract_type_data(types, type_id, |key| match key {
196 TypeData::Recursive(index) => Some(*index),
197 _ => None,
198 })
199}
200
201pub fn is_enum_type(types: &dyn TypeDatabase, type_id: TypeId) -> bool {
203 matches!(types.lookup(type_id), Some(TypeData::Enum(_, _)))
204}
205
206pub fn enum_components(types: &dyn TypeDatabase, type_id: TypeId) -> Option<(DefId, TypeId)> {
212 extract_type_data(types, type_id, |key| match key {
213 TypeData::Enum(def_id, member_type) => Some((*def_id, *member_type)),
214 _ => None,
215 })
216}
217
218pub fn application_id(types: &dyn TypeDatabase, type_id: TypeId) -> Option<TypeApplicationId> {
220 extract_type_data(types, type_id, |key| match key {
221 TypeData::Application(app_id) => Some(*app_id),
222 _ => None,
223 })
224}
225
226pub fn mapped_type_id(types: &dyn TypeDatabase, type_id: TypeId) -> Option<MappedTypeId> {
228 extract_type_data(types, type_id, |key| match key {
229 TypeData::Mapped(mapped_id) => Some(*mapped_id),
230 _ => None,
231 })
232}
233
234pub fn conditional_type_id(types: &dyn TypeDatabase, type_id: TypeId) -> Option<ConditionalTypeId> {
236 extract_type_data(types, type_id, |key| match key {
237 TypeData::Conditional(cond_id) => Some(*cond_id),
238 _ => None,
239 })
240}
241
242pub fn index_access_parts(types: &dyn TypeDatabase, type_id: TypeId) -> Option<(TypeId, TypeId)> {
244 extract_type_data(types, type_id, |key| match key {
245 TypeData::IndexAccess(object_type, index_type) => Some((*object_type, *index_type)),
246 _ => None,
247 })
248}
249
250pub fn type_query_symbol(types: &dyn TypeDatabase, type_id: TypeId) -> Option<SymbolRef> {
252 extract_type_data(types, type_id, |key| match key {
253 TypeData::TypeQuery(sym_ref) => Some(*sym_ref),
254 _ => None,
255 })
256}
257
258pub fn keyof_inner_type(types: &dyn TypeDatabase, type_id: TypeId) -> Option<TypeId> {
260 extract_type_data(types, type_id, |key| match key {
261 TypeData::KeyOf(inner) => Some(*inner),
262 _ => None,
263 })
264}
265
266pub fn readonly_inner_type(types: &dyn TypeDatabase, type_id: TypeId) -> Option<TypeId> {
268 extract_type_data(types, type_id, |key| match key {
269 TypeData::ReadonlyType(inner) => Some(*inner),
270 _ => None,
271 })
272}
273
274pub fn no_infer_inner_type(types: &dyn TypeDatabase, type_id: TypeId) -> Option<TypeId> {
276 extract_type_data(types, type_id, |key| match key {
277 TypeData::NoInfer(inner) => Some(*inner),
278 _ => None,
279 })
280}
281
282pub fn string_intrinsic_components(
284 types: &dyn TypeDatabase,
285 type_id: TypeId,
286) -> Option<(StringIntrinsicKind, TypeId)> {
287 extract_type_data(types, type_id, |key| match key {
288 TypeData::StringIntrinsic { kind, type_arg } => Some((*kind, *type_arg)),
289 _ => None,
290 })
291}
292
293pub fn unique_symbol_ref(types: &dyn TypeDatabase, type_id: TypeId) -> Option<SymbolRef> {
295 extract_type_data(types, type_id, |key| match key {
296 TypeData::UniqueSymbol(sym_ref) => Some(*sym_ref),
297 _ => None,
298 })
299}
300
301pub fn module_namespace_symbol_ref(types: &dyn TypeDatabase, type_id: TypeId) -> Option<SymbolRef> {
303 extract_type_data(types, type_id, |key| match key {
304 TypeData::ModuleNamespace(sym_ref) => Some(*sym_ref),
305 _ => None,
306 })
307}
308
309pub fn is_this_type(types: &dyn TypeDatabase, type_id: TypeId) -> bool {
311 extract_type_data(types, type_id, |key| match key {
312 TypeData::ThisType => Some(true),
313 _ => None,
314 })
315 .unwrap_or(false)
316}
317
318pub fn is_error_type(types: &dyn TypeDatabase, type_id: TypeId) -> bool {
320 extract_type_data(types, type_id, |key| match key {
321 TypeData::Error => Some(true),
322 _ => None,
323 })
324 .unwrap_or(false)
325}
326
327pub fn function_shape_id(types: &dyn TypeDatabase, type_id: TypeId) -> Option<FunctionShapeId> {
329 extract_type_data(types, type_id, |key| match key {
330 TypeData::Function(shape_id) => Some(*shape_id),
331 _ => None,
332 })
333}
334
335pub fn callable_shape_id(types: &dyn TypeDatabase, type_id: TypeId) -> Option<CallableShapeId> {
337 extract_type_data(types, type_id, |key| match key {
338 TypeData::Callable(shape_id) => Some(*shape_id),
339 _ => None,
340 })
341}
342
343pub fn collect_infer_bindings(types: &dyn TypeDatabase, type_id: TypeId) -> Vec<(Atom, TypeId)> {
354 let mut result = Vec::new();
355 let mut visited = FxHashSet::default();
356 collect_infer_bindings_inner(types, type_id, &mut result, &mut visited);
357 result
358}
359
360fn collect_infer_bindings_inner(
361 types: &dyn TypeDatabase,
362 type_id: TypeId,
363 result: &mut Vec<(Atom, TypeId)>,
364 visited: &mut FxHashSet<TypeId>,
365) {
366 if !visited.insert(type_id) {
367 return;
368 }
369
370 let key = match types.lookup(type_id) {
371 Some(key) => key,
372 None => return,
373 };
374
375 match key {
376 TypeData::Infer(info) => {
377 result.push((info.name, type_id));
378 if let Some(constraint) = info.constraint {
379 collect_infer_bindings_inner(types, constraint, result, visited);
380 }
381 if let Some(default) = info.default {
382 collect_infer_bindings_inner(types, default, result, visited);
383 }
384 }
385 TypeData::Array(elem) => {
386 collect_infer_bindings_inner(types, elem, result, visited);
387 }
388 TypeData::Tuple(elements) => {
389 let elements = types.tuple_list(elements);
390 for element in elements.iter() {
391 collect_infer_bindings_inner(types, element.type_id, result, visited);
392 }
393 }
394 TypeData::Union(members) | TypeData::Intersection(members) => {
395 let members = types.type_list(members);
396 for member in members.iter() {
397 collect_infer_bindings_inner(types, *member, result, visited);
398 }
399 }
400 TypeData::Object(shape_id) => {
401 let shape = types.object_shape(shape_id);
402 for prop in &shape.properties {
403 collect_infer_bindings_inner(types, prop.type_id, result, visited);
404 }
405 }
406 TypeData::ObjectWithIndex(shape_id) => {
407 let shape = types.object_shape(shape_id);
408 for prop in &shape.properties {
409 collect_infer_bindings_inner(types, prop.type_id, result, visited);
410 }
411 if let Some(index) = &shape.string_index {
412 collect_infer_bindings_inner(types, index.key_type, result, visited);
413 collect_infer_bindings_inner(types, index.value_type, result, visited);
414 }
415 if let Some(index) = &shape.number_index {
416 collect_infer_bindings_inner(types, index.key_type, result, visited);
417 collect_infer_bindings_inner(types, index.value_type, result, visited);
418 }
419 }
420 TypeData::Function(shape_id) => {
421 let shape = types.function_shape(shape_id);
422 for param in &shape.params {
423 collect_infer_bindings_inner(types, param.type_id, result, visited);
424 }
425 collect_infer_bindings_inner(types, shape.return_type, result, visited);
426 for param in &shape.type_params {
427 if let Some(constraint) = param.constraint {
428 collect_infer_bindings_inner(types, constraint, result, visited);
429 }
430 if let Some(default) = param.default {
431 collect_infer_bindings_inner(types, default, result, visited);
432 }
433 }
434 }
435 TypeData::Callable(shape_id) => {
436 let shape = types.callable_shape(shape_id);
437 for sig in &shape.call_signatures {
438 collect_infer_sig(types, sig, result, visited);
439 }
440 for sig in &shape.construct_signatures {
441 collect_infer_sig(types, sig, result, visited);
442 }
443 for prop in &shape.properties {
444 collect_infer_bindings_inner(types, prop.type_id, result, visited);
445 }
446 }
447 TypeData::TypeParameter(info) => {
448 if let Some(constraint) = info.constraint {
449 collect_infer_bindings_inner(types, constraint, result, visited);
450 }
451 if let Some(default) = info.default {
452 collect_infer_bindings_inner(types, default, result, visited);
453 }
454 }
455 TypeData::Application(app_id) => {
456 let app = types.type_application(app_id);
457 collect_infer_bindings_inner(types, app.base, result, visited);
458 for &arg in &app.args {
459 collect_infer_bindings_inner(types, arg, result, visited);
460 }
461 }
462 TypeData::Conditional(cond_id) => {
463 let cond = types.conditional_type(cond_id);
464 collect_infer_bindings_inner(types, cond.check_type, result, visited);
465 collect_infer_bindings_inner(types, cond.extends_type, result, visited);
466 collect_infer_bindings_inner(types, cond.true_type, result, visited);
467 collect_infer_bindings_inner(types, cond.false_type, result, visited);
468 }
469 TypeData::Mapped(mapped_id) => {
470 let mapped = types.mapped_type(mapped_id);
471 if let Some(constraint) = mapped.type_param.constraint {
472 collect_infer_bindings_inner(types, constraint, result, visited);
473 }
474 if let Some(default) = mapped.type_param.default {
475 collect_infer_bindings_inner(types, default, result, visited);
476 }
477 collect_infer_bindings_inner(types, mapped.constraint, result, visited);
478 if let Some(name_type) = mapped.name_type {
479 collect_infer_bindings_inner(types, name_type, result, visited);
480 }
481 collect_infer_bindings_inner(types, mapped.template, result, visited);
482 }
483 TypeData::IndexAccess(obj, idx) => {
484 collect_infer_bindings_inner(types, obj, result, visited);
485 collect_infer_bindings_inner(types, idx, result, visited);
486 }
487 TypeData::KeyOf(inner) | TypeData::ReadonlyType(inner) | TypeData::NoInfer(inner) => {
488 collect_infer_bindings_inner(types, inner, result, visited);
489 }
490 TypeData::TemplateLiteral(spans) => {
491 let spans = types.template_list(spans);
492 for span in spans.iter() {
493 if let TemplateSpan::Type(inner) = span {
494 collect_infer_bindings_inner(types, *inner, result, visited);
495 }
496 }
497 }
498 TypeData::StringIntrinsic { type_arg, .. } => {
499 collect_infer_bindings_inner(types, type_arg, result, visited);
500 }
501 TypeData::Enum(_def_id, member_type) => {
502 collect_infer_bindings_inner(types, member_type, result, visited);
503 }
504 TypeData::Intrinsic(_)
505 | TypeData::Literal(_)
506 | TypeData::Lazy(_)
507 | TypeData::Recursive(_)
508 | TypeData::BoundParameter(_)
509 | TypeData::TypeQuery(_)
510 | TypeData::UniqueSymbol(_)
511 | TypeData::ThisType
512 | TypeData::ModuleNamespace(_)
513 | TypeData::Error => {}
514 }
515}
516
517fn collect_infer_sig(
520 types: &dyn TypeDatabase,
521 sig: &crate::types::CallSignature,
522 result: &mut Vec<(Atom, TypeId)>,
523 visited: &mut FxHashSet<TypeId>,
524) {
525 for param in &sig.params {
526 collect_infer_bindings_inner(types, param.type_id, result, visited);
527 }
528 collect_infer_bindings_inner(types, sig.return_type, result, visited);
529 for param in &sig.type_params {
530 if let Some(constraint) = param.constraint {
531 collect_infer_bindings_inner(types, constraint, result, visited);
532 }
533 if let Some(default) = param.default {
534 collect_infer_bindings_inner(types, default, result, visited);
535 }
536 }
537}