from __future__ import annotations

from typing import Callable

from mypy import join
from mypy.erasetype import erase_type
from mypy.maptype import map_instance_to_supertype
from mypy.state import state
from mypy.subtypes import (
    are_parameters_compatible,
    find_member,
    is_callable_compatible,
    is_equivalent,
    is_proper_subtype,
    is_same_type,
    is_subtype,
)
from mypy.typeops import is_recursive_pair, make_simplified_union, tuple_fallback
from mypy.types import (
    MYPYC_NATIVE_INT_NAMES,
    TUPLE_LIKE_INSTANCE_NAMES,
    AnyType,
    CallableType,
    DeletedType,
    ErasedType,
    FunctionLike,
    Instance,
    LiteralType,
    NoneType,
    Overloaded,
    Parameters,
    ParamSpecType,
    PartialType,
    ProperType,
    TupleType,
    Type,
    TypeAliasType,
    TypedDictType,
    TypeGuardedType,
    TypeOfAny,
    TypeType,
    TypeVarLikeType,
    TypeVarTupleType,
    TypeVarType,
    TypeVisitor,
    UnboundType,
    UninhabitedType,
    UnionType,
    UnpackType,
    find_unpack_in_list,
    get_proper_type,
    get_proper_types,
    split_with_prefix_and_suffix,
)

# TODO Describe this module.

def trivial_meet(s: Type, t: Type) -> ProperType:
    """Return one of types (expanded) if it is a subtype of other, otherwise bottom type."""
    if is_subtype(s, t):
        return get_proper_type(s)
    elif is_subtype(t, s):
        return get_proper_type(t)
    else:
        if state.strict_optional:
            return UninhabitedType()
        else:
            return NoneType()

def meet_types(s: Type, t: Type) -> ProperType:
    """Return the greatest lower bound of two types."""
    if is_recursive_pair(s, t):
        # This case can trigger an infinite recursion, general support for this will be
        # tricky, so we use a trivial meet (like for protocols).
        return trivial_meet(s, t)
    s = get_proper_type(s)
    t = get_proper_type(t)

    if isinstance(s, Instance) and isinstance(t, Instance) and s.type == t.type:
        # Code in checker.py should merge any extra_items where possible, so we
        # should have only compatible extra_items here. We check this before
        # the below subtype check, so that extra_attrs will not get erased.
        if (s.extra_attrs or t.extra_attrs) and is_same_type(s, t):
            if s.extra_attrs and t.extra_attrs:
                if len(s.extra_attrs.attrs) > len(t.extra_attrs.attrs):
                    # Return the one that has more precise information.
                    return s
                return t
            if s.extra_attrs:
                return s
            return t

    if not isinstance(s, UnboundType) and not isinstance(t, UnboundType):
        if is_proper_subtype(s, t, ignore_promotions=True):
            return s
        if is_proper_subtype(t, s, ignore_promotions=True):
            return t

    if isinstance(s, ErasedType):
        return s
    if isinstance(s, AnyType):
        return t
    if isinstance(s, UnionType) and not isinstance(t, UnionType):
        s, t = t, s

    # Meets/joins require callable type normalization.
    s, t = join.normalize_callables(s, t)

    return t.accept(TypeMeetVisitor(s))

def narrow_declared_type(declared: Type, narrowed: Type) -> Type:
    """Return the declared type narrowed down to another type."""
    # TODO: check infinite recursion for aliases here.
    if isinstance(narrowed, TypeGuardedType):  # type: ignore[misc]
        # A type guard forces the new type even if it doesn't overlap the old.
        return narrowed.type_guard

    original_declared = declared
    original_narrowed = narrowed
    declared = get_proper_type(declared)
    narrowed = get_proper_type(narrowed)

    if declared == narrowed:
        return original_declared
    if isinstance(declared, UnionType):
        return make_simplified_union(
            [
                narrow_declared_type(x, narrowed)
                for x in declared.relevant_items()
                # This (ugly) special-casing is needed to support checking
                # branches like this:
                # x: Union[float, complex]
                # if isinstance(x, int):
                #     ...
                if (
                    is_overlapping_types(x, narrowed, ignore_promotions=True)
                    or is_subtype(narrowed, x, ignore_promotions=False)
                )
            ]
        )
    if is_enum_overlapping_union(declared, narrowed):
        return original_narrowed
    elif not is_overlapping_types(declared, narrowed, prohibit_none_typevar_overlap=True):
        if state.strict_optional:
            return UninhabitedType()
        else:
            return NoneType()
    elif isinstance(narrowed, UnionType):
        return make_simplified_union(
            [narrow_declared_type(declared, x) for x in narrowed.relevant_items()]
        )
    elif isinstance(narrowed, AnyType):
        return original_narrowed
    elif isinstance(narrowed, TypeVarType) and is_subtype(narrowed.upper_bound, declared):
        return narrowed
    elif isinstance(declared, TypeType) and isinstance(narrowed, TypeType):
        return TypeType.make_normalized(narrow_declared_type(declared.item, narrowed.item))
    elif (
        isinstance(declared, TypeType)
        and isinstance(narrowed, Instance)
        and narrowed.type.is_metaclass()
    ):
        # We'd need intersection types, so give up.
        return original_declared
    elif isinstance(declared, Instance):
        if declared.type.alt_promote:
            # Special case: low-level integer type can't be narrowed
            return original_declared
        if (
            isinstance(narrowed, Instance)
            and narrowed.type.alt_promote
            and narrowed.type.alt_promote.type is declared.type
        ):
            # Special case: 'int' can't be narrowed down to a native int type such as
            # i64, since they have different runtime representations.
            return original_declared
        return meet_types(original_declared, original_narrowed)
    elif isinstance(declared, (TupleType, TypeType, LiteralType)):
        return meet_types(original_declared, original_narrowed)
    elif isinstance(declared, TypedDictType) and isinstance(narrowed, Instance):
        # Special case useful for selecting TypedDicts from unions using isinstance(x, dict).
        if narrowed.type.fullname == "builtins.dict" and all(
            isinstance(t, AnyType) for t in get_proper_types(narrowed.args)
        ):
            return original_declared
        return meet_types(original_declared, original_narrowed)
    return original_narrowed

def get_possible_variants(typ: Type) -> list[Type]:
    """This function takes any "Union-like" type and returns a list of the available "options".

    Specifically, there are currently exactly three different types that can have
    "variants" or are "union-like":

    - Unions
    - TypeVars with value restrictions
    - Overloads

    This function will return a list of each "option" present in those types.

    If this function receives any other type, we return a list containing just that
    original type. (E.g. pretend the type was contained within a singleton union).

    The only current exceptions are regular TypeVars and ParamSpecs. For these "TypeVarLike"s,
    we return a list containing that TypeVarLike's upper bound.

    This function is useful primarily when checking to see if two types are overlapping:
    the algorithm to check if two unions are overlapping is fundamentally the same as
    the algorithm for checking if two overloads are overlapping.

    Normalizing both kinds of types in the same way lets us reuse the same algorithm
    for both.
    """
    typ = get_proper_type(typ)

    if isinstance(typ, TypeVarType):
        if len(typ.values) > 0:
            return typ.values
        else:
            return [typ.upper_bound]
    elif isinstance(typ, ParamSpecType):
        return [typ.upper_bound]
    elif isinstance(typ, TypeVarTupleType):
        return [typ.upper_bound]
    elif isinstance(typ, UnionType):
        return list(typ.items)
    elif isinstance(typ, Overloaded):
        # Note: doing 'return typ.items()' makes mypy
        # infer a too-specific return type of List[CallableType]
        return list(typ.items)
    else:
        return [typ]

def is_enum_overlapping_union(x: ProperType, y: ProperType) -> bool:
    """Return True if x is an Enum, and y is an Union with at least one Literal from x"""
    return (
        isinstance(x, Instance)
        and x.type.is_enum
        and isinstance(y, UnionType)
        and any(
            isinstance(p, LiteralType) and x.type == p.fallback.type
            for p in (get_proper_type(z) for z in y.relevant_items())
        )
    )

def is_literal_in_union(x: ProperType, y: ProperType) -> bool:
    """Return True if x is a Literal and y is an Union that includes x"""
    return (
        isinstance(x, LiteralType)
        and isinstance(y, UnionType)
        and any(x == get_proper_type(z) for z in y.items)
    )

def is_object(t: ProperType) -> bool:
    return isinstance(t, Instance) and t.type.fullname == "builtins.object"

