//! Contains representations of function literals. There are several complicating factors:
//!
//! - Functions can be generic, and can have specializations applied to them. These are not the
//!   same thing! For instance, a method of a generic class might not itself be generic, but it can
//!   still have the class's specialization applied to it.
//!
//! - Functions can be overloaded, and each overload can be independently generic or not, with
//!   different sets of typevars for different generic overloads. In some cases we need to consider
//!   each overload separately; in others we need to consider all of the overloads (and any
//!   implementation) as a single collective entity.
//!
//! - Certain “known” functions need special treatment — for instance, inferring a special return
//!   type, or raising custom diagnostics.
//!
//! - TODO: Some functions don't correspond to a function definition in the AST, and are instead
//!   synthesized as we mimic the behavior of the Python interpreter. Even though they are
//!   synthesized, and are “implemented” as Rust code, they are still functions from the POV of the
//!   rest of the type system.
//!
//! Given these constraints, we have the following representation: a function is a list of one or
//! more overloads, with zero or more specializations (more specifically, “type mappings”) applied
//! to it. [`FunctionType`] is the outermost type, which is what [`Type::FunctionLiteral`] wraps.
//! It contains the list of type mappings to apply. It wraps a [`FunctionLiteral`], which collects
//! together all of the overloads (and implementation) of an overloaded function. An
//! [`OverloadLiteral`] represents an individual function definition in the AST — that is, each
//! overload (and implementation) of an overloaded function, or the single definition of a
//! non-overloaded function.
//!
//! Technically, each `FunctionLiteral` wraps a particular overload and all _previous_ overloads.
//! So it's only true that it wraps _all_ overloads if you are looking at the last definition. For
//! instance, in
//!
//! ```py
//! @overload
//! def f(x: int) -> None: ...
//! # <-- 1
//!
//! @overload
//! def f(x: str) -> None: ...
//! # <-- 2
//!
//! def f(x): pass
//! # <-- 3
//! ```
//!
//! resolving `f` at each of the three numbered positions will give you a `FunctionType`, which
//! wraps a `FunctionLiteral`, which contain `OverloadLiteral`s only for the definitions that
//! appear before that position. We rely on the fact that later definitions shadow earlier ones, so
//! the public type of `f` is resolved at position 3, correctly giving you all of the overloads
//! (and the implementation).

use std::{borrow::Cow, str::FromStr};

use bitflags::bitflags;
use itertools::Either;
use ruff_db::PythonFile;
use ruff_db::diagnostic::{Annotation, DiagnosticId, Severity, Span};
use ruff_db::files::{File, FileRange};
use ruff_db::parsed::{ParsedModuleRef, parsed_module};
use ruff_db::source::source_text;
use ruff_diagnostics::{Edit, Fix};
use ruff_python_ast::find_node::covering_node;
use ruff_python_ast::{self as ast, OperatorPrecedence, ParameterWithDefault};
use ruff_text_size::Ranged;
use salsa::plumbing::AsId;
use ty_module_resolver::{ImportingFile, KnownModule, ModuleName, file_to_module, resolve_module};

use crate::place::{DefinedPlace, Definedness, Place, place_from_bindings};
use crate::types::call::{Binding, CallArguments};
use crate::types::callable::{CallableFunctionProvenance, CallableTypeKind};
use crate::types::constraints::ConstraintSet;
use crate::types::context::InferContext;
use crate::types::cyclic::ActiveRecursionDetector;
use crate::types::diagnostic::{
    ASSERT_TYPE_UNSPELLABLE_SUBTYPE, INVALID_ARGUMENT_TYPE, REDUNDANT_CAST, STATIC_ASSERT_ERROR,
    TYPE_ASSERTION_FAILURE, report_bad_argument_to_get_protocol_members,
    report_bad_argument_to_protocol_interface, report_invalid_total_ordering_call,
    report_issubclass_check_against_protocol_with_non_method_members,
    report_runtime_check_against_non_runtime_checkable_protocol,
    report_runtime_check_against_typed_dict,
};
use crate::types::display::DisplaySettings;
use crate::types::generics::{ApplySpecialization, GenericContext, typing_self};
use crate::types::infer::{infer_definition_types, nearest_enclosing_class, original_class_type};
use crate::types::known_instance::DeprecatedInstance;
use crate::types::list_members::all_members;
use crate::types::narrow::ClassInfoConstraintFunction;
use crate::types::relation::TypeRelationChecker;
use crate::types::signatures::{CallableSignature, ReturnCallableTypeVarScope, Signature};
use crate::types::tuple::TupleSpec;
use crate::types::variance::{TypeVarVariance, VarianceInferable};
use crate::types::visitor::non_any_dynamic_content;
use crate::types::{
    ApplyTypeMappingVisitor, BoundMethodType, BoundTypeVarIdentity, BoundTypeVarInstance,
    CallableType, ClassBase, ClassLiteral, ClassType, FindLegacyTypeVarsVisitor,
    IntersectionBuilder, KnownClass, KnownInstanceType, SpecialFormType, SubclassOfInner,
    SubclassOfType, Truthiness, Type, TypeContext, TypeMapping, TypeVarBoundOrConstraints,
    UnionBuilder, UnionType, binding_type, definition_expression_type, walk_signature,
};
use crate::{Db, FxOrderSet, ProgramEnvironment};
use ty_python_core::ast_ids::HasScopedUseId;
use ty_python_core::definition::Definition;
use ty_python_core::scope::ScopeId;
use ty_python_core::{FileScopeId, ProgramFile, SemanticIndex, semantic_index};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct RecursiveTypeNormalizationKey {
    function_literal: salsa::Id,
    nested: bool,
}

// Keep this detector thread-local rather than threading it through every recursive
// normalization helper. Passing an explicit visitor would model the recursion state more
// directly, but it would touch a wide slice of the type-normalization plumbing for no current
// behavioral benefit. This works because recursive normalization currently stays on a single
// thread/query stack; if that ever changes, revisit this and prefer explicit visitor propagation.
std::thread_local! {
    static ACTIVE_RECURSIVE_TYPE_NORMALIZATIONS: ActiveRecursionDetector<RecursiveTypeNormalizationKey> =
        ActiveRecursionDetector::default();
}

/// Runs recursive type normalization under a scoped guard keyed by function literal identity.
///
/// `TypeOf` can make a function signature refer back to the same function through many different
/// type components. Keeping this guard scoped here lets those components keep their ordinary
/// `recursive_type_normalized_impl(db, div, nested)` signatures.
fn visit_recursive_type_normalization<R>(
    function_literal: FunctionLiteral<'_>,
    nested: bool,
    on_cycle: impl FnOnce() -> R,
    func: impl FnOnce() -> R,
) -> R {
    ACTIVE_RECURSIVE_TYPE_NORMALIZATIONS.with(|detector| {
        detector.visit(
            &RecursiveTypeNormalizationKey {
                function_literal: function_literal.last_definition.as_id(),
                nested,
            },
            on_cycle,
            func,
        )
    })
}

/// A collection of useful spans for annotating functions.
///
/// This can be retrieved via `FunctionType::spans` or
/// `Type::function_spans`.
pub(crate) struct FunctionSpans {
    /// The span of the entire function "signature." This includes
    /// the name, parameter list and return type (if present).
    pub(crate) signature: Span,
    /// The span of the function name. i.e., `foo` in `def foo(): ...`.
    pub(crate) name: Span,
    /// The span of the parameter list, including the opening and
    /// closing parentheses.
    pub(crate) parameters: Span,
    /// The span of the annotated return type, if present.
    pub(crate) return_type: Option<Span>,
    /// A span that starts at the beginning of the first decorator (if any),
    /// and ends at the end of the function signature (either the last parameter,
    /// or the return type if present).
    pub(crate) decorators_and_header: Span,
}

bitflags! {
    #[derive(Copy, Clone, Debug, Eq, PartialEq, Default, Hash)]
    pub struct FunctionDecorators: u8 {
        /// `@classmethod`
        const CLASSMETHOD = 1 << 0;
        /// `@typing.no_type_check`
        const NO_TYPE_CHECK = 1 << 1;
        /// `@typing.overload`
        const OVERLOAD = 1 << 2;
        /// `@abc.abstractmethod`
        const ABSTRACT_METHOD = 1 << 3;
        /// `@typing.final`
        const FINAL = 1 << 4;
        /// `@staticmethod`
        const STATICMETHOD = 1 << 5;
        /// `@typing.override`
        const OVERRIDE = 1 << 6;
        /// `@typing.type_check_only`
        const TYPE_CHECK_ONLY = 1 << 7;
    }
}

impl get_size2::GetSize for FunctionDecorators {}

impl FunctionDecorators {
    pub(super) fn from_decorator_type(db: &dyn Db, decorator_type: Type) -> Self {
        match decorator_type {
            Type::FunctionLiteral(function) => match function.known(db) {
                Some(KnownFunction::NoTypeCheck) => FunctionDecorators::NO_TYPE_CHECK,
                Some(KnownFunction::Overload) => FunctionDecorators::OVERLOAD,
                Some(KnownFunction::AbstractMethod) => FunctionDecorators::ABSTRACT_METHOD,
                Some(KnownFunction::Final) => FunctionDecorators::FINAL,
                Some(KnownFunction::Override) => FunctionDecorators::OVERRIDE,
                Some(KnownFunction::TypeCheckOnly) => FunctionDecorators::TYPE_CHECK_ONLY,
                _ => FunctionDecorators::empty(),
            },
            Type::ClassLiteral(class) => match class.known(db) {
                Some(KnownClass::Classmethod) => FunctionDecorators::CLASSMETHOD,
                Some(KnownClass::Staticmethod) => FunctionDecorators::STATICMETHOD,
                _ => FunctionDecorators::empty(),
            },
            _ => FunctionDecorators::empty(),
        }
    }
}

bitflags! {
    /// Used for the return type of `dataclass_transform(…)` calls. Keeps track of the
    /// arguments that were passed in. For the precise meaning of the fields, see [1].
    ///
    /// [1]: https://docs.python.org/3/library/typing.html#typing.dataclass_transform
    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
    pub struct DataclassTransformerFlags: u8 {
        const EQ_DEFAULT = 1 << 0;
        const ORDER_DEFAULT = 1 << 1;
        const KW_ONLY_DEFAULT = 1 << 2;
        const FROZEN_DEFAULT = 1 << 3;
    }
}

impl get_size2::GetSize for DataclassTransformerFlags {}

impl Default for DataclassTransformerFlags {
    fn default() -> Self {
        Self::EQ_DEFAULT
    }
}

/// Metadata for a dataclass-transformer. Stored inside a `Type::DataclassTransformer(…)`
/// instance that we use as the return type for `dataclass_transform(…)` calls.
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
pub struct DataclassTransformerParams<'db> {
    #[returns(copy)]
    pub flags: DataclassTransformerFlags,

    #[returns(deref)]
    pub field_specifiers: Box<[Type<'db>]>,
}

impl get_size2::GetSize for DataclassTransformerParams<'_> {}

/// Whether a function should implicitly be treated as a staticmethod based on its name.
pub(crate) fn is_implicit_staticmethod(function_name: &str) -> bool {
    matches!(function_name, "__new__")
}

/// Whether a function should implicitly be treated as a classmethod based on its name.
pub(crate) fn is_implicit_classmethod(function_name: &str) -> bool {
    matches!(function_name, "__init_subclass__" | "__class_getitem__")
}

/// Representation of a function definition in the AST: either a non-generic function, or a generic
/// function that has not been specialized.
///
/// If a function has multiple overloads, each overload is represented by a separate function
/// definition in the AST, and is therefore a separate `OverloadLiteral` instance.
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
pub struct OverloadLiteral<'db> {
    /// Name of the function at definition.
    #[returns(ref)]
    pub name: ast::name::Name,

    /// Is this a function that we special-case somehow? If so, which one?
    #[returns(copy)]
    pub(crate) known: Option<KnownFunction>,

    /// The scope that's created by the function, in which the function body is evaluated.
    #[returns(copy)]
    pub(crate) body_scope: ScopeId<'db>,

    /// A set of special decorators that were applied to this function
    #[returns(copy)]
    pub(crate) decorators: FunctionDecorators,

    /// If `Some` then contains the `@warnings.deprecated`
    #[returns(copy)]
    pub(crate) deprecated: Option<DeprecatedInstance<'db>>,

    /// The arguments to `dataclass_transformer`, if this function was annotated
    /// with `@dataclass_transformer(...)`.
    #[returns(copy)]
    pub(crate) dataclass_transformer_params: Option<DataclassTransformerParams<'db>>,

    /// Whether this overload or implementation has an explicit return annotation.
    #[returns(copy)]
    pub(crate) has_explicit_return_annotation: bool,
}

// The Salsa heap is tracked separately.
impl get_size2::GetSize for OverloadLiteral<'_> {}

#[salsa::tracked]
impl<'db> OverloadLiteral<'db> {
    fn with_deprecated(self, db: &'db dyn Db, deprecated: DeprecatedInstance<'db>) -> Self {
        Self::new(
            db,
            self.name(db),
            self.known(db),
            self.body_scope(db),
            self.decorators(db),
            Some(deprecated),
            self.dataclass_transformer_params(db),
            self.has_explicit_return_annotation(db),
        )
    }

