Skip to main content

tsz_solver/operations/
mod.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//! Components are organized into separate modules:
24//! - `binary_ops`: Binary operation evaluation (+, -, *, /, etc.)
25//! - `call_args`: Argument checking, parameter analysis, tuple rest handling, placeholder detection
26//! - `constraints`: Type constraint collection for generic inference
27//! - `constructors`: Constructor (new) expression resolution
28//! - `generic_call`: Generic function call inference
29//! - `generics`: Generic type instantiation validation
30//! - `iterators`: Iterator/async iterator type extraction
31//! - `property`: Property access resolution (includes helpers for mapped, primitive, array, etc.)
32//! - `property_readonly`: Readonly property checks
33//! - `property_visitor`: `TypeVisitor` impl for `PropertyAccessEvaluator`
34//! - `compound_assignment`: Compound assignment operator classification and fallback types
35//! - `expression_ops`: Expression type computation (conditional, template, best common type)
36//! - `widening`: Type widening (literal → primitive) and `as const` assertion
37
38pub mod binary_ops;
39mod call_args;
40pub mod compound_assignment;
41mod constraints;
42mod constructors;
43pub mod expression_ops;
44mod generic_call;
45pub mod generics;
46pub mod iterators;
47pub mod property;
48mod property_readonly;
49mod property_visitor;
50pub mod widening;
51
52// Re-exports from submodules
53pub use binary_ops::{BinaryOpEvaluator, BinaryOpResult, PrimitiveClass};
54pub use compound_assignment::*;
55pub use expression_ops::*;
56pub use widening::*;
57
58use crate::diagnostics::PendingDiagnostic;
59use crate::instantiation::instantiate::{TypeSubstitution, instantiate_type};
60#[cfg(test)]
61use crate::types::*;
62use crate::types::{
63    CallSignature, CallableShape, CallableShapeId, FunctionShape, FunctionShapeId, IntrinsicKind,
64    LiteralValue, ParamInfo, TypeData, TypeId, TypeListId, TypePredicate,
65};
66use crate::visitor::TypeVisitor;
67use crate::{QueryDatabase, TypeDatabase};
68use rustc_hash::{FxHashMap, FxHashSet};
69use std::cell::RefCell;
70use tracing::debug;
71
72/// Maximum recursion depth for type constraint collection to prevent infinite loops.
73pub const MAX_CONSTRAINT_RECURSION_DEPTH: usize = 100;
74/// Maximum number of constrain-types steps per call evaluator pass.
75/// This caps pathological recursive inference explosions while preserving
76/// normal inference behavior on real-world calls.
77pub const MAX_CONSTRAINT_STEPS: usize = 20_000;
78
79pub trait AssignabilityChecker {
80    fn is_assignable_to(&mut self, source: TypeId, target: TypeId) -> bool;
81
82    fn is_assignable_to_strict(&mut self, source: TypeId, target: TypeId) -> bool {
83        self.is_assignable_to(source, target)
84    }
85
86    /// Assignability check for bivariant callback parameters.
87    ///
88    /// This is used for method parameter positions where TypeScript allows
89    /// bivariant checking for function-typed callbacks.
90    fn is_assignable_to_bivariant_callback(&mut self, source: TypeId, target: TypeId) -> bool {
91        self.is_assignable_to(source, target)
92    }
93
94    /// Evaluate/expand a type using the checker's resolver context.
95    /// This is needed during inference constraint collection, where Application types
96    /// like `Func<T>` must be expanded to their structural form (e.g., a Callable).
97    /// The default implementation returns the type unchanged (no resolver available).
98    fn evaluate_type(&mut self, type_id: TypeId) -> TypeId {
99        type_id
100    }
101}
102
103// =============================================================================
104// Function Call Resolution
105// =============================================================================
106
107/// Result of attempting to call a function type.
108#[derive(Clone, Debug)]
109pub enum CallResult {
110    /// Call succeeded, returns the result type
111    Success(TypeId),
112
113    /// Not a callable type
114    NotCallable { type_id: TypeId },
115
116    /// `this` type mismatch
117    ThisTypeMismatch {
118        expected_this: TypeId,
119        actual_this: TypeId,
120    },
121
122    /// Argument count mismatch
123    ArgumentCountMismatch {
124        expected_min: usize,
125        expected_max: Option<usize>,
126        actual: usize,
127    },
128
129    /// Overloaded call with arity "gap": no overload matches this exact arity,
130    /// but overloads exist for two surrounding fixed arities (TS2575).
131    OverloadArgumentCountMismatch {
132        actual: usize,
133        expected_low: usize,
134        expected_high: usize,
135    },
136
137    /// Argument type mismatch at specific position
138    ArgumentTypeMismatch {
139        index: usize,
140        expected: TypeId,
141        actual: TypeId,
142    },
143
144    /// TS2350: Only a void function can be called with the 'new' keyword.
145    NonVoidFunctionCalledWithNew,
146
147    /// Type parameter constraint violation (TS2322, not TS2345).
148    /// Used when inference from callback return types produces a type that
149    /// violates the type parameter's constraint. tsc reports TS2322 on the
150    /// return expression, not TS2345 on the whole callback argument.
151    TypeParameterConstraintViolation {
152        /// The inferred type that violated the constraint
153        inferred_type: TypeId,
154        /// The constraint type that was violated
155        constraint_type: TypeId,
156        /// The return type of the call (for type computation to continue)
157        return_type: TypeId,
158    },
159
160    /// No overload matched (for overloaded functions)
161    NoOverloadMatch {
162        func_type: TypeId,
163        arg_types: Vec<TypeId>,
164        failures: Vec<PendingDiagnostic>,
165        fallback_return: TypeId,
166    },
167}
168
169/// Evaluates function calls.
170pub struct CallEvaluator<'a, C: AssignabilityChecker> {
171    pub(crate) interner: &'a dyn QueryDatabase,
172    pub(crate) checker: &'a mut C,
173    pub(crate) defaulted_placeholders: FxHashSet<TypeId>,
174    force_bivariant_callbacks: bool,
175    /// Contextual type for the call expression's expected result
176    /// Used for contextual type inference in generic functions
177    pub(crate) contextual_type: Option<TypeId>,
178    /// The `this` type provided by the caller (e.g. `obj` in `obj.method()`)
179    pub(crate) actual_this_type: Option<TypeId>,
180    /// Current recursion depth for `constrain_types` to prevent infinite loops
181    pub(crate) constraint_recursion_depth: RefCell<usize>,
182    /// Total constrain-types steps for the current inference pass.
183    pub(crate) constraint_step_count: RefCell<usize>,
184    /// Visited (source, target) pairs during constraint collection.
185    pub(crate) constraint_pairs: RefCell<FxHashSet<(TypeId, TypeId)>>,
186    /// Memoized fixed members for target union types during one inference pass.
187    /// Keyed by the union `TypeId` used as the target in constrain-types.
188    pub(crate) constraint_fixed_union_members: RefCell<FxHashMap<TypeId, FxHashSet<TypeId>>>,
189    /// After a generic call resolves, holds the instantiated type predicate (if any).
190    /// This lets the checker retrieve the predicate with inferred type arguments applied.
191    pub last_instantiated_predicate: Option<(TypePredicate, Vec<ParamInfo>)>,
192}
193
194impl<'a, C: AssignabilityChecker> CallEvaluator<'a, C> {
195    pub fn new(interner: &'a dyn QueryDatabase, checker: &'a mut C) -> Self {
196        CallEvaluator {
197            interner,
198            checker,
199            defaulted_placeholders: FxHashSet::default(),
200            force_bivariant_callbacks: false,
201            contextual_type: None,
202            actual_this_type: None,
203            constraint_recursion_depth: RefCell::new(0),
204            constraint_step_count: RefCell::new(0),
205            constraint_pairs: RefCell::new(FxHashSet::default()),
206            constraint_fixed_union_members: RefCell::new(FxHashMap::default()),
207            last_instantiated_predicate: None,
208        }
209    }
210
211    /// Set the actual `this` type for the call evaluation.
212    pub const fn set_actual_this_type(&mut self, type_id: Option<TypeId>) {
213        self.actual_this_type = type_id;
214    }
215
216    /// Set the contextual type for this call evaluation.
217    /// This is used for contextual type inference when the expected return type
218    /// can help constrain generic type parameters.
219    /// Example: `let x: string = id(42)` should infer `T = string` from the context.
220    pub const fn set_contextual_type(&mut self, ctx_type: Option<TypeId>) {
221        self.contextual_type = ctx_type;
222    }
223
224    pub const fn set_force_bivariant_callbacks(&mut self, enabled: bool) {
225        self.force_bivariant_callbacks = enabled;
226    }
227
228    pub(crate) fn is_function_union_compat(
229        &mut self,
230        arg_type: TypeId,
231        mut target_type: TypeId,
232    ) -> bool {
233        if let Some(TypeData::Lazy(def_id)) = self.interner.lookup(target_type)
234            && let Some(resolved) = self.interner.resolve_lazy(def_id, self.interner)
235        {
236            target_type = resolved;
237            debug!(
238                target_type = target_type.0,
239                target_key = ?self.interner.lookup(target_type),
240                "is_function_union_compat: resolved lazy target"
241            );
242        }
243        if !matches!(self.interner.lookup(target_type), Some(TypeData::Union(_))) {
244            let evaluated = self.interner.evaluate_type(target_type);
245            if evaluated != target_type {
246                target_type = evaluated;
247                debug!(
248                    target_type = target_type.0,
249                    target_key = ?self.interner.lookup(target_type),
250                    "is_function_union_compat: evaluated target"
251                );
252            }
253            if let Some(TypeData::Lazy(def_id)) = self.interner.lookup(target_type)
254                && let Some(resolved) = self.interner.resolve_lazy(def_id, self.interner)
255            {
256                target_type = resolved;
257                debug!(
258                    target_type = target_type.0,
259                    target_key = ?self.interner.lookup(target_type),
260                    "is_function_union_compat: resolved lazy target after eval"
261                );
262            }
263        }
264        let Some(TypeData::Union(members_id)) = self.interner.lookup(target_type) else {
265            return false;
266        };
267        if !crate::type_queries::is_callable_type(self.interner, arg_type) {
268            return false;
269        }
270        let members = self.interner.type_list(members_id);
271        if members
272            .iter()
273            .any(|&member| self.checker.is_assignable_to(arg_type, member))
274        {
275            return true;
276        }
277        let synthetic_any_fn = self.interner.function(FunctionShape {
278            type_params: vec![],
279            params: vec![],
280            return_type: TypeId::ANY,
281            this_type: None,
282            type_predicate: None,
283            is_constructor: false,
284            is_method: false,
285        });
286        if members
287            .iter()
288            .any(|&member| self.checker.is_assignable_to(synthetic_any_fn, member))
289        {
290            return true;
291        }
292        members
293            .iter()
294            .any(|&member| self.is_function_like_union_member(member))
295    }
296
297    fn normalize_union_member(&self, mut member: TypeId) -> TypeId {
298        for _ in 0..8 {
299            let next = match self.interner.lookup(member) {
300                Some(TypeData::Lazy(def_id)) => self
301                    .interner
302                    .resolve_lazy(def_id, self.interner)
303                    .unwrap_or(member),
304                Some(TypeData::Application(_) | TypeData::Mapped(_)) => {
305                    self.interner.evaluate_type(member)
306                }
307                _ => member,
308            };
309            if next == member {
310                break;
311            }
312            member = next;
313        }
314        member
315    }
316
317    fn is_function_like_union_member(&self, member: TypeId) -> bool {
318        let member = self.normalize_union_member(member);
319        match self.interner.lookup(member) {
320            Some(TypeData::Intrinsic(IntrinsicKind::Function))
321            | Some(TypeData::Function(_) | TypeData::Callable(_)) => true,
322            Some(TypeData::Object(shape_id) | TypeData::ObjectWithIndex(shape_id)) => {
323                let shape = self.interner.object_shape(shape_id);
324                let apply = self.interner.intern_string("apply");
325                let call = self.interner.intern_string("call");
326                let has_apply = shape.properties.iter().any(|prop| prop.name == apply);
327                let has_call = shape.properties.iter().any(|prop| prop.name == call);
328                has_apply && has_call
329            }
330            Some(TypeData::Union(members_id)) => self
331                .interner
332                .type_list(members_id)
333                .iter()
334                .any(|&m| self.is_function_like_union_member(m)),
335            Some(TypeData::Intersection(members_id)) => self
336                .interner
337                .type_list(members_id)
338                .iter()
339                .any(|&m| self.is_function_like_union_member(m)),
340            _ => false,
341        }
342    }
343
344    pub fn infer_call_signature(&mut self, sig: &CallSignature, arg_types: &[TypeId]) -> TypeId {
345        let func = FunctionShape {
346            params: sig.params.clone(),
347            this_type: sig.this_type,
348            return_type: sig.return_type,
349            type_params: sig.type_params.clone(),
350            type_predicate: sig.type_predicate.clone(),
351            is_constructor: false,
352            is_method: sig.is_method,
353        };
354        match self.resolve_function_call(&func, arg_types) {
355            CallResult::Success(ret) => ret,
356            // Return ERROR instead of ANY to avoid silencing TS2322 errors
357            _ => TypeId::ERROR,
358        }
359    }
360
361    pub fn infer_generic_function(&mut self, func: &FunctionShape, arg_types: &[TypeId]) -> TypeId {
362        match self.resolve_function_call(func, arg_types) {
363            CallResult::Success(ret) => ret,
364            // Return ERROR instead of ANY to avoid silencing TS2322 errors
365            _ => TypeId::ERROR,
366        }
367    }
368
369    /// Retrieves the contextual function signature from a type.
370    ///
371    /// This is used to infer parameter types for function expressions.
372    /// e.g., given `let x: (a: string) => void = (a) => ...`, this returns
373    /// the shape of `(a: string) => void` so we can infer `a` is `string`.
374    ///
375    /// # Arguments
376    /// * `db` - The type database
377    /// * `type_id` - The contextual type to extract a signature from
378    ///
379    /// # Returns
380    /// * `Some(FunctionShape)` if the type suggests a function structure
381    /// * `None` if the type is not callable or has no suitable signature
382    pub fn get_contextual_signature(
383        db: &dyn TypeDatabase,
384        type_id: TypeId,
385    ) -> Option<FunctionShape> {
386        Self::get_contextual_signature_for_arity(db, type_id, None)
387    }
388
389    /// Get the contextual signature for a type, optionally filtering by argument count.
390    /// When `arg_count` is provided, selects the first overload whose arity matches.
391    pub fn get_contextual_signature_for_arity(
392        db: &dyn TypeDatabase,
393        type_id: TypeId,
394        arg_count: Option<usize>,
395    ) -> Option<FunctionShape> {
396        struct ContextualSignatureVisitor<'a> {
397            db: &'a dyn TypeDatabase,
398            arg_count: Option<usize>,
399        }
400
401        impl<'a> TypeVisitor for ContextualSignatureVisitor<'a> {
402            type Output = Option<FunctionShape>;
403
404            fn default_output() -> Self::Output {
405                None
406            }
407
408            fn visit_intrinsic(&mut self, _kind: IntrinsicKind) -> Self::Output {
409                None
410            }
411
412            fn visit_literal(&mut self, _value: &LiteralValue) -> Self::Output {
413                None
414            }
415
416            fn visit_ref(&mut self, ref_id: u32) -> Self::Output {
417                // Resolve the reference by converting to TypeId and recursing
418                // This handles named types like `type Handler<T> = ...`
419                self.visit_type(self.db, TypeId(ref_id))
420            }
421
422            fn visit_function(&mut self, shape_id: u32) -> Self::Output {
423                // Direct match: return the function shape
424                let shape = self.db.function_shape(FunctionShapeId(shape_id));
425                Some(shape.as_ref().clone())
426            }
427
428            fn visit_callable(&mut self, shape_id: u32) -> Self::Output {
429                let shape = self.db.callable_shape(CallableShapeId(shape_id));
430
431                // For contextual typing, prefer call signatures. Fall back to construct
432                // signatures when none exist (super()/new calls have construct sigs only).
433                let signatures = if shape.call_signatures.is_empty() {
434                    &shape.construct_signatures
435                } else {
436                    &shape.call_signatures
437                };
438
439                // If arg_count is provided, select the first overload whose arity matches.
440                let sig = if let Some(count) = self.arg_count {
441                    signatures
442                        .iter()
443                        .find(|sig| {
444                            let min_args =
445                                sig.params.iter().filter(|p| !p.optional && !p.rest).count();
446                            let has_rest = sig.params.iter().any(|p| p.rest);
447                            count >= min_args && (has_rest || count <= sig.params.len())
448                        })
449                        .or_else(|| signatures.first())
450                } else {
451                    signatures.first()
452                };
453
454                sig.map(|sig| FunctionShape {
455                    type_params: sig.type_params.clone(),
456                    params: sig.params.clone(),
457                    this_type: sig.this_type,
458                    return_type: sig.return_type,
459                    type_predicate: sig.type_predicate.clone(),
460                    is_constructor: false,
461                    is_method: sig.is_method,
462                })
463            }
464
465            fn visit_application(&mut self, app_id: u32) -> Self::Output {
466                use crate::types::TypeApplicationId;
467
468                // 1. Retrieve the application data (Base<Args>)
469                let app = self.db.type_application(TypeApplicationId(app_id));
470
471                // 2. Resolve the base type to get the generic function signature
472                // e.g., for Handler<string>, this gets the shape of Handler<T>
473                let base_shape = self.visit_type(self.db, app.base)?;
474
475                // 3. Build the substitution map
476                // Maps generic parameters (e.g., T) to arguments (e.g., string)
477                // This handles default type parameters automatically
478                let subst =
479                    TypeSubstitution::from_args(self.db, &base_shape.type_params, &app.args);
480
481                // Optimization: If no substitution is needed, return base as-is
482                if subst.is_empty() {
483                    return Some(base_shape);
484                }
485
486                // 4. Instantiate the components of the function shape
487                let instantiated_params: Vec<ParamInfo> = base_shape
488                    .params
489                    .iter()
490                    .map(|p| ParamInfo {
491                        name: p.name,
492                        type_id: instantiate_type(self.db, p.type_id, &subst),
493                        optional: p.optional,
494                        rest: p.rest,
495                    })
496                    .collect();
497
498                let instantiated_return = instantiate_type(self.db, base_shape.return_type, &subst);
499
500                let instantiated_this = base_shape
501                    .this_type
502                    .map(|t| instantiate_type(self.db, t, &subst));
503
504                // Handle type predicates (e.g., `x is T`)
505                let instantiated_predicate =
506                    base_shape
507                        .type_predicate
508                        .as_ref()
509                        .map(|pred| TypePredicate {
510                            asserts: pred.asserts,
511                            target: pred.target.clone(),
512                            type_id: pred.type_id.map(|t| instantiate_type(self.db, t, &subst)),
513                            parameter_index: pred.parameter_index,
514                        });
515
516                // 5. Return the concrete FunctionShape
517                Some(FunctionShape {
518                    // The generics are now consumed/applied, so the resulting signature
519                    // is concrete (not generic).
520                    type_params: Vec::new(),
521                    params: instantiated_params,
522                    this_type: instantiated_this,
523                    return_type: instantiated_return,
524                    type_predicate: instantiated_predicate,
525                    is_constructor: base_shape.is_constructor,
526                    is_method: base_shape.is_method,
527                })
528            }
529
530            fn visit_intersection(&mut self, list_id: u32) -> Self::Output {
531                let members = self.db.type_list(TypeListId(list_id));
532                for &member in members.iter() {
533                    if let Some(shape) = self.visit_type(self.db, member) {
534                        return Some(shape);
535                    }
536                }
537                None
538            }
539
540            // Future: Handle Union (return None or intersect of params)
541        }
542
543        let mut visitor = ContextualSignatureVisitor { db, arg_count };
544        visitor.visit_type(db, type_id)
545    }
546
547    /// Resolve a function call: func(args...) -> result
548    ///
549    /// This is pure type logic - no AST nodes, just types in and types out.
550    pub fn resolve_call(&mut self, func_type: TypeId, arg_types: &[TypeId]) -> CallResult {
551        self.last_instantiated_predicate = None;
552        // Look up the function shape
553        let key = match self.interner.lookup(func_type) {
554            Some(k) => k,
555            None => return CallResult::NotCallable { type_id: func_type },
556        };
557
558        match key {
559            TypeData::Function(f_id) => {
560                let shape = self.interner.function_shape(f_id);
561                self.resolve_function_call(shape.as_ref(), arg_types)
562            }
563            TypeData::Callable(c_id) => {
564                let shape = self.interner.callable_shape(c_id);
565                self.resolve_callable_call(shape.as_ref(), arg_types)
566            }
567            TypeData::Union(list_id) => {
568                // Handle union types: if all members are callable with compatible signatures,
569                // the union is callable
570                self.resolve_union_call(func_type, list_id, arg_types)
571            }
572            TypeData::Intersection(list_id) => {
573                // Handle intersection types: if any member is callable, use that
574                // This handles cases like: Function & { prop: number }
575                self.resolve_intersection_call(func_type, list_id, arg_types)
576            }
577            TypeData::Application(_app_id) => {
578                // Handle Application types (e.g., GenericCallable<string>)
579                // Evaluate the application type to properly instantiate its base type with arguments
580                let evaluated = self.checker.evaluate_type(func_type);
581                if evaluated != func_type {
582                    self.resolve_call(evaluated, arg_types)
583                } else {
584                    CallResult::NotCallable { type_id: func_type }
585                }
586            }
587            TypeData::TypeParameter(param_info) => {
588                // For type parameters with callable constraints (e.g., T extends { (): string }),
589                // resolve the call using the constraint type
590                if let Some(constraint) = param_info.constraint {
591                    self.resolve_call(constraint, arg_types)
592                } else {
593                    CallResult::NotCallable { type_id: func_type }
594                }
595            }
596            TypeData::Lazy(_)
597            | TypeData::Conditional(_)
598            | TypeData::IndexAccess(_, _)
599            | TypeData::Mapped(_)
600            | TypeData::TemplateLiteral(_) => {
601                // Resolve meta-types to their actual types before checking callability.
602                // This handles cases like conditional types that resolve to function types,
603                // index access types like T["method"], and mapped types.
604                let resolved = crate::evaluation::evaluate::evaluate_type(self.interner, func_type);
605                if resolved != func_type {
606                    self.resolve_call(resolved, arg_types)
607                } else {
608                    CallResult::NotCallable { type_id: func_type }
609                }
610            }
611            // The `Function` intrinsic type is callable in TypeScript and returns `any`.
612            // This matches tsc behavior: `declare const f: Function; f()` is valid.
613            TypeData::Intrinsic(IntrinsicKind::Function | IntrinsicKind::Any) => {
614                CallResult::Success(TypeId::ANY)
615            }
616            // `any` is callable and returns `any`
617            // `error` propagates as error
618            TypeData::Error => CallResult::Success(TypeId::ERROR),
619            _ => CallResult::NotCallable { type_id: func_type },
620        }
621    }
622
623    /// Resolve a call on a union type.
624    ///
625    /// This handles cases like:
626    /// - `(() => void) | (() => string)` - all members callable
627    /// - `string | (() => void)` - mixed callable/non-callable (returns `NotCallable`)
628    ///
629    /// When all union members are callable with compatible signatures, this returns
630    /// a union of their return types.
631    fn resolve_union_call(
632        &mut self,
633        union_type: TypeId,
634        list_id: TypeListId,
635        arg_types: &[TypeId],
636    ) -> CallResult {
637        let members = self.interner.type_list(list_id);
638
639        // Check each member of the union
640        let mut return_types = Vec::new();
641        let mut failures = Vec::new();
642
643        for &member in members.iter() {
644            let result = self.resolve_call(member, arg_types);
645            match result {
646                CallResult::Success(return_type) => {
647                    return_types.push(return_type);
648                }
649                CallResult::NotCallable { .. } => {
650                    // At least one member is not callable
651                    // This means the union as a whole is not callable
652                    // (we can't call a union without knowing which branch is active)
653                    return CallResult::NotCallable {
654                        type_id: union_type,
655                    };
656                }
657                other => {
658                    // Track failures for potential overload reporting
659                    failures.push(other);
660                }
661            }
662        }
663
664        // If any members succeeded, return a union of their return types
665        // TypeScript allows calling a union of functions if at least one member accepts the arguments
666        if !return_types.is_empty() {
667            if return_types.len() == 1 {
668                return CallResult::Success(return_types[0]);
669            }
670            // Return a union of all return types
671            let union_result = self.interner.union(return_types);
672            CallResult::Success(union_result)
673        } else if !failures.is_empty() {
674            // At least one member failed with a non-NotCallable error
675            // Check if all failures are ArgumentTypeMismatch - if so, compute the intersection
676            // of all parameter types to get the expected type (e.g., for union of functions
677            // with incompatible parameter types like (x: number) => void | (x: boolean) => void)
678            let all_arg_mismatches = failures
679                .iter()
680                .all(|f| matches!(f, CallResult::ArgumentTypeMismatch { .. }));
681
682            if all_arg_mismatches && !failures.is_empty() {
683                // Extract all parameter types from the failures
684                let mut param_types = Vec::new();
685                for failure in &failures {
686                    if let CallResult::ArgumentTypeMismatch { expected, .. } = failure {
687                        param_types.push(*expected);
688                    }
689                }
690
691                // Compute the intersection of all parameter types
692                // For incompatible primitives like number & boolean, this becomes never
693                let intersected_param = if param_types.len() == 1 {
694                    param_types[0]
695                } else {
696                    // Build intersection by combining all types
697                    let mut result = param_types[0];
698                    for &param_type in &param_types[1..] {
699                        result = self.interner.intersection2(result, param_type);
700                    }
701                    result
702                };
703
704                // Return a single ArgumentTypeMismatch with the intersected type
705                // Use the first argument type as the actual
706                let actual_arg_type =
707                    if let Some(CallResult::ArgumentTypeMismatch { actual, .. }) = failures.first()
708                    {
709                        *actual
710                    } else {
711                        // Should never reach here, but use ERROR instead of UNKNOWN
712                        TypeId::ERROR
713                    };
714
715                return CallResult::ArgumentTypeMismatch {
716                    index: 0,
717                    expected: intersected_param,
718                    actual: actual_arg_type,
719                };
720            }
721
722            // Not all argument type mismatches, return the first failure
723            failures
724                .into_iter()
725                .next()
726                .unwrap_or(CallResult::NotCallable {
727                    type_id: union_type,
728                })
729        } else {
730            // Should not reach here, but handle gracefully
731            CallResult::NotCallable {
732                type_id: union_type,
733            }
734        }
735    }
736
737    /// Resolve a call on an intersection type.
738    ///
739    /// This handles cases like:
740    /// - `Function & { prop: number }` - intersection with callable member
741    /// - Overloaded functions merged via intersection
742    ///
743    /// When at least one intersection member is callable, this delegates to that member.
744    /// For intersections with multiple callable members, we use the first one.
745    fn resolve_intersection_call(
746        &mut self,
747        intersection_type: TypeId,
748        list_id: TypeListId,
749        arg_types: &[TypeId],
750    ) -> CallResult {
751        let members = self.interner.type_list(list_id);
752
753        // For intersection types: if ANY member is callable, the intersection is callable
754        // This is different from unions where ALL members must be callable
755        // We try each member in order and use the first callable one
756        for &member in members.iter() {
757            let result = self.resolve_call(member, arg_types);
758            match result {
759                CallResult::Success(return_type) => {
760                    // Found a callable member - use its return type
761                    return CallResult::Success(return_type);
762                }
763                CallResult::NotCallable { .. } => {
764                    // This member is not callable, try the next one
765                    continue;
766                }
767                other => {
768                    // Got a different error (argument mismatch, etc.)
769                    // Return this error as it's likely the most relevant
770                    return other;
771                }
772            }
773        }
774
775        // No members were callable
776        CallResult::NotCallable {
777            type_id: intersection_type,
778        }
779    }
780
781    /// Resolve a call to a simple function type.
782    pub(crate) fn resolve_function_call(
783        &mut self,
784        func: &FunctionShape,
785        arg_types: &[TypeId],
786    ) -> CallResult {
787        // Handle generic functions FIRST so uninstantiated this_types don't fail assignability
788        if !func.type_params.is_empty() {
789            return self.resolve_generic_call(func, arg_types);
790        }
791
792        // Check `this` context if specified by the function shape
793        if let Some(expected_this) = func.this_type {
794            if let Some(actual_this) = self.actual_this_type {
795                if !self.checker.is_assignable_to(actual_this, expected_this) {
796                    return CallResult::ThisTypeMismatch {
797                        expected_this,
798                        actual_this,
799                    };
800                }
801            }
802            // Note: if `actual_this_type` is None, we technically should check if `void` is assignable to `expected_this`.
803            // But TSC behavior for missing `this` might require strict checking. Let's do it:
804            else if !self.checker.is_assignable_to(TypeId::VOID, expected_this) {
805                return CallResult::ThisTypeMismatch {
806                    expected_this,
807                    actual_this: TypeId::VOID,
808                };
809            }
810        }
811
812        // Check argument count
813        let (min_args, max_args) = self.arg_count_bounds(&func.params);
814
815        if arg_types.len() < min_args {
816            return CallResult::ArgumentCountMismatch {
817                expected_min: min_args,
818                expected_max: max_args,
819                actual: arg_types.len(),
820            };
821        }
822
823        if let Some(max) = max_args
824            && arg_types.len() > max
825        {
826            return CallResult::ArgumentCountMismatch {
827                expected_min: min_args,
828                expected_max: Some(max),
829                actual: arg_types.len(),
830            };
831        }
832
833        // Generic functions handled above
834
835        if let Some(result) = self.check_argument_types(&func.params, arg_types, func.is_method) {
836            return result;
837        }
838
839        CallResult::Success(func.return_type)
840    }
841
842    /// Resolve a call to a callable type (with overloads).
843    pub(crate) fn resolve_callable_call(
844        &mut self,
845        callable: &CallableShape,
846        arg_types: &[TypeId],
847    ) -> CallResult {
848        // If there are no call signatures at all, this type is not callable
849        // (e.g., a class constructor without call signatures)
850        if callable.call_signatures.is_empty() {
851            return CallResult::NotCallable {
852                type_id: self.interner.callable(callable.clone()),
853            };
854        }
855
856        if callable.call_signatures.len() == 1 {
857            let sig = &callable.call_signatures[0];
858            let func = FunctionShape {
859                params: sig.params.clone(),
860                this_type: sig.this_type,
861                return_type: sig.return_type,
862                type_params: sig.type_params.clone(),
863                type_predicate: sig.type_predicate.clone(),
864                is_constructor: false,
865                is_method: sig.is_method,
866            };
867            return self.resolve_function_call(&func, arg_types);
868        }
869
870        // Try each call signature
871        let mut failures = Vec::new();
872        let mut all_arg_count_mismatches = true;
873        let mut min_expected = usize::MAX;
874        let mut max_expected = 0;
875        let mut any_has_rest = false;
876        let actual_count = arg_types.len();
877        let mut exact_expected_counts = FxHashSet::default();
878        // Track if exactly one overload matched argument count but had a type mismatch.
879        // When there is a single "count-compatible" overload that fails only on types,
880        // tsc reports TS2345 (the inner type error) rather than TS2769 (no overload matched).
881        let mut type_mismatch_count: usize = 0;
882        let mut first_type_mismatch: Option<(usize, TypeId, TypeId)> = None; // (index, expected, actual)
883        let mut all_mismatches_identical = true;
884        let mut has_non_count_non_type_failure = false;
885
886        for sig in &callable.call_signatures {
887            // Convert CallSignature to FunctionShape
888            let func = FunctionShape {
889                params: sig.params.clone(),
890                this_type: sig.this_type,
891                return_type: sig.return_type,
892                type_params: sig.type_params.clone(),
893                type_predicate: sig.type_predicate.clone(),
894                is_constructor: false,
895                is_method: sig.is_method,
896            };
897            tracing::debug!("resolve_callable_call: signature = {sig:?}");
898
899            match self.resolve_function_call(&func, arg_types) {
900                CallResult::Success(ret) => return CallResult::Success(ret),
901                CallResult::TypeParameterConstraintViolation { return_type, .. } => {
902                    // Constraint violation is a "near match" - return the type
903                    // for overload resolution (treat as success with error)
904                    return CallResult::Success(return_type);
905                }
906                CallResult::ArgumentTypeMismatch {
907                    index,
908                    expected,
909                    actual,
910                } => {
911                    all_arg_count_mismatches = false;
912                    type_mismatch_count += 1;
913                    if type_mismatch_count == 1 {
914                        first_type_mismatch = Some((index, expected, actual));
915                    } else if first_type_mismatch != Some((index, expected, actual)) {
916                        all_mismatches_identical = false;
917                    }
918                    failures.push(
919                        crate::diagnostics::PendingDiagnosticBuilder::argument_not_assignable(
920                            actual, expected,
921                        ),
922                    );
923                }
924                CallResult::ArgumentCountMismatch {
925                    expected_min,
926                    expected_max,
927                    actual,
928                } => {
929                    if expected_max.is_none() {
930                        any_has_rest = true;
931                    } else if expected_min == expected_max.unwrap_or(expected_min) {
932                        exact_expected_counts.insert(expected_min);
933                    }
934                    let expected = expected_max.unwrap_or(expected_min);
935                    min_expected = min_expected.min(expected_min);
936                    max_expected = max_expected.max(expected);
937                    failures.push(
938                        crate::diagnostics::PendingDiagnosticBuilder::argument_count_mismatch(
939                            expected, actual,
940                        ),
941                    );
942                }
943                _ => {
944                    all_arg_count_mismatches = false;
945                    has_non_count_non_type_failure = true;
946                }
947            }
948        }
949
950        // If all signatures failed due to argument count mismatch, report TS2554 instead of TS2769
951        if all_arg_count_mismatches && !failures.is_empty() {
952            if !any_has_rest
953                && !exact_expected_counts.is_empty()
954                && !exact_expected_counts.contains(&actual_count)
955            {
956                let mut lower = None;
957                let mut upper = None;
958                for &count in &exact_expected_counts {
959                    if count < actual_count {
960                        lower = Some(lower.map_or(count, |prev: usize| prev.max(count)));
961                    } else if count > actual_count {
962                        upper = Some(upper.map_or(count, |prev: usize| prev.min(count)));
963                    }
964                }
965                if let (Some(expected_low), Some(expected_high)) = (lower, upper) {
966                    return CallResult::OverloadArgumentCountMismatch {
967                        actual: actual_count,
968                        expected_low,
969                        expected_high,
970                    };
971                }
972            }
973            return CallResult::ArgumentCountMismatch {
974                expected_min: min_expected,
975                expected_max: if any_has_rest {
976                    None
977                } else if max_expected > min_expected {
978                    Some(max_expected)
979                } else {
980                    Some(min_expected)
981                },
982                actual: actual_count,
983            };
984        }
985
986        // If all type mismatches are identical (or there's exactly one), and no other failures occurred,
987        // report TS2345 (the inner type error) instead of TS2769. This handles duplicate signatures
988        // or overloads where the failing parameter has the exact same type in all matching overloads.
989        if !has_non_count_non_type_failure
990            && type_mismatch_count > 0
991            && all_mismatches_identical
992            && let Some((index, expected, actual)) = first_type_mismatch
993        {
994            return CallResult::ArgumentTypeMismatch {
995                index,
996                expected,
997                actual,
998            };
999        }
1000
1001        // If we got here, no signature matched
1002        CallResult::NoOverloadMatch {
1003            func_type: self.interner.callable(callable.clone()),
1004            arg_types: arg_types.to_vec(),
1005            failures,
1006            fallback_return: callable
1007                .call_signatures
1008                .first()
1009                .map(|s| s.return_type)
1010                .unwrap_or(TypeId::ANY),
1011        }
1012    }
1013}
1014
1015pub fn infer_call_signature<C: AssignabilityChecker>(
1016    interner: &dyn QueryDatabase,
1017    checker: &mut C,
1018    sig: &CallSignature,
1019    arg_types: &[TypeId],
1020) -> TypeId {
1021    let mut evaluator = CallEvaluator::new(interner, checker);
1022    evaluator.infer_call_signature(sig, arg_types)
1023}
1024
1025pub fn infer_generic_function<C: AssignabilityChecker>(
1026    interner: &dyn QueryDatabase,
1027    checker: &mut C,
1028    func: &FunctionShape,
1029    arg_types: &[TypeId],
1030) -> TypeId {
1031    let mut evaluator = CallEvaluator::new(interner, checker);
1032    evaluator.infer_generic_function(func, arg_types)
1033}
1034
1035pub fn resolve_call_with_checker<C: AssignabilityChecker>(
1036    interner: &dyn QueryDatabase,
1037    checker: &mut C,
1038    func_type: TypeId,
1039    arg_types: &[TypeId],
1040    force_bivariant_callbacks: bool,
1041    contextual_type: Option<TypeId>,
1042    actual_this_type: Option<TypeId>,
1043) -> (CallResult, Option<(TypePredicate, Vec<ParamInfo>)>) {
1044    let mut evaluator = CallEvaluator::new(interner, checker);
1045    evaluator.set_force_bivariant_callbacks(force_bivariant_callbacks);
1046    evaluator.set_contextual_type(contextual_type);
1047    evaluator.set_actual_this_type(actual_this_type);
1048    let result = evaluator.resolve_call(func_type, arg_types);
1049    let predicate = evaluator.last_instantiated_predicate.take();
1050    (result, predicate)
1051}
1052
1053pub fn resolve_new_with_checker<C: AssignabilityChecker>(
1054    interner: &dyn QueryDatabase,
1055    checker: &mut C,
1056    type_id: TypeId,
1057    arg_types: &[TypeId],
1058    force_bivariant_callbacks: bool,
1059) -> CallResult {
1060    let mut evaluator = CallEvaluator::new(interner, checker);
1061    evaluator.set_force_bivariant_callbacks(force_bivariant_callbacks);
1062    evaluator.resolve_new(type_id, arg_types)
1063}
1064
1065pub fn compute_contextual_types_with_compat_checker<'a, R, F>(
1066    interner: &'a dyn QueryDatabase,
1067    resolver: &'a R,
1068    shape: &FunctionShape,
1069    arg_types: &[TypeId],
1070    contextual_type: Option<TypeId>,
1071    configure_checker: F,
1072) -> TypeSubstitution
1073where
1074    R: crate::TypeResolver,
1075    F: FnOnce(&mut crate::CompatChecker<'a, R>),
1076{
1077    let mut checker = crate::CompatChecker::with_resolver(interner, resolver);
1078    configure_checker(&mut checker);
1079
1080    let mut evaluator = CallEvaluator::new(interner, &mut checker);
1081    evaluator.set_contextual_type(contextual_type);
1082    evaluator.compute_contextual_types(shape, arg_types)
1083}
1084
1085pub fn get_contextual_signature_with_compat_checker(
1086    db: &dyn TypeDatabase,
1087    type_id: TypeId,
1088) -> Option<FunctionShape> {
1089    CallEvaluator::<crate::CompatChecker>::get_contextual_signature(db, type_id)
1090}
1091
1092// Re-exports from submodules
1093pub use generics::{GenericInstantiationResult, solve_generic_instantiation};
1094pub use iterators::{IteratorInfo, get_async_iterable_element_type, get_iterator_info};
1095
1096#[cfg(test)]
1097#[path = "../../tests/operations_tests.rs"]
1098mod tests;