Skip to main content

tsz_solver/visitors/
visitor.rs

1//! Type Visitor Pattern
2//!
3//! This module implements the Visitor pattern for `TypeData` operations,
4//! providing a clean alternative to repetitive match statements.
5//!
6//! # Benefits
7//!
8//! - **Centralized type logic**: All type handling in one place
9//! - **Easier to extend**: Add new visitors without modifying existing code
10//! - **Type-safe**: Compiler ensures all variants are handled
11//! - **Composable**: Visitors can be combined and chained
12//!
13//! # Usage
14//!
15//! ```rust,ignore
16//! use crate::visitor::{TypeVisitor, TypeKind, is_type_kind};
17//! use crate::types::{IntrinsicKind, LiteralValue};
18//!
19//! struct MyVisitor;
20//!
21//! impl TypeVisitor for MyVisitor {
22//!     type Output = bool;
23//!
24//!     fn visit_intrinsic(&mut self, kind: IntrinsicKind) -> Self::Output {
25//!         matches!(kind, IntrinsicKind::Any)
26//!     }
27//!
28//!     fn visit_literal(&mut self, value: &LiteralValue) -> Self::Output {
29//!         matches!(value, LiteralValue::Boolean(true))
30//!     }
31//!
32//!     fn default_output() -> Self::Output {
33//!         false
34//!     }
35//! }
36//!
37//! // Or use convenience functions:
38//! let is_object = is_type_kind(&types, type_id, TypeKind::Object);
39//! ```
40
41use crate::def::DefId;
42use crate::types::{IntrinsicKind, StringIntrinsicKind, TupleElement, TypeParamInfo};
43use crate::{LiteralValue, SymbolRef, TypeData, TypeDatabase, TypeId};
44use rustc_hash::FxHashSet;
45
46// Re-export type data extraction helpers (extracted to visitor_extract.rs)
47pub use super::visitor_extract::*;
48
49// Re-export type predicate functions (extracted to visitor_predicates.rs)
50pub use super::visitor_predicates::*;
51
52// =============================================================================
53// Type Visitor Trait
54// =============================================================================
55
56/// Visitor pattern for `TypeData` traversal and transformation.
57///
58/// Implement this trait to perform custom operations on types without
59/// writing repetitive match statements. Each method corresponds to a
60/// `TypeData` variant and receives the relevant data for that type.
61pub trait TypeVisitor: Sized {
62    /// The output type produced by visiting.
63    type Output;
64
65    // =========================================================================
66    // Core Type Visitors
67    // =========================================================================
68
69    /// Visit an intrinsic type (any, unknown, never, void, etc.).
70    fn visit_intrinsic(&mut self, kind: IntrinsicKind) -> Self::Output;
71
72    /// Visit a literal type (string, number, boolean, bigint literals).
73    fn visit_literal(&mut self, value: &LiteralValue) -> Self::Output;
74
75    // =========================================================================
76    // Composite Types - Default implementations provided
77    // =========================================================================
78
79    /// Visit an object type with properties.
80    fn visit_object(&mut self, _shape_id: u32) -> Self::Output {
81        Self::default_output()
82    }
83
84    /// Visit an object type with index signatures.
85    fn visit_object_with_index(&mut self, _shape_id: u32) -> Self::Output {
86        Self::default_output()
87    }
88
89    /// Visit a union type (A | B | C).
90    fn visit_union(&mut self, _list_id: u32) -> Self::Output {
91        Self::default_output()
92    }
93
94    /// Visit an intersection type (A & B & C).
95    fn visit_intersection(&mut self, _list_id: u32) -> Self::Output {
96        Self::default_output()
97    }
98
99    /// Visit an array type T[].
100    fn visit_array(&mut self, _element_type: TypeId) -> Self::Output {
101        Self::default_output()
102    }
103
104    /// Visit a tuple type [T, U, V].
105    fn visit_tuple(&mut self, _list_id: u32) -> Self::Output {
106        Self::default_output()
107    }
108
109    /// Visit a function type.
110    fn visit_function(&mut self, _shape_id: u32) -> Self::Output {
111        Self::default_output()
112    }
113
114    /// Visit a callable type with call/construct signatures.
115    fn visit_callable(&mut self, _shape_id: u32) -> Self::Output {
116        Self::default_output()
117    }
118
119    /// Visit a type parameter (generic type variable).
120    fn visit_type_parameter(&mut self, _param_info: &TypeParamInfo) -> Self::Output {
121        Self::default_output()
122    }
123
124    /// Visit a bound type parameter using De Bruijn index for alpha-equivalence.
125    ///
126    /// This is used for canonicalizing generic types to achieve structural identity,
127    /// where `type F<T> = T` and `type G<U> = U` are considered identical.
128    /// The index represents which parameter in the binding scope (0 = innermost).
129    fn visit_bound_parameter(&mut self, _de_bruijn_index: u32) -> Self::Output {
130        Self::default_output()
131    }
132
133    /// Visit a named type reference (interface, class, type alias).
134    fn visit_ref(&mut self, _symbol_ref: u32) -> Self::Output {
135        Self::default_output()
136    }
137
138    /// Visit an enum type with nominal identity and structural member types.
139    fn visit_enum(&mut self, _def_id: u32, _member_type: TypeId) -> Self::Output {
140        Self::default_output()
141    }
142
143    /// Visit a lazy type reference using `DefId`.
144    fn visit_lazy(&mut self, _def_id: u32) -> Self::Output {
145        Self::default_output()
146    }
147
148    /// Visit a recursive type reference using De Bruijn index.
149    ///
150    /// This is used for canonicalizing recursive types to achieve O(1) equality.
151    /// The index represents how many levels up the nesting chain to refer to.
152    fn visit_recursive(&mut self, _de_bruijn_index: u32) -> Self::Output {
153        Self::default_output()
154    }
155
156    /// Visit a generic type application Base<Args>.
157    fn visit_application(&mut self, _app_id: u32) -> Self::Output {
158        Self::default_output()
159    }
160
161    /// Visit a conditional type T extends U ? X : Y.
162    fn visit_conditional(&mut self, _cond_id: u32) -> Self::Output {
163        Self::default_output()
164    }
165
166    /// Visit a mapped type { [K in Keys]: V }.
167    fn visit_mapped(&mut self, _mapped_id: u32) -> Self::Output {
168        Self::default_output()
169    }
170
171    /// Visit an indexed access type T[K].
172    fn visit_index_access(&mut self, _object_type: TypeId, _key_type: TypeId) -> Self::Output {
173        Self::default_output()
174    }
175
176    /// Visit a template literal type `hello${x}world`.
177    fn visit_template_literal(&mut self, _template_id: u32) -> Self::Output {
178        Self::default_output()
179    }
180
181    /// Visit a type query (typeof expr).
182    fn visit_type_query(&mut self, _symbol_ref: u32) -> Self::Output {
183        Self::default_output()
184    }
185
186    /// Visit a keyof type.
187    fn visit_keyof(&mut self, _type_id: TypeId) -> Self::Output {
188        Self::default_output()
189    }
190
191    /// Visit a readonly type modifier.
192    fn visit_readonly_type(&mut self, _inner_type: TypeId) -> Self::Output {
193        Self::default_output()
194    }
195
196    /// Visit a unique symbol type.
197    fn visit_unique_symbol(&mut self, _symbol_ref: u32) -> Self::Output {
198        Self::default_output()
199    }
200
201    /// Visit an infer type (for type inference in conditional types).
202    fn visit_infer(&mut self, _param_info: &TypeParamInfo) -> Self::Output {
203        Self::default_output()
204    }
205
206    /// Visit a this type (polymorphic this parameter).
207    fn visit_this_type(&mut self) -> Self::Output {
208        Self::default_output()
209    }
210
211    /// Visit a string manipulation intrinsic type.
212    fn visit_string_intrinsic(
213        &mut self,
214        _kind: StringIntrinsicKind,
215        _type_arg: TypeId,
216    ) -> Self::Output {
217        Self::default_output()
218    }
219
220    /// Visit an error type.
221    fn visit_error(&mut self) -> Self::Output {
222        Self::default_output()
223    }
224
225    /// Visit a `NoInfer`<T> type (TypeScript 5.4+).
226    /// Traverses the inner type (`NoInfer` is transparent for traversal).
227    fn visit_no_infer(&mut self, _inner: TypeId) -> Self::Output {
228        Self::default_output()
229    }
230
231    /// Visit a module namespace type (import * as ns).
232    fn visit_module_namespace(&mut self, _symbol_ref: u32) -> Self::Output {
233        Self::default_output()
234    }
235
236    // =========================================================================
237    // Helper Methods
238    // =========================================================================
239
240    /// Default output for unimplemented variants.
241    fn default_output() -> Self::Output;
242
243    /// Visit a type by dispatching to the appropriate method.
244    ///
245    /// This is the main entry point for using the visitor.
246    fn visit_type(&mut self, types: &dyn TypeDatabase, type_id: TypeId) -> Self::Output {
247        match types.lookup(type_id) {
248            Some(ref type_key) => self.visit_type_key(types, type_key),
249            None => Self::default_output(),
250        }
251    }
252
253    /// Visit a `TypeData` by dispatching to the appropriate method.
254    fn visit_type_key(&mut self, _types: &dyn TypeDatabase, type_key: &TypeData) -> Self::Output {
255        match type_key {
256            TypeData::Intrinsic(kind) => self.visit_intrinsic(*kind),
257            TypeData::Literal(value) => self.visit_literal(value),
258            TypeData::Object(id) => self.visit_object(id.0),
259            TypeData::ObjectWithIndex(id) => self.visit_object_with_index(id.0),
260            TypeData::Union(id) => self.visit_union(id.0),
261            TypeData::Intersection(id) => self.visit_intersection(id.0),
262            TypeData::Array(element_type) => self.visit_array(*element_type),
263            TypeData::Tuple(id) => self.visit_tuple(id.0),
264            TypeData::Function(id) => self.visit_function(id.0),
265            TypeData::Callable(id) => self.visit_callable(id.0),
266            TypeData::TypeParameter(info) => self.visit_type_parameter(info),
267            TypeData::BoundParameter(index) => self.visit_bound_parameter(*index),
268            TypeData::Lazy(def_id) => self.visit_lazy(def_id.0),
269            TypeData::Recursive(index) => self.visit_recursive(*index),
270            TypeData::Enum(def_id, member_type) => self.visit_enum(def_id.0, *member_type),
271            TypeData::Application(id) => self.visit_application(id.0),
272            TypeData::Conditional(id) => self.visit_conditional(id.0),
273            TypeData::Mapped(id) => self.visit_mapped(id.0),
274            TypeData::IndexAccess(obj, key) => self.visit_index_access(*obj, *key),
275            TypeData::TemplateLiteral(id) => self.visit_template_literal(id.0),
276            TypeData::TypeQuery(sym_ref) => self.visit_type_query(sym_ref.0),
277            TypeData::KeyOf(type_id) => self.visit_keyof(*type_id),
278            TypeData::ReadonlyType(inner) => self.visit_readonly_type(*inner),
279            TypeData::UniqueSymbol(sym_ref) => self.visit_unique_symbol(sym_ref.0),
280            TypeData::Infer(info) => self.visit_infer(info),
281            TypeData::ThisType => self.visit_this_type(),
282            TypeData::StringIntrinsic { kind, type_arg } => {
283                self.visit_string_intrinsic(*kind, *type_arg)
284            }
285            TypeData::ModuleNamespace(sym_ref) => self.visit_module_namespace(sym_ref.0),
286            TypeData::NoInfer(inner) => self.visit_no_infer(*inner),
287            TypeData::Error => self.visit_error(),
288        }
289    }
290}
291
292// =============================================================================
293// Type Traversal Helpers
294// =============================================================================
295
296/// Invoke a function on each immediate child `TypeId` of a `TypeData`.
297///
298/// This function provides a simple way to traverse the type graph without
299/// requiring the full Visitor pattern. It's useful for operations like:
300/// - Populating caches (ensuring all nested types are resolved)
301/// - Collecting dependencies
302/// - Type environment population
303///
304/// # Parameters
305///
306/// * `db` - The type database to look up type structures
307/// * `key` - The `TypeData` whose children should be visited
308/// * `f` - Function to call for each child `TypeId`
309///
310/// # Examples
311///
312/// ```rust,ignore
313/// use crate::visitor::for_each_child;
314///
315/// for_each_child(types, &type_key, |child_id| {
316///     // Process each nested type
317/// });
318/// ```
319///
320/// # `TypeData` Variants Handled
321///
322/// This function handles ALL `TypeData` variants to ensure complete traversal:
323/// - **Single nested types**: Array, `ReadonlyType`, `KeyOf`, etc.
324/// - **Multiple members**: Union, Intersection
325/// - **Structured types**: Object, Tuple, Function, Callable
326/// - **Complex types**: Application, Conditional, Mapped, `IndexAccess`
327/// - **Template literals**: Iterates over template spans
328/// - **String intrinsics**: Visits type argument
329/// - **Leaf types**: Intrinsic, Literal, Lazy, `TypeQuery`, etc. (no children)
330pub fn for_each_child<F>(db: &dyn TypeDatabase, key: &TypeData, mut f: F)
331where
332    F: FnMut(TypeId),
333{
334    match key {
335        // Single nested type
336        TypeData::Array(inner)
337        | TypeData::ReadonlyType(inner)
338        | TypeData::KeyOf(inner)
339        | TypeData::NoInfer(inner) => {
340            f(*inner);
341        }
342
343        // Composite types with multiple members
344        TypeData::Union(list_id) | TypeData::Intersection(list_id) => {
345            for &member in db.type_list(*list_id).iter() {
346                f(member);
347            }
348        }
349
350        // Object types with properties and index signatures
351        TypeData::Object(shape_id) | TypeData::ObjectWithIndex(shape_id) => {
352            let shape = db.object_shape(*shape_id);
353            for prop in &shape.properties {
354                f(prop.type_id);
355                f(prop.write_type); // IMPORTANT: Must visit both read and write types
356            }
357            if let Some(ref sig) = shape.string_index {
358                f(sig.key_type);
359                f(sig.value_type);
360            }
361            if let Some(ref sig) = shape.number_index {
362                f(sig.key_type);
363                f(sig.value_type);
364            }
365        }
366
367        // Tuple types
368        TypeData::Tuple(tuple_id) => {
369            for elem in db.tuple_list(*tuple_id).iter() {
370                f(elem.type_id);
371            }
372        }
373
374        // Function types
375        TypeData::Function(func_id) => {
376            let sig = db.function_shape(*func_id);
377            f(sig.return_type);
378            if let Some(this_type) = sig.this_type {
379                f(this_type);
380            }
381            if let Some(ref type_predicate) = sig.type_predicate
382                && let Some(type_id) = type_predicate.type_id
383            {
384                f(type_id);
385            }
386            for param in &sig.params {
387                f(param.type_id);
388            }
389            for type_param in &sig.type_params {
390                if let Some(constraint) = type_param.constraint {
391                    f(constraint);
392                }
393                if let Some(default) = type_param.default {
394                    f(default);
395                }
396            }
397        }
398
399        // Callable types
400        TypeData::Callable(callable_id) => {
401            let callable = db.callable_shape(*callable_id);
402            for sig in &callable.call_signatures {
403                f(sig.return_type);
404                if let Some(this_type) = sig.this_type {
405                    f(this_type);
406                }
407                if let Some(ref type_predicate) = sig.type_predicate
408                    && let Some(type_id) = type_predicate.type_id
409                {
410                    f(type_id);
411                }
412                for param in &sig.params {
413                    f(param.type_id);
414                }
415                for type_param in &sig.type_params {
416                    if let Some(constraint) = type_param.constraint {
417                        f(constraint);
418                    }
419                    if let Some(default) = type_param.default {
420                        f(default);
421                    }
422                }
423            }
424            for sig in &callable.construct_signatures {
425                f(sig.return_type);
426                if let Some(this_type) = sig.this_type {
427                    f(this_type);
428                }
429                if let Some(ref type_predicate) = sig.type_predicate
430                    && let Some(type_id) = type_predicate.type_id
431                {
432                    f(type_id);
433                }
434                for param in &sig.params {
435                    f(param.type_id);
436                }
437                for type_param in &sig.type_params {
438                    if let Some(constraint) = type_param.constraint {
439                        f(constraint);
440                    }
441                    if let Some(default) = type_param.default {
442                        f(default);
443                    }
444                }
445            }
446            // Visit prototype properties
447            for prop in &callable.properties {
448                f(prop.type_id);
449                f(prop.write_type);
450            }
451            if let Some(ref sig) = callable.string_index {
452                f(sig.key_type);
453                f(sig.value_type);
454            }
455            if let Some(ref sig) = callable.number_index {
456                f(sig.key_type);
457                f(sig.value_type);
458            }
459        }
460
461        // Type applications
462        TypeData::Application(app_id) => {
463            let app = db.type_application(*app_id);
464            f(app.base);
465            for &arg in &app.args {
466                f(arg);
467            }
468        }
469
470        // Conditional types
471        TypeData::Conditional(cond_id) => {
472            let cond = db.conditional_type(*cond_id);
473            f(cond.check_type);
474            f(cond.extends_type);
475            f(cond.true_type);
476            f(cond.false_type);
477        }
478
479        // Mapped types
480        TypeData::Mapped(mapped_id) => {
481            let mapped = db.mapped_type(*mapped_id);
482            if let Some(constraint) = mapped.type_param.constraint {
483                f(constraint);
484            }
485            if let Some(default) = mapped.type_param.default {
486                f(default);
487            }
488            f(mapped.constraint);
489            f(mapped.template);
490            if let Some(name_type) = mapped.name_type {
491                f(name_type);
492            }
493        }
494
495        // Index access types
496        TypeData::IndexAccess(obj, idx) => {
497            f(*obj);
498            f(*idx);
499        }
500
501        // Template literal types
502        TypeData::TemplateLiteral(template_id) => {
503            for span in db.template_list(*template_id).iter() {
504                match span {
505                    crate::types::TemplateSpan::Text(_) => {}
506                    crate::types::TemplateSpan::Type(type_id) => {
507                        f(*type_id);
508                    }
509                }
510            }
511        }
512
513        // String intrinsics
514        TypeData::StringIntrinsic { type_arg, .. } => {
515            f(*type_arg);
516        }
517
518        // Type parameters with constraints
519        TypeData::TypeParameter(info) | TypeData::Infer(info) => {
520            if let Some(constraint) = info.constraint {
521                f(constraint);
522            }
523        }
524
525        // Enum types
526        TypeData::Enum(_def_id, member_type) => {
527            f(*member_type);
528        }
529
530        // Leaf types - no children to visit
531        TypeData::Intrinsic(_)
532        | TypeData::Literal(_)
533        | TypeData::Lazy(_)
534        | TypeData::Recursive(_)
535        | TypeData::BoundParameter(_)
536        | TypeData::TypeQuery(_)
537        | TypeData::UniqueSymbol(_)
538        | TypeData::ThisType
539        | TypeData::ModuleNamespace(_)
540        | TypeData::Error => {}
541    }
542}
543
544/// Invoke a function on each immediate child `TypeId` of `type_id`.
545///
546/// This helper keeps callers from requiring direct `lookup()` access when they
547/// only need one-level traversal.
548pub fn for_each_child_type_id<F>(db: &dyn TypeDatabase, type_id: TypeId, f: F)
549where
550    F: FnMut(TypeId),
551{
552    let Some(key) = db.lookup(type_id) else {
553        return;
554    };
555    for_each_child(db, &key, f);
556}
557
558/// Walk all transitively referenced type IDs from `root`.
559///
560/// Convenience wrapper around [`for_each_child`] that takes a `TypeId` instead of `&TypeData`.
561///
562/// Looks up the type data for `type_id` and visits its direct children.
563/// If the type cannot be resolved, this is a no-op.
564pub fn for_each_child_by_id<F>(db: &dyn TypeDatabase, type_id: TypeId, f: F)
565where
566    F: FnMut(TypeId),
567{
568    if let Some(type_data) = db.lookup(type_id) {
569        for_each_child(db, &type_data, f);
570    }
571}
572
573/// The callback is invoked once per unique reachable type (including `root`).
574pub fn walk_referenced_types<F>(types: &dyn TypeDatabase, root: TypeId, mut f: F)
575where
576    F: FnMut(TypeId),
577{
578    let mut visited = FxHashSet::default();
579    let mut stack = vec![root];
580
581    while let Some(current) = stack.pop() {
582        if !visited.insert(current) {
583            continue;
584        }
585        f(current);
586
587        let Some(key) = types.lookup(current) else {
588            continue;
589        };
590        for_each_child(types, &key, |child| stack.push(child));
591    }
592}
593
594/// Collect all unique lazy `DefIds` reachable from `root`.
595pub fn collect_lazy_def_ids(types: &dyn TypeDatabase, root: TypeId) -> Vec<DefId> {
596    let mut out = Vec::new();
597    let mut seen = FxHashSet::default();
598
599    walk_referenced_types(types, root, |type_id| {
600        if let Some(TypeData::Lazy(def_id)) = types.lookup(type_id)
601            && seen.insert(def_id)
602        {
603            out.push(def_id);
604        }
605    });
606
607    out
608}
609
610/// Collect all unique enum `DefIds` reachable from `root`.
611pub fn collect_enum_def_ids(types: &dyn TypeDatabase, root: TypeId) -> Vec<DefId> {
612    let mut out = Vec::new();
613    let mut seen = FxHashSet::default();
614
615    walk_referenced_types(types, root, |type_id| {
616        if let Some(TypeData::Enum(def_id, _)) = types.lookup(type_id)
617            && seen.insert(def_id)
618        {
619            out.push(def_id);
620        }
621    });
622
623    out
624}
625
626/// Collect all unique type-query symbol references reachable from `root`.
627pub fn collect_type_queries(types: &dyn TypeDatabase, root: TypeId) -> Vec<SymbolRef> {
628    let mut out = Vec::new();
629    let mut seen = FxHashSet::default();
630
631    walk_referenced_types(types, root, |type_id| {
632        if let Some(TypeData::TypeQuery(symbol_ref)) = types.lookup(type_id)
633            && seen.insert(symbol_ref)
634        {
635            out.push(symbol_ref);
636        }
637    });
638
639    out
640}
641
642// =============================================================================
643// Common Visitor Implementations
644// =============================================================================
645
646/// Classification of types into broad categories.
647#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
648pub enum TypeKind {
649    /// Primitive types (string, number, boolean, etc.)
650    Primitive,
651    /// Literal types ("hello", 42, true)
652    Literal,
653    /// Object types
654    Object,
655    /// Array types
656    Array,
657    /// Tuple types
658    Tuple,
659    /// Union types
660    Union,
661    /// Intersection types
662    Intersection,
663    /// Function/callable types
664    Function,
665    /// Generic types (type applications)
666    Generic,
667    /// Type parameters (T, K, etc.)
668    TypeParameter,
669    /// Conditional types (T extends U ? X : Y)
670    Conditional,
671    /// Mapped types ({ [K in Keys]: V })
672    Mapped,
673    /// Index access types (T[K])
674    IndexAccess,
675    /// Template literal types (`hello${T}`)
676    TemplateLiteral,
677    /// Type query (typeof expr)
678    TypeQuery,
679    /// `KeyOf` types (keyof T)
680    KeyOf,
681    /// Named type references (interfaces, type aliases)
682    Reference,
683    /// Error types
684    Error,
685    /// Other/unknown
686    Other,
687}
688
689/// Visitor that checks if a type is a specific `TypeKind`.
690pub struct TypeKindVisitor {
691    /// The kind to check for.
692    pub target_kind: TypeKind,
693}
694
695impl TypeKindVisitor {
696    /// Create a new `TypeKindVisitor`.
697    pub const fn new(target_kind: TypeKind) -> Self {
698        Self { target_kind }
699    }
700
701    /// Get the kind of a type from its `TypeData`.
702    pub const fn get_kind(type_key: &TypeData) -> TypeKind {
703        match type_key {
704            TypeData::Intrinsic(_)
705            | TypeData::Enum(_, _)
706            | TypeData::UniqueSymbol(_)
707            | TypeData::StringIntrinsic { .. } => TypeKind::Primitive,
708            TypeData::Literal(_) => TypeKind::Literal,
709            TypeData::Object(_) | TypeData::ObjectWithIndex(_) | TypeData::ModuleNamespace(_) => {
710                TypeKind::Object
711            }
712            TypeData::Array(_) => TypeKind::Array,
713            TypeData::Tuple(_) => TypeKind::Tuple,
714            TypeData::Union(_) => TypeKind::Union,
715            TypeData::Intersection(_) => TypeKind::Intersection,
716            TypeData::Function(_) | TypeData::Callable(_) => TypeKind::Function,
717            TypeData::Application(_) => TypeKind::Generic,
718            TypeData::TypeParameter(_) | TypeData::Infer(_) | TypeData::BoundParameter(_) => {
719                TypeKind::TypeParameter
720            }
721            TypeData::Conditional(_) => TypeKind::Conditional,
722            TypeData::Lazy(_) | TypeData::Recursive(_) => TypeKind::Reference,
723            TypeData::Mapped(_) => TypeKind::Mapped,
724            TypeData::IndexAccess(_, _) => TypeKind::IndexAccess,
725            TypeData::TemplateLiteral(_) => TypeKind::TemplateLiteral,
726            TypeData::TypeQuery(_) => TypeKind::TypeQuery,
727            TypeData::KeyOf(_) => TypeKind::KeyOf,
728            TypeData::ReadonlyType(_inner) => {
729                // Readonly doesn't change the kind - look through it
730                // Note: This requires lookup which we don't have here
731                // For now, return Other and let callers handle it
732                TypeKind::Other
733            }
734            TypeData::NoInfer(_inner) => {
735                // NoInfer doesn't change the kind - look through it
736                TypeKind::Other
737            }
738            TypeData::ThisType => TypeKind::TypeParameter, // this is type-parameter-like
739            TypeData::Error => TypeKind::Error,
740        }
741    }
742
743    /// Get the kind of a type by `TypeId`.
744    pub fn get_kind_of(types: &dyn TypeDatabase, type_id: TypeId) -> TypeKind {
745        match types.lookup(type_id) {
746            Some(ref type_key) => Self::get_kind(type_key),
747            None => TypeKind::Other,
748        }
749    }
750}
751
752impl TypeVisitor for TypeKindVisitor {
753    type Output = bool;
754
755    fn visit_intrinsic(&mut self, _kind: IntrinsicKind) -> Self::Output {
756        self.target_kind == TypeKind::Primitive
757    }
758
759    fn visit_literal(&mut self, _value: &LiteralValue) -> Self::Output {
760        self.target_kind == TypeKind::Literal
761    }
762
763    fn visit_type_key(&mut self, _types: &dyn TypeDatabase, type_key: &TypeData) -> Self::Output {
764        Self::get_kind(type_key) == self.target_kind
765    }
766
767    fn default_output() -> Self::Output {
768        false
769    }
770}
771
772/// Visitor that collects all `TypeIds` referenced by a type.
773///
774/// Useful for finding dependencies or tracking type usage.
775pub struct TypeCollectorVisitor {
776    /// Set of collected type IDs.
777    pub types: FxHashSet<TypeId>,
778    /// Maximum depth to traverse.
779    pub max_depth: usize,
780}
781
782impl TypeCollectorVisitor {
783    /// Create a new `TypeCollectorVisitor`.
784    pub fn new() -> Self {
785        Self {
786            types: FxHashSet::default(),
787            max_depth: 10,
788        }
789    }
790
791    /// Create a new `TypeCollectorVisitor` with custom max depth.
792    pub fn with_max_depth(max_depth: usize) -> Self {
793        Self {
794            types: FxHashSet::default(),
795            max_depth,
796        }
797    }
798}
799
800impl Default for TypeCollectorVisitor {
801    fn default() -> Self {
802        Self::new()
803    }
804}
805
806impl TypeVisitor for TypeCollectorVisitor {
807    type Output = ();
808
809    fn visit_intrinsic(&mut self, _kind: IntrinsicKind) -> Self::Output {
810        // Intrinsic types have no child types to collect
811    }
812
813    fn visit_literal(&mut self, _value: &LiteralValue) -> Self::Output {
814        // Literal types have no child types to collect
815    }
816
817    fn visit_array(&mut self, element_type: TypeId) -> Self::Output {
818        if self.max_depth > 0 {
819            self.types.insert(element_type);
820        }
821    }
822
823    fn visit_index_access(&mut self, object_type: TypeId, key_type: TypeId) -> Self::Output {
824        if self.max_depth > 0 {
825            self.types.insert(object_type);
826            self.types.insert(key_type);
827        }
828    }
829
830    fn visit_keyof(&mut self, type_id: TypeId) -> Self::Output {
831        if self.max_depth > 0 {
832            self.types.insert(type_id);
833        }
834    }
835
836    fn visit_readonly_type(&mut self, inner_type: TypeId) -> Self::Output {
837        if self.max_depth > 0 {
838            self.types.insert(inner_type);
839        }
840    }
841
842    fn visit_string_intrinsic(
843        &mut self,
844        _kind: StringIntrinsicKind,
845        type_arg: TypeId,
846    ) -> Self::Output {
847        if self.max_depth > 0 {
848            self.types.insert(type_arg);
849        }
850    }
851
852    fn visit_enum(&mut self, _def_id: u32, member_type: TypeId) -> Self::Output {
853        if self.max_depth > 0 {
854            self.types.insert(member_type);
855        }
856    }
857
858    fn default_output() -> Self::Output {}
859}
860
861/// Visitor that checks if a type matches a specific predicate.
862pub struct TypePredicateVisitor<F>
863where
864    F: Fn(&TypeData) -> bool,
865{
866    /// Predicate function to test against `TypeData`.
867    pub predicate: F,
868}
869
870impl<F> TypePredicateVisitor<F>
871where
872    F: Fn(&TypeData) -> bool,
873{
874    /// Create a new `TypePredicateVisitor`.
875    pub const fn new(predicate: F) -> Self {
876        Self { predicate }
877    }
878}
879
880impl<F> TypeVisitor for TypePredicateVisitor<F>
881where
882    F: Fn(&TypeData) -> bool,
883{
884    type Output = bool;
885
886    fn visit_type_key(&mut self, _types: &dyn TypeDatabase, type_key: &TypeData) -> Self::Output {
887        (self.predicate)(type_key)
888    }
889
890    fn visit_intrinsic(&mut self, _kind: IntrinsicKind) -> Self::Output {
891        false
892    }
893
894    fn visit_literal(&mut self, _value: &LiteralValue) -> Self::Output {
895        false
896    }
897
898    fn default_output() -> Self::Output {
899        false
900    }
901}
902
903// =============================================================================
904// Convenience Functions
905// =============================================================================
906
907/// Check if a type is a specific kind using the `TypeKindVisitor`.
908///
909/// # Example
910///
911/// ```rust,ignore
912/// use crate::visitor::{is_type_kind, TypeKind};
913///
914/// let is_object = is_type_kind(&types, type_id, TypeKind::Object);
915/// ```
916pub fn is_type_kind(types: &dyn TypeDatabase, type_id: TypeId, kind: TypeKind) -> bool {
917    let mut visitor = TypeKindVisitor::new(kind);
918    visitor.visit_type(types, type_id)
919}
920
921/// Collect all types referenced by a type.
922///
923/// # Example
924///
925/// ```rust,ignore
926/// use crate::visitor::collect_referenced_types;
927///
928/// let types = collect_referenced_types(&type_interner, type_id);
929/// ```
930pub fn collect_referenced_types(types: &dyn TypeDatabase, type_id: TypeId) -> FxHashSet<TypeId> {
931    collect_all_types(types, type_id)
932}
933
934/// Test a type against a predicate function.
935///
936/// # Example
937///
938/// ```rust,ignore
939/// use crate::{TypeData, LiteralValue, visitor::test_type};
940///
941/// let is_string_literal = test_type(&types, type_id, |key| {
942///     matches!(key, TypeData::Literal(LiteralValue::String(_)))
943/// });
944/// ```
945pub fn test_type<F>(types: &dyn TypeDatabase, type_id: TypeId, predicate: F) -> bool
946where
947    F: Fn(&TypeData) -> bool,
948{
949    let mut visitor = TypePredicateVisitor::new(predicate);
950    visitor.visit_type(types, type_id)
951}
952
953// =============================================================================
954// Recursive Type Visitor - Traverses into nested types
955// =============================================================================
956
957/// A visitor that recursively collects all types referenced by a root type.
958/// Unlike `TypeCollectorVisitor`, this properly traverses into nested structures.
959pub struct RecursiveTypeCollector<'a> {
960    types: &'a dyn TypeDatabase,
961    collected: FxHashSet<TypeId>,
962    guard: crate::recursion::RecursionGuard<TypeId>,
963}
964
965impl<'a> RecursiveTypeCollector<'a> {
966    pub fn new(types: &'a dyn TypeDatabase) -> Self {
967        Self {
968            types,
969            collected: FxHashSet::default(),
970            guard: crate::recursion::RecursionGuard::with_profile(
971                crate::recursion::RecursionProfile::ShallowTraversal,
972            ),
973        }
974    }
975
976    pub fn with_max_depth(types: &'a dyn TypeDatabase, max_depth: usize) -> Self {
977        Self {
978            types,
979            collected: FxHashSet::default(),
980            guard: crate::recursion::RecursionGuard::new(max_depth as u32, 100_000),
981        }
982    }
983
984    /// Collect all types reachable from the given type.
985    pub fn collect(&mut self, type_id: TypeId) -> FxHashSet<TypeId> {
986        self.visit(type_id);
987        std::mem::take(&mut self.collected)
988    }
989
990    fn visit(&mut self, type_id: TypeId) {
991        // Already collected
992        if self.collected.contains(&type_id) {
993            return;
994        }
995
996        // Unified enter: checks depth, cycle, iterations
997        match self.guard.enter(type_id) {
998            crate::recursion::RecursionResult::Entered => {}
999            _ => return,
1000        }
1001
1002        self.collected.insert(type_id);
1003
1004        if let Some(key) = self.types.lookup(type_id) {
1005            self.visit_key(&key);
1006        }
1007
1008        self.guard.leave(type_id);
1009    }
1010
1011    fn visit_key(&mut self, key: &TypeData) {
1012        match key {
1013            TypeData::Intrinsic(_)
1014            | TypeData::Literal(_)
1015            | TypeData::Error
1016            | TypeData::ThisType
1017            | TypeData::BoundParameter(_)
1018            | TypeData::Lazy(_)
1019            | TypeData::Recursive(_)
1020            | TypeData::TypeQuery(_)
1021            | TypeData::UniqueSymbol(_)
1022            | TypeData::ModuleNamespace(_) => {
1023                // Leaf types - nothing to traverse
1024            }
1025            TypeData::NoInfer(inner) => {
1026                // Traverse inner type
1027                self.visit(*inner);
1028            }
1029            TypeData::Object(shape_id) | TypeData::ObjectWithIndex(shape_id) => {
1030                let shape = self.types.object_shape(*shape_id);
1031                for prop in &shape.properties {
1032                    self.visit(prop.type_id);
1033                    self.visit(prop.write_type);
1034                }
1035                if let Some(ref idx) = shape.string_index {
1036                    self.visit(idx.key_type);
1037                    self.visit(idx.value_type);
1038                }
1039                if let Some(ref idx) = shape.number_index {
1040                    self.visit(idx.key_type);
1041                    self.visit(idx.value_type);
1042                }
1043            }
1044            TypeData::Union(list_id) | TypeData::Intersection(list_id) => {
1045                let members = self.types.type_list(*list_id);
1046                for &member in members.iter() {
1047                    self.visit(member);
1048                }
1049            }
1050            TypeData::Array(elem) => {
1051                self.visit(*elem);
1052            }
1053            TypeData::Tuple(list_id) => {
1054                let elements = self.types.tuple_list(*list_id);
1055                for elem in elements.iter() {
1056                    self.visit(elem.type_id);
1057                }
1058            }
1059            TypeData::Function(shape_id) => {
1060                let shape = self.types.function_shape(*shape_id);
1061                for param in &shape.params {
1062                    self.visit(param.type_id);
1063                }
1064                self.visit(shape.return_type);
1065                if let Some(this_type) = shape.this_type {
1066                    self.visit(this_type);
1067                }
1068                if let Some(ref type_predicate) = shape.type_predicate
1069                    && let Some(type_id) = type_predicate.type_id
1070                {
1071                    self.visit(type_id);
1072                }
1073                for type_param in &shape.type_params {
1074                    if let Some(constraint) = type_param.constraint {
1075                        self.visit(constraint);
1076                    }
1077                    if let Some(default) = type_param.default {
1078                        self.visit(default);
1079                    }
1080                }
1081            }
1082            TypeData::Callable(shape_id) => {
1083                let shape = self.types.callable_shape(*shape_id);
1084                for sig in &shape.call_signatures {
1085                    for param in &sig.params {
1086                        self.visit(param.type_id);
1087                    }
1088                    self.visit(sig.return_type);
1089                    if let Some(this_type) = sig.this_type {
1090                        self.visit(this_type);
1091                    }
1092                    if let Some(ref type_predicate) = sig.type_predicate
1093                        && let Some(type_id) = type_predicate.type_id
1094                    {
1095                        self.visit(type_id);
1096                    }
1097                    for type_param in &sig.type_params {
1098                        if let Some(constraint) = type_param.constraint {
1099                            self.visit(constraint);
1100                        }
1101                        if let Some(default) = type_param.default {
1102                            self.visit(default);
1103                        }
1104                    }
1105                }
1106                for sig in &shape.construct_signatures {
1107                    for param in &sig.params {
1108                        self.visit(param.type_id);
1109                    }
1110                    self.visit(sig.return_type);
1111                    if let Some(this_type) = sig.this_type {
1112                        self.visit(this_type);
1113                    }
1114                    if let Some(ref type_predicate) = sig.type_predicate
1115                        && let Some(type_id) = type_predicate.type_id
1116                    {
1117                        self.visit(type_id);
1118                    }
1119                    for type_param in &sig.type_params {
1120                        if let Some(constraint) = type_param.constraint {
1121                            self.visit(constraint);
1122                        }
1123                        if let Some(default) = type_param.default {
1124                            self.visit(default);
1125                        }
1126                    }
1127                }
1128                for prop in &shape.properties {
1129                    self.visit(prop.type_id);
1130                    self.visit(prop.write_type);
1131                }
1132                if let Some(ref sig) = shape.string_index {
1133                    self.visit(sig.key_type);
1134                    self.visit(sig.value_type);
1135                }
1136                if let Some(ref sig) = shape.number_index {
1137                    self.visit(sig.key_type);
1138                    self.visit(sig.value_type);
1139                }
1140            }
1141            TypeData::TypeParameter(info) | TypeData::Infer(info) => {
1142                if let Some(constraint) = info.constraint {
1143                    self.visit(constraint);
1144                }
1145                if let Some(default) = info.default {
1146                    self.visit(default);
1147                }
1148            }
1149            TypeData::Application(app_id) => {
1150                let app = self.types.type_application(*app_id);
1151                self.visit(app.base);
1152                for &arg in &app.args {
1153                    self.visit(arg);
1154                }
1155            }
1156            TypeData::Conditional(cond_id) => {
1157                let cond = self.types.conditional_type(*cond_id);
1158                self.visit(cond.check_type);
1159                self.visit(cond.extends_type);
1160                self.visit(cond.true_type);
1161                self.visit(cond.false_type);
1162            }
1163            TypeData::Mapped(mapped_id) => {
1164                let mapped = self.types.mapped_type(*mapped_id);
1165                if let Some(constraint) = mapped.type_param.constraint {
1166                    self.visit(constraint);
1167                }
1168                if let Some(default) = mapped.type_param.default {
1169                    self.visit(default);
1170                }
1171                self.visit(mapped.constraint);
1172                self.visit(mapped.template);
1173                if let Some(name_type) = mapped.name_type {
1174                    self.visit(name_type);
1175                }
1176            }
1177            TypeData::IndexAccess(obj, idx) => {
1178                self.visit(*obj);
1179                self.visit(*idx);
1180            }
1181            TypeData::TemplateLiteral(list_id) => {
1182                let spans = self.types.template_list(*list_id);
1183                for span in spans.iter() {
1184                    if let crate::types::TemplateSpan::Type(type_id) = span {
1185                        self.visit(*type_id);
1186                    }
1187                }
1188            }
1189            TypeData::KeyOf(inner) | TypeData::ReadonlyType(inner) => {
1190                self.visit(*inner);
1191            }
1192            TypeData::StringIntrinsic { type_arg, .. } => {
1193                self.visit(*type_arg);
1194            }
1195            TypeData::Enum(_def_id, member_type) => {
1196                // Traverse into the structural member type
1197                self.visit(*member_type);
1198            }
1199        }
1200    }
1201}
1202
1203/// Collect all types recursively reachable from a root type.
1204pub fn collect_all_types(types: &dyn TypeDatabase, type_id: TypeId) -> FxHashSet<TypeId> {
1205    let mut collector = RecursiveTypeCollector::new(types);
1206    collector.collect(type_id)
1207}
1208
1209// =============================================================================
1210// Const Assertion Visitor
1211// =============================================================================
1212
1213/// Visitor that applies `as const` transformation to a type.
1214///
1215/// This visitor implements the const assertion logic from TypeScript:
1216/// - Literals: Preserved as-is
1217/// - Arrays: Converted to readonly tuples
1218/// - Tuples: Marked readonly, elements recursively const-asserted
1219/// - Objects: All properties marked readonly, recursively const-asserted
1220/// - Other types: Preserved as-is (any, unknown, primitives, etc.)
1221pub struct ConstAssertionVisitor<'a> {
1222    /// The type database/interner.
1223    pub db: &'a dyn TypeDatabase,
1224    /// Unified recursion guard for cycle detection.
1225    pub guard: crate::recursion::RecursionGuard<TypeId>,
1226}
1227
1228impl<'a> ConstAssertionVisitor<'a> {
1229    /// Create a new `ConstAssertionVisitor`.
1230    pub fn new(db: &'a dyn TypeDatabase) -> Self {
1231        Self {
1232            db,
1233            guard: crate::recursion::RecursionGuard::with_profile(
1234                crate::recursion::RecursionProfile::ConstAssertion,
1235            ),
1236        }
1237    }
1238
1239    /// Apply const assertion to a type, returning the transformed type ID.
1240    pub fn apply_const_assertion(&mut self, type_id: TypeId) -> TypeId {
1241        // Prevent infinite recursion
1242        match self.guard.enter(type_id) {
1243            crate::recursion::RecursionResult::Entered => {}
1244            _ => return type_id,
1245        }
1246
1247        let result = match self.db.lookup(type_id) {
1248            // Arrays: Convert to readonly tuple
1249            Some(TypeData::Array(element_type)) => {
1250                let const_element = self.apply_const_assertion(element_type);
1251                // Arrays become readonly tuples when const-asserted
1252                let tuple_elem = TupleElement {
1253                    type_id: const_element,
1254                    name: None,
1255                    optional: false,
1256                    rest: false,
1257                };
1258                let tuple_type = self.db.tuple(vec![tuple_elem]);
1259                self.db.readonly_type(tuple_type)
1260            }
1261
1262            // Tuples: Mark readonly and recurse on elements
1263            Some(TypeData::Tuple(list_id)) => {
1264                let elements = self.db.tuple_list(list_id);
1265                let const_elements: Vec<TupleElement> = elements
1266                    .iter()
1267                    .map(|elem| {
1268                        let const_type = self.apply_const_assertion(elem.type_id);
1269                        TupleElement {
1270                            type_id: const_type,
1271                            name: elem.name,
1272                            optional: elem.optional,
1273                            rest: elem.rest,
1274                        }
1275                    })
1276                    .collect();
1277                let tuple_type = self.db.tuple(const_elements);
1278                self.db.readonly_type(tuple_type)
1279            }
1280
1281            // Objects: Mark all properties readonly and recurse
1282            Some(TypeData::Object(shape_id)) => {
1283                let shape = self.db.object_shape(shape_id);
1284                let mut new_props = Vec::with_capacity(shape.properties.len());
1285
1286                for prop in &shape.properties {
1287                    let const_prop_type = self.apply_const_assertion(prop.type_id);
1288                    let const_write_type = self.apply_const_assertion(prop.write_type);
1289                    new_props.push(crate::types::PropertyInfo {
1290                        name: prop.name,
1291                        type_id: const_prop_type,
1292                        write_type: const_write_type,
1293                        optional: prop.optional,
1294                        readonly: true, // Mark as readonly
1295                        is_method: prop.is_method,
1296                        visibility: prop.visibility,
1297                        parent_id: prop.parent_id,
1298                    });
1299                }
1300
1301                self.db.object(new_props)
1302            }
1303
1304            // Objects with index signatures
1305            Some(TypeData::ObjectWithIndex(shape_id)) => {
1306                let shape = self.db.object_shape(shape_id);
1307                let mut new_props = Vec::with_capacity(shape.properties.len());
1308
1309                for prop in &shape.properties {
1310                    let const_prop_type = self.apply_const_assertion(prop.type_id);
1311                    let const_write_type = self.apply_const_assertion(prop.write_type);
1312                    new_props.push(crate::types::PropertyInfo {
1313                        name: prop.name,
1314                        type_id: const_prop_type,
1315                        write_type: const_write_type,
1316                        optional: prop.optional,
1317                        readonly: true, // Mark as readonly
1318                        is_method: prop.is_method,
1319                        visibility: prop.visibility,
1320                        parent_id: prop.parent_id,
1321                    });
1322                }
1323
1324                // Mark index signatures as readonly
1325                let string_index =
1326                    shape
1327                        .string_index
1328                        .as_ref()
1329                        .map(|idx| crate::types::IndexSignature {
1330                            key_type: idx.key_type,
1331                            value_type: self.apply_const_assertion(idx.value_type),
1332                            readonly: true,
1333                        });
1334
1335                let number_index =
1336                    shape
1337                        .number_index
1338                        .as_ref()
1339                        .map(|idx| crate::types::IndexSignature {
1340                            key_type: idx.key_type,
1341                            value_type: self.apply_const_assertion(idx.value_type),
1342                            readonly: true,
1343                        });
1344
1345                let mut new_shape = (*shape).clone();
1346                new_shape.properties = new_props;
1347                new_shape.string_index = string_index;
1348                new_shape.number_index = number_index;
1349
1350                self.db.object_with_index(new_shape)
1351            }
1352
1353            // Readonly types: Unwrap, process, re-wrap
1354            Some(TypeData::ReadonlyType(inner)) => {
1355                let const_inner = self.apply_const_assertion(inner);
1356                self.db.readonly_type(const_inner)
1357            }
1358
1359            // Unions: Recursively apply to all members
1360            Some(TypeData::Union(list_id)) => {
1361                let members = self.db.type_list(list_id);
1362                let const_members: Vec<TypeId> = members
1363                    .iter()
1364                    .map(|&m| self.apply_const_assertion(m))
1365                    .collect();
1366                self.db.union(const_members)
1367            }
1368
1369            // Intersections: Recursively apply to all members
1370            Some(TypeData::Intersection(list_id)) => {
1371                let members = self.db.type_list(list_id);
1372                let const_members: Vec<TypeId> = members
1373                    .iter()
1374                    .map(|&m| self.apply_const_assertion(m))
1375                    .collect();
1376                self.db.intersection(const_members)
1377            }
1378
1379            // All other types: preserved as-is
1380            _ => type_id,
1381        };
1382
1383        self.guard.leave(type_id);
1384        result
1385    }
1386}