    fn with_dataclass_transformer_params(
        self,
        db: &'db dyn Db,
        params: DataclassTransformerParams<'db>,
    ) -> Self {
        Self::new(
            db,
            self.name(db),
            self.known(db),
            self.body_scope(db),
            self.decorators(db),
            self.deprecated(db),
            Some(params),
            self.has_explicit_return_annotation(db),
        )
    }

    fn file(self, db: &'db dyn Db) -> File {
        // NOTE: Do not use `self.definition(db).file(db)` here, as that could create a
        // cross-module dependency on the full AST.
        self.body_scope(db).file(db)
    }

    pub(crate) fn python_file(self, db: &'db dyn Db) -> PythonFile<'db> {
        self.body_scope(db).python_file(db)
    }

    pub(crate) fn program_file(self, db: &'db dyn Db) -> ProgramFile<'db> {
        self.body_scope(db).program_file(db)
    }

    pub(crate) fn has_known_decorator(self, db: &dyn Db, decorator: FunctionDecorators) -> bool {
        self.decorators(db).contains(decorator)
    }

    pub(crate) fn is_overload(self, db: &dyn Db) -> bool {
        self.has_known_decorator(db, FunctionDecorators::OVERLOAD)
    }

    /// Returns true if this overload is decorated with `@staticmethod`, or if it is implicitly a
    /// staticmethod.
    fn is_staticmethod(self, db: &dyn Db) -> bool {
        self.has_known_decorator(db, FunctionDecorators::STATICMETHOD)
            || is_implicit_staticmethod(self.name(db))
    }

    /// Returns true if this overload is decorated with `@classmethod`, or if it is implicitly a
    /// classmethod.
    fn is_classmethod(self, db: &dyn Db) -> bool {
        self.has_known_decorator(db, FunctionDecorators::CLASSMETHOD)
            || is_implicit_classmethod(self.name(db))
    }

    /// Returns true if this overload has an implicit `self` or `cls` receiver parameter.
    pub(crate) fn has_implicit_receiver(self, db: &'db dyn Db) -> bool {
        self.body_scope(db).is_method_scope(db) && !self.is_staticmethod(db)
    }

    pub(crate) fn node<'ast>(
        self,
        db: &dyn Db,
        file: File,
        module: &'ast ParsedModuleRef,
    ) -> &'ast ast::StmtFunctionDef {
        debug_assert_eq!(
            file,
            self.file(db),
            "OverloadLiteral::node() must be called with the same file as the one where \
            the function is defined."
        );

        self.body_scope(db).node(db).expect_function().node(module)
    }

    /// Iterate through the decorators on this function, returning the span of the first one
    /// that matches the given predicate.
    fn find_decorator_span(
        self,
        db: &'db dyn Db,
        predicate: impl Fn(Type<'db>) -> bool,
    ) -> Option<Span> {
        let definition = self.definition(db);
        let file = definition.file(db);
        self.node(
            db,
            file,
            &parsed_module(db, definition.python_file(db)).load(db),
        )
        .decorator_list
        .iter()
        .find(|decorator| {
            predicate(definition_expression_type(
                db,
                definition,
                &decorator.expression,
            ))
        })
        .map(|decorator| Span::from(file).with_range(decorator.range))
    }

    /// Iterate through the decorators on this function, returning the span of the first one
    /// that matches the given [`KnownFunction`].
    pub(super) fn find_known_decorator_span(
        self,
        db: &'db dyn Db,
        needle: KnownFunction,
    ) -> Option<Span> {
        self.find_decorator_span(db, |ty| {
            ty.as_function_literal()
                .is_some_and(|f| f.is_known(db, needle))
        })
    }

    /// Returns the [`FileRange`] of the function's name.
    pub(crate) fn focus_range(self, db: &dyn Db, module: &ParsedModuleRef) -> FileRange {
        FileRange::new(
            self.file(db),
            self.body_scope(db)
                .node(db)
                .expect_function()
                .node(module)
                .name
                .range,
        )
    }

    /// Returns the [`Definition`] of this function.
    ///
    /// ## Warning
    ///
    /// This uses the semantic index to find the definition of the function. This means that if the
    /// calling query is not in the same file as this function is defined in, then this will create
    /// a cross-module dependency directly on the full AST which will lead to cache
    /// over-invalidation.
    fn definition(self, db: &'db dyn Db) -> Definition<'db> {
        let body_scope = self.body_scope(db);
        let index = semantic_index(db, body_scope.program_file(db));
        index.expect_single_definition(body_scope.node(db).expect_function())
    }

    /// Returns the overload immediately before this one in the AST. Returns `None` if there is no
    /// previous overload.
    fn previous_overload(self, db: &'db dyn Db) -> Option<FunctionLiteral<'db>> {
        // The semantic model records a use for each function on the name node. This is used
        // here to get the previous function definition with the same name.
        let scope = self.definition(db).scope(db);
        let module = parsed_module(db, self.python_file(db)).load(db);
        let use_def =
            semantic_index(db, scope.program_file(db)).use_def_map(scope.file_scope_id(db));
        let use_id = self
            .body_scope(db)
            .node(db)
            .expect_function()
            .node(&module)
            .name
            .scoped_use_id(db, self.program_file(db));

        let env = ProgramEnvironment::from_scope(scope);
        let Place::Defined(DefinedPlace {
            ty: previous_type,
            definedness: Definedness::AlwaysDefined,
            provenance,
            ..
        }) = place_from_bindings(db, &env, use_def.bindings_at_use(use_id)).place
        else {
            return None;
        };

        let previous_literal = match previous_type {
            Type::FunctionLiteral(previous_type) => previous_type.literal(db),
            Type::Callable(_) => {
                let definition = provenance.definition()?;
                infer_definition_types(db, definition)
                    .function_type(definition)?
                    .literal(db)
            }
            _ => return None,
        };
        let previous_overload = previous_literal.last_definition;
        if !previous_overload.is_overload(db) {
            return None;
        }

        // These can both happen in edge cases where a definition created with a `def`
        // statement shadows a non-`def` symbol with the same name.
        if previous_overload.name(db) != self.name(db) {
            return None;
        }
        if previous_overload.definition(db).scope(db) != scope {
            return None;
        }

        Some(previous_literal)
    }

    /// Typed internally-visible signature for this function.
    ///
    /// This represents the annotations on the function itself, unmodified by decorators and
    /// overloads.
    ///
    /// ## Warning
    ///
    /// This uses the semantic index to find the definition of the function. This means that if the
    /// calling query is not in the same file as this function is defined in, then this will create
    /// a cross-module dependency directly on the full AST which will lead to cache
    /// over-invalidation.
    pub(crate) fn signature(self, db: &'db dyn Db) -> Signature<'db> {
        let scope = self.body_scope(db);
        let program_file = self.program_file(db);
        let python_file = program_file.python_file(db);
        let mut signature = self.raw_signature(db, ReturnCallableTypeVarScope::Public);
        let module = parsed_module(db, python_file).load(db);
        let function_node = scope.node(db).expect_function().node(&module);
        let index = semantic_index(db, program_file);
        let file_scope_id = scope.file_scope_id(db);
        let is_generator = file_scope_id.is_generator_function(index);

        if function_node.is_async && !is_generator {
            let env = ProgramEnvironment::from_file(program_file);
            signature = signature.wrap_coroutine_return_type(db, &env);
        }

        signature
    }

    /// Returns the effective signatures of this overload after applying decorators.
    pub(crate) fn decorated_signatures(
        self,
        db: &'db dyn Db,
    ) -> impl Iterator<Item = Signature<'db>> + Clone + 'db {
        match binding_type(db, self.definition(db)) {
            Type::Callable(callable) => {
                Either::Left(callable.signatures(db).overloads.iter().cloned())
            }
            _ => Either::Right(std::iter::once(self.signature(db))),
        }
    }

    /// Typed internally-visible "raw" signature for this function.
    /// That is, the return types of async functions are not wrapped in `CoroutineType[...]`.
    /// The `return_callable_typevar_scope` controls whether type variables that only appear in a
    /// return-position `Callable` stay bound to the function or move to the returned callable.
    ///
    /// ## Warning
    ///
    /// This uses the semantic index to find the definition of the function. This means that if the
    /// calling query is not in the same file as this function is defined in, then this will create
    /// a cross-module dependency directly on the full AST which will lead to cache
    /// over-invalidation.
    pub(super) fn raw_signature(
        self,
        db: &'db dyn Db,
        return_callable_typevar_scope: ReturnCallableTypeVarScope,
    ) -> Signature<'db> {
        /// `self` or `cls` can be implicitly positional-only if:
        /// - It is a method AND
        /// - No parameters in the method use PEP-570 syntax AND
        /// - It is not a `@staticmethod` AND
        /// - `self`/`cls` is not explicitly positional-only using the PEP-484 convention AND
        /// - Either the next parameter after `self`/`cls` uses the PEP-484 convention,
        ///   or the enclosing class is a `Protocol` class
        fn has_implicitly_positional_only_first_param<'db>(
            db: &'db dyn Db,
            literal: OverloadLiteral<'db>,
            node: &ast::StmtFunctionDef,
            scope: FileScopeId,
            index: &SemanticIndex,
        ) -> bool {
            let parameters = &node.parameters;

            if !parameters.posonlyargs.is_empty() {
                return false;
            }

            let Some(first_param) = parameters.args.first() else {
                return false;
            };

            if first_param.uses_pep_484_positional_only_convention() {
                return false;
            }

            if literal.is_staticmethod(db) && literal.name(db) != "__new__" {
                return false;
            }

            let Some(class_definition) = index.class_definition_of_method(scope) else {
                return false;
            };

            // `self` and `cls` are always positional-only if the next parameter uses the
            // PEP-484 convention.
            if parameters
                .args
                .get(1)
                .is_some_and(ParameterWithDefault::uses_pep_484_positional_only_convention)
            {
                return true;
            }

            // If there isn't any parameter other than `self`/`cls`,
            // or there is but it isn't using the PEP-484 convention,
            // then `self`/`cls` are only implicitly positional-only if
            // it is a protocol class.
            original_class_type(db, class_definition)
                .map(|class_literal| class_literal.default_specialization(db))
                .is_some_and(|class| class.is_protocol(db))
        }

        let env = &ProgramEnvironment::from_scope(self.body_scope(db));
        let scope = self.body_scope(db);
        let program_file = self.program_file(db);
        let python_file = program_file.python_file(db);
        let module = parsed_module(db, python_file).load(db);
        let function_stmt_node = scope.node(db).expect_function().node(&module);
        let definition = self.definition(db);
        let index = semantic_index(db, program_file);
        let pep695_ctx = function_stmt_node.type_params.as_ref().map(|type_params| {
            GenericContext::from_type_params(db, index, definition, type_params)
        });
        let file_scope_id = scope.file_scope_id(db);

        let has_implicitly_positional_first_parameter = has_implicitly_positional_only_first_param(
            db,
            self,
            function_stmt_node,
            file_scope_id,
            index,
        );

        let mut raw_signature = Signature::from_function(
            db,
            pep695_ctx,
            definition,
            function_stmt_node,
            has_implicitly_positional_first_parameter,
            return_callable_typevar_scope,
        );

        let generic_context = raw_signature.generic_context;
        raw_signature.add_implicit_self_annotation(db, env, || {
            let is_staticmethod = self.is_staticmethod(db);
            let is_dunder_new = self.name(db) == "__new__";
            if is_staticmethod && !is_dunder_new {
                return None;
            }

            // We have not yet added an implicit annotation to the `self` parameter, so any
            // typevars that currently appear in the method's generic context come from explicit
            // annotations.
            let method_has_explicit_self = generic_context
                .is_some_and(|context| context.variables(db).any(|v| v.typevar(db).is_self(db)));

            let class_scope_id = definition.scope(db);
            let class_scope = index.scope(class_scope_id.file_scope_id(db));
            let class_node = class_scope.node().as_class()?;
            let class_def = index.expect_single_definition(class_node);
            let class_literal = original_class_type(db, class_def)?;
            let class_is_generic = class_literal.generic_context(db).is_some();
            let class_is_fallback = class_literal
                .known(db)
                .is_some_and(KnownClass::is_fallback_class);

            // Normally we implicitly annotate `self` or `cls` with `Self` or `type[Self]`, and
            // create a `Self` typevar that we then have to solve for whenever this method is
            // called. As an optimization, we can skip creating that typevar in certain situations:
            //
            //   - The method cannot use explicit `Self` in any other parameter annotations,
            //     or in its return type. If it does, then we really do need specialization
            //     inference at each call site to see which specific instance type should be
            //     used in those other parameters / return type.
            //
            //   - The class cannot be generic. If it is, then we might need an actual `Self`
            //     typevar to help carry through constraints that relate the instance type to
            //     other typevars in the method signature.
            //
            //   - The class cannot be a "fallback class". A fallback class is used like a mixin,
            //     and so we need specialization inference to determine the "real" class that the
            //     fallback is augmenting. (See KnownClass::is_fallback_class for more details.)
            if method_has_explicit_self || class_is_generic || class_is_fallback {
                let scope_id = definition.scope(db);
                let typevar_binding_context = Some(definition);
                let index = semantic_index(db, scope_id.program_file(db));
                let class = nearest_enclosing_class(db, index, scope_id).unwrap();

                let typing_self = typing_self(db, scope_id, typevar_binding_context, class.into())
                    .expect(
                        "We should always find the surrounding class \
                     for an implicit self: Self annotation",
                    );

                if self.is_classmethod(db) || is_dunder_new {
                    Some(SubclassOfType::from(
                        db,
                        env,
                        SubclassOfInner::TypeVar(typing_self),
                    ))
                } else {
                    Some(Type::TypeVar(typing_self))
                }
            } else {
                // If skip creating the typevar, we use "instance of class" or "subclass of
                // class" as the implicit annotation instead.
                if self.is_classmethod(db) || is_dunder_new {
                    Some(SubclassOfType::from(
                        db,
                        env,
                        SubclassOfInner::Class(ClassType::NonGeneric(class_literal)),
                    ))
                } else {
                    Some(class_literal.to_non_generic_instance(db, env))
                }
            }
        });

        raw_signature
    }

    pub(crate) fn parameter_span(
        self,
        db: &'db dyn Db,
        parameter_index: Option<usize>,
    ) -> (Span, Span) {
        let file = self.file(db);
        let span = Span::from(file);
        let module = parsed_module(db, self.python_file(db)).load(db);
        let func_def = self.node(db, file, &module);
        let range = parameter_index
            .and_then(|parameter_index| {
                func_def
                    .parameters
                    .iter()
                    .nth(parameter_index)
                    .map(|param| param.range())
            })
            .unwrap_or(func_def.parameters.range);
        let name_span = span.clone().with_range(func_def.name.range);
        let parameter_span = span.with_range(range);
        (name_span, parameter_span)
    }

    pub(crate) fn spans(self, db: &'db dyn Db) -> FunctionSpans {
        let file = self.file(db);
        let span = Span::from(file);
        let module = parsed_module(db, self.python_file(db)).load(db);
        let func_def = self.node(db, file, &module);
        let return_type_range = func_def.returns.as_ref().map(|returns| returns.range());
        let mut signature = func_def.name.range.cover(func_def.parameters.range);
        if let Some(return_type_range) = return_type_range {
            signature = signature.cover(return_type_range);
        }
        FunctionSpans {
            signature: span.clone().with_range(signature),
            name: span.clone().with_range(func_def.name.range),
            parameters: span.clone().with_range(func_def.parameters.range),
            return_type: return_type_range.map(|range| span.clone().with_range(range)),
            decorators_and_header: span.with_range(signature.cover_offset(func_def.start())),
        }
    }
}

/// Representation of a function definition in the AST, along with any previous overloads of the
/// function. Each overload can be separately generic or not, and each generic overload uses
/// distinct typevars.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, get_size2::GetSize, salsa::SalsaValue)]
pub struct FunctionLiteral<'db> {
    pub(crate) last_definition: OverloadLiteral<'db>,
    overloaded: bool,
}

impl<'db> FunctionLiteral<'db> {
    pub(super) fn new(db: &'db dyn Db, last_definition: OverloadLiteral<'db>) -> Self {
        Self {
            last_definition,
            overloaded: last_definition.is_overload(db)
                || last_definition.previous_overload(db).is_some(),
        }
    }