def is_overlapping_types(
    left: Type,
    right: Type,
    ignore_promotions: bool = False,
    prohibit_none_typevar_overlap: bool = False,
    overlap_for_overloads: bool = False,
    seen_types: set[tuple[Type, Type]] | None = None,
) -> bool:
    """Can a value of type 'left' also be of type 'right' or vice-versa?

    If 'ignore_promotions' is True, we ignore promotions while checking for overlaps.
    If 'prohibit_none_typevar_overlap' is True, we disallow None from overlapping with
    TypeVars (in both strict-optional and non-strict-optional mode).
    If 'overlap_for_overloads' is True, we check for overlaps more strictly (to avoid false
    positives), for example: None only overlaps with explicitly optional types, Any
    doesn't overlap with anything except object, we don't ignore positional argument names.
    """
    if isinstance(left, TypeGuardedType) or isinstance(  # type: ignore[misc]
        right, TypeGuardedType
    ):
        # A type guard forces the new type even if it doesn't overlap the old.
        return True

    if seen_types is None:
        seen_types = set()
    if (left, right) in seen_types:
        return True
    if isinstance(left, TypeAliasType) and isinstance(right, TypeAliasType):
        seen_types.add((left, right))

    left, right = get_proper_types((left, right))

    def _is_overlapping_types(left: Type, right: Type) -> bool:
        """Encode the kind of overlapping check to perform.

        This function mostly exists, so we don't have to repeat keyword arguments everywhere.
        """
        return is_overlapping_types(
            left,
            right,
            ignore_promotions=ignore_promotions,
            prohibit_none_typevar_overlap=prohibit_none_typevar_overlap,
            overlap_for_overloads=overlap_for_overloads,
            seen_types=seen_types.copy(),
        )

    # We should never encounter this type.
    if isinstance(left, PartialType) or isinstance(right, PartialType):
        assert False, "Unexpectedly encountered partial type"

    # We should also never encounter these types, but it's possible a few
    # have snuck through due to unrelated bugs. For now, we handle these
    # in the same way we handle 'Any'.
    #
    # TODO: Replace these with an 'assert False' once we are more confident.
    illegal_types = (UnboundType, ErasedType, DeletedType)
    if isinstance(left, illegal_types) or isinstance(right, illegal_types):
        return True

    # When running under non-strict optional mode, simplify away types of
    # the form 'Union[A, B, C, None]' into just 'Union[A, B, C]'.

    if not state.strict_optional:
        if isinstance(left, UnionType):
            left = UnionType.make_union(left.relevant_items())
        if isinstance(right, UnionType):
            right = UnionType.make_union(right.relevant_items())
        left, right = get_proper_types((left, right))

    # 'Any' may or may not be overlapping with the other type
    if isinstance(left, AnyType) or isinstance(right, AnyType):
        return not overlap_for_overloads or is_object(left) or is_object(right)

    # We check for complete overlaps next as a general-purpose failsafe.
    # If this check fails, we start checking to see if there exists a
    # *partial* overlap between types.
    #
    # These checks will also handle the NoneType and UninhabitedType cases for us.

    # enums are sometimes expanded into an Union of Literals
    # when that happens we want to make sure we treat the two as overlapping
    # and crucially, we want to do that *fast* in case the enum is large
    # so we do it before expanding variants below to avoid O(n**2) behavior
    if (
        is_enum_overlapping_union(left, right)
        or is_enum_overlapping_union(right, left)
        or is_literal_in_union(left, right)
        or is_literal_in_union(right, left)
    ):
        return True

    def is_none_object_overlap(t1: Type, t2: Type) -> bool:
        t1, t2 = get_proper_types((t1, t2))
        return (
            isinstance(t1, NoneType)
            and isinstance(t2, Instance)
            and t2.type.fullname == "builtins.object"
        )

    if overlap_for_overloads:
        if is_none_object_overlap(left, right) or is_none_object_overlap(right, left):
            return False

    def _is_subtype(left: Type, right: Type) -> bool:
        if overlap_for_overloads:
            return is_proper_subtype(left, right, ignore_promotions=ignore_promotions)
        else:
            return is_subtype(left, right, ignore_promotions=ignore_promotions)

    if _is_subtype(left, right) or _is_subtype(right, left):
        return True

    # See the docstring for 'get_possible_variants' for more info on what the
    # following lines are doing.

    left_possible = get_possible_variants(left)
    right_possible = get_possible_variants(right)

    # Now move on to checking multi-variant types like Unions. We also perform
    # the same logic if either type happens to be a TypeVar/ParamSpec/TypeVarTuple.
    #
    # Handling the TypeVarLikes now lets us simulate having them bind to the corresponding
    # type -- if we deferred these checks, the "return-early" logic of the other
    # checks will prevent us from detecting certain overlaps.
    #
    # If both types are singleton variants (and are not TypeVarLikes), we've hit the base case:
    # we skip these checks to avoid infinitely recursing.

    def is_none_typevarlike_overlap(t1: Type, t2: Type) -> bool:
        t1, t2 = get_proper_types((t1, t2))
        return isinstance(t1, NoneType) and isinstance(t2, TypeVarLikeType)

    if prohibit_none_typevar_overlap:
        if is_none_typevarlike_overlap(left, right) or is_none_typevarlike_overlap(right, left):
            return False

    if (
        len(left_possible) > 1
        or len(right_possible) > 1
        or isinstance(left, TypeVarLikeType)
        or isinstance(right, TypeVarLikeType)
    ):
        for l in left_possible:
            for r in right_possible:
                if _is_overlapping_types(l, r):
                    return True
        return False

    # Now that we've finished handling TypeVarLikes, we're free to end early
    # if one one of the types is None and we're running in strict-optional mode.
    # (None only overlaps with None in strict-optional mode).
    #
    # We must perform this check after the TypeVarLike checks because
    # a TypeVar could be bound to None, for example.

    if state.strict_optional and isinstance(left, NoneType) != isinstance(right, NoneType):
        return False

    # Next, we handle single-variant types that may be inherently partially overlapping:
    #
    # - TypedDicts
    # - Tuples
    #
    # If we cannot identify a partial overlap and end early, we degrade these two types
    # into their 'Instance' fallbacks.

    if isinstance(left, TypedDictType) and isinstance(right, TypedDictType):
        return are_typed_dicts_overlapping(left, right, _is_overlapping_types)
    elif typed_dict_mapping_pair(left, right):
        # Overlaps between TypedDicts and Mappings require dedicated logic.
        return typed_dict_mapping_overlap(left, right, overlapping=_is_overlapping_types)
    elif isinstance(left, TypedDictType):
        left = left.fallback
    elif isinstance(right, TypedDictType):
        right = right.fallback

    if is_tuple(left) and is_tuple(right):
        return are_tuples_overlapping(left, right, _is_overlapping_types)
    elif isinstance(left, TupleType):
        left = tuple_fallback(left)
    elif isinstance(right, TupleType):
        right = tuple_fallback(right)

    # Next, we handle single-variant types that cannot be inherently partially overlapping,
    # but do require custom logic to inspect.
    #
    # As before, we degrade into 'Instance' whenever possible.

    if isinstance(left, TypeType) and isinstance(right, TypeType):
        return _is_overlapping_types(left.item, right.item)

    def _type_object_overlap(left: Type, right: Type) -> bool:
        """Special cases for type object types overlaps."""
        # TODO: these checks are a bit in gray area, adjust if they cause problems.
        left, right = get_proper_types((left, right))
        # 1. Type[C] vs Callable[..., C] overlap even if the latter is not class object.
        if isinstance(left, TypeType) and isinstance(right, CallableType):
            return _is_overlapping_types(left.item, right.ret_type)
        # 2. Type[C] vs Meta, where Meta is a metaclass for C.
        if isinstance(left, TypeType) and isinstance(right, Instance):
            if isinstance(left.item, Instance):
                left_meta = left.item.type.metaclass_type
                if left_meta is not None:
                    return _is_overlapping_types(left_meta, right)
                # builtins.type (default metaclass) overlaps with all metaclasses
                return right.type.has_base("builtins.type")
            elif isinstance(left.item, AnyType):
                return right.type.has_base("builtins.type")
        # 3. Callable[..., C] vs Meta is considered below, when we switch to fallbacks.
        return False

    if isinstance(left, TypeType) or isinstance(right, TypeType):
        return _type_object_overlap(left, right) or _type_object_overlap(right, left)

    if isinstance(left, Parameters) and isinstance(right, Parameters):
        return are_parameters_compatible(
            left,
            right,
            is_compat=_is_overlapping_types,
            is_proper_subtype=False,
            ignore_pos_arg_names=not overlap_for_overloads,
            allow_partial_overlap=True,
        )
    # A `Parameters` does not overlap with anything else, however
    if isinstance(left, Parameters) or isinstance(right, Parameters):
        return False

    if isinstance(left, CallableType) and isinstance(right, CallableType):
        return is_callable_compatible(
            left,
            right,
            is_compat=_is_overlapping_types,
            is_proper_subtype=False,
            ignore_pos_arg_names=not overlap_for_overloads,
            allow_partial_overlap=True,
        )

    call = None
    other = None
    if isinstance(left, CallableType) and isinstance(right, Instance):
        call = find_member("__call__", right, right, is_operator=True)
        other = left
    if isinstance(right, CallableType) and isinstance(left, Instance):
        call = find_member("__call__", left, left, is_operator=True)
        other = right
    if isinstance(get_proper_type(call), FunctionLike):
        assert call is not None and other is not None
        return _is_overlapping_types(call, other)

    if isinstance(left, CallableType):
        left = left.fallback
    if isinstance(right, CallableType):
        right = right.fallback

    if isinstance(left, LiteralType) and isinstance(right, LiteralType):
        if left.value == right.value:
            # If values are the same, we still need to check if fallbacks are overlapping,
            # this is done below.
            left = left.fallback
            right = right.fallback
        else:
            return False
    elif isinstance(left, LiteralType):
        left = left.fallback
    elif isinstance(right, LiteralType):
        right = right.fallback

    # Finally, we handle the case where left and right are instances.

    if isinstance(left, Instance) and isinstance(right, Instance):
        # First we need to handle promotions and structural compatibility for instances
        # that came as fallbacks, so simply call is_subtype() to avoid code duplication.
        if _is_subtype(left, right) or _is_subtype(right, left):
            return True

        if right.type.fullname == "builtins.int" and left.type.fullname in MYPYC_NATIVE_INT_NAMES:
            return True

        # Two unrelated types cannot be partially overlapping: they're disjoint.
        if left.type.has_base(right.type.fullname):
            left = map_instance_to_supertype(left, right.type)
        elif right.type.has_base(left.type.fullname):
            right = map_instance_to_supertype(right, left.type)
        else:
            return False

        if len(left.args) == len(right.args):
            # Note: we don't really care about variance here, since the overlapping check
            # is symmetric and since we want to return 'True' even for partial overlaps.
            #
            # For example, suppose we have two types Wrapper[Parent] and Wrapper[Child].
            # It doesn't matter whether Wrapper is covariant or contravariant since
            # either way, one of the two types will overlap with the other.
            #
            # Similarly, if Wrapper was invariant, the two types could still be partially
            # overlapping -- what if Wrapper[Parent] happened to contain only instances of
            # specifically Child?
            #
            # Or, to use a more concrete example, List[Union[A, B]] and List[Union[B, C]]
            # would be considered partially overlapping since it's possible for both lists
            # to contain only instances of B at runtime.
            if all(
                _is_overlapping_types(left_arg, right_arg)
                for left_arg, right_arg in zip(left.args, right.args)
            ):
                return True

        return False

    # We ought to have handled every case by now: we conclude the
    # two types are not overlapping, either completely or partially.
    #
    # Note: it's unclear however, whether returning False is the right thing
    # to do when inferring reachability -- see  https://github.com/python/mypy/issues/5529

    assert type(left) != type(right), f"{type(left)} vs {type(right)}"
    return False

