Skip to main content

tsz_solver/
operations.rs

1//! Type operations and expression evaluation.
2//!
3//! This module contains the "brain" of the type system - all the logic for
4//! evaluating expressions, resolving calls, accessing properties, etc.
5//!
6//! ## Architecture Principle
7//!
8//! The Solver handles **WHAT** (type operations and relations), while the
9//! Checker handles **WHERE** (AST traversal, scoping, control flow).
10//!
11//! All functions here:
12//! - Take `TypeId` as input (not AST nodes)
13//! - Return structured results (not formatted error strings)
14//! - Are pure logic (no side effects, no diagnostic formatting)
15//!
16//! This allows the Solver to be:
17//! - Unit tested without AST nodes
18//! - Reused across different checkers
19//! - Optimized independently
20//!
21//! ## Module Organization
22//!
23//! Some components have been extracted to separate modules:
24//! - `binary_ops`: Binary operation evaluation (+, -, *, /, etc.)
25
26// Re-exports from extracted modules
27// Note: These are intentionally pub re-exported for external API use
28pub use crate::binary_ops::{BinaryOpEvaluator, BinaryOpResult, PrimitiveClass};
29
30use crate::diagnostics::PendingDiagnostic;
31use crate::instantiate::{TypeSubstitution, instantiate_type};
32#[cfg(test)]
33use crate::types::*;
34use crate::types::{
35    CallSignature, CallableShape, CallableShapeId, FunctionShape, FunctionShapeId, IntrinsicKind,
36    LiteralValue, ParamInfo, TemplateSpan, TupleElement, TypeData, TypeId, TypeListId,
37    TypePredicate,
38};
39use crate::visitor::TypeVisitor;
40use crate::{QueryDatabase, TypeDatabase};
41use rustc_hash::{FxHashMap, FxHashSet};
42use std::cell::RefCell;
43use tracing::{debug, trace};
44
45/// Maximum recursion depth for type constraint collection to prevent infinite loops.
46pub const MAX_CONSTRAINT_RECURSION_DEPTH: usize = 100;
47
48pub trait AssignabilityChecker {
49    fn is_assignable_to(&mut self, source: TypeId, target: TypeId) -> bool;
50
51    fn is_assignable_to_strict(&mut self, source: TypeId, target: TypeId) -> bool {
52        self.is_assignable_to(source, target)
53    }
54
55    /// Assignability check for bivariant callback parameters.
56    ///
57    /// This is used for method parameter positions where TypeScript allows
58    /// bivariant checking for function-typed callbacks.
59    fn is_assignable_to_bivariant_callback(&mut self, source: TypeId, target: TypeId) -> bool {
60        self.is_assignable_to(source, target)
61    }
62
63    /// Evaluate/expand a type using the checker's resolver context.
64    /// This is needed during inference constraint collection, where Application types
65    /// like `Func<T>` must be expanded to their structural form (e.g., a Callable).
66    /// The default implementation returns the type unchanged (no resolver available).
67    fn evaluate_type(&mut self, type_id: TypeId) -> TypeId {
68        type_id
69    }
70}
71
72// =============================================================================
73// Function Call Resolution
74// =============================================================================
75
76/// Result of attempting to call a function type.
77#[derive(Clone, Debug)]
78pub enum CallResult {
79    /// Call succeeded, returns the result type
80    Success(TypeId),
81
82    /// Not a callable type
83    NotCallable { type_id: TypeId },
84
85    /// `this` type mismatch
86    ThisTypeMismatch {
87        expected_this: TypeId,
88        actual_this: TypeId,
89    },
90
91    /// Argument count mismatch
92    ArgumentCountMismatch {
93        expected_min: usize,
94        expected_max: Option<usize>,
95        actual: usize,
96    },
97
98    /// Overloaded call with arity "gap": no overload matches this exact arity,
99    /// but overloads exist for two surrounding fixed arities (TS2575).
100    OverloadArgumentCountMismatch {
101        actual: usize,
102        expected_low: usize,
103        expected_high: usize,
104    },
105
106    /// Argument type mismatch at specific position
107    ArgumentTypeMismatch {
108        index: usize,
109        expected: TypeId,
110        actual: TypeId,
111    },
112
113    /// TS2350: Only a void function can be called with the 'new' keyword.
114    NonVoidFunctionCalledWithNew,
115
116    /// Type parameter constraint violation (TS2322, not TS2345).
117    /// Used when inference from callback return types produces a type that
118    /// violates the type parameter's constraint. tsc reports TS2322 on the
119    /// return expression, not TS2345 on the whole callback argument.
120    TypeParameterConstraintViolation {
121        /// The inferred type that violated the constraint
122        inferred_type: TypeId,
123        /// The constraint type that was violated
124        constraint_type: TypeId,
125        /// The return type of the call (for type computation to continue)
126        return_type: TypeId,
127    },
128
129    /// No overload matched (for overloaded functions)
130    NoOverloadMatch {
131        func_type: TypeId,
132        arg_types: Vec<TypeId>,
133        failures: Vec<PendingDiagnostic>,
134        fallback_return: TypeId,
135    },
136}
137
138struct TupleRestExpansion {
139    /// Fixed elements before the variadic portion (prefix)
140    fixed: Vec<TupleElement>,
141    /// The variadic element type (e.g., T for ...T[])
142    variadic: Option<TypeId>,
143    /// Fixed elements after the variadic portion (suffix/tail)
144    tail: Vec<TupleElement>,
145}
146
147/// Evaluates function calls.
148pub struct CallEvaluator<'a, C: AssignabilityChecker> {
149    pub(crate) interner: &'a dyn QueryDatabase,
150    pub(crate) checker: &'a mut C,
151    pub(crate) defaulted_placeholders: FxHashSet<TypeId>,
152    force_bivariant_callbacks: bool,
153    /// Contextual type for the call expression's expected result
154    /// Used for contextual type inference in generic functions
155    pub(crate) contextual_type: Option<TypeId>,
156    /// The `this` type provided by the caller (e.g. `obj` in `obj.method()`)
157    pub(crate) actual_this_type: Option<TypeId>,
158    /// Current recursion depth for `constrain_types` to prevent infinite loops
159    pub(crate) constraint_recursion_depth: RefCell<usize>,
160    /// Visited (source, target) pairs during constraint collection.
161    pub(crate) constraint_pairs: RefCell<FxHashSet<(TypeId, TypeId)>>,
162    /// After a generic call resolves, holds the instantiated type predicate (if any).
163    /// This lets the checker retrieve the predicate with inferred type arguments applied.
164    pub last_instantiated_predicate: Option<(TypePredicate, Vec<ParamInfo>)>,
165}
166
167impl<'a, C: AssignabilityChecker> CallEvaluator<'a, C> {
168    pub fn new(interner: &'a dyn QueryDatabase, checker: &'a mut C) -> Self {
169        CallEvaluator {
170            interner,
171            checker,
172            defaulted_placeholders: FxHashSet::default(),
173            force_bivariant_callbacks: false,
174            contextual_type: None,
175            actual_this_type: None,
176            constraint_recursion_depth: RefCell::new(0),
177            constraint_pairs: RefCell::new(FxHashSet::default()),
178            last_instantiated_predicate: None,
179        }
180    }
181
182    /// Set the actual `this` type for the call evaluation.
183    pub const fn set_actual_this_type(&mut self, type_id: Option<TypeId>) {
184        self.actual_this_type = type_id;
185    }
186
187    /// Set the contextual type for this call evaluation.
188    /// This is used for contextual type inference when the expected return type
189    /// can help constrain generic type parameters.
190    /// Example: `let x: string = id(42)` should infer `T = string` from the context.
191    pub const fn set_contextual_type(&mut self, ctx_type: Option<TypeId>) {
192        self.contextual_type = ctx_type;
193    }
194
195    pub const fn set_force_bivariant_callbacks(&mut self, enabled: bool) {
196        self.force_bivariant_callbacks = enabled;
197    }
198
199    pub(crate) fn is_function_union_compat(
200        &mut self,
201        arg_type: TypeId,
202        mut target_type: TypeId,
203    ) -> bool {
204        if let Some(TypeData::Lazy(def_id)) = self.interner.lookup(target_type)
205            && let Some(resolved) = self.interner.resolve_lazy(def_id, self.interner)
206        {
207            target_type = resolved;
208            debug!(
209                target_type = target_type.0,
210                target_key = ?self.interner.lookup(target_type),
211                "is_function_union_compat: resolved lazy target"
212            );
213        }
214        if !matches!(self.interner.lookup(target_type), Some(TypeData::Union(_))) {
215            let evaluated = self.interner.evaluate_type(target_type);
216            if evaluated != target_type {
217                target_type = evaluated;
218                debug!(
219                    target_type = target_type.0,
220                    target_key = ?self.interner.lookup(target_type),
221                    "is_function_union_compat: evaluated target"
222                );
223            }
224            if let Some(TypeData::Lazy(def_id)) = self.interner.lookup(target_type)
225                && let Some(resolved) = self.interner.resolve_lazy(def_id, self.interner)
226            {
227                target_type = resolved;
228                debug!(
229                    target_type = target_type.0,
230                    target_key = ?self.interner.lookup(target_type),
231                    "is_function_union_compat: resolved lazy target after eval"
232                );
233            }
234        }
235        let Some(TypeData::Union(members_id)) = self.interner.lookup(target_type) else {
236            return false;
237        };
238        if !crate::type_queries::is_callable_type(self.interner, arg_type) {
239            return false;
240        }
241        let members = self.interner.type_list(members_id);
242        if members
243            .iter()
244            .any(|&member| self.checker.is_assignable_to(arg_type, member))
245        {
246            return true;
247        }
248        let synthetic_any_fn = self.interner.function(FunctionShape {
249            type_params: vec![],
250            params: vec![],
251            return_type: TypeId::ANY,
252            this_type: None,
253            type_predicate: None,
254            is_constructor: false,
255            is_method: false,
256        });
257        if members
258            .iter()
259            .any(|&member| self.checker.is_assignable_to(synthetic_any_fn, member))
260        {
261            return true;
262        }
263        members
264            .iter()
265            .any(|&member| self.is_function_like_union_member(member))
266    }
267
268    fn normalize_union_member(&self, mut member: TypeId) -> TypeId {
269        for _ in 0..8 {
270            let next = match self.interner.lookup(member) {
271                Some(TypeData::Lazy(def_id)) => self
272                    .interner
273                    .resolve_lazy(def_id, self.interner)
274                    .unwrap_or(member),
275                Some(TypeData::Application(_) | TypeData::Mapped(_)) => {
276                    self.interner.evaluate_type(member)
277                }
278                _ => member,
279            };
280            if next == member {
281                break;
282            }
283            member = next;
284        }
285        member
286    }
287
288    fn is_function_like_union_member(&self, member: TypeId) -> bool {
289        let member = self.normalize_union_member(member);
290        match self.interner.lookup(member) {
291            Some(TypeData::Intrinsic(IntrinsicKind::Function))
292            | Some(TypeData::Function(_) | TypeData::Callable(_)) => true,
293            Some(TypeData::Object(shape_id) | TypeData::ObjectWithIndex(shape_id)) => {
294                let shape = self.interner.object_shape(shape_id);
295                let apply = self.interner.intern_string("apply");
296                let call = self.interner.intern_string("call");
297                let has_apply = shape.properties.iter().any(|prop| prop.name == apply);
298                let has_call = shape.properties.iter().any(|prop| prop.name == call);
299                has_apply && has_call
300            }
301            Some(TypeData::Union(members_id)) => self
302                .interner
303                .type_list(members_id)
304                .iter()
305                .any(|&m| self.is_function_like_union_member(m)),
306            Some(TypeData::Intersection(members_id)) => self
307                .interner
308                .type_list(members_id)
309                .iter()
310                .any(|&m| self.is_function_like_union_member(m)),
311            _ => false,
312        }
313    }
314
315    pub fn infer_call_signature(&mut self, sig: &CallSignature, arg_types: &[TypeId]) -> TypeId {
316        let func = FunctionShape {
317            params: sig.params.clone(),
318            this_type: sig.this_type,
319            return_type: sig.return_type,
320            type_params: sig.type_params.clone(),
321            type_predicate: sig.type_predicate.clone(),
322            is_constructor: false,
323            is_method: sig.is_method,
324        };
325        match self.resolve_function_call(&func, arg_types) {
326            CallResult::Success(ret) => ret,
327            // Return ERROR instead of ANY to avoid silencing TS2322 errors
328            _ => TypeId::ERROR,
329        }
330    }
331
332    pub fn infer_generic_function(&mut self, func: &FunctionShape, arg_types: &[TypeId]) -> TypeId {
333        match self.resolve_function_call(func, arg_types) {
334            CallResult::Success(ret) => ret,
335            // Return ERROR instead of ANY to avoid silencing TS2322 errors
336            _ => TypeId::ERROR,
337        }
338    }
339
340    /// Retrieves the contextual function signature from a type.
341    ///
342    /// This is used to infer parameter types for function expressions.
343    /// e.g., given `let x: (a: string) => void = (a) => ...`, this returns
344    /// the shape of `(a: string) => void` so we can infer `a` is `string`.
345    ///
346    /// # Arguments
347    /// * `db` - The type database
348    /// * `type_id` - The contextual type to extract a signature from
349    ///
350    /// # Returns
351    /// * `Some(FunctionShape)` if the type suggests a function structure
352    /// * `None` if the type is not callable or has no suitable signature
353    pub fn get_contextual_signature(
354        db: &dyn TypeDatabase,
355        type_id: TypeId,
356    ) -> Option<FunctionShape> {
357        Self::get_contextual_signature_for_arity(db, type_id, None)
358    }
359
360    /// Get the contextual signature for a type, optionally filtering by argument count.
361    /// When `arg_count` is provided, selects the first overload whose arity matches.
362    pub fn get_contextual_signature_for_arity(
363        db: &dyn TypeDatabase,
364        type_id: TypeId,
365        arg_count: Option<usize>,
366    ) -> Option<FunctionShape> {
367        struct ContextualSignatureVisitor<'a> {
368            db: &'a dyn TypeDatabase,
369            arg_count: Option<usize>,
370        }
371
372        impl<'a> TypeVisitor for ContextualSignatureVisitor<'a> {
373            type Output = Option<FunctionShape>;
374
375            fn default_output() -> Self::Output {
376                None
377            }
378
379            fn visit_intrinsic(&mut self, _kind: IntrinsicKind) -> Self::Output {
380                None
381            }
382
383            fn visit_literal(&mut self, _value: &LiteralValue) -> Self::Output {
384                None
385            }
386
387            fn visit_ref(&mut self, ref_id: u32) -> Self::Output {
388                // Resolve the reference by converting to TypeId and recursing
389                // This handles named types like `type Handler<T> = ...`
390                self.visit_type(self.db, TypeId(ref_id))
391            }
392
393            fn visit_function(&mut self, shape_id: u32) -> Self::Output {
394                // Direct match: return the function shape
395                let shape = self.db.function_shape(FunctionShapeId(shape_id));
396                Some(shape.as_ref().clone())
397            }
398
399            fn visit_callable(&mut self, shape_id: u32) -> Self::Output {
400                let shape = self.db.callable_shape(CallableShapeId(shape_id));
401
402                // For contextual typing, prefer call signatures. Fall back to construct
403                // signatures when none exist (super()/new calls have construct sigs only).
404                let signatures = if shape.call_signatures.is_empty() {
405                    &shape.construct_signatures
406                } else {
407                    &shape.call_signatures
408                };
409
410                // If arg_count is provided, select the first overload whose arity matches.
411                let sig = if let Some(count) = self.arg_count {
412                    signatures
413                        .iter()
414                        .find(|sig| {
415                            let min_args =
416                                sig.params.iter().filter(|p| !p.optional && !p.rest).count();
417                            let has_rest = sig.params.iter().any(|p| p.rest);
418                            count >= min_args && (has_rest || count <= sig.params.len())
419                        })
420                        .or_else(|| signatures.first())
421                } else {
422                    signatures.first()
423                };
424
425                sig.map(|sig| FunctionShape {
426                    type_params: sig.type_params.clone(),
427                    params: sig.params.clone(),
428                    this_type: sig.this_type,
429                    return_type: sig.return_type,
430                    type_predicate: sig.type_predicate.clone(),
431                    is_constructor: false,
432                    is_method: sig.is_method,
433                })
434            }
435
436            fn visit_application(&mut self, app_id: u32) -> Self::Output {
437                use crate::types::TypeApplicationId;
438
439                // 1. Retrieve the application data (Base<Args>)
440                let app = self.db.type_application(TypeApplicationId(app_id));
441
442                // 2. Resolve the base type to get the generic function signature
443                // e.g., for Handler<string>, this gets the shape of Handler<T>
444                let base_shape = self.visit_type(self.db, app.base)?;
445
446                // 3. Build the substitution map
447                // Maps generic parameters (e.g., T) to arguments (e.g., string)
448                // This handles default type parameters automatically
449                let subst =
450                    TypeSubstitution::from_args(self.db, &base_shape.type_params, &app.args);
451
452                // Optimization: If no substitution is needed, return base as-is
453                if subst.is_empty() {
454                    return Some(base_shape);
455                }
456
457                // 4. Instantiate the components of the function shape
458                let instantiated_params: Vec<ParamInfo> = base_shape
459                    .params
460                    .iter()
461                    .map(|p| ParamInfo {
462                        name: p.name,
463                        type_id: instantiate_type(self.db, p.type_id, &subst),
464                        optional: p.optional,
465                        rest: p.rest,
466                    })
467                    .collect();
468
469                let instantiated_return = instantiate_type(self.db, base_shape.return_type, &subst);
470
471                let instantiated_this = base_shape
472                    .this_type
473                    .map(|t| instantiate_type(self.db, t, &subst));
474
475                // Handle type predicates (e.g., `x is T`)
476                let instantiated_predicate =
477                    base_shape
478                        .type_predicate
479                        .as_ref()
480                        .map(|pred| TypePredicate {
481                            asserts: pred.asserts,
482                            target: pred.target.clone(),
483                            type_id: pred.type_id.map(|t| instantiate_type(self.db, t, &subst)),
484                            parameter_index: pred.parameter_index,
485                        });
486
487                // 5. Return the concrete FunctionShape
488                Some(FunctionShape {
489                    // The generics are now consumed/applied, so the resulting signature
490                    // is concrete (not generic).
491                    type_params: Vec::new(),
492                    params: instantiated_params,
493                    this_type: instantiated_this,
494                    return_type: instantiated_return,
495                    type_predicate: instantiated_predicate,
496                    is_constructor: base_shape.is_constructor,
497                    is_method: base_shape.is_method,
498                })
499            }
500
501            fn visit_intersection(&mut self, list_id: u32) -> Self::Output {
502                let members = self.db.type_list(TypeListId(list_id));
503                for &member in members.iter() {
504                    if let Some(shape) = self.visit_type(self.db, member) {
505                        return Some(shape);
506                    }
507                }
508                None
509            }
510
511            // Future: Handle Union (return None or intersect of params)
512        }
513
514        let mut visitor = ContextualSignatureVisitor { db, arg_count };
515        visitor.visit_type(db, type_id)
516    }
517
518    /// Resolve a function call: func(args...) -> result
519    ///
520    /// This is pure type logic - no AST nodes, just types in and types out.
521    pub fn resolve_call(&mut self, func_type: TypeId, arg_types: &[TypeId]) -> CallResult {
522        self.last_instantiated_predicate = None;
523        // Look up the function shape
524        let key = match self.interner.lookup(func_type) {
525            Some(k) => k,
526            None => return CallResult::NotCallable { type_id: func_type },
527        };
528
529        match key {
530            TypeData::Function(f_id) => {
531                let shape = self.interner.function_shape(f_id);
532                self.resolve_function_call(shape.as_ref(), arg_types)
533            }
534            TypeData::Callable(c_id) => {
535                let shape = self.interner.callable_shape(c_id);
536                self.resolve_callable_call(shape.as_ref(), arg_types)
537            }
538            TypeData::Union(list_id) => {
539                // Handle union types: if all members are callable with compatible signatures,
540                // the union is callable
541                self.resolve_union_call(func_type, list_id, arg_types)
542            }
543            TypeData::Intersection(list_id) => {
544                // Handle intersection types: if any member is callable, use that
545                // This handles cases like: Function & { prop: number }
546                self.resolve_intersection_call(func_type, list_id, arg_types)
547            }
548            TypeData::Application(_app_id) => {
549                // Handle Application types (e.g., GenericCallable<string>)
550                // Evaluate the application type to properly instantiate its base type with arguments
551                let evaluated = self.checker.evaluate_type(func_type);
552                if evaluated != func_type {
553                    self.resolve_call(evaluated, arg_types)
554                } else {
555                    CallResult::NotCallable { type_id: func_type }
556                }
557            }
558            TypeData::TypeParameter(param_info) => {
559                // For type parameters with callable constraints (e.g., T extends { (): string }),
560                // resolve the call using the constraint type
561                if let Some(constraint) = param_info.constraint {
562                    self.resolve_call(constraint, arg_types)
563                } else {
564                    CallResult::NotCallable { type_id: func_type }
565                }
566            }
567            TypeData::Lazy(_)
568            | TypeData::Conditional(_)
569            | TypeData::IndexAccess(_, _)
570            | TypeData::Mapped(_)
571            | TypeData::TemplateLiteral(_) => {
572                // Resolve meta-types to their actual types before checking callability.
573                // This handles cases like conditional types that resolve to function types,
574                // index access types like T["method"], and mapped types.
575                let resolved = crate::evaluate::evaluate_type(self.interner, func_type);
576                if resolved != func_type {
577                    self.resolve_call(resolved, arg_types)
578                } else {
579                    CallResult::NotCallable { type_id: func_type }
580                }
581            }
582            // The `Function` intrinsic type is callable in TypeScript and returns `any`.
583            // This matches tsc behavior: `declare const f: Function; f()` is valid.
584            TypeData::Intrinsic(IntrinsicKind::Function | IntrinsicKind::Any) => {
585                CallResult::Success(TypeId::ANY)
586            }
587            // `any` is callable and returns `any`
588            // `error` propagates as error
589            TypeData::Error => CallResult::Success(TypeId::ERROR),
590            _ => CallResult::NotCallable { type_id: func_type },
591        }
592    }
593
594    /// Resolve a call on a union type.
595    ///
596    /// This handles cases like:
597    /// - `(() => void) | (() => string)` - all members callable
598    /// - `string | (() => void)` - mixed callable/non-callable (returns `NotCallable`)
599    ///
600    /// When all union members are callable with compatible signatures, this returns
601    /// a union of their return types.
602    fn resolve_union_call(
603        &mut self,
604        union_type: TypeId,
605        list_id: TypeListId,
606        arg_types: &[TypeId],
607    ) -> CallResult {
608        let members = self.interner.type_list(list_id);
609
610        // Check each member of the union
611        let mut return_types = Vec::new();
612        let mut failures = Vec::new();
613
614        for &member in members.iter() {
615            let result = self.resolve_call(member, arg_types);
616            match result {
617                CallResult::Success(return_type) => {
618                    return_types.push(return_type);
619                }
620                CallResult::NotCallable { .. } => {
621                    // At least one member is not callable
622                    // This means the union as a whole is not callable
623                    // (we can't call a union without knowing which branch is active)
624                    return CallResult::NotCallable {
625                        type_id: union_type,
626                    };
627                }
628                other => {
629                    // Track failures for potential overload reporting
630                    failures.push(other);
631                }
632            }
633        }
634
635        // If any members succeeded, return a union of their return types
636        // TypeScript allows calling a union of functions if at least one member accepts the arguments
637        if !return_types.is_empty() {
638            if return_types.len() == 1 {
639                return CallResult::Success(return_types[0]);
640            }
641            // Return a union of all return types
642            let union_result = self.interner.union(return_types);
643            CallResult::Success(union_result)
644        } else if !failures.is_empty() {
645            // At least one member failed with a non-NotCallable error
646            // Check if all failures are ArgumentTypeMismatch - if so, compute the intersection
647            // of all parameter types to get the expected type (e.g., for union of functions
648            // with incompatible parameter types like (x: number) => void | (x: boolean) => void)
649            let all_arg_mismatches = failures
650                .iter()
651                .all(|f| matches!(f, CallResult::ArgumentTypeMismatch { .. }));
652
653            if all_arg_mismatches && !failures.is_empty() {
654                // Extract all parameter types from the failures
655                let mut param_types = Vec::new();
656                for failure in &failures {
657                    if let CallResult::ArgumentTypeMismatch { expected, .. } = failure {
658                        param_types.push(*expected);
659                    }
660                }
661
662                // Compute the intersection of all parameter types
663                // For incompatible primitives like number & boolean, this becomes never
664                let intersected_param = if param_types.len() == 1 {
665                    param_types[0]
666                } else {
667                    // Build intersection by combining all types
668                    let mut result = param_types[0];
669                    for &param_type in &param_types[1..] {
670                        result = self.interner.intersection2(result, param_type);
671                    }
672                    result
673                };
674
675                // Return a single ArgumentTypeMismatch with the intersected type
676                // Use the first argument type as the actual
677                let actual_arg_type =
678                    if let Some(CallResult::ArgumentTypeMismatch { actual, .. }) = failures.first()
679                    {
680                        *actual
681                    } else {
682                        // Should never reach here, but use ERROR instead of UNKNOWN
683                        TypeId::ERROR
684                    };
685
686                return CallResult::ArgumentTypeMismatch {
687                    index: 0,
688                    expected: intersected_param,
689                    actual: actual_arg_type,
690                };
691            }
692
693            // Not all argument type mismatches, return the first failure
694            failures
695                .into_iter()
696                .next()
697                .unwrap_or(CallResult::NotCallable {
698                    type_id: union_type,
699                })
700        } else {
701            // Should not reach here, but handle gracefully
702            CallResult::NotCallable {
703                type_id: union_type,
704            }
705        }
706    }
707
708    /// Resolve a call on an intersection type.
709    ///
710    /// This handles cases like:
711    /// - `Function & { prop: number }` - intersection with callable member
712    /// - Overloaded functions merged via intersection
713    ///
714    /// When at least one intersection member is callable, this delegates to that member.
715    /// For intersections with multiple callable members, we use the first one.
716    fn resolve_intersection_call(
717        &mut self,
718        intersection_type: TypeId,
719        list_id: TypeListId,
720        arg_types: &[TypeId],
721    ) -> CallResult {
722        let members = self.interner.type_list(list_id);
723
724        // For intersection types: if ANY member is callable, the intersection is callable
725        // This is different from unions where ALL members must be callable
726        // We try each member in order and use the first callable one
727        for &member in members.iter() {
728            let result = self.resolve_call(member, arg_types);
729            match result {
730                CallResult::Success(return_type) => {
731                    // Found a callable member - use its return type
732                    return CallResult::Success(return_type);
733                }
734                CallResult::NotCallable { .. } => {
735                    // This member is not callable, try the next one
736                    continue;
737                }
738                other => {
739                    // Got a different error (argument mismatch, etc.)
740                    // Return this error as it's likely the most relevant
741                    return other;
742                }
743            }
744        }
745
746        // No members were callable
747        CallResult::NotCallable {
748            type_id: intersection_type,
749        }
750    }
751
752    /// Expand a `TypeParameter` to its constraint (if it has one).
753    /// This is used when a `TypeParameter` from an outer scope is used as an argument.
754    fn expand_type_param(&self, ty: TypeId) -> TypeId {
755        match self.interner.lookup(ty) {
756            Some(TypeData::TypeParameter(tp)) => tp.constraint.unwrap_or(ty),
757            _ => ty,
758        }
759    }
760
761    /// Resolve a call to a simple function type.
762    pub(crate) fn resolve_function_call(
763        &mut self,
764        func: &FunctionShape,
765        arg_types: &[TypeId],
766    ) -> CallResult {
767        // Handle generic functions FIRST so uninstantiated this_types don't fail assignability
768        if !func.type_params.is_empty() {
769            return self.resolve_generic_call(func, arg_types);
770        }
771
772        // Check `this` context if specified by the function shape
773        if let Some(expected_this) = func.this_type {
774            if let Some(actual_this) = self.actual_this_type {
775                if !self.checker.is_assignable_to(actual_this, expected_this) {
776                    return CallResult::ThisTypeMismatch {
777                        expected_this,
778                        actual_this,
779                    };
780                }
781            }
782            // Note: if `actual_this_type` is None, we technically should check if `void` is assignable to `expected_this`.
783            // But TSC behavior for missing `this` might require strict checking. Let's do it:
784            else if !self.checker.is_assignable_to(TypeId::VOID, expected_this) {
785                return CallResult::ThisTypeMismatch {
786                    expected_this,
787                    actual_this: TypeId::VOID,
788                };
789            }
790        }
791
792        // Check argument count
793        let (min_args, max_args) = self.arg_count_bounds(&func.params);
794
795        if arg_types.len() < min_args {
796            return CallResult::ArgumentCountMismatch {
797                expected_min: min_args,
798                expected_max: max_args,
799                actual: arg_types.len(),
800            };
801        }
802
803        if let Some(max) = max_args
804            && arg_types.len() > max
805        {
806            return CallResult::ArgumentCountMismatch {
807                expected_min: min_args,
808                expected_max: Some(max),
809                actual: arg_types.len(),
810            };
811        }
812
813        // Generic functions handled above
814
815        if let Some(result) = self.check_argument_types(&func.params, arg_types, func.is_method) {
816            return result;
817        }
818
819        CallResult::Success(func.return_type)
820    }
821
822    /// Resolve a call to a generic function by inferring type arguments.
823    fn check_argument_types(
824        &mut self,
825        params: &[ParamInfo],
826        arg_types: &[TypeId],
827        allow_bivariant_callbacks: bool,
828    ) -> Option<CallResult> {
829        self.check_argument_types_with(params, arg_types, false, allow_bivariant_callbacks)
830    }
831
832    pub(crate) fn check_argument_types_with(
833        &mut self,
834        params: &[ParamInfo],
835        arg_types: &[TypeId],
836        strict: bool,
837        allow_bivariant_callbacks: bool,
838    ) -> Option<CallResult> {
839        let arg_count = arg_types.len();
840        for (i, arg_type) in arg_types.iter().enumerate() {
841            let Some(param_type) = self.param_type_for_arg_index(params, i, arg_count) else {
842                break;
843            };
844
845            if *arg_type == param_type {
846                continue;
847            }
848
849            // Expand TypeParameters to their constraints for assignability checking when the
850            // *parameter* expects a concrete type (e.g. `object`) but the argument is an outer
851            // type parameter with a compatible constraint.
852            //
853            // IMPORTANT: Do **not** expand when the parameter type is itself a type parameter;
854            // otherwise a call like `freeze(obj)` where `obj: T extends object` can incorrectly
855            // compare `object` (expanded) against `T` and fail, even though inference would (and
856            // tsc does) infer the inner `T` to the outer `T`.
857            let expanded_arg_type = match self.interner.lookup(param_type) {
858                Some(TypeData::TypeParameter(_) | TypeData::Infer(_)) => *arg_type,
859                _ => self.expand_type_param(*arg_type),
860            };
861
862            if expanded_arg_type == param_type {
863                continue;
864            }
865
866            let assignable = if allow_bivariant_callbacks || self.force_bivariant_callbacks {
867                self.checker
868                    .is_assignable_to_bivariant_callback(expanded_arg_type, param_type)
869            } else if strict {
870                let result = self
871                    .checker
872                    .is_assignable_to_strict(expanded_arg_type, param_type);
873                if !result {
874                    tracing::debug!(
875                        "Strict assignability failed at index {}: {:?} <: {:?}",
876                        i,
877                        self.interner.lookup(expanded_arg_type),
878                        self.interner.lookup(param_type)
879                    );
880                }
881                result
882            } else {
883                self.checker.is_assignable_to(expanded_arg_type, param_type)
884            };
885
886            if !assignable {
887                return Some(CallResult::ArgumentTypeMismatch {
888                    index: i,
889                    expected: param_type,
890                    actual: *arg_type,
891                });
892            }
893        }
894        None
895    }
896
897    pub(crate) fn arg_count_bounds(&self, params: &[ParamInfo]) -> (usize, Option<usize>) {
898        let required = params.iter().filter(|p| !p.optional && !p.rest).count();
899        let rest_param = params.last().filter(|param| param.rest);
900        let Some(rest_param) = rest_param else {
901            return (required, Some(params.len()));
902        };
903
904        let rest_param_type = self.unwrap_readonly(rest_param.type_id);
905        match self.interner.lookup(rest_param_type) {
906            Some(TypeData::Tuple(elements)) => {
907                let elements = self.interner.tuple_list(elements);
908                let (rest_min, rest_max) = self.tuple_length_bounds(&elements);
909                let min = required + rest_min;
910                let max = rest_max.map(|max| required + max);
911                (min, max)
912            }
913            _ => (required, None),
914        }
915    }
916
917    pub(crate) fn param_type_for_arg_index(
918        &self,
919        params: &[ParamInfo],
920        arg_index: usize,
921        arg_count: usize,
922    ) -> Option<TypeId> {
923        let rest_param = params.last().filter(|param| param.rest);
924        let rest_start = if rest_param.is_some() {
925            params.len().saturating_sub(1)
926        } else {
927            params.len()
928        };
929
930        if arg_index < rest_start {
931            return Some(params[arg_index].type_id);
932        }
933
934        let rest_param = rest_param?;
935        let offset = arg_index - rest_start;
936        let rest_arg_count = arg_count.saturating_sub(rest_start);
937
938        let rest_param_type = self.unwrap_readonly(rest_param.type_id);
939        trace!(
940            rest_param_type_id = %rest_param_type.0,
941            rest_param_type_key = ?self.interner.lookup(rest_param_type),
942            "Extracting element type from rest parameter"
943        );
944        match self.interner.lookup(rest_param_type) {
945            Some(TypeData::Array(elem)) => {
946                trace!(
947                    elem_type_id = %elem.0,
948                    elem_type_key = ?self.interner.lookup(elem),
949                    "Extracted array element type"
950                );
951                Some(elem)
952            }
953            Some(TypeData::Tuple(elements)) => {
954                let elements = self.interner.tuple_list(elements);
955                self.tuple_rest_element_type(&elements, offset, rest_arg_count)
956            }
957            other => {
958                trace!(?other, "Rest param is not Array or Tuple, returning as-is");
959                Some(rest_param_type)
960            }
961        }
962    }
963
964    fn tuple_length_bounds(&self, elements: &[TupleElement]) -> (usize, Option<usize>) {
965        let mut min = 0usize;
966        let mut max = 0usize;
967        let mut variadic = false;
968
969        for elem in elements {
970            if elem.rest {
971                let expansion = self.expand_tuple_rest(elem.type_id);
972                for fixed in expansion.fixed {
973                    max += 1;
974                    if !fixed.optional {
975                        min += 1;
976                    }
977                }
978                if expansion.variadic.is_some() {
979                    variadic = true;
980                }
981                // Count tail elements from nested tuple spreads
982                for tail_elem in expansion.tail {
983                    max += 1;
984                    if !tail_elem.optional {
985                        min += 1;
986                    }
987                }
988                continue;
989            }
990            max += 1;
991            if !elem.optional {
992                min += 1;
993            }
994        }
995
996        (min, if variadic { None } else { Some(max) })
997    }
998
999    fn tuple_rest_element_type(
1000        &self,
1001        elements: &[TupleElement],
1002        offset: usize,
1003        rest_arg_count: usize,
1004    ) -> Option<TypeId> {
1005        let rest_index = elements.iter().position(|elem| elem.rest);
1006        let Some(rest_index) = rest_index else {
1007            return elements.get(offset).map(|elem| elem.type_id);
1008        };
1009
1010        let (prefix, rest_and_tail) = elements.split_at(rest_index);
1011        let rest_elem = &rest_and_tail[0];
1012        let outer_tail = &rest_and_tail[1..];
1013
1014        let expansion = self.expand_tuple_rest(rest_elem.type_id);
1015        let prefix_len = prefix.len();
1016        let rest_fixed_len = expansion.fixed.len();
1017        let expansion_tail_len = expansion.tail.len();
1018        let outer_tail_len = outer_tail.len();
1019        // Total suffix = expansion.tail + outer_tail
1020        let total_suffix_len = expansion_tail_len + outer_tail_len;
1021
1022        if let Some(variadic) = expansion.variadic {
1023            let suffix_start = rest_arg_count.saturating_sub(total_suffix_len);
1024            if offset >= suffix_start {
1025                let suffix_index = offset - suffix_start;
1026                // First check expansion.tail, then outer_tail
1027                if suffix_index < expansion_tail_len {
1028                    return Some(expansion.tail[suffix_index].type_id);
1029                }
1030                let outer_index = suffix_index - expansion_tail_len;
1031                return outer_tail.get(outer_index).map(|elem| elem.type_id);
1032            }
1033            if offset < prefix_len {
1034                return Some(prefix[offset].type_id);
1035            }
1036            let fixed_end = prefix_len + rest_fixed_len;
1037            if offset < fixed_end {
1038                return Some(expansion.fixed[offset - prefix_len].type_id);
1039            }
1040            return Some(variadic);
1041        }
1042
1043        // No variadic: prefix + expansion.fixed + expansion.tail + outer_tail
1044        let mut index = offset;
1045        if index < prefix_len {
1046            return Some(prefix[index].type_id);
1047        }
1048        index -= prefix_len;
1049        if index < rest_fixed_len {
1050            return Some(expansion.fixed[index].type_id);
1051        }
1052        index -= rest_fixed_len;
1053        if index < expansion_tail_len {
1054            return Some(expansion.tail[index].type_id);
1055        }
1056        index -= expansion_tail_len;
1057        outer_tail.get(index).map(|elem| elem.type_id)
1058    }
1059
1060    pub(crate) fn rest_element_type(&self, type_id: TypeId) -> TypeId {
1061        match self.interner.lookup(type_id) {
1062            Some(TypeData::Array(elem)) => elem,
1063            _ => type_id,
1064        }
1065    }
1066
1067    /// Maximum iterations for type unwrapping loops to prevent infinite loops.
1068    const MAX_UNWRAP_ITERATIONS: usize = 1000;
1069
1070    fn unwrap_readonly(&self, mut type_id: TypeId) -> TypeId {
1071        let mut iterations = 0;
1072        loop {
1073            iterations += 1;
1074            if iterations > Self::MAX_UNWRAP_ITERATIONS {
1075                // Safety limit reached - return current type to prevent infinite loop
1076                return type_id;
1077            }
1078            match self.interner.lookup(type_id) {
1079                Some(TypeData::ReadonlyType(inner) | TypeData::NoInfer(inner)) => {
1080                    type_id = inner;
1081                }
1082                _ => return type_id,
1083            }
1084        }
1085    }
1086
1087    fn expand_tuple_rest(&self, type_id: TypeId) -> TupleRestExpansion {
1088        match self.interner.lookup(type_id) {
1089            Some(TypeData::Array(elem)) => TupleRestExpansion {
1090                fixed: Vec::new(),
1091                variadic: Some(elem),
1092                tail: Vec::new(),
1093            },
1094            Some(TypeData::Tuple(elements)) => {
1095                let elements = self.interner.tuple_list(elements);
1096                let mut fixed = Vec::new();
1097                for (i, elem) in elements.iter().enumerate() {
1098                    if elem.rest {
1099                        let inner = self.expand_tuple_rest(elem.type_id);
1100                        fixed.extend(inner.fixed);
1101                        // Capture tail elements: inner.tail + elements after the rest
1102                        let mut tail = inner.tail;
1103                        tail.extend(elements[i + 1..].iter().cloned());
1104                        return TupleRestExpansion {
1105                            fixed,
1106                            variadic: inner.variadic,
1107                            tail,
1108                        };
1109                    }
1110                    fixed.push(elem.clone());
1111                }
1112                TupleRestExpansion {
1113                    fixed,
1114                    variadic: None,
1115                    tail: Vec::new(),
1116                }
1117            }
1118            _ => TupleRestExpansion {
1119                fixed: Vec::new(),
1120                variadic: Some(type_id),
1121                tail: Vec::new(),
1122            },
1123        }
1124    }
1125
1126    pub(crate) fn rest_tuple_inference_target(
1127        &mut self,
1128        params: &[ParamInfo],
1129        arg_types: &[TypeId],
1130        var_map: &FxHashMap<TypeId, crate::infer::InferenceVar>,
1131    ) -> Option<(usize, TypeId, TypeId)> {
1132        let rest_param = params.last().filter(|param| param.rest)?;
1133        let rest_start = params.len().saturating_sub(1);
1134
1135        let rest_param_type = self.unwrap_readonly(rest_param.type_id);
1136        let target = match self.interner.lookup(rest_param_type) {
1137            Some(TypeData::TypeParameter(_)) if var_map.contains_key(&rest_param_type) => {
1138                Some((rest_start, rest_param_type, 0))
1139            }
1140            Some(TypeData::Tuple(elements)) => {
1141                let elements = self.interner.tuple_list(elements);
1142                elements.iter().enumerate().find_map(|(i, elem)| {
1143                    if !elem.rest {
1144                        return None;
1145                    }
1146                    if !var_map.contains_key(&elem.type_id) {
1147                        return None;
1148                    }
1149
1150                    // Count trailing elements after the variadic part, but allow optional
1151                    // tail elements to be omitted when they don't match.
1152                    let tail = &elements[i + 1..];
1153                    let min_index = rest_start + i;
1154                    let mut trailing_count = 0usize;
1155                    let mut arg_index = arg_types.len();
1156                    for tail_elem in tail.iter().rev() {
1157                        if arg_index <= min_index {
1158                            break;
1159                        }
1160                        let arg_type = arg_types[arg_index - 1];
1161                        let assignable = self.checker.is_assignable_to(arg_type, tail_elem.type_id);
1162                        if tail_elem.optional && !assignable {
1163                            break;
1164                        }
1165                        trailing_count += 1;
1166                        arg_index -= 1;
1167                    }
1168                    Some((rest_start + i, elem.type_id, trailing_count))
1169                })
1170            }
1171            _ => None,
1172        }?;
1173
1174        let (start_index, target_type, trailing_count) = target;
1175        if start_index >= arg_types.len() {
1176            return None;
1177        }
1178
1179        // Extract the arguments that should be inferred for the variadic type parameter,
1180        // excluding both prefix fixed elements and trailing fixed elements.
1181        // For example, for `...args: [number, ...T, boolean]` with call `foo(1, 'a', 'b', true)`:
1182        //   - rest_start = 0 (rest param index)
1183        //   - start_index = 1 (after the prefix `number`)
1184        //   - trailing_count = 1 (the trailing `boolean`)
1185        //   - we should infer T from ['a', 'b'], not [1, 'a', 'b', true]
1186        //
1187        // The variadic arguments start at start_index and end before trailing elements.
1188        let end_index = arg_types.len().saturating_sub(trailing_count);
1189        let tuple_elements: Vec<TupleElement> = if start_index < end_index {
1190            arg_types[start_index..end_index]
1191                .iter()
1192                .map(|&ty| TupleElement {
1193                    type_id: ty,
1194                    name: None,
1195                    optional: false,
1196                    rest: false,
1197                })
1198                .collect()
1199        } else {
1200            Vec::new()
1201        };
1202        Some((
1203            start_index,
1204            target_type,
1205            self.interner.tuple(tuple_elements),
1206        ))
1207    }
1208
1209    pub(crate) fn type_contains_placeholder(
1210        &self,
1211        ty: TypeId,
1212        var_map: &FxHashMap<TypeId, crate::infer::InferenceVar>,
1213        visited: &mut FxHashSet<TypeId>,
1214    ) -> bool {
1215        if var_map.contains_key(&ty) {
1216            return true;
1217        }
1218        if !visited.insert(ty) {
1219            return false;
1220        }
1221
1222        let key = match self.interner.lookup(ty) {
1223            Some(key) => key,
1224            None => return false,
1225        };
1226
1227        match key {
1228            TypeData::Array(elem) => self.type_contains_placeholder(elem, var_map, visited),
1229            TypeData::Tuple(elements) => {
1230                let elements = self.interner.tuple_list(elements);
1231                elements
1232                    .iter()
1233                    .any(|elem| self.type_contains_placeholder(elem.type_id, var_map, visited))
1234            }
1235            TypeData::Union(members) | TypeData::Intersection(members) => {
1236                let members = self.interner.type_list(members);
1237                members
1238                    .iter()
1239                    .any(|&member| self.type_contains_placeholder(member, var_map, visited))
1240            }
1241            TypeData::Object(shape_id) => {
1242                let shape = self.interner.object_shape(shape_id);
1243                shape
1244                    .properties
1245                    .iter()
1246                    .any(|prop| self.type_contains_placeholder(prop.type_id, var_map, visited))
1247            }
1248            TypeData::ObjectWithIndex(shape_id) => {
1249                let shape = self.interner.object_shape(shape_id);
1250                shape
1251                    .properties
1252                    .iter()
1253                    .any(|prop| self.type_contains_placeholder(prop.type_id, var_map, visited))
1254                    || shape.string_index.as_ref().is_some_and(|idx| {
1255                        self.type_contains_placeholder(idx.key_type, var_map, visited)
1256                            || self.type_contains_placeholder(idx.value_type, var_map, visited)
1257                    })
1258                    || shape.number_index.as_ref().is_some_and(|idx| {
1259                        self.type_contains_placeholder(idx.key_type, var_map, visited)
1260                            || self.type_contains_placeholder(idx.value_type, var_map, visited)
1261                    })
1262            }
1263            TypeData::Application(app_id) => {
1264                let app = self.interner.type_application(app_id);
1265                self.type_contains_placeholder(app.base, var_map, visited)
1266                    || app
1267                        .args
1268                        .iter()
1269                        .any(|&arg| self.type_contains_placeholder(arg, var_map, visited))
1270            }
1271            TypeData::Function(shape_id) => {
1272                let shape = self.interner.function_shape(shape_id);
1273                shape.type_params.iter().any(|tp| {
1274                    tp.constraint.is_some_and(|constraint| {
1275                        self.type_contains_placeholder(constraint, var_map, visited)
1276                    }) || tp.default.is_some_and(|default| {
1277                        self.type_contains_placeholder(default, var_map, visited)
1278                    })
1279                }) || shape
1280                    .params
1281                    .iter()
1282                    .any(|param| self.type_contains_placeholder(param.type_id, var_map, visited))
1283                    || shape.this_type.is_some_and(|this_type| {
1284                        self.type_contains_placeholder(this_type, var_map, visited)
1285                    })
1286                    || self.type_contains_placeholder(shape.return_type, var_map, visited)
1287                    || shape.type_predicate.as_ref().is_some_and(|pred| {
1288                        pred.type_id
1289                            .is_some_and(|ty| self.type_contains_placeholder(ty, var_map, visited))
1290                    })
1291            }
1292            TypeData::Callable(shape_id) => {
1293                let shape = self.interner.callable_shape(shape_id);
1294                let in_call = shape.call_signatures.iter().any(|sig| {
1295                    sig.type_params.iter().any(|tp| {
1296                        tp.constraint.is_some_and(|constraint| {
1297                            self.type_contains_placeholder(constraint, var_map, visited)
1298                        }) || tp.default.is_some_and(|default| {
1299                            self.type_contains_placeholder(default, var_map, visited)
1300                        })
1301                    }) || sig.params.iter().any(|param| {
1302                        self.type_contains_placeholder(param.type_id, var_map, visited)
1303                    }) || sig.this_type.is_some_and(|this_type| {
1304                        self.type_contains_placeholder(this_type, var_map, visited)
1305                    }) || self.type_contains_placeholder(sig.return_type, var_map, visited)
1306                        || sig.type_predicate.as_ref().is_some_and(|pred| {
1307                            pred.type_id.is_some_and(|ty| {
1308                                self.type_contains_placeholder(ty, var_map, visited)
1309                            })
1310                        })
1311                });
1312                if in_call {
1313                    return true;
1314                }
1315                let in_construct = shape.construct_signatures.iter().any(|sig| {
1316                    sig.type_params.iter().any(|tp| {
1317                        tp.constraint.is_some_and(|constraint| {
1318                            self.type_contains_placeholder(constraint, var_map, visited)
1319                        }) || tp.default.is_some_and(|default| {
1320                            self.type_contains_placeholder(default, var_map, visited)
1321                        })
1322                    }) || sig.params.iter().any(|param| {
1323                        self.type_contains_placeholder(param.type_id, var_map, visited)
1324                    }) || sig.this_type.is_some_and(|this_type| {
1325                        self.type_contains_placeholder(this_type, var_map, visited)
1326                    }) || self.type_contains_placeholder(sig.return_type, var_map, visited)
1327                        || sig.type_predicate.as_ref().is_some_and(|pred| {
1328                            pred.type_id.is_some_and(|ty| {
1329                                self.type_contains_placeholder(ty, var_map, visited)
1330                            })
1331                        })
1332                });
1333                if in_construct {
1334                    return true;
1335                }
1336                shape
1337                    .properties
1338                    .iter()
1339                    .any(|prop| self.type_contains_placeholder(prop.type_id, var_map, visited))
1340            }
1341            TypeData::Conditional(cond_id) => {
1342                let cond = self.interner.conditional_type(cond_id);
1343                self.type_contains_placeholder(cond.check_type, var_map, visited)
1344                    || self.type_contains_placeholder(cond.extends_type, var_map, visited)
1345                    || self.type_contains_placeholder(cond.true_type, var_map, visited)
1346                    || self.type_contains_placeholder(cond.false_type, var_map, visited)
1347            }
1348            TypeData::Mapped(mapped_id) => {
1349                let mapped = self.interner.mapped_type(mapped_id);
1350                mapped.type_param.constraint.is_some_and(|constraint| {
1351                    self.type_contains_placeholder(constraint, var_map, visited)
1352                }) || mapped.type_param.default.is_some_and(|default| {
1353                    self.type_contains_placeholder(default, var_map, visited)
1354                }) || self.type_contains_placeholder(mapped.constraint, var_map, visited)
1355                    || self.type_contains_placeholder(mapped.template, var_map, visited)
1356            }
1357            TypeData::IndexAccess(obj, idx) => {
1358                self.type_contains_placeholder(obj, var_map, visited)
1359                    || self.type_contains_placeholder(idx, var_map, visited)
1360            }
1361            TypeData::KeyOf(operand)
1362            | TypeData::ReadonlyType(operand)
1363            | TypeData::NoInfer(operand) => {
1364                self.type_contains_placeholder(operand, var_map, visited)
1365            }
1366            TypeData::TemplateLiteral(spans) => {
1367                let spans = self.interner.template_list(spans);
1368                spans.iter().any(|span| match span {
1369                    TemplateSpan::Text(_) => false,
1370                    TemplateSpan::Type(inner) => {
1371                        self.type_contains_placeholder(*inner, var_map, visited)
1372                    }
1373                })
1374            }
1375            TypeData::StringIntrinsic { type_arg, .. } => {
1376                self.type_contains_placeholder(type_arg, var_map, visited)
1377            }
1378            TypeData::Enum(_def_id, member_type) => {
1379                self.type_contains_placeholder(member_type, var_map, visited)
1380            }
1381            TypeData::TypeParameter(_)
1382            | TypeData::Infer(_)
1383            | TypeData::Intrinsic(_)
1384            | TypeData::Literal(_)
1385            | TypeData::Lazy(_)
1386            | TypeData::Recursive(_)
1387            | TypeData::BoundParameter(_)
1388            | TypeData::TypeQuery(_)
1389            | TypeData::UniqueSymbol(_)
1390            | TypeData::ThisType
1391            | TypeData::ModuleNamespace(_)
1392            | TypeData::Error => false,
1393        }
1394    }
1395
1396    /// Check if a type is contextually sensitive (requires contextual typing for inference).
1397    ///
1398    /// Contextually sensitive types include:
1399    /// - Function types (lambda expressions)
1400    /// - Callable types (object with call signatures)
1401    /// - Union/Intersection types containing contextually sensitive members
1402    /// - Object literals with callable properties (methods)
1403    ///
1404    /// These types need deferred inference in Round 2 after non-contextual
1405    /// arguments have been processed and type variables have been fixed.
1406    pub(crate) fn is_contextually_sensitive(&self, type_id: TypeId) -> bool {
1407        let key = match self.interner.lookup(type_id) {
1408            Some(key) => key,
1409            None => return false,
1410        };
1411
1412        match key {
1413            // Function and callable types are contextually sensitive (lambdas or objects
1414            // with call signatures).
1415            TypeData::Function(_) | TypeData::Callable(_) => true,
1416
1417            // Union/Intersection: contextually sensitive if any member is
1418            TypeData::Union(members) | TypeData::Intersection(members) => {
1419                let members = self.interner.type_list(members);
1420                members
1421                    .iter()
1422                    .any(|&member| self.is_contextually_sensitive(member))
1423            }
1424
1425            // Object types: check if any property is callable (has methods)
1426            TypeData::Object(shape_id) | TypeData::ObjectWithIndex(shape_id) => {
1427                let shape = self.interner.object_shape(shape_id);
1428                shape
1429                    .properties
1430                    .iter()
1431                    .any(|prop| self.is_contextually_sensitive(prop.type_id))
1432            }
1433
1434            // Array types: check element type
1435            TypeData::Array(elem) => self.is_contextually_sensitive(elem),
1436
1437            // Tuple types: check all elements
1438            TypeData::Tuple(elements) => {
1439                let elements = self.interner.tuple_list(elements);
1440                elements
1441                    .iter()
1442                    .any(|elem| self.is_contextually_sensitive(elem.type_id))
1443            }
1444
1445            // Type applications: check base and arguments
1446            TypeData::Application(app_id) => {
1447                let app = self.interner.type_application(app_id);
1448                self.is_contextually_sensitive(app.base)
1449                    || app
1450                        .args
1451                        .iter()
1452                        .any(|&arg| self.is_contextually_sensitive(arg))
1453            }
1454
1455            // Readonly types: look through to inner type
1456            TypeData::ReadonlyType(inner) | TypeData::NoInfer(inner) => {
1457                self.is_contextually_sensitive(inner)
1458            }
1459
1460            // Type parameters with constraints: check constraint
1461            TypeData::TypeParameter(info) | TypeData::Infer(info) => info
1462                .constraint
1463                .is_some_and(|constraint| self.is_contextually_sensitive(constraint)),
1464
1465            // Index access: check both object and key types
1466            TypeData::IndexAccess(obj, key) => {
1467                self.is_contextually_sensitive(obj) || self.is_contextually_sensitive(key)
1468            }
1469
1470            // Conditional types: check all branches
1471            TypeData::Conditional(cond_id) => {
1472                let cond = self.interner.conditional_type(cond_id);
1473                self.is_contextually_sensitive(cond.check_type)
1474                    || self.is_contextually_sensitive(cond.extends_type)
1475                    || self.is_contextually_sensitive(cond.true_type)
1476                    || self.is_contextually_sensitive(cond.false_type)
1477            }
1478
1479            // Mapped types: check constraint and template
1480            TypeData::Mapped(mapped_id) => {
1481                let mapped = self.interner.mapped_type(mapped_id);
1482                self.is_contextually_sensitive(mapped.constraint)
1483                    || self.is_contextually_sensitive(mapped.template)
1484            }
1485
1486            // KeyOf, StringIntrinsic: check operand
1487            TypeData::KeyOf(operand)
1488            | TypeData::StringIntrinsic {
1489                type_arg: operand, ..
1490            } => self.is_contextually_sensitive(operand),
1491
1492            // Enum types: check member type
1493            TypeData::Enum(_def_id, member_type) => self.is_contextually_sensitive(member_type),
1494
1495            // Template literals: check type spans
1496            TypeData::TemplateLiteral(spans) => {
1497                let spans = self.interner.template_list(spans);
1498                spans.iter().any(|span| match span {
1499                    TemplateSpan::Text(_) => false,
1500                    TemplateSpan::Type(inner) => self.is_contextually_sensitive(*inner),
1501                })
1502            }
1503
1504            // Non-contextually sensitive types
1505            TypeData::Intrinsic(_)
1506            | TypeData::Literal(_)
1507            | TypeData::Lazy(_)
1508            | TypeData::Recursive(_)
1509            | TypeData::BoundParameter(_)
1510            | TypeData::TypeQuery(_)
1511            | TypeData::UniqueSymbol(_)
1512            | TypeData::ThisType
1513            | TypeData::ModuleNamespace(_)
1514            | TypeData::Error => false,
1515        }
1516    }
1517
1518    /// Resolve a call to a callable type (with overloads).
1519    pub(crate) fn resolve_callable_call(
1520        &mut self,
1521        callable: &CallableShape,
1522        arg_types: &[TypeId],
1523    ) -> CallResult {
1524        // If there are no call signatures at all, this type is not callable
1525        // (e.g., a class constructor without call signatures)
1526        if callable.call_signatures.is_empty() {
1527            return CallResult::NotCallable {
1528                type_id: self.interner.callable(callable.clone()),
1529            };
1530        }
1531
1532        if callable.call_signatures.len() == 1 {
1533            let sig = &callable.call_signatures[0];
1534            let func = FunctionShape {
1535                params: sig.params.clone(),
1536                this_type: sig.this_type,
1537                return_type: sig.return_type,
1538                type_params: sig.type_params.clone(),
1539                type_predicate: sig.type_predicate.clone(),
1540                is_constructor: false,
1541                is_method: sig.is_method,
1542            };
1543            return self.resolve_function_call(&func, arg_types);
1544        }
1545
1546        // Try each call signature
1547        let mut failures = Vec::new();
1548        let mut all_arg_count_mismatches = true;
1549        let mut min_expected = usize::MAX;
1550        let mut max_expected = 0;
1551        let mut any_has_rest = false;
1552        let actual_count = arg_types.len();
1553        let mut exact_expected_counts = FxHashSet::default();
1554        // Track if exactly one overload matched argument count but had a type mismatch.
1555        // When there is a single "count-compatible" overload that fails only on types,
1556        // tsc reports TS2345 (the inner type error) rather than TS2769 (no overload matched).
1557        let mut type_mismatch_count: usize = 0;
1558        let mut first_type_mismatch: Option<(usize, TypeId, TypeId)> = None; // (index, expected, actual)
1559        let mut all_mismatches_identical = true;
1560        let mut has_non_count_non_type_failure = false;
1561
1562        for sig in &callable.call_signatures {
1563            // Convert CallSignature to FunctionShape
1564            let func = FunctionShape {
1565                params: sig.params.clone(),
1566                this_type: sig.this_type,
1567                return_type: sig.return_type,
1568                type_params: sig.type_params.clone(),
1569                type_predicate: sig.type_predicate.clone(),
1570                is_constructor: false,
1571                is_method: sig.is_method,
1572            };
1573            tracing::debug!("resolve_callable_call: signature = {sig:?}");
1574
1575            match self.resolve_function_call(&func, arg_types) {
1576                CallResult::Success(ret) => return CallResult::Success(ret),
1577                CallResult::TypeParameterConstraintViolation { return_type, .. } => {
1578                    // Constraint violation is a "near match" - return the type
1579                    // for overload resolution (treat as success with error)
1580                    return CallResult::Success(return_type);
1581                }
1582                CallResult::ArgumentTypeMismatch {
1583                    index,
1584                    expected,
1585                    actual,
1586                } => {
1587                    all_arg_count_mismatches = false;
1588                    type_mismatch_count += 1;
1589                    if type_mismatch_count == 1 {
1590                        first_type_mismatch = Some((index, expected, actual));
1591                    } else if first_type_mismatch != Some((index, expected, actual)) {
1592                        all_mismatches_identical = false;
1593                    }
1594                    failures.push(
1595                        crate::diagnostics::PendingDiagnosticBuilder::argument_not_assignable(
1596                            actual, expected,
1597                        ),
1598                    );
1599                }
1600                CallResult::ArgumentCountMismatch {
1601                    expected_min,
1602                    expected_max,
1603                    actual,
1604                } => {
1605                    if expected_max.is_none() {
1606                        any_has_rest = true;
1607                    } else if expected_min == expected_max.unwrap_or(expected_min) {
1608                        exact_expected_counts.insert(expected_min);
1609                    }
1610                    let expected = expected_max.unwrap_or(expected_min);
1611                    min_expected = min_expected.min(expected_min);
1612                    max_expected = max_expected.max(expected);
1613                    failures.push(
1614                        crate::diagnostics::PendingDiagnosticBuilder::argument_count_mismatch(
1615                            expected, actual,
1616                        ),
1617                    );
1618                }
1619                _ => {
1620                    all_arg_count_mismatches = false;
1621                    has_non_count_non_type_failure = true;
1622                }
1623            }
1624        }
1625
1626        // If all signatures failed due to argument count mismatch, report TS2554 instead of TS2769
1627        if all_arg_count_mismatches && !failures.is_empty() {
1628            if !any_has_rest
1629                && !exact_expected_counts.is_empty()
1630                && !exact_expected_counts.contains(&actual_count)
1631            {
1632                let mut lower = None;
1633                let mut upper = None;
1634                for &count in &exact_expected_counts {
1635                    if count < actual_count {
1636                        lower = Some(lower.map_or(count, |prev: usize| prev.max(count)));
1637                    } else if count > actual_count {
1638                        upper = Some(upper.map_or(count, |prev: usize| prev.min(count)));
1639                    }
1640                }
1641                if let (Some(expected_low), Some(expected_high)) = (lower, upper) {
1642                    return CallResult::OverloadArgumentCountMismatch {
1643                        actual: actual_count,
1644                        expected_low,
1645                        expected_high,
1646                    };
1647                }
1648            }
1649            return CallResult::ArgumentCountMismatch {
1650                expected_min: min_expected,
1651                expected_max: if any_has_rest {
1652                    None
1653                } else if max_expected > min_expected {
1654                    Some(max_expected)
1655                } else {
1656                    Some(min_expected)
1657                },
1658                actual: actual_count,
1659            };
1660        }
1661
1662        // If all type mismatches are identical (or there's exactly one), and no other failures occurred,
1663        // report TS2345 (the inner type error) instead of TS2769. This handles duplicate signatures
1664        // or overloads where the failing parameter has the exact same type in all matching overloads.
1665        if !has_non_count_non_type_failure
1666            && type_mismatch_count > 0
1667            && all_mismatches_identical
1668            && let Some((index, expected, actual)) = first_type_mismatch
1669        {
1670            return CallResult::ArgumentTypeMismatch {
1671                index,
1672                expected,
1673                actual,
1674            };
1675        }
1676
1677        // If we got here, no signature matched
1678        CallResult::NoOverloadMatch {
1679            func_type: self.interner.callable(callable.clone()),
1680            arg_types: arg_types.to_vec(),
1681            failures,
1682            fallback_return: callable
1683                .call_signatures
1684                .first()
1685                .map(|s| s.return_type)
1686                .unwrap_or(TypeId::ANY),
1687        }
1688    }
1689}
1690
1691pub fn infer_call_signature<C: AssignabilityChecker>(
1692    interner: &dyn QueryDatabase,
1693    checker: &mut C,
1694    sig: &CallSignature,
1695    arg_types: &[TypeId],
1696) -> TypeId {
1697    let mut evaluator = CallEvaluator::new(interner, checker);
1698    evaluator.infer_call_signature(sig, arg_types)
1699}
1700
1701pub fn infer_generic_function<C: AssignabilityChecker>(
1702    interner: &dyn QueryDatabase,
1703    checker: &mut C,
1704    func: &FunctionShape,
1705    arg_types: &[TypeId],
1706) -> TypeId {
1707    let mut evaluator = CallEvaluator::new(interner, checker);
1708    evaluator.infer_generic_function(func, arg_types)
1709}
1710
1711pub fn resolve_call_with_checker<C: AssignabilityChecker>(
1712    interner: &dyn QueryDatabase,
1713    checker: &mut C,
1714    func_type: TypeId,
1715    arg_types: &[TypeId],
1716    force_bivariant_callbacks: bool,
1717    contextual_type: Option<TypeId>,
1718    actual_this_type: Option<TypeId>,
1719) -> (CallResult, Option<(TypePredicate, Vec<ParamInfo>)>) {
1720    let mut evaluator = CallEvaluator::new(interner, checker);
1721    evaluator.set_force_bivariant_callbacks(force_bivariant_callbacks);
1722    evaluator.set_contextual_type(contextual_type);
1723    evaluator.set_actual_this_type(actual_this_type);
1724    let result = evaluator.resolve_call(func_type, arg_types);
1725    let predicate = evaluator.last_instantiated_predicate.take();
1726    (result, predicate)
1727}
1728
1729pub fn resolve_new_with_checker<C: AssignabilityChecker>(
1730    interner: &dyn QueryDatabase,
1731    checker: &mut C,
1732    type_id: TypeId,
1733    arg_types: &[TypeId],
1734    force_bivariant_callbacks: bool,
1735) -> CallResult {
1736    let mut evaluator = CallEvaluator::new(interner, checker);
1737    evaluator.set_force_bivariant_callbacks(force_bivariant_callbacks);
1738    evaluator.resolve_new(type_id, arg_types)
1739}
1740
1741pub fn compute_contextual_types_with_compat_checker<'a, R, F>(
1742    interner: &'a dyn QueryDatabase,
1743    resolver: &'a R,
1744    shape: &FunctionShape,
1745    arg_types: &[TypeId],
1746    contextual_type: Option<TypeId>,
1747    configure_checker: F,
1748) -> TypeSubstitution
1749where
1750    R: crate::TypeResolver,
1751    F: FnOnce(&mut crate::CompatChecker<'a, R>),
1752{
1753    let mut checker = crate::CompatChecker::with_resolver(interner, resolver);
1754    configure_checker(&mut checker);
1755
1756    let mut evaluator = CallEvaluator::new(interner, &mut checker);
1757    evaluator.set_contextual_type(contextual_type);
1758    evaluator.compute_contextual_types(shape, arg_types)
1759}
1760
1761pub fn get_contextual_signature_with_compat_checker(
1762    db: &dyn TypeDatabase,
1763    type_id: TypeId,
1764) -> Option<FunctionShape> {
1765    CallEvaluator::<crate::CompatChecker>::get_contextual_signature(db, type_id)
1766}
1767
1768// Re-exports from extracted modules
1769pub use crate::operations_generics::{GenericInstantiationResult, solve_generic_instantiation};
1770pub use crate::operations_iterators::{
1771    IteratorInfo, get_async_iterable_element_type, get_iterator_info,
1772};
1773
1774#[cfg(test)]
1775#[path = "../tests/operations_tests.rs"]
1776mod tests;