    /// Ignore previous overloads when applying decorators to an individual definition.
    pub(super) const fn without_overloads(self) -> Self {
        Self {
            overloaded: false,
            ..self
        }
    }

    /// Preserve the overload set and last-definition identity while updating decorator metadata.
    pub(super) fn with_last_definition_metadata(
        self,
        db: &'db dyn Db,
        decorated: OverloadLiteral<'db>,
    ) -> Self {
        let definition = self.last_definition;
        Self {
            last_definition: OverloadLiteral::new(
                db,
                definition.name(db),
                definition.known(db),
                definition.body_scope(db),
                definition.decorators(db),
                decorated.deprecated(db),
                decorated.dataclass_transformer_params(db),
                definition.has_explicit_return_annotation(db),
            ),
            ..self
        }
    }

    fn name(self, db: &'db dyn Db) -> &'db ast::name::Name {
        // All of the overloads of a function literal should have the same name.
        self.last_definition.name(db)
    }

    fn known(self, db: &'db dyn Db) -> Option<KnownFunction> {
        // Whether a function is known is based on its name (and its containing module's name), so
        // all overloads should be known (or not) equivalently.
        self.last_definition.known(db)
    }

    fn has_known_decorator(self, db: &'db dyn Db, decorator: FunctionDecorators) -> bool {
        self.iter_overloads_and_implementation(db)
            .any(|overload| overload.decorators(db).contains(decorator))
    }

    /// If the implementation of this function is deprecated, returns the `@warnings.deprecated`.
    ///
    /// Checking if an overload is deprecated requires deeper call analysis.
    fn implementation_deprecated(self, db: &'db dyn Db) -> Option<DeprecatedInstance<'db>> {
        let (_overloads, implementation) = self.overloads_and_implementation(db);
        implementation.and_then(|overload| overload.deprecated(db))
    }

    fn definition(self, db: &'db dyn Db) -> Definition<'db> {
        self.last_definition.definition(db)
    }

    fn parameter_span(self, db: &'db dyn Db, parameter_index: Option<usize>) -> (Span, Span) {
        self.last_definition.parameter_span(db, parameter_index)
    }

    fn spans(self, db: &'db dyn Db) -> FunctionSpans {
        self.last_definition.spans(db)
    }

    fn overloads_and_implementation(
        self,
        db: &'db dyn Db,
    ) -> (&'db [OverloadLiteral<'db>], Option<OverloadLiteral<'db>>) {
        #[salsa::tracked(
            returns(ref),
            cycle_initial=|_, _, _| (Box::default(), None),
            heap_size=ruff_memory_usage::heap_size,
        )]
        fn overloads_and_implementation_inner<'db>(
            db: &'db dyn Db,
            self_overload: OverloadLiteral<'db>,
        ) -> (Box<[OverloadLiteral<'db>]>, Option<OverloadLiteral<'db>>) {
            let mut current = self_overload;
            let mut overloads = vec![];

            while let Some(previous) = current.previous_overload(db) {
                let overload = previous.last_definition;
                overloads.push(overload);
                current = overload;
            }

            // Overloads are inserted in reverse order, from bottom to top.
            overloads.reverse();

            let implementation = if self_overload.is_overload(db) {
                overloads.push(self_overload);
                None
            } else {
                Some(self_overload)
            };

            (overloads.into_boxed_slice(), implementation)
        }

        if !self.overloaded {
            return (&[], Some(self.last_definition));
        }

        let (overloads, implementation) =
            overloads_and_implementation_inner(db, self.last_definition);
        (overloads.as_ref(), *implementation)
    }

    pub(super) fn has_separate_implementation(self, db: &'db dyn Db) -> bool {
        self.overloaded && !self.last_definition.is_overload(db)
    }

    fn iter_overloads_and_implementation(
        self,
        db: &'db dyn Db,
    ) -> impl DoubleEndedIterator<Item = OverloadLiteral<'db>> + 'db {
        let (overloads, implementation) = self.overloads_and_implementation(db);
        overloads.iter().copied().chain(implementation)
    }

    /// Typed externally-visible signature for this function.
    ///
    /// This is the signature as seen by external callers, possibly modified by decorators and/or
    /// overloaded.
    ///
    /// ## Warning
    ///
    /// This uses the semantic index to find the definition of the function. This means that if the
    /// calling query is not in the same file as this function is defined in, then this will create
    /// a cross-module dependency directly on the full AST which will lead to cache
    /// over-invalidation.
    fn signature(self, db: &'db dyn Db) -> CallableSignature<'db> {
        // We only include an implementation (i.e. a definition not decorated with `@overload`) if
        // it's the only definition.
        let (overloads, implementation) = self.overloads_and_implementation(db);
        if let Some(implementation) = implementation
            && overloads.is_empty()
        {
            return CallableSignature::single(implementation.signature(db));
        }

        CallableSignature::from_overloads(overloads.iter().enumerate().flat_map(
            |(source_overload_index, overload)| {
                // The last overload may still be inferred, so querying its binding would create a cycle.
                if *overload == self.last_definition {
                    Either::Left(std::iter::once(
                        overload
                            .signature(db)
                            .with_source_overload_index(Some(source_overload_index)),
                    ))
                } else {
                    Either::Right(overload.decorated_signatures(db).map(move |signature| {
                        signature.with_source_overload_index(Some(source_overload_index))
                    }))
                }
            },
        ))
    }

    /// Typed externally-visible signature of the last overload or implementation of this function.
    ///
    /// ## Warning
    ///
    /// This uses the semantic index to find the definition of the function. This means that if the
    /// calling query is not in the same file as this function is defined in, then this will create
    /// a cross-module dependency directly on the full AST which will lead to cache
    /// over-invalidation.
    fn last_definition_signature(self, db: &'db dyn Db) -> Signature<'db> {
        self.last_definition.signature(db)
    }

    /// Typed externally-visible "raw" signature of the last overload or implementation of this function.
    /// The `return_callable_typevar_scope` controls whether type variables that only appear in a
    /// return-position `Callable` stay bound to the function or move to the returned callable.
    ///
    /// ## Warning
    ///
    /// This uses the semantic index to find the definition of the function. This means that if the
    /// calling query is not in the same file as this function is defined in, then this will create
    /// a cross-module dependency directly on the full AST which will lead to cache
    /// over-invalidation.
    fn last_definition_raw_signature(
        self,
        db: &'db dyn Db,
        return_callable_typevar_scope: ReturnCallableTypeVarScope,
    ) -> Signature<'db> {
        self.last_definition
            .raw_signature(db, return_callable_typevar_scope)
    }

    /// Return `Some()` if this function is an abstract method.
    ///
    /// A method can be abstract if it is explicitly decorated with `@abstractmethod`,
    /// or if it is an overloaded `Protocol` method without an implementation,
    /// or if it is a `Protocol` method with a body that solely consists of `pass`/`...`
    /// statements, or if it is a `Protocol` method that only has a docstring,
    /// or if it is a `Protocol` method whose body only consists of a single
    /// `raise NotImplementedError` statement.
    fn as_abstract_method(
        self,
        db: &'db dyn Db,
        enclosing_class: ClassType<'db>,
    ) -> Option<AbstractMethodKind> {
        if self.has_known_decorator(db, FunctionDecorators::ABSTRACT_METHOD) {
            return Some(AbstractMethodKind::Explicit);
        }
        if self.definition(db).file(db).is_stub(db) {
            return None;
        }
        if !enclosing_class.is_protocol(db) {
            return None;
        }
        match self.body_kind(db) {
            FunctionBodyKind::Stub => Some(AbstractMethodKind::ImplicitDueToStubBody),
            FunctionBodyKind::AlwaysRaisesNotImplementedError => {
                Some(AbstractMethodKind::ImplicitDueToAlwaysRaising)
            }
            FunctionBodyKind::Regular => None,
        }
    }

    /// Returns the [`FunctionBodyKind`] of this function.
    ///
    /// For functions without an implementation (e.g., overloaded functions),
    /// returns [`FunctionBodyKind::Stub`].
    fn body_kind(self, db: &'db dyn Db) -> FunctionBodyKind {
        #[salsa::tracked(returns(copy))]
        fn implementation_body_kind<'db>(
            db: &'db dyn Db,
            implementation: OverloadLiteral<'db>,
        ) -> FunctionBodyKind {
            let definition = implementation.definition(db);
            let program_file = definition.program_file(db);
            let python_file = program_file.python_file(db);
            let env = ProgramEnvironment::from_file(program_file);
            let file = python_file.file(db);
            let module = parsed_module(db, python_file).load(db);
            let node = implementation.node(db, file, &module);
            function_body_kind(db, &env, node, |expr| {
                definition_expression_type(db, definition, expr)
            })
        }

        let (_, implementation) = self.overloads_and_implementation(db);
        let Some(implementation) = implementation else {
            return FunctionBodyKind::Stub;
        };
        implementation_body_kind(db, implementation)
    }

    /// Returns `true` if this function has a trivial body.
    ///
    /// A trivial body is one that consists only of `...`, `pass`, or
    /// `raise NotImplementedError`.
    ///
    /// Methods defined in stub files are never considered to have trivial bodies,
    /// since stubs use `...` as a placeholder regardless of the runtime implementation.
    fn has_trivial_body(self, db: &'db dyn Db) -> bool {
        !self.definition(db).file(db).is_stub(db)
            && matches!(
                self.body_kind(db),
                FunctionBodyKind::Stub | FunctionBodyKind::AlwaysRaisesNotImplementedError
            )
    }
}

/// ## Warning
///
/// This uses the semantic index to find the definition of the function. This means that if the
/// calling query is not in the same file as this function is defined in, then this will create
/// a cross-module dependency directly on the full AST which will lead to cache
/// over-invalidation. Cross-module callers should use the tracked
/// [`FunctionType::last_definition_raw_signature`] query instead.
pub(super) fn same_module_uncached_raw_signature<'db>(
    db: &'db dyn Db,
    function: FunctionType<'db>,
    return_callable_typevar_scope: ReturnCallableTypeVarScope,
) -> Signature<'db> {
    function
        .literal(db)
        .last_definition_raw_signature(db, return_callable_typevar_scope)
}