def is_overlapping_erased_types(
    left: Type, right: Type, *, ignore_promotions: bool = False
) -> bool:
    """The same as 'is_overlapping_erased_types', except the types are erased first."""
    return is_overlapping_types(
        erase_type(left),
        erase_type(right),
        ignore_promotions=ignore_promotions,
        prohibit_none_typevar_overlap=True,
    )

def are_typed_dicts_overlapping(
    left: TypedDictType, right: TypedDictType, is_overlapping: Callable[[Type, Type], bool]
) -> bool:
    """Returns 'true' if left and right are overlapping TypeDictTypes."""
    # All required keys in left are present and overlapping with something in right
    for key in left.required_keys:
        if key not in right.items:
            return False
        if not is_overlapping(left.items[key], right.items[key]):
            return False

    # Repeat check in the other direction
    for key in right.required_keys:
        if key not in left.items:
            return False
        if not is_overlapping(left.items[key], right.items[key]):
            return False

    # The presence of any additional optional keys does not affect whether the two
    # TypedDicts are partially overlapping: the dicts would be overlapping if the
    # keys happened to be missing.
    return True

def are_tuples_overlapping(
    left: Type, right: Type, is_overlapping: Callable[[Type, Type], bool]
) -> bool:
    """Returns true if left and right are overlapping tuples."""
    left, right = get_proper_types((left, right))
    left = adjust_tuple(left, right) or left
    right = adjust_tuple(right, left) or right
    assert isinstance(left, TupleType), f"Type {left} is not a tuple"
    assert isinstance(right, TupleType), f"Type {right} is not a tuple"

    # This algorithm works well if only one tuple is variadic, if both are
    # variadic we may get rare false negatives for overlapping prefix/suffix.
    # Also, this ignores empty unpack case, but it is probably consistent with
    # how we handle e.g. empty lists in overload overlaps.
    # TODO: write a more robust algorithm for cases where both types are variadic.
    left_unpack = find_unpack_in_list(left.items)
    right_unpack = find_unpack_in_list(right.items)
    if left_unpack is not None:
        left = expand_tuple_if_possible(left, len(right.items))
    if right_unpack is not None:
        right = expand_tuple_if_possible(right, len(left.items))

    if len(left.items) != len(right.items):
        return False
    return all(is_overlapping(l, r) for l, r in zip(left.items, right.items))

def expand_tuple_if_possible(tup: TupleType, target: int) -> TupleType:
    if len(tup.items) > target + 1:
        return tup
    extra = target + 1 - len(tup.items)
    new_items = []
    for it in tup.items:
        if not isinstance(it, UnpackType):
            new_items.append(it)
            continue
        unpacked = get_proper_type(it.type)
        if isinstance(unpacked, TypeVarTupleType):
            instance = unpacked.tuple_fallback
        else:
            # Nested non-variadic tuples should be normalized at this point.
            assert isinstance(unpacked, Instance)
            instance = unpacked
        assert instance.type.fullname == "builtins.tuple"
        new_items.extend([instance.args[0]] * extra)
    return tup.copy_modified(items=new_items)

def adjust_tuple(left: ProperType, r: ProperType) -> TupleType | None:
    """Find out if `left` is a Tuple[A, ...], and adjust its length to `right`"""
    if isinstance(left, Instance) and left.type.fullname == "builtins.tuple":
        n = r.length() if isinstance(r, TupleType) else 1
        return TupleType([left.args[0]] * n, left)
    return None

def is_tuple(typ: Type) -> bool:
    typ = get_proper_type(typ)
    return isinstance(typ, TupleType) or (
        isinstance(typ, Instance) and typ.type.fullname == "builtins.tuple"
    )

