From 42c075f7c6d53352b4869f16093ce3367fed19e8 Mon Sep 17 00:00:00 2001 From: Puneet Dixit Date: Thu, 21 May 2026 12:02:13 +0530 Subject: [PATCH] Fix exhaustive match after unreachable cases --- mypy/checker.py | 8 ++++++-- test-data/unit/check-python310.test | 15 +++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 2cb3d69b2d77..438574641fce 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -5944,6 +5944,7 @@ def visit_match_stmt(self, s: MatchStmt) -> None: # The second pass narrows down the types and type checks bodies. unmatched_types: TypeMap = {s.subject: UninhabitedType()} for p, g, b in zip(s.patterns, s.guards, s.bodies): + case_is_unreachable = self.binder.is_unreachable() current_subject_type = self.expr_checker.narrow_type_from_binder( named_subject, subject_type ) @@ -5996,8 +5997,11 @@ def visit_match_stmt(self, s: MatchStmt) -> None: self.accept(b) else: self.accept(b) - self.push_type_map(else_map, from_assignment=False) - unmatched_types = else_map + if not case_is_unreachable: + # Unreachable cases are still checked above, but they shouldn't revive + # subject types that were already handled by earlier cases. + self.push_type_map(else_map, from_assignment=False) + unmatched_types = else_map if not is_unreachable_map(unmatched_types) and not self.current_node_deferred: for typ in unmatched_types.values(): diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test index 03a0934b662c..fd30de289ecc 100644 --- a/test-data/unit/check-python310.test +++ b/test-data/unit/check-python310.test @@ -3625,6 +3625,21 @@ match b: case _: pass +[case testExhaustiveMatchIgnoresUnreachableClassPattern] +# flags: --enable-error-code exhaustive-match +class A: pass +class B: pass + +x: A | None + +match x: + case None: + pass + case A() as a: + pass + case B(): + pass + [case testNonExhaustiveMatchWithFlag] # flags: --enable-error-code exhaustive-match --strict-equality --warn-unreachable