/// Indicates whether a method is explicitly or implicitly abstract.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, get_size2::GetSize)]
pub(super) enum AbstractMethodKind {
    /// The method is explicitly marked as abstract using `@abstractmethod`.
    Explicit,
    /// The method is implicitly abstract due to being in a `Protocol` class without an
    /// implementation.
    ImplicitDueToStubBody,
    /// The method is implicitly abstract due to being in a `Protocol` class with a body that
    /// solely consists of `raise NotImplementedError` statements.
    ImplicitDueToAlwaysRaising,
}

impl AbstractMethodKind {
    pub(super) const fn is_explicit(self) -> bool {
        matches!(self, AbstractMethodKind::Explicit)
    }

    pub(super) const fn is_implicit_due_to_stub_body(self) -> bool {
        matches!(self, AbstractMethodKind::ImplicitDueToStubBody)
    }
}

/// Contains potentially modified signatures for a function literal.
///
/// This uncommon payload is boxed so that ordinary function types only retain the literal and one
/// optional pointer.
#[derive(Clone, Debug, PartialEq, Eq, Hash, get_size2::GetSize, salsa::SalsaValue)]
pub struct UpdatedFunctionSignatures<'db> {
    /// Contains a potentially modified signature for this function literal, in case certain
    /// operations (like type mappings) have been applied to it.
    ///
    /// See also: [`FunctionLiteral::signature`].
    signature: Option<CallableSignature<'db>>,

    /// Contains the potentially modified callables for the implementation of an overloaded
    /// function, in case decorators or type mappings have been applied to it. Each callable can
    /// itself be overloaded.
    ///
    /// See also: [`FunctionLiteral::last_definition_signature`].
    implementation_callables: Option<Box<[CallableType<'db>]>>,
}

impl<'db> UpdatedFunctionSignatures<'db> {
    fn new(
        signature: Option<CallableSignature<'db>>,
        implementation_callables: Option<Box<[CallableType<'db>]>>,
    ) -> Option<Box<Self>> {
        (signature.is_some() || implementation_callables.is_some()).then(|| {
            Box::new(Self {
                signature,
                implementation_callables,
            })
        })
    }
}

/// Represents a function type, which might be a non-generic function, or a specialization of a
/// generic function.
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
pub struct FunctionType<'db> {
    #[returns(copy)]
    pub(crate) literal: FunctionLiteral<'db>,

    #[returns(ref)]
    updated_signatures: Option<Box<UpdatedFunctionSignatures<'db>>>,
}

// The Salsa heap is tracked separately.
impl get_size2::GetSize for FunctionType<'_> {}

pub(super) fn walk_function_type<'db, V: super::visitor::TypeVisitor<'db> + ?Sized>(
    db: &'db dyn Db,
    function: FunctionType<'db>,
    visitor: &V,
) {
    if let Some(callable_signature) = function.updated_signature(db) {
        for signature in &callable_signature.overloads {
            walk_signature(db, signature, visitor);
        }
    }
    if let Some(callables) = function.updated_implementation_callables(db) {
        for callable in callables {
            visitor.visit_callable_type(db, *callable);
        }
    }
}

#[salsa::tracked]
impl<'db> FunctionType<'db> {
    fn updated_signature(self, db: &'db dyn Db) -> Option<&'db CallableSignature<'db>> {
        self.updated_signatures(db)
            .as_deref()
            .and_then(|updated| updated.signature.as_ref())
    }

    fn updated_implementation_signature(self, db: &'db dyn Db) -> Option<&'db Signature<'db>> {
        let [callable] = self.updated_implementation_callables(db)? else {
            return None;
        };
        let [signature] = callable.signatures(db).overloads.as_slice() else {
            return None;
        };
        Some(signature)
    }

    fn updated_implementation_callables(self, db: &'db dyn Db) -> Option<&'db [CallableType<'db>]> {
        self.updated_signatures(db)
            .as_deref()
            .and_then(|updated| updated.implementation_callables.as_deref())
    }

    /// Return all effective implementation callables, falling back to the raw implementation.
    pub(super) fn implementation_callables(self, db: &'db dyn Db) -> Cow<'db, [CallableType<'db>]> {
        self.updated_implementation_callables(db).map_or_else(
            || {
                Cow::Owned(vec![CallableType::single(
                    db,
                    self.last_definition_signature(db).clone(),
                )])
            },
            Cow::Borrowed,
        )
    }

    /// Retain decorated implementation callables without changing the caller-visible overloads.
    pub(super) fn with_implementation_callables(
        self,
        db: &'db dyn Db,
        implementation_callables: Box<[CallableType<'db>]>,
    ) -> Self {
        Self::new(
            db,
            self.literal(db),
            UpdatedFunctionSignatures::new(
                self.updated_signature(db).cloned(),
                Some(implementation_callables),
            ),
        )
    }

    pub(crate) fn with_inherited_generic_context(
        self,
        db: &'db dyn Db,
        inherited_generic_context: GenericContext<'db>,
    ) -> Self {
        let updated_signature = self
            .signature(db)
            .with_inherited_generic_context(db, inherited_generic_context);
        let literal = self.literal(db);
        let updated_implementation_callables = literal.has_separate_implementation(db).then(|| {
            self.implementation_callables(db)
                .iter()
                .map(|callable| {
                    CallableType::new(
                        db,
                        callable
                            .signatures(db)
                            .with_inherited_generic_context(db, inherited_generic_context),
                        callable.kind(db),
                        callable.provenance(db),
                    )
                })
                .collect()
        });
        Self::new(
            db,
            literal,
            UpdatedFunctionSignatures::new(
                Some(updated_signature),
                updated_implementation_callables,
            ),
        )
    }

    pub(crate) fn apply_type_mapping_impl<'a>(
        self,
        db: &'db dyn Db,
        type_mapping: &TypeMapping<'a, 'db>,
        tcx: TypeContext<'db>,
        visitor: &ApplyTypeMappingVisitor<'_, 'db>,
    ) -> Self {
        // Returned-callable rescoping and type-alias specialization should not rebuild signatures from the
        // function literal; doing so can re-enter recursive `TypeOf` evaluation.
        let literal = self.literal(db);
        let (updated_signature, updated_implementation_callables) = if matches!(
            type_mapping,
            TypeMapping::ApplySpecialization(
                ApplySpecialization::ReturnCallables(_) | ApplySpecialization::TypeAlias(_)
            ) | TypeMapping::ApplySpecializationWithMaterialization {
                specialization: ApplySpecialization::ReturnCallables(_)
                    | ApplySpecialization::TypeAlias(_),
                ..
            }
        ) {
            (
                self.updated_signature(db).map(|signature| {
                    signature.apply_type_mapping_impl(db, type_mapping, tcx, visitor)
                }),
                self.updated_implementation_callables(db).map(|callables| {
                    callables
                        .iter()
                        .map(|callable| {
                            callable.apply_type_mapping_impl(db, type_mapping, tcx, visitor)
                        })
                        .collect()
                }),
            )
        } else {
            (
                Some(
                    self.signature(db)
                        .apply_type_mapping_impl(db, type_mapping, tcx, visitor),
                ),
                literal.has_separate_implementation(db).then(|| {
                    self.implementation_callables(db)
                        .iter()
                        .map(|callable| {
                            callable.apply_type_mapping_impl(db, type_mapping, tcx, visitor)
                        })
                        .collect()
                }),
            )
        };

        if updated_signature.is_none() && updated_implementation_callables.is_none() {
            self
        } else {
            Self::new(
                db,
                literal,
                UpdatedFunctionSignatures::new(updated_signature, updated_implementation_callables),
            )
        }
    }

    pub(crate) fn with_dataclass_transformer_params(
        self,
        db: &'db dyn Db,
        params: DataclassTransformerParams<'db>,
    ) -> Self {
        // A decorator only applies to the specific overload that it is attached to, not to all
        // previous overloads.
        let literal = self.literal(db);
        let literal = FunctionLiteral {
            last_definition: literal
                .last_definition
                .with_dataclass_transformer_params(db, params),
            ..literal
        };
        Self::new(db, literal, None)
    }

    pub(crate) fn with_deprecated(
        self,
        db: &'db dyn Db,
        deprecated: DeprecatedInstance<'db>,
    ) -> Self {
        // A decorator only applies to the specific overload that it is attached to, not to all
        // previous overloads.
        let literal = self.literal(db);
        let literal = FunctionLiteral {
            last_definition: literal.last_definition.with_deprecated(db, deprecated),
            ..literal
        };
        Self::new(db, literal, self.updated_signatures(db))
    }

    /// Returns the [`File`] in which this function is defined.
    pub(crate) fn file(self, db: &'db dyn Db) -> File {
        self.literal(db).last_definition.file(db)
    }

    pub(crate) fn python_file(self, db: &'db dyn Db) -> PythonFile<'db> {
        self.literal(db).last_definition.python_file(db)
    }

    pub(crate) fn program_file(self, db: &'db dyn Db) -> ProgramFile<'db> {
        self.literal(db).last_definition.program_file(db)
    }

    /// Returns the AST node for this function.
    pub(super) fn node<'ast>(
        self,
        db: &dyn Db,
        file: File,
        module: &'ast ParsedModuleRef,
    ) -> &'ast ast::StmtFunctionDef {
        self.literal(db).last_definition.node(db, file, module)
    }

    pub(crate) fn name(self, db: &'db dyn Db) -> &'db ast::name::Name {
        self.literal(db).name(db)
    }

    pub(crate) fn known(self, db: &'db dyn Db) -> Option<KnownFunction> {
        self.literal(db).known(db)
    }

    pub(crate) fn is_known(self, db: &'db dyn Db, known_function: KnownFunction) -> bool {
        self.known(db) == Some(known_function)
    }

    /// Returns if any of the overloads of this function have a particular decorator.
    ///
    /// Some decorators are expected to appear on every overload; others are expected to appear
    /// only the implementation or first overload. This method does not check either of those
    /// conditions.
    pub(crate) fn has_known_decorator(
        self,
        db: &'db dyn Db,
        decorator: FunctionDecorators,
    ) -> bool {
        self.literal(db).has_known_decorator(db, decorator)
    }

    /// Returns true if this method is decorated with `@classmethod`, or if it is implicitly a
    /// classmethod.
    pub(crate) fn is_classmethod(self, db: &'db dyn Db) -> bool {
        self.iter_overloads_and_implementation(db)
            .any(|overload| overload.is_classmethod(db))
    }

    /// Returns true if this method is decorated with `@staticmethod`, or if it is implicitly a
    /// static method.
    pub(crate) fn is_staticmethod(self, db: &'db dyn Db) -> bool {
        self.iter_overloads_and_implementation(db)
            .any(|overload| overload.is_staticmethod(db))
    }

    /// Returns true if this function has an implicit `self` or `cls` receiver parameter.
    pub(crate) fn has_implicit_receiver(self, db: &'db dyn Db) -> bool {
        self.literal(db).last_definition.has_implicit_receiver(db)
    }

    /// If the implementation of this function is deprecated, returns the `@warnings.deprecated`.
    ///
    /// Checking if an overload is deprecated requires deeper call analysis.
    pub(crate) fn implementation_deprecated(
        self,
        db: &'db dyn Db,
    ) -> Option<DeprecatedInstance<'db>> {
        self.literal(db).implementation_deprecated(db)
    }

    /// Returns the [`Definition`] of the implementation or first overload of this function.
    ///
    /// ## Warning
    ///
    /// This uses the semantic index to find the definition of the function. This means that if the
    /// calling query is not in the same file as this function is defined in, then this will create
    /// a cross-module dependency directly on the full AST which will lead to cache
    /// over-invalidation.
    pub(crate) fn definition(self, db: &'db dyn Db) -> Definition<'db> {
        self.literal(db).definition(db)
    }

    /// Returns `true` if this function's last definition uses the same place as `other`.
    pub(crate) fn has_same_place_as(self, db: &'db dyn Db, other: FunctionType<'db>) -> bool {
        self.last_definition(db).place(db) == other.last_definition(db).place(db)
    }

    /// Returns the [`Definition`] for the last overload or implementation in this function.
    pub(crate) fn last_definition(self, db: &'db dyn Db) -> Definition<'db> {
        self.literal(db).last_definition.definition(db)
    }

    /// Returns `true` if this function includes `definition` as one of its overload signatures or
    /// implementation.
    pub(crate) fn contains_definition(self, db: &'db dyn Db, definition: Definition<'db>) -> bool {
        self.iter_overloads_and_implementation(db)
            .any(|overload| overload.definition(db) == definition)
    }

    /// Returns a tuple of two spans. The first is
    /// the span for the identifier of the function
    /// definition for `self`. The second is
    /// the span for the parameter in the function
    /// definition for `self`.
    ///
    /// If there are no meaningful spans, then this
    /// returns `None`. For example, when this type
    /// isn't callable.
    ///
    /// When `parameter_index` is `None`, then the
    /// second span returned covers the entire parameter
    /// list.
    ///
    /// # Performance
    ///
    /// Note that this may introduce cross-module
    /// dependencies. This can have an impact on
    /// the effectiveness of incremental caching
    /// and should therefore be used judiciously.
    ///
    /// An example of a good use case is to improve
    /// a diagnostic.
    pub(crate) fn parameter_span(
        self,
        db: &'db dyn Db,
        parameter_index: Option<usize>,
    ) -> (Span, Span) {
        self.literal(db).parameter_span(db, parameter_index)
    }

    /// Returns a collection of useful spans for a
    /// function signature. These are useful for
    /// creating annotations on diagnostics.
    ///
    /// # Performance
    ///
    /// Note that this may introduce cross-module
    /// dependencies. This can have an impact on
    /// the effectiveness of incremental caching
    /// and should therefore be used judiciously.
    ///
    /// An example of a good use case is to improve
    /// a diagnostic.
    pub(crate) fn spans(self, db: &'db dyn Db) -> FunctionSpans {
        self.literal(db).spans(db)
    }

    /// Returns `true` if this function has a trivial body.
    pub(crate) fn has_trivial_body(self, db: &'db dyn Db) -> bool {
        self.literal(db).has_trivial_body(db)
    }

    /// Returns `true` if any overload or implementation has an explicit return annotation.
    ///
    /// This distinguishes untyped decorators that infer an unknown return from decorators that
    /// explicitly promise a replacement type:
    /// ```python
    /// def identity(cls):
    ///     return cls
    ///
    /// def replace(cls) -> object:
    ///     return object()
    /// ```
    pub(crate) fn has_explicit_return_annotation(self, db: &'db dyn Db) -> bool {
        self.iter_overloads_and_implementation(db)
            .any(|overload| overload.has_explicit_return_annotation(db))
    }

    /// Returns all of the overload signatures and the implementation definition, if any, of this
    /// function. The overload signatures will be in source order.
    pub(crate) fn overloads_and_implementation(
        self,
        db: &'db dyn Db,
    ) -> (&'db [OverloadLiteral<'db>], Option<OverloadLiteral<'db>>) {
        self.literal(db).overloads_and_implementation(db)
    }

    /// Returns an iterator of all of the definitions of this function, including both overload
    /// signatures and any implementation, all in source order.
    pub(crate) fn iter_overloads_and_implementation(
        self,
        db: &'db dyn Db,
    ) -> impl DoubleEndedIterator<Item = OverloadLiteral<'db>> + 'db {
        self.literal(db).iter_overloads_and_implementation(db)
    }

    pub(crate) fn first_overload_or_implementation(self, db: &'db dyn Db) -> OverloadLiteral<'db> {
        self.iter_overloads_and_implementation(db)
            .next()
            .expect("A function must have at least one overload/implementation")
    }

    /// Typed externally-visible signature for this function.
    ///
    /// This is the signature as seen by external callers, possibly modified by decorators and/or
    /// overloaded.
    ///
    /// ## Why is this a salsa query?
    ///
    /// This is a salsa query to short-circuit the invalidation
    /// when the function's AST node changes.
    ///
    /// Were this not a salsa query, then the calling query
    /// would depend on the function's AST and rerun for every change in that file.
    #[salsa::tracked(
        returns(ref),
        cycle_initial=|db, id, function: FunctionType<'db>| {
            let env = ProgramEnvironment::from_scope(
                function.literal(db).last_definition.body_scope(db),
            );
            CallableSignature::cycle_initial(db, &env, id)
        },
        cycle_fn=|db, cycle, previous, value: CallableSignature<'db>, function: FunctionType<'db>| {
            let env = ProgramEnvironment::from_scope(
                function.literal(db).last_definition.body_scope(db),
            );
            value.cycle_normalized(db, &env, previous, cycle)
        },
        heap_size=ruff_memory_usage::heap_size,
    )]
    pub(crate) fn signature(self, db: &'db dyn Db) -> CallableSignature<'db> {
        self.updated_signature(db)
            .cloned()
            .unwrap_or_else(|| self.literal(db).signature(db))
    }

    /// Infer the variance of a type variable within this function's signature.
    ///
    /// This is tracked because signatures can contain recursive `TypeOf` references back to the
    /// function itself. Class and generic-alias variance use the same `Bivariant` cycle fallback.
    #[salsa::tracked(
        returns(copy),
        cycle_initial=|_, _, _, _| TypeVarVariance::Bivariant,
        heap_size=ruff_memory_usage::heap_size,
    )]
    pub(crate) fn variance_of(
        self,
        db: &'db dyn Db,
        typevar: BoundTypeVarIdentity<'db>,
    ) -> TypeVarVariance {
        let env = ProgramEnvironment::from_scope(self.literal(db).last_definition.body_scope(db));
        self.signature(db).variance_of(db, &env, typevar)
    }