class TypeMeetVisitor(TypeVisitor[ProperType]):
    def __init__(self, s: ProperType) -> None:
        self.s = s

    def visit_unbound_type(self, t: UnboundType) -> ProperType:
        if isinstance(self.s, NoneType):
            if state.strict_optional:
                return AnyType(TypeOfAny.special_form)
            else:
                return self.s
        elif isinstance(self.s, UninhabitedType):
            return self.s
        else:
            return AnyType(TypeOfAny.special_form)

    def visit_any(self, t: AnyType) -> ProperType:
        return self.s

    def visit_union_type(self, t: UnionType) -> ProperType:
        if isinstance(self.s, UnionType):
            meets: list[Type] = []
            for x in t.items:
                for y in self.s.items:
                    meets.append(meet_types(x, y))
        else:
            meets = [meet_types(x, self.s) for x in t.items]
        return make_simplified_union(meets)

    def visit_none_type(self, t: NoneType) -> ProperType:
        if state.strict_optional:
            if isinstance(self.s, NoneType) or (
                isinstance(self.s, Instance) and self.s.type.fullname == "builtins.object"
            ):
                return t
            else:
                return UninhabitedType()
        else:
            return t

    def visit_uninhabited_type(self, t: UninhabitedType) -> ProperType:
        return t

    def visit_deleted_type(self, t: DeletedType) -> ProperType:
        if isinstance(self.s, NoneType):
            if state.strict_optional:
                return t
            else:
                return self.s
        elif isinstance(self.s, UninhabitedType):
            return self.s
        else:
            return t

    def visit_erased_type(self, t: ErasedType) -> ProperType:
        return self.s

    def visit_type_var(self, t: TypeVarType) -> ProperType:
        if isinstance(self.s, TypeVarType) and self.s.id == t.id:
            return self.s
        else:
            return self.default(self.s)

    def visit_param_spec(self, t: ParamSpecType) -> ProperType:
        if self.s == t:
            return self.s
        else:
            return self.default(self.s)

    def visit_type_var_tuple(self, t: TypeVarTupleType) -> ProperType:
        if isinstance(self.s, TypeVarTupleType) and self.s.id == t.id:
            return self.s if self.s.min_len > t.min_len else t
        else:
            return self.default(self.s)

    def visit_unpack_type(self, t: UnpackType) -> ProperType:
        raise NotImplementedError

    def visit_parameters(self, t: Parameters) -> ProperType:
        if isinstance(self.s, Parameters):
            if len(t.arg_types) != len(self.s.arg_types):
                return self.default(self.s)
            from mypy.join import join_types

            return t.copy_modified(
                arg_types=[join_types(s_a, t_a) for s_a, t_a in zip(self.s.arg_types, t.arg_types)]
            )
        else:
            return self.default(self.s)

    def visit_instance(self, t: Instance) -> ProperType:
        if isinstance(self.s, Instance):
            if t.type == self.s.type:
                if is_subtype(t, self.s) or is_subtype(self.s, t):
                    # Combine type arguments. We could have used join below
                    # equivalently.
                    args: list[Type] = []
                    # N.B: We use zip instead of indexing because the lengths might have
                    # mismatches during daemon reprocessing.
                    if t.type.has_type_var_tuple_type:
                        # We handle meet of variadic instances by simply creating correct mapping
                        # for type arguments and compute the individual meets same as for regular
                        # instances. All the heavy lifting is done in the meet of tuple types.
                        s = self.s
                        assert s.type.type_var_tuple_prefix is not None
                        assert s.type.type_var_tuple_suffix is not None
                        prefix = s.type.type_var_tuple_prefix
                        suffix = s.type.type_var_tuple_suffix
                        tvt = s.type.defn.type_vars[prefix]
                        assert isinstance(tvt, TypeVarTupleType)
                        fallback = tvt.tuple_fallback
                        s_prefix, s_middle, s_suffix = split_with_prefix_and_suffix(
                            s.args, prefix, suffix
                        )
                        t_prefix, t_middle, t_suffix = split_with_prefix_and_suffix(
                            t.args, prefix, suffix
                        )
                        s_args = s_prefix + (TupleType(list(s_middle), fallback),) + s_suffix
                        t_args = t_prefix + (TupleType(list(t_middle), fallback),) + t_suffix
                    else:
                        t_args = t.args
                        s_args = self.s.args
                    for ta, sa, tv in zip(t_args, s_args, t.type.defn.type_vars):
                        meet = self.meet(ta, sa)
                        if isinstance(tv, TypeVarTupleType):
                            # Correctly unpack possible outcomes of meets of tuples: it can be
                            # either another tuple type or Never (normalized as *tuple[Never, ...])
                            if isinstance(meet, TupleType):
                                args.extend(meet.items)
                                continue
                            else:
                                assert isinstance(meet, UninhabitedType)
                                meet = UnpackType(tv.tuple_fallback.copy_modified(args=[meet]))
                        args.append(meet)
                    return Instance(t.type, args)
                else:
                    if state.strict_optional:
                        return UninhabitedType()
                    else:
                        return NoneType()
            else:
                alt_promote = t.type.alt_promote
                if alt_promote and alt_promote.type is self.s.type:
                    return t
                alt_promote = self.s.type.alt_promote
                if alt_promote and alt_promote.type is t.type:
                    return self.s
                if is_subtype(t, self.s):
                    return t
                elif is_subtype(self.s, t):
                    # See also above comment.
                    return self.s
                else:
                    if state.strict_optional:
                        return UninhabitedType()
                    else:
                        return NoneType()
        elif isinstance(self.s, FunctionLike) and t.type.is_protocol:
            call = join.unpack_callback_protocol(t)
            if call:
                return meet_types(call, self.s)
        elif isinstance(self.s, FunctionLike) and self.s.is_type_obj() and t.type.is_metaclass():
            if is_subtype(self.s.fallback, t):
                return self.s
            return self.default(self.s)
        elif isinstance(self.s, TypeType):
            return meet_types(t, self.s)
        elif isinstance(self.s, TupleType):
            return meet_types(t, self.s)
        elif isinstance(self.s, LiteralType):
            return meet_types(t, self.s)
        elif isinstance(self.s, TypedDictType):
            return meet_types(t, self.s)
        return self.default(self.s)

    def visit_callable_type(self, t: CallableType) -> ProperType:
        if isinstance(self.s, CallableType) and join.is_similar_callables(t, self.s):
            if is_equivalent(t, self.s):
                return join.combine_similar_callables(t, self.s)
            result = meet_similar_callables(t, self.s)
            # We set the from_type_type flag to suppress error when a collection of
            # concrete class objects gets inferred as their common abstract superclass.
            if not (
                (t.is_type_obj() and t.type_object().is_abstract)
                or (self.s.is_type_obj() and self.s.type_object().is_abstract)
            ):
                result.from_type_type = True
            if isinstance(get_proper_type(result.ret_type), UninhabitedType):
                # Return a plain None or <uninhabited> instead of a weird function.
                return self.default(self.s)
            return result
        elif isinstance(self.s, TypeType) and t.is_type_obj() and not t.is_generic():
            # In this case we are able to potentially produce a better meet.
            res = meet_types(self.s.item, t.ret_type)
            if not isinstance(res, (NoneType, UninhabitedType)):
                return TypeType.make_normalized(res)
            return self.default(self.s)
        elif isinstance(self.s, Instance) and self.s.type.is_protocol:
            call = join.unpack_callback_protocol(self.s)
            if call:
                return meet_types(t, call)
        return self.default(self.s)

    def visit_overloaded(self, t: Overloaded) -> ProperType:
        # TODO: Implement a better algorithm that covers at least the same cases
        # as TypeJoinVisitor.visit_overloaded().
        s = self.s
        if isinstance(s, FunctionLike):
            if s.items == t.items:
                return Overloaded(t.items)
            elif is_subtype(s, t):
                return s
            elif is_subtype(t, s):
                return t
            else:
                return meet_types(t.fallback, s.fallback)
        elif isinstance(self.s, Instance) and self.s.type.is_protocol:
            call = join.unpack_callback_protocol(self.s)
            if call:
                return meet_types(t, call)
        return meet_types(t.fallback, s)

    def meet_tuples(self, s: TupleType, t: TupleType) -> list[Type] | None:
        """Meet two tuple types while handling variadic entries.

        This is surprisingly tricky, and we don't handle some tricky corner cases.
        Most of the trickiness comes from the variadic tuple items like *tuple[X, ...]
        since they can have arbitrary partial overlaps (while *Ts can't be split). This
        function is roughly a mirror of join_tuples() w.r.t. to the fact that fixed
        tuples are subtypes of variadic ones but not vice versa.
        """
        s_unpack_index = find_unpack_in_list(s.items)
        t_unpack_index = find_unpack_in_list(t.items)
        if s_unpack_index is None and t_unpack_index is None:
            if s.length() == t.length():
                items: list[Type] = []
                for i in range(t.length()):
                    items.append(self.meet(t.items[i], s.items[i]))
                return items
            return None
        if s_unpack_index is not None and t_unpack_index is not None:
            # The only simple case we can handle if both tuples are variadic
            # is when their structure fully matches. Other cases are tricky because
            # a variadic item is effectively a union of tuples of all length, thus
            # potentially causing overlap between a suffix in `s` and a prefix
            # in `t` (see how this is handled in is_subtype() for details).
            # TODO: handle more cases (like when both prefix/suffix are shorter in s or t).
            if s.length() == t.length() and s_unpack_index == t_unpack_index:
                unpack_index = s_unpack_index
                s_unpack = s.items[unpack_index]
                assert isinstance(s_unpack, UnpackType)
                s_unpacked = get_proper_type(s_unpack.type)
                t_unpack = t.items[unpack_index]
                assert isinstance(t_unpack, UnpackType)
                t_unpacked = get_proper_type(t_unpack.type)
                if not (isinstance(s_unpacked, Instance) and isinstance(t_unpacked, Instance)):
                    return None
                meet = self.meet(s_unpacked, t_unpacked)
                if not isinstance(meet, Instance):
                    return None
                m_prefix: list[Type] = []
                for si, ti in zip(s.items[:unpack_index], t.items[:unpack_index]):
                    m_prefix.append(meet_types(si, ti))
                m_suffix: list[Type] = []
                for si, ti in zip(s.items[unpack_index + 1 :], t.items[unpack_index + 1 :]):
                    m_suffix.append(meet_types(si, ti))
                return m_prefix + [UnpackType(meet)] + m_suffix
            return None
        if s_unpack_index is not None:
            variadic = s
            unpack_index = s_unpack_index
            fixed = t
        else:
            assert t_unpack_index is not None
            variadic = t
            unpack_index = t_unpack_index
            fixed = s
        # If one tuple is variadic one, and the other one is fixed, the meet will be fixed.
        unpack = variadic.items[unpack_index]
        assert isinstance(unpack, UnpackType)
        unpacked = get_proper_type(unpack.type)
        if not isinstance(unpacked, Instance):
            return None
        if fixed.length() < variadic.length() - 1:
            return None
        prefix_len = unpack_index
        suffix_len = variadic.length() - prefix_len - 1
        prefix, middle, suffix = split_with_prefix_and_suffix(
            tuple(fixed.items), prefix_len, suffix_len
        )
        items = []
        for fi, vi in zip(prefix, variadic.items[:prefix_len]):
            items.append(self.meet(fi, vi))
        for mi in middle:
            items.append(self.meet(mi, unpacked.args[0]))
        if suffix_len:
            for fi, vi in zip(suffix, variadic.items[-suffix_len:]):
                items.append(self.meet(fi, vi))
        return items

    def visit_tuple_type(self, t: TupleType) -> ProperType:
        if isinstance(self.s, TupleType):
            items = self.meet_tuples(self.s, t)
            if items is None:
                return self.default(self.s)
            # TODO: What if the fallbacks are different?
            return TupleType(items, tuple_fallback(t))
        elif isinstance(self.s, Instance):
            # meet(Tuple[t1, t2, <...>], Tuple[s, ...]) == Tuple[meet(t1, s), meet(t2, s), <...>].
            if self.s.type.fullname in TUPLE_LIKE_INSTANCE_NAMES and self.s.args:
                return t.copy_modified(items=[meet_types(it, self.s.args[0]) for it in t.items])
            elif is_proper_subtype(t, self.s):
                # A named tuple that inherits from a normal class
                return t
            elif self.s.type.has_type_var_tuple_type and is_subtype(t, self.s):
                # This is a bit ad-hoc but more principled handling is tricky, and this
                # special case is important for type narrowing in binder to work.
                return t
        return self.default(self.s)

    def visit_typeddict_type(self, t: TypedDictType) -> ProperType:
        if isinstance(self.s, TypedDictType):
            for name, l, r in self.s.zip(t):
                if not is_equivalent(l, r) or (name in t.required_keys) != (
                    name in self.s.required_keys
                ):
                    return self.default(self.s)
            item_list: list[tuple[str, Type]] = []
            for item_name, s_item_type, t_item_type in self.s.zipall(t):
                if s_item_type is not None:
                    item_list.append((item_name, s_item_type))
                else:
                    # at least one of s_item_type and t_item_type is not None
                    assert t_item_type is not None
                    item_list.append((item_name, t_item_type))
            items = dict(item_list)
            fallback = self.s.create_anonymous_fallback()
            required_keys = t.required_keys | self.s.required_keys
            readonly_keys = t.readonly_keys | self.s.readonly_keys
            return TypedDictType(items, required_keys, readonly_keys, fallback)
        elif isinstance(self.s, Instance) and is_subtype(t, self.s):
            return t
        else:
            return self.default(self.s)

    def visit_literal_type(self, t: LiteralType) -> ProperType:
        if isinstance(self.s, LiteralType) and self.s == t:
            return t
        elif isinstance(self.s, Instance) and is_subtype(t.fallback, self.s):
            return t
        else:
            return self.default(self.s)

    def visit_partial_type(self, t: PartialType) -> ProperType:
        # We can't determine the meet of partial types. We should never get here.
        assert False, "Internal error"

    def visit_type_type(self, t: TypeType) -> ProperType:
        if isinstance(self.s, TypeType):
            typ = self.meet(t.item, self.s.item)
            if not isinstance(typ, NoneType):
                typ = TypeType.make_normalized(typ, line=t.line)
            return typ
        elif isinstance(self.s, Instance) and self.s.type.fullname == "builtins.type":
            return t
        elif isinstance(self.s, CallableType):
            return self.meet(t, self.s)
        else:
            return self.default(self.s)

    def visit_type_alias_type(self, t: TypeAliasType) -> ProperType:
        assert False, f"This should be never called, got {t}"

    def meet(self, s: Type, t: Type) -> ProperType:
        return meet_types(s, t)

    def default(self, typ: Type) -> ProperType:
        if isinstance(typ, UnboundType):
            return AnyType(TypeOfAny.special_form)
        else:
            if state.strict_optional:
                return UninhabitedType()
            else:
                return NoneType()

