diff --git a/mypy/checker.py b/mypy/checker.py index fc9733117a0a..ca55f5fe12d7 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -5108,12 +5108,26 @@ def visit_for_stmt(self, s: ForStmt) -> None: s.inferred_item_type = item_type s.inferred_iterator_type = iterator_type + proper_iter_type = get_proper_type(iterator_type) + if isinstance(proper_iter_type, Instance) and any( + base.fullname == "typing.Generator" for base in proper_iter_type.type.mro + ): + supertype = self.named_type("typing.Generator").type + super_instance = map_instance_to_supertype(proper_iter_type, supertype) + if isinstance(get_proper_type(super_instance.args[2]), UninhabitedType): + exit_condition = IntExpr(1) + else: + exit_condition = IntExpr(0) + else: + exit_condition = IntExpr(0) + self.accept_loop( s.body, s.else_body, on_enter_body=lambda: self.analyze_index_variables( s.index, item_type, s.index_type is None, s ), + exit_condition=exit_condition, ) def analyze_async_iterable_item_type(self, expr: Expression) -> tuple[Type, Type]: diff --git a/test-data/unit/check-unreachable-code.test b/test-data/unit/check-unreachable-code.test index 368431127b76..8a7ee3866dcb 100644 --- a/test-data/unit/check-unreachable-code.test +++ b/test-data/unit/check-unreachable-code.test @@ -1587,3 +1587,51 @@ x = 0 # not unreachable f2: Callable[[], NoReturn] = lambda: foo() x = 0 # not unreachable + +[case testUnendingForLoop] +# flags: --warn-unreachable +from typing import Generator, NoReturn, TypeVar + +def generator() -> Generator[int, None, NoReturn]: + while True: + yield 1 + +def foo() -> int: + for x in generator(): + return x + + y = 0 # E: Statement is unreachable + +def bar() -> int: # E: Missing return statement + for x in generator(): + break + + y = 0 + +def baz() -> int: + for x in generator(): + pass + +A = TypeVar("A") +B = TypeVar("B") +C = TypeVar("C") + +class GeneratorSubtype(Generator[A, B, C]): + ... + +def foo2(gen: GeneratorSubtype[int, None, NoReturn]) -> int: + for x in gen: + return x + + y = 0 # E: Statement is unreachable + +def bar2(gen: GeneratorSubtype[int, None, NoReturn]) -> int: # E: Missing return statement + for x in gen: + break + + y = 0 + +def baz2(gen: GeneratorSubtype[int, None, NoReturn]) -> int: + for x in gen: + pass +[builtins fixtures/tuple.pyi]