    /// Typed externally-visible signature of the last overload or implementation of this function.
    ///
    /// ## Why is this a salsa query?
    ///
    /// This is a salsa query to short-circuit the invalidation
    /// when the function's AST node changes.
    ///
    /// Were this not a salsa query, then the calling query
    /// would depend on the function's AST and rerun for every change in that file.
    #[salsa::tracked(
        returns(ref),
        cycle_initial=|_, _, _|Signature::bottom(),
        heap_size=ruff_memory_usage::heap_size,
    )]
    pub(crate) fn last_definition_signature(self, db: &'db dyn Db) -> Signature<'db> {
        let literal = self.literal(db);
        if literal.has_separate_implementation(db) {
            self.updated_implementation_signature(db)
                .cloned()
                .unwrap_or_else(|| literal.last_definition_signature(db))
        } else {
            self.updated_signature(db)
                .and_then(|signature| signature.overloads.last().cloned())
                .unwrap_or_else(|| literal.last_definition_signature(db))
        }
    }

    /// Typed externally-visible "raw" signature of the last overload or implementation of this function.
    /// The `return_callable_typevar_scope` controls whether type variables that only appear in a
    /// return-position `Callable` stay bound to the function or move to the returned callable.
    #[salsa::tracked(
        returns(ref),
        cycle_initial=|_, _, _, _|Signature::bottom(),
        heap_size=ruff_memory_usage::heap_size,
    )]
    pub(super) fn last_definition_raw_signature(
        self,
        db: &'db dyn Db,
        return_callable_typevar_scope: ReturnCallableTypeVarScope,
    ) -> Signature<'db> {
        self.literal(db)
            .last_definition_raw_signature(db, return_callable_typevar_scope)
    }

    /// Return the kind for this function when it is converted into a [`CallableType`].
    pub(crate) fn callable_type_kind(self, db: &'db dyn Db) -> CallableTypeKind {
        if self.is_classmethod(db) {
            CallableTypeKind::ClassMethodLike
        } else if self.is_staticmethod(db) {
            CallableTypeKind::StaticMethodLike
        } else {
            CallableTypeKind::FunctionLike
        }
    }

    /// Convert the `FunctionType` into a [`CallableType`].
    pub(crate) fn into_callable_type(self, db: &'db dyn Db) -> CallableType<'db> {
        CallableType::new(
            db,
            self.signature(db),
            self.callable_type_kind(db),
            CallableFunctionProvenance::from_function_return_annotation(
                self.has_explicit_return_annotation(db),
            ),
        )
    }

    /// Convert the `FunctionType` into a [`BoundMethodType`].
    pub(crate) fn into_bound_method_type(
        self,
        db: &'db dyn Db,
        self_instance: Type<'db>,
    ) -> BoundMethodType<'db> {
        BoundMethodType::new(db, self, self_instance)
    }

    pub(crate) fn find_legacy_typevars_impl(
        self,
        db: &'db dyn Db,
        env: &ProgramEnvironment<'db>,
        binding_context: Option<Definition<'db>>,
        typevars: &mut FxOrderSet<BoundTypeVarInstance<'db>>,
        visitor: &FindLegacyTypeVarsVisitor<'db>,
    ) {
        let signatures = self.signature(db);
        for signature in &signatures.overloads {
            signature.find_legacy_typevars_impl(db, env, binding_context, typevars, visitor);
        }
    }

    pub(crate) fn recursive_type_normalized_impl(
        self,
        db: &'db dyn Db,
        env: &ProgramEnvironment<'db>,
        div: Type<'db>,
        nested: bool,
    ) -> Option<Self> {
        visit_recursive_type_normalization(
            self.literal(db),
            nested,
            || None,
            || {
                let literal = self.literal(db);
                let updated_signature = match self.updated_signature(db) {
                    Some(signature) => {
                        Some(signature.recursive_type_normalized_impl(db, env, div, nested)?)
                    }
                    None => None,
                };
                let updated_implementation_callables =
                    match self.updated_implementation_callables(db) {
                        Some(callables) => Some(
                            callables
                                .iter()
                                .map(|callable| {
                                    callable.recursive_type_normalized_impl(db, env, div, nested)
                                })
                                .collect::<Option<Box<_>>>()?,
                        ),
                        None => None,
                    };
                Some(Self::new(
                    db,
                    literal,
                    UpdatedFunctionSignatures::new(
                        updated_signature,
                        updated_implementation_callables,
                    ),
                ))
            },
        )
    }

    pub(super) fn as_abstract_method(
        self,
        db: &'db dyn Db,
        enclosing_class: ClassType<'db>,
    ) -> Option<AbstractMethodKind> {
        self.literal(db).as_abstract_method(db, enclosing_class)
    }
}

impl<'c, 'db> TypeRelationChecker<'_, 'c, 'db> {
    pub(super) fn check_function_pair(
        &self,
        db: &'db dyn Db,
        source: FunctionType<'db>,
        target: FunctionType<'db>,
    ) -> ConstraintSet<'db, 'c> {
        if source.literal(db) != target.literal(db) {
            return self.never();
        }
        self.check_callable_signature_pair(db, source.signature(db), target.signature(db))
    }
}

/// Check the second argument to `isinstance()` or `issubclass()` for types that cannot be used
/// at runtime (protocol classes, typed dicts, `typing.Any` in `isinstance`, and invalid
/// `UnionType` elements). Handles class literals, tuples (including nested tuples), and
/// recursively validates each element.
///
/// `classinfo_expr` is the AST expression corresponding to `classinfo`, if available. It is
/// used for precise annotation spans (e.g., highlighting just the `UnionType` inside a tuple
/// rather than the whole tuple). It may be `None` when the tuple is not a literal in the AST
/// (e.g., when it's stored in a variable).
fn check_classinfo_in_isinstance<'db>(
    db: &'db dyn Db,
    context: &InferContext<'db, '_>,
    call_expression: &ast::ExprCall,
    function: KnownFunction,
    classinfo: Type<'db>,
    classinfo_expr: Option<&ast::Expr>,
) {
    match classinfo {
        Type::ClassLiteral(class) => {
            if class.is_typed_dict(db) {
                report_runtime_check_against_typed_dict(context, call_expression, class, function);
            } else if let Some(protocol_class) = class.into_protocol_class(db) {
                if !protocol_class.is_runtime_checkable(db) {
                    report_runtime_check_against_non_runtime_checkable_protocol(
                        context,
                        call_expression,
                        protocol_class,
                        function,
                    );
                } else if function == KnownFunction::IsSubclass {
                    let non_method_members = protocol_class.interface(db).non_method_members(db);
                    if !non_method_members.is_empty() {
                        report_issubclass_check_against_protocol_with_non_method_members(
                            context,
                            call_expression,
                            protocol_class,
                            &non_method_members,
                        );
                    }
                }
            }
        }
        Type::SpecialForm(SpecialFormType::Any) if function == KnownFunction::IsInstance => {
            let Some(builder) = context.report_lint(&INVALID_ARGUMENT_TYPE, call_expression) else {
                return;
            };
            let mut diagnostic = builder.into_diagnostic(format_args!(
                "`typing.Any` cannot be used with `isinstance()`"
            ));
            diagnostic
                .set_primary_annotation_message("This call will raise `TypeError` at runtime");
        }
        Type::KnownInstance(KnownInstanceType::UnionType(_)) => {
            report_invalid_union_type_elements(
                db,
                context,
                call_expression,
                function,
                classinfo,
                classinfo_expr,
            );
        }
        Type::NominalInstance(nominal)
            if let Some(tuple_spec) = nominal.tuple_spec(db, context.program_environment()) =>
        {
            let element_exprs = match classinfo_expr {
                Some(ast::Expr::Tuple(tuple_expr)) => Some(&tuple_expr.elts),
                _ => None,
            };
            for (index, element) in tuple_spec.iter_element_types(db).enumerate() {
                let element_expr = element_exprs.and_then(|elts| elts.get(index));
                check_classinfo_in_isinstance(
                    db,
                    context,
                    call_expression,
                    function,
                    element,
                    element_expr,
                );
            }
        }

        _ => {}
    }
}