def meet_similar_callables(t: CallableType, s: CallableType) -> CallableType:
    from mypy.join import match_generic_callables, safe_join

    t, s = match_generic_callables(t, s)
    arg_types: list[Type] = []
    for i in range(len(t.arg_types)):
        arg_types.append(safe_join(t.arg_types[i], s.arg_types[i]))
    # TODO in combine_similar_callables also applies here (names and kinds)
    # The fallback type can be either 'function' or 'type'. The result should have 'function' as
    # fallback only if both operands have it as 'function'.
    if t.fallback.type.fullname != "builtins.function":
        fallback = t.fallback
    else:
        fallback = s.fallback
    return t.copy_modified(
        arg_types=arg_types,
        ret_type=meet_types(t.ret_type, s.ret_type),
        fallback=fallback,
        name=None,
    )

def meet_type_list(types: list[Type]) -> Type:
    if not types:
        # This should probably be builtins.object but that is hard to get and
        # it doesn't matter for any current users.
        return AnyType(TypeOfAny.implementation_artifact)
    met = types[0]
    for t in types[1:]:
        met = meet_types(met, t)
    return met

def typed_dict_mapping_pair(left: Type, right: Type) -> bool:
    """Is this a pair where one type is a TypedDict and another one is an instance of Mapping?

    This case requires a precise/principled consideration because there are two use cases
    that push the boundary the opposite ways: we need to avoid spurious overlaps to avoid
    false positives for overloads, but we also need to avoid spuriously non-overlapping types
    to avoid false positives with --strict-equality.
    """
    left, right = get_proper_types((left, right))
    assert not isinstance(left, TypedDictType) or not isinstance(right, TypedDictType)

    if isinstance(left, TypedDictType):
        _, other = left, right
    elif isinstance(right, TypedDictType):
        _, other = right, left
    else:
        return False
    return isinstance(other, Instance) and other.type.has_base("typing.Mapping")

def typed_dict_mapping_overlap(
    left: Type, right: Type, overlapping: Callable[[Type, Type], bool]
) -> bool:
    """Check if a TypedDict type is overlapping with a Mapping.

    The basic logic here consists of two rules:

    * A TypedDict with some required keys is overlapping with Mapping[str, <some type>]
      if and only if every key type is overlapping with <some type>. For example:

      - TypedDict(x=int, y=str) overlaps with Dict[str, Union[str, int]]
      - TypedDict(x=int, y=str) doesn't overlap with Dict[str, int]

      Note that any additional non-required keys can't change the above result.

    * A TypedDict with no required keys overlaps with Mapping[str, <some type>] if and
      only if at least one of key types overlaps with <some type>. For example:

      - TypedDict(x=str, y=str, total=False) overlaps with Dict[str, str]
      - TypedDict(x=str, y=str, total=False) doesn't overlap with Dict[str, int]
      - TypedDict(x=int, y=str, total=False) overlaps with Dict[str, str]

    * A TypedDict with at least one ReadOnly[] key does not overlap
      with Dict or MutableMapping, because they assume mutable data.

    As usual empty, dictionaries lie in a gray area. In general, List[str] and List[str]
    are considered non-overlapping despite empty list belongs to both. However, List[int]
    and List[Never] are considered overlapping.

    So here we follow the same logic: a TypedDict with no required keys is considered
    non-overlapping with Mapping[str, <some type>], but is considered overlapping with
    Mapping[Never, Never]. This way we avoid false positives for overloads, and also
    avoid false positives for comparisons like SomeTypedDict == {} under --strict-equality.
    """
    left, right = get_proper_types((left, right))
    assert not isinstance(left, TypedDictType) or not isinstance(right, TypedDictType)

    if isinstance(left, TypedDictType):
        assert isinstance(right, Instance)
        typed, other = left, right
    else:
        assert isinstance(left, Instance)
        assert isinstance(right, TypedDictType)
        typed, other = right, left

    mutable_mapping = next(
        (base for base in other.type.mro if base.fullname == "typing.MutableMapping"), None
    )
    if mutable_mapping is not None and typed.readonly_keys:
        return False

    mapping = next(base for base in other.type.mro if base.fullname == "typing.Mapping")
    other = map_instance_to_supertype(other, mapping)
    key_type, value_type = get_proper_types(other.args)

    # TODO: is there a cleaner way to get str_type here?
    fallback = typed.as_anonymous().fallback
    str_type = fallback.type.bases[0].args[0]  # typing._TypedDict inherits Mapping[str, object]

    # Special case: a TypedDict with no required keys overlaps with an empty dict.
    if isinstance(key_type, UninhabitedType) and isinstance(value_type, UninhabitedType):
        return not typed.required_keys

    if typed.required_keys:
        if not overlapping(key_type, str_type):
            return False
        return all(overlapping(typed.items[k], value_type) for k in typed.required_keys)
    else:
        if not overlapping(key_type, str_type):
            return False
        non_required = set(typed.items.keys()) - typed.required_keys
        return any(overlapping(typed.items[k], value_type) for k in non_required)


