diff --git a/mypy/checker.py b/mypy/checker.py index 3bd9c494a8905..a54da9aa9bd0b 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -4944,7 +4944,7 @@ def visit_match_stmt(self, s: MatchStmt) -> None: self.push_type_map(pattern_map) self.push_type_map(pattern_type.captures) if g is not None: - with self.binder.frame_context(can_skip=True, fall_through=3): + with self.binder.frame_context(can_skip=False, fall_through=3): gt = get_proper_type(self.expr_checker.accept(g)) if isinstance(gt, DeletedType): @@ -4953,6 +4953,21 @@ def visit_match_stmt(self, s: MatchStmt) -> None: guard_map, guard_else_map = self.find_isinstance_check(g) else_map = or_conditional_maps(else_map, guard_else_map) + # If the guard narrowed the subject, copy the narrowed types over + if isinstance(p, AsPattern): + case_target = p.pattern or p.name + if isinstance(case_target, NameExpr): + for type_map in (guard_map, else_map): + if not type_map: + continue + for expr in list(type_map): + if not ( + isinstance(expr, NameExpr) + and expr.fullname == case_target.fullname + ): + continue + type_map[s.subject] = type_map[expr] + self.push_type_map(guard_map) self.accept(b) else: diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test index 75293ce9d1938..d494e1bc34a51 100644 --- a/test-data/unit/check-python310.test +++ b/test-data/unit/check-python310.test @@ -1372,7 +1372,7 @@ match m: reveal_type(m) # N: Revealed type is "__main__.Medal" [case testMatchNarrowUsingPatternGuardSpecialCase] -def f(x: int | str) -> int: # E: Missing return statement +def f(x: int | str) -> int: match x: case x if isinstance(x, str): return 0 @@ -1973,3 +1973,46 @@ def f2(x: T) -> None: case DataFrame(): # type: ignore[misc] pass [builtins fixtures/primitives.pyi] + +[case testMatchGuardReachability] +# flags: --warn-unreachable +def f1(e: int) -> int: + match e: + case x if True: + return x + case _: + return 0 # E: Statement is unreachable + e = 0 # E: Statement is unreachable + + +def f2(e: int) -> int: + match e: + case x if bool(): + return x + case _: + return 0 + e = 0 # E: Statement is unreachable + +def f3(e: int | str | bytes) -> int: + match e: + case x if isinstance(x, int): + return x + case [x]: + return 0 # E: Statement is unreachable + case str(x): + return 0 + reveal_type(e) # N: Revealed type is "builtins.bytes" + return 0 + +def f4(e: int | str | bytes) -> int: + match e: + case x if isinstance(x, int): + pass + case [x]: + return 0 # E: Statement is unreachable + case str(x): + return 0 + reveal_type(e) # N: Revealed type is "Union[builtins.int, builtins.bytes]" + return 0 + +[builtins fixtures/primitives.pyi]