/// Report an error if a `types.UnionType` instance passed to `isinstance()`/`issubclass()`
/// contains elements that are not class objects.
fn report_invalid_union_type_elements<'db>(
    db: &'db dyn Db,
    context: &InferContext<'db, '_>,
    call_expression: &ast::ExprCall,
    function: KnownFunction,
    union_type: Type<'db>,
    union_type_expr: Option<&ast::Expr>,
) {
    fn find_invalid_elements<'db>(
        db: &'db dyn Db,
        env: &ProgramEnvironment<'db>,
        function: KnownFunction,
        ty: Type<'db>,
        invalid_elements: &mut Vec<Type<'db>>,
    ) {
        match ty {
            Type::ClassLiteral(_) => {}
            Type::NominalInstance(instance)
                if instance.has_known_class(db, KnownClass::NoneType) => {}
            Type::SpecialForm(special_form) if special_form.is_valid_isinstance_target() => {}
            // `Any` can be used in `issubclass()` calls but not `isinstance()` calls
            Type::SpecialForm(SpecialFormType::Any) if function == KnownFunction::IsSubclass => {}
            Type::KnownInstance(KnownInstanceType::UnionType(instance)) => {
                match instance.value_expression_types(db, env) {
                    Ok(value_expression_types) => {
                        for element in value_expression_types {
                            find_invalid_elements(db, env, function, element, invalid_elements);
                        }
                    }
                    Err(_) => {
                        invalid_elements.push(ty);
                    }
                }
            }
            _ => invalid_elements.push(ty),
        }
    }

    let mut invalid_elements = vec![];
    let env = context.program_environment();
    find_invalid_elements(db, env, function, union_type, &mut invalid_elements);

    let Some((first_invalid_element, other_invalid_elements)) = invalid_elements.split_first()
    else {
        return;
    };

    let Some(builder) = context.report_lint(&INVALID_ARGUMENT_TYPE, call_expression) else {
        return;
    };

    let function_name: &str = function.into();

    let mut diagnostic =
        builder.into_diagnostic(format_args!("Invalid second argument to `{function_name}`"));
    diagnostic.info(format_args!(
        "A `UnionType` instance can only be used as the second argument to \
        `{function_name}` if all elements are class objects"
    ));
    if let Some(union_type_expr) = union_type_expr {
        diagnostic.annotate(
            Annotation::secondary(context.span(union_type_expr))
                .message("This `UnionType` instance contains non-class elements"),
        );
    }

    // When we have a secondary annotation pointing at the UnionType expression,
    // "the union" is unambiguous. Otherwise, spell out the union type in the message.
    let env = context.program_environment();
    let union_suffix = match (&union_type_expr, union_type) {
        (None, Type::KnownInstance(KnownInstanceType::UnionType(instance))) => {
            match instance.union_type(db) {
                Ok(ty) => format!(" `{}`", ty.display(db, env)),
                Err(_) => String::new(),
            }
        }
        _ => String::new(),
    };

    match other_invalid_elements {
        [] => diagnostic.info(format_args!(
            "Element `{}` in the union{union_suffix} is not a class object",
            first_invalid_element.display(db, env)
        )),
        [single] => diagnostic.info(format_args!(
            "Elements `{}` and `{}` in the union{union_suffix} are not class objects",
            first_invalid_element.display(db, env),
            single.display(db, env),
        )),
        _ => diagnostic.info(format_args!(
            "Element `{}` in the union{union_suffix}, and {} more elements, are not class objects",
            first_invalid_element.display(db, env),
            other_invalid_elements.len(),
        )),
    }
}

/// Evaluate an `isinstance` call. Return `Truthiness::AlwaysTrue` if we can definitely infer that
/// this will return `True` at runtime, `Truthiness::AlwaysFalse` if we can definitely infer
/// that this will return `False` at runtime, or `Truthiness::Ambiguous` if we should infer `bool`
/// instead.
fn is_instance_truthiness<'db>(
    db: &'db dyn Db,
    env: &ProgramEnvironment<'db>,
    ty: Type<'db>,
    class: ClassLiteral<'db>,
) -> Truthiness {
    let is_instance = |ty: &Type<'_>| {
        ty.as_nominal_instance().is_some_and(|instance| {
            instance
                .class(db, env)
                .is_subtype_of_class_literal(db, class)
        })
    };

    let always_true_if = |test: bool| {
        if test {
            Truthiness::AlwaysTrue
        } else {
            Truthiness::Ambiguous
        }
    };

    match ty {
        Type::Union(..) => {
            // We do not handle unions specifically here, because something like `A | SubclassOfA` would
            // have been simplified to `A` anyway
            Truthiness::Ambiguous
        }

        // Create a new intersection that maps type variables to their upper bounds,
        // and evaluate the truthiness of the `isinstance()` check with that type.
        // Along the way, short-circuit to `AlwaysTrue` if we find any positive element
        // that is always true.
        Type::Intersection(intersection) => {
            let mut effective = IntersectionBuilder::new(db, env);
            let mut found_tvars_or_newtypes = false;

            for &positive in intersection.positive(db) {
                if is_instance_truthiness(db, env, positive, class).is_always_true() {
                    return Truthiness::AlwaysTrue;
                } else if let Type::TypeVar(tvar) = positive {
                    match tvar.typevar(db).bound_or_constraints(db, env) {
                        Some(TypeVarBoundOrConstraints::UpperBound(bound)) => {
                            effective.add_positive_in_place(bound);
                        }
                        Some(TypeVarBoundOrConstraints::Constraints(constraints)) => {
                            effective.add_positive_in_place(constraints.as_type(db, env));
                        }
                        // A typevar without bounds/constraints has `object` as its implicit upper bound,
                        // and adding `object` to an intersection is a no-op
                        None => {}
                    }
                    found_tvars_or_newtypes = true;
                } else if let Type::NewTypeInstance(newtype) = positive {
                    found_tvars_or_newtypes = true;
                    effective.add_positive_in_place(newtype.concrete_base_type(db));
                } else {
                    effective.add_positive_in_place(positive);
                }
            }

            if !found_tvars_or_newtypes {
                return Truthiness::Ambiguous;
            }

            for &negative in intersection.negative(db) {
                if is_instance_truthiness(db, env, negative, class).is_always_true() {
                    return Truthiness::AlwaysFalse;
                }
                effective.add_negative_in_place(negative);
            }

            let effective = effective.build();

            if effective == ty {
                Truthiness::Ambiguous
            } else {
                is_instance_truthiness(db, env, effective, class)
            }
        }

        Type::EnumComplement(complement) => {
            is_instance_truthiness(db, env, complement.to_intersection(db, env), class)
        }

        Type::NominalInstance(..) => always_true_if(is_instance(&ty)),

        Type::NewTypeInstance(newtype) => {
            always_true_if(is_instance(&newtype.concrete_base_type(db)))
        }

        Type::LiteralValue(..) | Type::ModuleLiteral(..) | Type::FunctionLiteral(..) => {
            always_true_if(
                ty.literal_fallback_instance(db, env)
                    .as_ref()
                    .is_some_and(is_instance),
            )
        }

        Type::ClassLiteral(..) => {
            always_true_if(is_instance(&KnownClass::Type.to_instance(db, env)))
        }

        Type::TypeAlias(alias) => is_instance_truthiness(db, env, alias.value_type(db), class),

        Type::TypeVar(bound_typevar) => {
            match bound_typevar.typevar(db).bound_or_constraints(db, env) {
                None => is_instance_truthiness(db, env, Type::object(), class),
                Some(TypeVarBoundOrConstraints::UpperBound(bound)) => {
                    is_instance_truthiness(db, env, bound, class)
                }
                Some(TypeVarBoundOrConstraints::Constraints(constraints)) => always_true_if(
                    constraints
                        .elements(db)
                        .iter()
                        .all(|c| is_instance_truthiness(db, env, *c, class).is_always_true()),
                ),
            }
        }

        Type::BoundMethod(..)
        | Type::KnownBoundMethod(..)
        | Type::WrapperDescriptor(..)
        | Type::DataclassDecorator(..)
        | Type::DataclassTransformer(..)
        | Type::GenericAlias(..)
        | Type::SubclassOf(..)
        | Type::ProtocolInstance(..)
        | Type::SpecialForm(..)
        | Type::KnownInstance(..)
        | Type::PropertyInstance(..)
        | Type::AlwaysTruthy
        | Type::AlwaysFalsy
        | Type::BoundSuper(..)
        | Type::TypeIs(..)
        | Type::TypeGuard(..)
        | Type::TypeForm(..)
        | Type::Callable(..)
        | Type::Dynamic(..)
        | Type::Divergent(_)
        | Type::Never
        | Type::TypedDict(_) => {
            // We could probably try to infer more precise types in some of these cases, but it's unclear
            // if it's worth the effort.
            Truthiness::Ambiguous
        }
    }
}

/// Return whether a fixed `isinstance` tuple covers every possible type of an input.
///
/// Each class in the tuple uses the same truthiness inference as a single-class `isinstance` check.
///
/// ```python
/// def f(x: A | B) -> bool:
///     if isinstance(x, (A, B)):
///         return True
/// ```
fn is_instance_tuple_exhaustive<'db>(
    db: &'db dyn Db,
    env: &ProgramEnvironment<'db>,
    ty: Type<'db>,
    classinfo: Type<'db>,
) -> bool {
    let Some(tuple) = classinfo.tuple_instance_spec(db, env) else {
        return false;
    };
    if tuple.is_variadic() {
        return false;
    }

    is_instance_tuple_covers(db, env, &tuple, ty, &ActiveRecursionDetector::default())
}

fn is_instance_tuple_covers<'db>(
    db: &'db dyn Db,
    env: &ProgramEnvironment<'db>,
    tuple: &TupleSpec<'db>,
    ty: Type<'db>,
    recursion_guard: &ActiveRecursionDetector<Type<'db>>,
) -> bool {
    match ty {
        Type::TypeAlias(alias) => recursion_guard.visit(
            &ty,
            || true,
            || is_instance_tuple_covers(db, env, tuple, alias.value_type(db), recursion_guard),
        ),
        Type::Union(union) => union
            .elements(db)
            .iter()
            .all(|element| is_instance_tuple_covers(db, env, tuple, *element, recursion_guard)),
        Type::Intersection(intersection) => intersection
            .positive(db)
            .iter()
            .any(|element| is_instance_tuple_covers(db, env, tuple, *element, recursion_guard)),
        Type::TypeVar(typevar) => match typevar.typevar(db).bound_or_constraints(db, env) {
            Some(TypeVarBoundOrConstraints::UpperBound(bound)) => {
                is_instance_tuple_covers(db, env, tuple, bound, recursion_guard)
            }
            Some(TypeVarBoundOrConstraints::Constraints(constraints)) => {
                constraints.elements(db).iter().all(|constraint| {
                    is_instance_tuple_covers(db, env, tuple, *constraint, recursion_guard)
                })
            }
            None => is_instance_tuple_covers(db, env, tuple, Type::object(), recursion_guard),
        },
        ty => tuple.fixed_elements().any(|element| {
            let Type::ClassLiteral(class) = element else {
                return false;
            };
            is_instance_truthiness(db, env, ty, *class).is_always_true()
        }),
    }
}

/// Returns `true` if the function body is stub-like, ignoring a leading docstring.
pub(crate) fn function_has_stub_body(node: &ast::StmtFunctionDef) -> bool {
    let suite = ast::helpers::body_without_leading_docstring(&node.body);

    suite.iter().all(|stmt| match stmt {
        ast::Stmt::Pass(_) => true,
        ast::Stmt::Expr(ast::StmtExpr { value, .. }) => value.is_ellipsis_literal_expr(),
        _ => false,
    })
}

/// Classify the body of this function:
/// - [`FunctionBodyKind::Stub`] if it is a stub function (i.e., only contains `pass` or `...`
/// - [`FunctionBodyKind::AlwaysRaisesNotImplementedError`] if it consists of a single
///   `raise NotImplementedError` statement
/// - [`FunctionBodyKind::Regular`] otherwise
///
/// In all cases, we allow a docstring as the first statement in the function body;
/// the analysis is only done on the remaining statements if the first is a docstring.
pub(super) fn function_body_kind<'db>(
    db: &'db dyn Db,
    env: &ProgramEnvironment<'db>,
    node: &ast::StmtFunctionDef,
    infer_type: impl Fn(&ast::Expr) -> Type<'db>,
) -> FunctionBodyKind {
    // Allow docstrings, but only as the first statement.
    let suite = ast::helpers::body_without_leading_docstring(&node.body);

    if function_has_stub_body(node) {
        return FunctionBodyKind::Stub;
    }

    if let [ast::Stmt::Raise(raise)] = suite
        && let ast::StmtRaise {
            exc: Some(exc),
            cause: None,
            node_index: _,
            range: _,
        } = raise
    {
        if infer_type(exc).is_subtype_of(
            db,
            env,
            UnionType::from_two_elements(
                db,
                env,
                KnownClass::NotImplementedError.to_class_literal(db, env),
                KnownClass::NotImplementedError.to_instance(db, env),
            ),
        ) {
            return FunctionBodyKind::AlwaysRaisesNotImplementedError;
        }
    }

    FunctionBodyKind::Regular
}

/// Classification of function body kinds.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub(super) enum FunctionBodyKind {
    /// The function body only consists of `...`, `pass`, and/or a docstring.
    Stub,
    /// The function body consists of a single `raise NotImplementedError` statement,
    /// possibly preceded by a docstring.
    AlwaysRaisesNotImplementedError,
    /// Any function body that is not a stub and does not consist of a single
    /// `raise NotImplementedError` statement.
    Regular,
}