"""Utility for dumping memory usage stats.

This is tailored to mypy and knows (a little) about which list objects are
owned by particular AST nodes, etc.
"""

from __future__ import annotations

import gc
import sys
from collections import defaultdict
from typing import Dict, Iterable, cast

from mypy.nodes import FakeInfo, Node
from mypy.types import Type
from mypy.util import get_class_descriptors

def collect_memory_stats() -> tuple[dict[str, int], dict[str, int]]:
    """Return stats about memory use.

    Return a tuple with these items:
      - Dict from object kind to number of instances of that kind
      - Dict from object kind to total bytes used by all instances of that kind
    """
    objs = gc.get_objects()
    find_recursive_objects(objs)

    inferred = {}
    for obj in objs:
        if type(obj) is FakeInfo:
            # Processing these would cause a crash.
            continue
        n = type(obj).__name__
        if hasattr(obj, "__dict__"):
            # Keep track of which class a particular __dict__ is associated with.
            inferred[id(obj.__dict__)] = f"{n} (__dict__)"
        if isinstance(obj, (Node, Type)):  # type: ignore[misc]
            if hasattr(obj, "__dict__"):
                for x in obj.__dict__.values():
                    if isinstance(x, list):
                        # Keep track of which node a list is associated with.
                        inferred[id(x)] = f"{n} (list)"
                    if isinstance(x, tuple):
                        # Keep track of which node a list is associated with.
                        inferred[id(x)] = f"{n} (tuple)"

            for k in get_class_descriptors(type(obj)):
                x = getattr(obj, k, None)
                if isinstance(x, list):
                    inferred[id(x)] = f"{n} (list)"
                if isinstance(x, tuple):
                    inferred[id(x)] = f"{n} (tuple)"

    freqs: dict[str, int] = {}
    memuse: dict[str, int] = {}
    for obj in objs:
        if id(obj) in inferred:
            name = inferred[id(obj)]
        else:
            name = type(obj).__name__
        freqs[name] = freqs.get(name, 0) + 1
        memuse[name] = memuse.get(name, 0) + sys.getsizeof(obj)

    return freqs, memuse

