diff --git a/mypy/checker.py b/mypy/checker.py index fbaee0174d49..ab8f9aeeba79 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -8173,10 +8173,13 @@ def get_type_range_of_type(self, typ: Type) -> TypeRange | None: # Type[A] means "any type that is a subtype of A" rather than "precisely type A" # we indicate this by setting is_upper_bound flag is_upper_bound = True - if isinstance(typ.item, NoneType): + item = typ.item + if isinstance(item, TypeVarType) and item.id.is_self(): + is_upper_bound = False + elif isinstance(item, NoneType): # except for Type[None], because "'NoneType' is not an acceptable base type" is_upper_bound = False - if isinstance(typ.item, Instance) and typ.item.type.is_final: + elif isinstance(item, Instance) and item.type.is_final: is_upper_bound = False return TypeRange(typ.item, is_upper_bound=is_upper_bound) if isinstance(typ, AnyType): diff --git a/test-data/unit/check-isinstance.test b/test-data/unit/check-isinstance.test index c5695864d6b1..80f0df207e3b 100644 --- a/test-data/unit/check-isinstance.test +++ b/test-data/unit/check-isinstance.test @@ -3322,3 +3322,16 @@ def f2(x: A | None, t: type[A]): else: reveal_type(x) # N: Revealed type is "None" [builtins fixtures/isinstancelist.pyi] + +[case testNarrowingSelfInClassMethod] +# flags: --warn-unreachable +from typing import Self, Union + +class Foo: + @classmethod + def foo(cls, x: Union[Self, float]) -> None: + if isinstance(x, cls): + reveal_type(x) # N: Revealed type is "Self`0" + else: + reveal_type(x) # N: Revealed type is "builtins.float" +[builtins fixtures/isinstancelist.pyi]