/// Non-exhaustive enumeration of known functions (e.g. `builtins.reveal_type`, ...) that might
/// have special behavior.
#[derive(
    Debug,
    Copy,
    Clone,
    PartialEq,
    Eq,
    Hash,
    strum_macros::EnumString,
    strum_macros::IntoStaticStr,
    get_size2::GetSize,
)]
#[strum(serialize_all = "snake_case")]
#[cfg_attr(test, derive(strum_macros::EnumIter))]
pub enum KnownFunction {
    /// `builtins.isinstance`
    #[strum(serialize = "isinstance")]
    IsInstance,
    /// `builtins.issubclass`
    #[strum(serialize = "issubclass")]
    IsSubclass,
    /// `builtins.hasattr`
    #[strum(serialize = "hasattr")]
    HasAttr,
    /// `builtins.reveal_type`, `typing.reveal_type` or `typing_extensions.reveal_type`
    RevealType,
    /// `builtins.len`
    Len,
    /// `builtins.repr`
    Repr,
    /// `builtins.__import__`, which returns the top-level module.
    #[strum(serialize = "__import__")]
    DunderImport,
    /// `collections.namedtuple`
    #[strum(serialize = "namedtuple")]
    NamedTuple,
    /// `importlib.import_module`, which returns the submodule.
    ImportModule,
    /// `typing(_extensions).final`
    Final,
    /// `typing(_extensions).disjoint_base`
    DisjointBase,
    /// [`typing(_extensions).no_type_check`](https://typing.python.org/en/latest/spec/directives.html#no-type-check)
    NoTypeCheck,
    /// `typing(_extensions).type_check_only`
    TypeCheckOnly,

    /// `typing(_extensions).assert_type`
    AssertType,
    /// `typing(_extensions).assert_never`
    AssertNever,
    /// `typing(_extensions).cast`
    Cast,
    /// `typing(_extensions).overload`
    Overload,
    /// `typing(_extensions).override`
    Override,
    /// `typing(_extensions).is_protocol`
    IsProtocol,
    /// `typing(_extensions).get_protocol_members`
    GetProtocolMembers,
    /// `typing(_extensions).runtime_checkable`
    RuntimeCheckable,
    /// `typing(_extensions).dataclass_transform`
    DataclassTransform,

    /// `abc.abstractmethod`
    #[strum(serialize = "abstractmethod")]
    AbstractMethod,

    /// `dataclasses.dataclass`
    Dataclass,
    /// `dataclasses.field`
    Field,

    /// `pydantic.fields.Field`
    #[strum(serialize = "Field")]
    PydanticField,
    /// `pydantic.functional_validators.field_validator`
    #[strum(serialize = "field_validator")]
    PydanticFieldValidator,

    /// `functools.total_ordering`
    TotalOrdering,

    /// `inspect.getattr_static`
    GetattrStatic,

    /// `ty_extensions.static_assert`
    StaticAssert,
    /// `ty_extensions._internal.is_equivalent_to`
    IsEquivalentTo,
    /// `ty_extensions._internal.is_subtype_of`
    IsSubtypeOf,
    /// `ty_extensions._internal.is_assignable_to`
    IsAssignableTo,
    /// `ty_extensions._internal.is_constraint_set_assignable_to`
    IsConstraintSetAssignableTo,
    /// `ty_extensions._internal.is_disjoint_from`
    IsDisjointFrom,
    /// `ty_extensions._internal.is_singleton`
    IsSingleton,
    /// `ty_extensions._internal.generic_context`
    GenericContext,
    /// `ty_extensions._internal.into_callable`
    IntoCallable,
    /// `ty_extensions._internal.into_regular_callable`
    IntoRegularCallable,
    /// `ty_extensions._internal.dunder_all_names`
    DunderAllNames,
    /// `ty_extensions._internal.enum_members`
    EnumMembers,
    /// `ty_extensions._internal.all_members`
    AllMembers,
    /// `ty_extensions._internal.has_member`
    HasMember,
    /// `ty_extensions._internal.reveal_protocol_interface`
    RevealProtocolInterface,
    /// `ty_extensions._internal.reveal_mro`
    RevealMro,
    /// `struct.unpack`
    Unpack,
    /// `types.new_class`
    NewClass,
}

fn call_argument_node<'a>(
    call_expression: &'a ast::ExprCall,
    name: &str,
    position: usize,
) -> Option<ast::AnyNodeRef<'a>> {
    call_expression
        .arguments
        .find_argument(name, position)
        .map(|argument| match argument {
            ast::ArgOrKeyword::Arg(expr) => ast::AnyNodeRef::from(expr),
            ast::ArgOrKeyword::Keyword(keyword) => ast::AnyNodeRef::from(keyword),
        })
}

impl KnownFunction {
    pub fn into_classinfo_constraint_function(self) -> Option<ClassInfoConstraintFunction> {
        match self {
            Self::IsInstance => Some(ClassInfoConstraintFunction::IsInstance),
            Self::IsSubclass => Some(ClassInfoConstraintFunction::IsSubclass),
            _ => None,
        }
    }