def print_memory_profile(run_gc: bool = True) -> None:
    if not sys.platform.startswith("win"):
        import resource

        system_memuse = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
    else:
        system_memuse = -1  # TODO: Support this on Windows
    if run_gc:
        gc.collect()
    freqs, memuse = collect_memory_stats()
    print("%7s  %7s  %7s  %s" % ("Freq", "Size(k)", "AvgSize", "Type"))
    print("-------------------------------------------")
    totalmem = 0
    i = 0
    for n, mem in sorted(memuse.items(), key=lambda x: -x[1]):
        f = freqs[n]
        if i < 50:
            print("%7d  %7d  %7.0f  %s" % (f, mem // 1024, mem / f, n))
        i += 1
        totalmem += mem
    print()
    print("Mem usage RSS   ", system_memuse // 1024)
    print("Total reachable ", totalmem // 1024)

def find_recursive_objects(objs: list[object]) -> None:
    """Find additional objects referenced by objs and append them to objs.

    We use this since gc.get_objects() does not return objects without pointers
    in them such as strings.
    """
    seen = {id(o) for o in objs}

    def visit(o: object) -> None:
        if id(o) not in seen:
            objs.append(o)
            seen.add(id(o))

    for obj in objs.copy():
        if type(obj) is FakeInfo:
            # Processing these would cause a crash.
            continue
        if type(obj) in (dict, defaultdict):
            for key, val in cast(Dict[object, object], obj).items():
                visit(key)
                visit(val)
        if type(obj) in (list, tuple, set):
            for x in cast(Iterable[object], obj):
                visit(x)
        if hasattr(obj, "__slots__"):
            for base in type.mro(type(obj)):
                for slot in getattr(base, "__slots__", ()):
                    if hasattr(obj, slot):
                        visit(getattr(obj, slot))


"""Message constants for generating error messages during type checking.

Literal messages should be defined as constants in this module so they won't get out of sync
if used in more than one place, and so that they can be easily introspected. These messages are
ultimately consumed by messages.MessageBuilder.fail(). For more non-trivial message generation,
add a method to MessageBuilder and call this instead.
"""

from __future__ import annotations

from typing import Final, NamedTuple

from mypy import errorcodes as codes

class ErrorMessage(NamedTuple):
    value: str
    code: codes.ErrorCode | None = None

    def format(self, *args: object, **kwargs: object) -> ErrorMessage:
        return ErrorMessage(self.value.format(*args, **kwargs), code=self.code)

    def with_additional_msg(self, info: str) -> ErrorMessage:
        return ErrorMessage(self.value + info, code=self.code)

# Invalid types
INVALID_TYPE_RAW_ENUM_VALUE: Final = ErrorMessage(
    "Invalid type: try using Literal[{}.{}] instead?", codes.VALID_TYPE
)

# Type checker error message constants
NO_RETURN_VALUE_EXPECTED: Final = ErrorMessage("No return value expected", codes.RETURN_VALUE)
MISSING_RETURN_STATEMENT: Final = ErrorMessage("Missing return statement", codes.RETURN)
EMPTY_BODY_ABSTRACT: Final = ErrorMessage(
    "If the method is meant to be abstract, use @abc.abstractmethod", codes.EMPTY_BODY
)
INVALID_IMPLICIT_RETURN: Final = ErrorMessage("Implicit return in function which does not return")
INCOMPATIBLE_RETURN_VALUE_TYPE: Final = ErrorMessage(
    "Incompatible return value type", codes.RETURN_VALUE
)
RETURN_VALUE_EXPECTED: Final = ErrorMessage("Return value expected", codes.RETURN_VALUE)
NO_RETURN_EXPECTED: Final = ErrorMessage("Return statement in function which does not return")
INVALID_EXCEPTION: Final = ErrorMessage("Exception must be derived from BaseException")
INVALID_EXCEPTION_TYPE: Final = ErrorMessage(
    "Exception type must be derived from BaseException (or be a tuple of exception classes)"
)
INVALID_EXCEPTION_GROUP: Final = ErrorMessage(
    "Exception type in except* cannot derive from BaseExceptionGroup"
)
RETURN_IN_ASYNC_GENERATOR: Final = ErrorMessage(
    '"return" with value in async generator is not allowed'
)
INVALID_RETURN_TYPE_FOR_GENERATOR: Final = ErrorMessage(
    'The return type of a generator function should be "Generator" or one of its supertypes'
)
INVALID_RETURN_TYPE_FOR_ASYNC_GENERATOR: Final = ErrorMessage(
    'The return type of an async generator function should be "AsyncGenerator" or one of its '
    "supertypes"
)
YIELD_VALUE_EXPECTED: Final = ErrorMessage("Yield value expected")
INCOMPATIBLE_TYPES: Final = ErrorMessage("Incompatible types")
INCOMPATIBLE_TYPES_IN_ASSIGNMENT: Final = ErrorMessage(
    "Incompatible types in assignment", code=codes.ASSIGNMENT
)
COVARIANT_OVERRIDE_OF_MUTABLE_ATTRIBUTE: Final = ErrorMessage(
    "Covariant override of a mutable attribute", code=codes.MUTABLE_OVERRIDE
)
INCOMPATIBLE_TYPES_IN_AWAIT: Final = ErrorMessage('Incompatible types in "await"')
INCOMPATIBLE_REDEFINITION: Final = ErrorMessage("Incompatible redefinition")
INCOMPATIBLE_TYPES_IN_ASYNC_WITH_AENTER: Final = (
    'Incompatible types in "async with" for "__aenter__"'
)
INCOMPATIBLE_TYPES_IN_ASYNC_WITH_AEXIT: Final = (
    'Incompatible types in "async with" for "__aexit__"'
)
INCOMPATIBLE_TYPES_IN_ASYNC_FOR: Final = 'Incompatible types in "async for"'
INVALID_TYPE_FOR_SLOTS: Final = 'Invalid type for "__slots__"'

ASYNC_FOR_OUTSIDE_COROUTINE: Final = '"async for" outside async function'
ASYNC_WITH_OUTSIDE_COROUTINE: Final = '"async with" outside async function'

INCOMPATIBLE_TYPES_IN_YIELD: Final = ErrorMessage('Incompatible types in "yield"')
INCOMPATIBLE_TYPES_IN_YIELD_FROM: Final = ErrorMessage('Incompatible types in "yield from"')
INCOMPATIBLE_TYPES_IN_STR_INTERPOLATION: Final = "Incompatible types in string interpolation"
INCOMPATIBLE_TYPES_IN_CAPTURE: Final = ErrorMessage("Incompatible types in capture pattern")
MUST_HAVE_NONE_RETURN_TYPE: Final = ErrorMessage('The return type of "{}" must be None')
TUPLE_INDEX_OUT_OF_RANGE: Final = ErrorMessage("Tuple index out of range")
AMBIGUOUS_SLICE_OF_VARIADIC_TUPLE: Final = ErrorMessage("Ambiguous slice of a variadic tuple")
TOO_MANY_TARGETS_FOR_VARIADIC_UNPACK: Final = ErrorMessage(
    "Too many assignment targets for variadic unpack"
)
INVALID_SLICE_INDEX: Final = ErrorMessage("Slice index must be an integer, SupportsIndex or None")
CANNOT_INFER_LAMBDA_TYPE: Final = ErrorMessage("Cannot infer type of lambda")
CANNOT_ACCESS_INIT: Final = (
    'Accessing "__init__" on an instance is unsound, since instance.__init__ could be from'
    " an incompatible subclass"
)
NON_INSTANCE_NEW_TYPE: Final = ErrorMessage('"__new__" must return a class instance (got {})')
INVALID_NEW_TYPE: Final = ErrorMessage('Incompatible return type for "__new__"')
BAD_CONSTRUCTOR_TYPE: Final = ErrorMessage("Unsupported decorated constructor type")
CANNOT_ASSIGN_TO_METHOD: Final = "Cannot assign to a method"
CANNOT_ASSIGN_TO_TYPE: Final = "Cannot assign to a type"
INCONSISTENT_ABSTRACT_OVERLOAD: Final = ErrorMessage(
    "Overloaded method has both abstract and non-abstract variants"
)
MULTIPLE_OVERLOADS_REQUIRED: Final = ErrorMessage("Single overload definition, multiple required")
READ_ONLY_PROPERTY_OVERRIDES_READ_WRITE: Final = ErrorMessage(
    "Read-only property cannot override read-write property"
)
FORMAT_REQUIRES_MAPPING: Final = "Format requires a mapping"
RETURN_TYPE_CANNOT_BE_CONTRAVARIANT: Final = ErrorMessage(
    "Cannot use a contravariant type variable as return type"
)
FUNCTION_PARAMETER_CANNOT_BE_COVARIANT: Final = ErrorMessage(
    "Cannot use a covariant type variable as a parameter"
)
INCOMPATIBLE_IMPORT_OF: Final = ErrorMessage('Incompatible import of "{}"', code=codes.ASSIGNMENT)
FUNCTION_TYPE_EXPECTED: Final = ErrorMessage(
    "Function is missing a type annotation", codes.NO_UNTYPED_DEF
)
ONLY_CLASS_APPLICATION: Final = ErrorMessage(
    "Type application is only supported for generic classes"
)
RETURN_TYPE_EXPECTED: Final = ErrorMessage(
    "Function is missing a return type annotation", codes.NO_UNTYPED_DEF
)
ARGUMENT_TYPE_EXPECTED: Final = ErrorMessage(
    "Function is missing a type annotation for one or more arguments", codes.NO_UNTYPED_DEF
)
KEYWORD_ARGUMENT_REQUIRES_STR_KEY_TYPE: Final = ErrorMessage(
    'Keyword argument only valid with "str" key type in call to "dict"'
)
ALL_MUST_BE_SEQ_STR: Final = ErrorMessage("Type of __all__ must be {}, not {}")
INVALID_TYPEDDICT_ARGS: Final = ErrorMessage(
    "Expected keyword arguments, {...}, or dict(...) in TypedDict constructor"
)
TYPEDDICT_KEY_MUST_BE_STRING_LITERAL: Final = ErrorMessage(
    "Expected TypedDict key to be string literal"
)
TYPEDDICT_OVERRIDE_MERGE: Final = 'Overwriting TypedDict field "{}" while merging'
MALFORMED_ASSERT: Final = ErrorMessage("Assertion is always true, perhaps remove parentheses?")
DUPLICATE_TYPE_SIGNATURES: Final = ErrorMessage("Function has duplicate type signatures")
DESCRIPTOR_SET_NOT_CALLABLE: Final = ErrorMessage("{}.__set__ is not callable")
DESCRIPTOR_GET_NOT_CALLABLE: Final = "{}.__get__ is not callable"
MODULE_LEVEL_GETATTRIBUTE: Final = ErrorMessage(
    "__getattribute__ is not valid at the module level"
)
CLASS_VAR_CONFLICTS_SLOTS: Final = '"{}" in __slots__ conflicts with class variable access'
NAME_NOT_IN_SLOTS: Final = ErrorMessage(
    'Trying to assign name "{}" that is not in "__slots__" of type "{}"'
)
TYPE_ALWAYS_TRUE: Final = ErrorMessage(
    "{} which does not implement __bool__ or __len__ "
    "so it could always be true in boolean context",
    code=codes.TRUTHY_BOOL,
)
TYPE_ALWAYS_TRUE_UNIONTYPE: Final = ErrorMessage(
    "{} of which no members implement __bool__ or __len__ "
    "so it could always be true in boolean context",
    code=codes.TRUTHY_BOOL,
)
FUNCTION_ALWAYS_TRUE: Final = ErrorMessage(
    "Function {} could always be true in boolean context", code=codes.TRUTHY_FUNCTION
)
ITERABLE_ALWAYS_TRUE: Final = ErrorMessage(
    "{} which can always be true in boolean context. Consider using {} instead.",
    code=codes.TRUTHY_ITERABLE,
)
NOT_CALLABLE: Final = "{} not callable"
TYPE_MUST_BE_USED: Final = "Value of type {} must be used"

# Generic
GENERIC_INSTANCE_VAR_CLASS_ACCESS: Final = (
    "Access to generic instance variables via class is ambiguous"
)
GENERIC_CLASS_VAR_ACCESS: Final = "Access to generic class variables is ambiguous"
BARE_GENERIC: Final = "Missing type parameters for generic type {}"
IMPLICIT_GENERIC_ANY_BUILTIN: Final = (
    'Implicit generic "Any". Use "{}" and specify generic parameters'
)
INVALID_UNPACK: Final = "{} cannot be unpacked (must be tuple or TypeVarTuple)"
INVALID_UNPACK_POSITION: Final = "Unpack is only valid in a variadic position"
INVALID_PARAM_SPEC_LOCATION: Final = "Invalid location for ParamSpec {}"
INVALID_PARAM_SPEC_LOCATION_NOTE: Final = (
    'You can use ParamSpec as the first argument to Callable, e.g., "Callable[{}, int]"'
)

# TypeVar
INCOMPATIBLE_TYPEVAR_VALUE: Final = 'Value of type variable "{}" of {} cannot be {}'
CANNOT_USE_TYPEVAR_AS_EXPRESSION: Final = 'Type variable "{}.{}" cannot be used as an expression'
INVALID_TYPEVAR_AS_TYPEARG: Final = 'Type variable "{}" not valid as type argument value for "{}"'
INVALID_TYPEVAR_ARG_BOUND: Final = 'Type argument {} of "{}" must be a subtype of {}'
INVALID_TYPEVAR_ARG_VALUE: Final = 'Invalid type argument value for "{}"'
TYPEVAR_VARIANCE_DEF: Final = 'TypeVar "{}" may only be a literal bool'
TYPEVAR_ARG_MUST_BE_TYPE: Final = '{} "{}" must be a type'
TYPEVAR_UNEXPECTED_ARGUMENT: Final = 'Unexpected argument to "TypeVar()"'
UNBOUND_TYPEVAR: Final = (
    "A function returning TypeVar should receive at least "
    "one argument containing the same TypeVar"
)
TYPE_PARAMETERS_SHOULD_BE_DECLARED: Final = (
    "All type parameters should be declared ({} not declared)"
)

# Super
TOO_MANY_ARGS_FOR_SUPER: Final = ErrorMessage('Too many arguments for "super"')
SUPER_WITH_SINGLE_ARG_NOT_SUPPORTED: Final = ErrorMessage(
    '"super" with a single argument not supported'
)
UNSUPPORTED_ARG_1_FOR_SUPER: Final = ErrorMessage('Unsupported argument 1 for "super"')
UNSUPPORTED_ARG_2_FOR_SUPER: Final = ErrorMessage('Unsupported argument 2 for "super"')
SUPER_VARARGS_NOT_SUPPORTED: Final = ErrorMessage('Varargs not supported with "super"')
SUPER_POSITIONAL_ARGS_REQUIRED: Final = ErrorMessage('"super" only accepts positional arguments')
SUPER_ARG_2_NOT_INSTANCE_OF_ARG_1: Final = ErrorMessage(
    'Argument 2 for "super" not an instance of argument 1'
)
TARGET_CLASS_HAS_NO_BASE_CLASS: Final = ErrorMessage("Target class has no base class")
SUPER_OUTSIDE_OF_METHOD_NOT_SUPPORTED: Final = ErrorMessage(
    '"super()" outside of a method is not supported'
)
SUPER_ENCLOSING_POSITIONAL_ARGS_REQUIRED: Final = ErrorMessage(
    '"super()" requires one or two positional arguments in enclosing function'
)

# Self-type
MISSING_OR_INVALID_SELF_TYPE: Final = ErrorMessage(
    "Self argument missing for a non-static method (or an invalid type for self)"
)
ERASED_SELF_TYPE_NOT_SUPERTYPE: Final = ErrorMessage(
    'The erased type of self "{}" is not a supertype of its class "{}"'
)

# Final
CANNOT_INHERIT_FROM_FINAL: Final = ErrorMessage('Cannot inherit from final class "{}"')
DEPENDENT_FINAL_IN_CLASS_BODY: Final = ErrorMessage(
    "Final name declared in class body cannot depend on type variables"
)
CANNOT_ACCESS_FINAL_INSTANCE_ATTR: Final = (
    'Cannot access final instance attribute "{}" on class object'
)
CANNOT_MAKE_DELETABLE_FINAL: Final = ErrorMessage("Deletable attribute cannot be final")

# Enum
ENUM_MEMBERS_ATTR_WILL_BE_OVERRIDEN: Final = ErrorMessage(
    'Assigned "__members__" will be overridden by "Enum" internally'
)

# ClassVar
CANNOT_OVERRIDE_INSTANCE_VAR: Final = ErrorMessage(
    'Cannot override instance variable (previously declared on base class "{}") with class '
    "variable"
)
CANNOT_OVERRIDE_CLASS_VAR: Final = ErrorMessage(
    'Cannot override class variable (previously declared on base class "{}") with instance '
    "variable"
)
CLASS_VAR_WITH_TYPEVARS: Final = "ClassVar cannot contain type variables"
CLASS_VAR_WITH_GENERIC_SELF: Final = "ClassVar cannot contain Self type in generic classes"
CLASS_VAR_OUTSIDE_OF_CLASS: Final = "ClassVar can only be used for assignments in class body"

# Protocol
RUNTIME_PROTOCOL_EXPECTED: Final = ErrorMessage(
    "Only @runtime_checkable protocols can be used with instance and class checks"
)
CANNOT_INSTANTIATE_PROTOCOL: Final = ErrorMessage('Cannot instantiate protocol class "{}"')
TOO_MANY_UNION_COMBINATIONS: Final = ErrorMessage(
    "Not all union combinations were tried because there are too many unions"
)

CONTIGUOUS_ITERABLE_EXPECTED: Final = ErrorMessage("Contiguous iterable with same type expected")
ITERABLE_TYPE_EXPECTED: Final = ErrorMessage("Invalid type '{}' for *expr (iterable expected)")
TYPE_GUARD_POS_ARG_REQUIRED: Final = ErrorMessage("Type {} requires positional argument")

# Match Statement
MISSING_MATCH_ARGS: Final = 'Class "{}" doesn\'t define "__match_args__"'
OR_PATTERN_ALTERNATIVE_NAMES: Final = "Alternative patterns bind different names"
CLASS_PATTERN_GENERIC_TYPE_ALIAS: Final = (
    "Class pattern class must not be a type alias with type parameters"
)
CLASS_PATTERN_TYPE_REQUIRED: Final = 'Expected type in class pattern; found "{}"'
CLASS_PATTERN_TOO_MANY_POSITIONAL_ARGS: Final = "Too many positional patterns for class pattern"
CLASS_PATTERN_KEYWORD_MATCHES_POSITIONAL: Final = (
    'Keyword "{}" already matches a positional pattern'
)
CLASS_PATTERN_DUPLICATE_KEYWORD_PATTERN: Final = 'Duplicate keyword pattern "{}"'
CLASS_PATTERN_UNKNOWN_KEYWORD: Final = 'Class "{}" has no attribute "{}"'
CLASS_PATTERN_CLASS_OR_STATIC_METHOD: Final = "Cannot have both classmethod and staticmethod"
MULTIPLE_ASSIGNMENTS_IN_PATTERN: Final = 'Multiple assignments to name "{}" in pattern'
CANNOT_MODIFY_MATCH_ARGS: Final = 'Cannot assign to "__match_args__"'

DATACLASS_FIELD_ALIAS_MUST_BE_LITERAL: Final = (
    '"alias" argument to dataclass field must be a string literal'
)
DATACLASS_POST_INIT_MUST_BE_A_FUNCTION: Final = '"__post_init__" method must be an instance method'

# fastparse
FAILED_TO_MERGE_OVERLOADS: Final = ErrorMessage(
    "Condition can't be inferred, unable to merge overloads"
)
TYPE_IGNORE_WITH_ERRCODE_ON_MODULE: Final = ErrorMessage(
    "type ignore with error code is not supported for modules; "
    'use `# mypy: disable-error-code="{}"`',
    codes.SYNTAX,
)
INVALID_TYPE_IGNORE: Final = ErrorMessage('Invalid "type: ignore" comment', codes.SYNTAX)
TYPE_COMMENT_SYNTAX_ERROR_VALUE: Final = ErrorMessage(
    'Syntax error in type comment "{}"', codes.SYNTAX
)
ELLIPSIS_WITH_OTHER_TYPEARGS: Final = ErrorMessage(
    "Ellipses cannot accompany other argument types in function type signature", codes.SYNTAX
)
TYPE_SIGNATURE_TOO_MANY_ARGS: Final = ErrorMessage(
    "Type signature has too many arguments", codes.SYNTAX
)
TYPE_SIGNATURE_TOO_FEW_ARGS: Final = ErrorMessage(
    "Type signature has too few arguments", codes.SYNTAX
)
ARG_CONSTRUCTOR_NAME_EXPECTED: Final = ErrorMessage("Expected arg constructor name", codes.SYNTAX)
ARG_CONSTRUCTOR_TOO_MANY_ARGS: Final = ErrorMessage(
    "Too many arguments for argument constructor", codes.SYNTAX
)
MULTIPLE_VALUES_FOR_NAME_KWARG: Final = ErrorMessage(
    '"{}" gets multiple values for keyword argument "name"', codes.SYNTAX
)
MULTIPLE_VALUES_FOR_TYPE_KWARG: Final = ErrorMessage(
    '"{}" gets multiple values for keyword argument "type"', codes.SYNTAX
)
ARG_CONSTRUCTOR_UNEXPECTED_ARG: Final = ErrorMessage(
    'Unexpected argument "{}" for argument constructor', codes.SYNTAX
)
ARG_NAME_EXPECTED_STRING_LITERAL: Final = ErrorMessage(
    "Expected string literal for argument name, got {}", codes.SYNTAX
)
NARROWED_TYPE_NOT_SUBTYPE: Final = ErrorMessage(
    "Narrowed type {} is not a subtype of input type {}", codes.NARROWED_TYPE_NOT_SUBTYPE
)
TYPE_VAR_TOO_FEW_CONSTRAINED_TYPES: Final = ErrorMessage(
    "Type variable must have at least two constrained types", codes.MISC
)

TYPE_VAR_YIELD_EXPRESSION_IN_BOUND: Final = ErrorMessage(
    "Yield expression cannot be used as a type variable bound", codes.SYNTAX
)

TYPE_VAR_NAMED_EXPRESSION_IN_BOUND: Final = ErrorMessage(
    "Named expression cannot be used as a type variable bound", codes.SYNTAX
)

TYPE_VAR_AWAIT_EXPRESSION_IN_BOUND: Final = ErrorMessage(
    "Await expression cannot be used as a type variable bound", codes.SYNTAX
)

TYPE_ALIAS_WITH_YIELD_EXPRESSION: Final = ErrorMessage(
    "Yield expression cannot be used within a type alias", codes.SYNTAX
)

TYPE_ALIAS_WITH_NAMED_EXPRESSION: Final = ErrorMessage(
    "Named expression cannot be used within a type alias", codes.SYNTAX
)

TYPE_ALIAS_WITH_AWAIT_EXPRESSION: Final = ErrorMessage(
    "Await expression cannot be used within a type alias", codes.SYNTAX
)

TYPE_PARAM_DEFAULT_NOT_SUPPORTED: Final = ErrorMessage(
    "Type parameter default types not supported when using Python 3.12 type parameter syntax",
    codes.MISC,
)