    pub(crate) fn try_from_definition_and_name<'db>(
        db: &'db dyn Db,
        definition: Definition<'db>,
        name: &str,
    ) -> Option<Self> {
        // Special case: `__dataclass_transform__` is recognized as `DataclassTransform`
        // regardless of module, for backwards compatibility with earlier versions of the
        // `dataclass_transform` specification. This matches pyright's behavior:
        // https://github.com/microsoft/pyright/blob/1.1.396/packages/pyright-internal/src/analyzer/dataClasses.ts#L1024-L1033
        if name == "__dataclass_transform__" {
            return Some(Self::DataclassTransform);
        }

        let candidate = Self::from_str(name).ok()?;
        candidate
            .check_module(
                file_to_module(db, definition.program_file(db).resolver_file(db))?.known(db)?,
            )
            .then_some(candidate)
    }

    /// Return `true` if `self` is defined in `module`
    const fn check_module(self, module: KnownModule) -> bool {
        match self {
            Self::IsInstance
            | Self::IsSubclass
            | Self::HasAttr
            | Self::Len
            | Self::Repr
            | Self::DunderImport => module.is_builtins(),
            Self::AssertType
            | Self::AssertNever
            | Self::Cast
            | Self::Overload
            | Self::Override
            | Self::RevealType
            | Self::Final
            | Self::IsProtocol
            | Self::GetProtocolMembers
            | Self::RuntimeCheckable
            | Self::DataclassTransform
            | Self::DisjointBase
            | Self::NoTypeCheck => {
                matches!(module, KnownModule::Typing | KnownModule::TypingExtensions)
            }
            Self::AbstractMethod => {
                matches!(module, KnownModule::Abc)
            }
            Self::Dataclass | Self::Field => {
                matches!(module, KnownModule::Dataclasses)
            }
            Self::PydanticField => matches!(module, KnownModule::PydanticFields),
            Self::PydanticFieldValidator => {
                matches!(module, KnownModule::PydanticFunctionalValidators)
            }
            Self::TotalOrdering => module.is_functools(),
            Self::GetattrStatic => module.is_inspect(),
            Self::StaticAssert => module.is_ty_extensions(),
            Self::IsAssignableTo
            | Self::IsConstraintSetAssignableTo
            | Self::IsDisjointFrom
            | Self::IsEquivalentTo
            | Self::IsSingleton
            | Self::IsSubtypeOf
            | Self::GenericContext
            | Self::IntoCallable
            | Self::IntoRegularCallable
            | Self::DunderAllNames
            | Self::EnumMembers
            | Self::HasMember
            | Self::RevealProtocolInterface
            | Self::RevealMro
            | Self::AllMembers => module.is_ty_extensions_internal(),
            Self::ImportModule => module.is_importlib(),
            Self::Unpack => {
                matches!(module, KnownModule::Struct)
            }
            Self::NewClass => {
                matches!(module, KnownModule::Types)
            }

            Self::TypeCheckOnly => matches!(module, KnownModule::Typing),
            Self::NamedTuple => matches!(module, KnownModule::Collections),
        }
    }

    /// Evaluate a call to this known function, and emit any diagnostics that are necessary
    /// as a result of the call.
    pub(super) fn check_call<'db>(
        self,
        context: &InferContext<'db, '_>,
        overload: &mut Binding<'db>,
        call_arguments: &CallArguments<'_, 'db>,
        call_expression: &ast::ExprCall,
    ) {
        let db = context.db();
        let parameter_types = overload.parameter_types();

        match self {
            KnownFunction::RevealType => {
                let env = context.program_environment();
                let revealed_type = overload
                    .arguments_for_parameter(call_arguments, 0)
                    .fold(UnionBuilder::new(db, env), |builder, (_, ty)| {
                        builder.add(ty)
                    })
                    .build();
                report_revealed_type(
                    context,
                    revealed_type,
                    call_argument_node(call_expression, "obj", 0)
                        .unwrap_or_else(|| ast::AnyNodeRef::from(call_expression)),
                );
            }

            KnownFunction::HasMember => {
                let [Some(ty), Some(Type::LiteralValue(literal))] = parameter_types else {
                    return;
                };
                let Some(member) = literal.as_string() else {
                    return;
                };
                let env = context.program_environment();
                let ty_members = all_members(db, env, *ty);
                overload.set_return_type(Type::bool_literal(
                    ty_members.iter().any(|m| m.name == member.value(db)),
                ));
            }

            KnownFunction::AssertType => {
                let [Some(actual_ty), Some(asserted_ty)] = parameter_types else {
                    return;
                };
                let env = context.program_environment();
                let asserted_ty = asserted_ty.project_type_form(db, env);
                if actual_ty.is_equivalent_to(db, env, asserted_ty) {
                    return;
                }
                let diagnostic = if actual_ty.is_spellable(db)
                    || !actual_ty.is_subtype_of(db, env, asserted_ty)
                {
                    &TYPE_ASSERTION_FAILURE
                } else {
                    &ASSERT_TYPE_UNSPELLABLE_SUBTYPE
                };
                if let Some(builder) = context.report_lint(diagnostic, call_expression) {
                    let mut diagnostic = builder.into_diagnostic(format_args!(
                        "Argument does not have asserted type `{}`",
                        asserted_ty.display(db, env),
                    ));

                    diagnostic.annotate(
                        Annotation::secondary(
                            context.span(
                                call_argument_node(call_expression, "val", 0)
                                    .unwrap_or_else(|| ast::AnyNodeRef::from(call_expression)),
                            ),
                        )
                        .message(format_args!(
                            "Inferred type is `{}`",
                            actual_ty.display(db, env)
                        )),
                    );

                    if actual_ty.is_subtype_of(db, env, asserted_ty) {
                        diagnostic.info(format_args!(
                            "`{inferred_type}` is a subtype of `{asserted_type}`, but they are not equivalent",
                            asserted_type = asserted_ty.display(db, env),
                            inferred_type = actual_ty.display(db, env),
                        ));
                    } else {
                        diagnostic.info(format_args!(
                            "`{asserted_type}` and `{inferred_type}` are not equivalent types",
                            asserted_type = asserted_ty.display(db, env),
                            inferred_type = actual_ty.display(db, env),
                        ));
                    }

                    diagnostic.set_concise_message(format_args!(
                        "Type `{}` does not match asserted type `{}`",
                        actual_ty.display(db, env),
                        asserted_ty.display(db, env),
                    ));
                }
            }

            KnownFunction::AssertNever => {
                let [Some(actual_ty)] = parameter_types else {
                    return;
                };
                let env = context.program_environment();
                if actual_ty.is_equivalent_to(db, env, Type::Never) {
                    return;
                }
                if let Some(builder) = context.report_lint(&TYPE_ASSERTION_FAILURE, call_expression)
                {
                    let mut diagnostic =
                        builder.into_diagnostic("Argument does not have asserted type `Never`");
                    diagnostic.annotate(
                        Annotation::secondary(
                            context.span(
                                call_argument_node(call_expression, "arg", 0)
                                    .unwrap_or_else(|| ast::AnyNodeRef::from(call_expression)),
                            ),
                        )
                        .message(format_args!(
                            "Inferred type of argument is `{}`",
                            actual_ty.display(db, env)
                        )),
                    );
                    diagnostic.info(format_args!(
                        "`Never` and `{inferred_type}` are not equivalent types",
                        inferred_type = actual_ty.display(db, env),
                    ));

                    diagnostic.set_concise_message(format_args!(
                        "Type `{}` is not equivalent to `Never`",
                        actual_ty.display(db, env),
                    ));
                }
            }

            KnownFunction::StaticAssert => {
                let [Some(parameter_ty), message] = parameter_types else {
                    return;
                };
                let env = context.program_environment();
                let truthiness = match parameter_ty.try_bool(db, env) {
                    Ok(truthiness) => truthiness,
                    Err(err) => {
                        err.report_diagnostic(
                            context,
                            call_argument_node(call_expression, "condition", 0)
                                .unwrap_or_else(|| ast::AnyNodeRef::from(call_expression)),
                        );

                        return;
                    }
                };

                if let Some(builder) = context.report_lint(&STATIC_ASSERT_ERROR, call_expression) {
                    if truthiness.is_always_true() {
                        return;
                    }
                    let mut diagnostic = if let Some(message) = message
                        .and_then(Type::as_string_literal)
                        .map(|s| s.value(db))
                    {
                        builder.into_diagnostic(format_args!("Static assertion error: {message}"))
                    } else if *parameter_ty == Type::bool_literal(false) {
                        builder.into_diagnostic(
                            "Static assertion error: argument evaluates to `False`",
                        )
                    } else if truthiness.is_always_false() {
                        builder.into_diagnostic(format_args!(
                            "Static assertion error: argument of type `{parameter_ty}` \
                            is always falsy",
                            parameter_ty = parameter_ty.display(db, env)
                        ))
                    } else {
                        builder.into_diagnostic(format_args!(
                            "Static assertion error: argument of type `{parameter_ty}` \
                            has an ambiguous static truthiness",
                            parameter_ty = parameter_ty.display(db, env)
                        ))
                    };
                    if let Some(condition) = call_argument_node(call_expression, "condition", 0) {
                        diagnostic.annotate(
                            Annotation::secondary(context.span(condition)).message(format_args!(
                                "Inferred type of argument is `{}`",
                                parameter_ty.display(db, env)
                            )),
                        );
                    }
                }
            }

            KnownFunction::Cast => {
                let [Some(casted_type), Some(source_type)] = parameter_types else {
                    return;
                };
                let env = context.program_environment();
                let casted_type = casted_type.project_type_form(db, env);
                if source_type.is_equivalent_to(db, env, casted_type)
                    && non_any_dynamic_content(db, env, *source_type).is_absent()
                    && non_any_dynamic_content(db, env, casted_type).is_absent()
                {
                    if let Some(builder) = context.report_lint(&REDUNDANT_CAST, call_expression) {
                        let source_display = source_type.display(db, env).to_string();
                        let casted_display = casted_type.display(db, env).to_string();
                        let mut diagnostic = builder.into_diagnostic(format_args!(
                            "Value is already of type `{casted_display}`",
                        ));
                        if source_display != casted_display {
                            diagnostic.info(format_args!(
                                "`{casted_display}` is equivalent to `{source_display}`",
                            ));
                        }
                        if let Some(value) = call_expression.arguments.find_argument_value("val", 1)
                        {
                            let covering = covering_node(
                                context.module().syntax().into(),
                                call_expression.range(),
                            );
                            let needs_parens = covering
                                .parent()
                                .and_then(ast::AnyNodeRef::as_expr_ref)
                                .is_some_and(|parent| {
                                    let value_precedence = OperatorPrecedence::from_expr(value);
                                    OperatorPrecedence::from_expr_ref(parent) >= value_precedence
                                });
                            let value_text = &source_text(db, context.file())[value.range()];
                            let replacement = if needs_parens {
                                format!("({value_text})")
                            } else {
                                value_text.to_string()
                            };
                            diagnostic.help("Remove the redundant `cast`");
                            diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
                                replacement,
                                call_expression.range(),
                            )));
                        }
                    }
                }
            }

            KnownFunction::GetProtocolMembers => {
                let [Some(Type::ClassLiteral(class))] = parameter_types else {
                    return;
                };
                if class.is_protocol(context.db()) {
                    return;
                }
                report_bad_argument_to_get_protocol_members(context, call_expression, *class);
            }

            KnownFunction::RevealProtocolInterface => {
                let [Some(param_type)] = parameter_types else {
                    return;
                };
                let env = context.program_environment();
                let Some(protocol_class) = param_type
                    .to_class_type(db)
                    .and_then(|class| class.into_protocol_class(db))
                else {
                    report_bad_argument_to_protocol_interface(
                        context,
                        call_expression,
                        *param_type,
                    );
                    return;
                };
                if let Some(builder) =
                    context.report_diagnostic(DiagnosticId::RevealedType, Severity::Info)
                {
                    let mut diag = builder.into_diagnostic("Revealed protocol interface");
                    let span = context.span(
                        call_argument_node(call_expression, "protocol", 0)
                            .unwrap_or_else(|| ast::AnyNodeRef::from(call_expression)),
                    );
                    diag.annotate(Annotation::primary(span).message(format_args!(
                        "`{}`",
                        protocol_class.interface(db).display(db, env)
                    )));
                }
            }

            KnownFunction::RevealMro => {
                let [Some(param_type)] = parameter_types else {
                    return;
                };
                let mut good_argument = true;
                let classes = match param_type {
                    Type::ClassLiteral(class) => vec![ClassType::NonGeneric(*class)],
                    Type::GenericAlias(generic_alias) => vec![ClassType::Generic(*generic_alias)],
                    Type::Union(union) => {
                        let elements = union.elements(db);
                        let mut classes = Vec::with_capacity(elements.len());
                        for element in elements {
                            match element {
                                Type::ClassLiteral(class) => {
                                    classes.push(ClassType::NonGeneric(*class));
                                }
                                Type::GenericAlias(generic_alias) => {
                                    classes.push(ClassType::Generic(*generic_alias));
                                }
                                _ => {
                                    good_argument = false;
                                    break;
                                }
                            }
                        }
                        classes
                    }
                    _ => {
                        good_argument = false;
                        vec![]
                    }
                };
                if !good_argument {
                    let Some(builder) =
                        context.report_lint(&INVALID_ARGUMENT_TYPE, call_expression)
                    else {
                        return;
                    };
                    let mut diagnostic =
                        builder.into_diagnostic("Invalid argument to `reveal_mro`");
                    diagnostic.set_primary_annotation_message(format_args!(
                        "Can only pass a class object, generic alias or a union thereof"
                    ));
                    return;
                }
                if let Some(builder) =
                    context.report_diagnostic(DiagnosticId::RevealedType, Severity::Info)
                {
                    let env = context.program_environment();
                    let mut diag = builder.into_diagnostic("Revealed MRO");
                    let span = context.span(
                        call_argument_node(call_expression, "cls", 0)
                            .unwrap_or_else(|| ast::AnyNodeRef::from(call_expression)),
                    );
                    let mut message = String::new();
                    let display_settings = DisplaySettings::from_possibly_ambiguous_types(
                        db,
                        env,
                        classes
                            .iter()
                            .flat_map(|class| class.iter_mro(db))
                            .filter_map(ClassBase::into_class),
                    );
                    for (i, class) in classes.iter().enumerate() {
                        message.push('(');
                        for class in class.iter_mro(db) {
                            message.push_str(
                                &class
                                    .display_with(db, env, display_settings.clone())
                                    .to_string(),
                            );
                            // Omit the comma for the last element (which is always `object`)
                            if class
                                .into_class()
                                .is_none_or(|base| !base.is_object(context.db()))
                            {
                                message.push_str(", ");
                            }
                        }
                        // If the last element was also the first element
                        // (i.e., it's a length-1 tuple -- which can only happen if we're revealing
                        // the MRO for `object` itself), add a trailing comma so that it's still a
                        // valid tuple display.
                        if class.is_object(db) {
                            message.push(',');
                        }
                        message.push(')');
                        if i < classes.len() - 1 {
                            message.push_str(" | ");
                        }
                    }
                    diag.annotate(Annotation::primary(span).message(message));
                }
            }

            KnownFunction::IsInstance | KnownFunction::IsSubclass => {
                let [Some(first_arg), Some(second_argument)] = parameter_types else {
                    return;
                };

                check_classinfo_in_isinstance(
                    db,
                    context,
                    call_expression,
                    self,
                    *second_argument,
                    call_expression.arguments.args.get(1),
                );

                if self == KnownFunction::IsInstance {
                    let env = context.program_environment();
                    let truthiness = match second_argument {
                        Type::ClassLiteral(class) => {
                            is_instance_truthiness(db, env, *first_arg, *class)
                        }
                        Type::SpecialForm(
                            SpecialFormType::TypingCallable
                            | SpecialFormType::CollectionsAbcCallable,
                        ) => {
                            let callable_top = Type::Callable(CallableType::unknown(db))
                                .top_materialization(db, env);
                            if first_arg.is_subtype_of(db, env, callable_top) {
                                Truthiness::AlwaysTrue
                            } else {
                                Truthiness::Ambiguous
                            }
                        }
                        _ if is_instance_tuple_exhaustive(
                            db,
                            env,
                            *first_arg,
                            *second_argument,
                        ) =>
                        {
                            Truthiness::AlwaysTrue
                        }
                        _ => Truthiness::Ambiguous,
                    };
                    overload.set_return_type(Type::from_truthiness(db, env, truthiness));
                }
            }

            known @ (KnownFunction::DunderImport | KnownFunction::ImportModule) => {
                let [Some(first), rest @ ..] = parameter_types else {
                    return;
                };
                let Some(full_module_name) = first.as_string_literal() else {
                    return;
                };

                if rest.iter().any(Option::is_some) {
                    return;
                }

                let module_name = full_module_name.value(db);

                if known == KnownFunction::DunderImport && module_name.contains('.') {
                    // `__import__("collections.abc")` returns the `collections` module.
                    // `importlib.import_module("collections.abc")` returns the `collections.abc` module.
                    // ty doesn't have a way to represent the return type of the former yet.
                    // https://github.com/astral-sh/ruff/pull/19008#discussion_r2173481311
                    return;
                }

                let Some(module_name) = ModuleName::new(module_name) else {
                    return;
                };
                let importing_file = ImportingFile::File(
                    context.file(),
                    context.program_environment().resolver_environment(db),
                );
                let Some(module) = resolve_module(db, importing_file, &module_name) else {
                    return;
                };

                overload.set_return_type(Type::module_literal(db, context.program_file(), module));
            }

            KnownFunction::TotalOrdering => {
                // When `total_ordering(cls)` is called as a function (not as a decorator),
                // check that the class defines at least one ordering method.
                let [Some(class_type)] = parameter_types else {
                    return;
                };

                let class = match class_type {
                    Type::ClassLiteral(class) => ClassType::NonGeneric(*class),
                    Type::GenericAlias(generic) => ClassType::Generic(*generic),
                    _ => return,
                };

                if !class.has_ordering_method_in_mro(db) {
                    report_invalid_total_ordering_call(
                        context,
                        class.class_literal(db),
                        call_expression,
                    );
                }
            }

            _ => {}
        }
    }

    pub(crate) fn name(self) -> &'static str {
        self.into()
    }
}

/// Emit a `revealed-type` diagnostic for a `reveal_type(...)` call.
pub(super) fn report_revealed_type<'db>(
    context: &InferContext<'db, '_>,
    revealed_type: Type<'db>,
    argument_node: impl Ranged,
) {
    let db = context.db();
    if let Some(builder) = context.report_diagnostic(DiagnosticId::RevealedType, Severity::Info) {
        let env = context.program_environment();
        let mut diag = builder.into_diagnostic("Revealed type");
        diag.annotate(
            Annotation::primary(context.span(argument_node)).message(format_args!(
                "`{}`",
                revealed_type.display(db, env).preserve_long_unions()
            )),
        );
    }
}

#[cfg(test)]
pub(crate) mod tests {
    use strum::IntoEnumIterator;

    use super::*;
    use crate::db::tests::setup_db;
    use crate::place::known_module_symbol;

    #[test]
    fn known_function_roundtrip_from_str() {
        let db = setup_db();

        for function in KnownFunction::iter() {
            let function_name = function.name();

            let module = match function {
                KnownFunction::Len
                | KnownFunction::Repr
                | KnownFunction::IsInstance
                | KnownFunction::HasAttr
                | KnownFunction::IsSubclass
                | KnownFunction::DunderImport => KnownModule::Builtins,

                KnownFunction::AbstractMethod => KnownModule::Abc,

                KnownFunction::Dataclass | KnownFunction::Field => KnownModule::Dataclasses,

                KnownFunction::PydanticField => KnownModule::PydanticFields,
                KnownFunction::PydanticFieldValidator => KnownModule::PydanticFunctionalValidators,

                KnownFunction::GetattrStatic => KnownModule::Inspect,

                KnownFunction::Cast
                | KnownFunction::Final
                | KnownFunction::Overload
                | KnownFunction::Override
                | KnownFunction::RevealType
                | KnownFunction::AssertType
                | KnownFunction::AssertNever
                | KnownFunction::IsProtocol
                | KnownFunction::GetProtocolMembers
                | KnownFunction::RuntimeCheckable
                | KnownFunction::DataclassTransform
                | KnownFunction::DisjointBase
                | KnownFunction::NoTypeCheck => KnownModule::TypingExtensions,

                KnownFunction::TypeCheckOnly => KnownModule::Typing,

                KnownFunction::StaticAssert => KnownModule::TyExtensions,

                KnownFunction::IsSingleton
                | KnownFunction::IsSubtypeOf
                | KnownFunction::GenericContext
                | KnownFunction::IntoCallable
                | KnownFunction::IntoRegularCallable
                | KnownFunction::DunderAllNames
                | KnownFunction::EnumMembers
                | KnownFunction::IsDisjointFrom
                | KnownFunction::IsAssignableTo
                | KnownFunction::IsConstraintSetAssignableTo
                | KnownFunction::IsEquivalentTo
                | KnownFunction::HasMember
                | KnownFunction::RevealProtocolInterface
                | KnownFunction::RevealMro
                | KnownFunction::AllMembers => KnownModule::TyExtensionsInternal,

                KnownFunction::ImportModule => KnownModule::ImportLib,
                KnownFunction::NamedTuple => KnownModule::Collections,
                KnownFunction::TotalOrdering => KnownModule::Functools,
                KnownFunction::Unpack => KnownModule::Struct,
                KnownFunction::NewClass => KnownModule::Types,
            };

            if module.is_third_party() {
                continue;
            }

            let function_definition =
                known_module_symbol(&db, &db.program_environment(), module, function_name)
                    .place
                    .expect_type()
                    .expect_function_literal()
                    .definition(&db);

            assert_eq!(
                KnownFunction::try_from_definition_and_name(
                    &db,
                    function_definition,
                    function_name
                ),
                Some(function),
                "The strum `EnumString` implementation appears to be incorrect for `{function_name}`"
            );
        }
    }
}
