Repository: python/mypy
Issue category: Bug Report

Structural Pattern Matching Exhaustiveness check when using OR
<!--
If you're not sure whether what you're experiencing is a mypy bug, please see the "Question and Help" form instead.
Please consider:

- checking our common issues page: https://mypy.readthedocs.io/en/stable/common_issues.html
- searching our issue tracker: https://github.com/python/mypy/issues to see if it's already been reported
- asking on gitter chat: https://gitter.im/python/typing
-->

**Bug Report**

Mypy fails to identify missing value comparisons in my `match` when I use `|`

**To Reproduce**

[Gist URL](https://gist.github.com/mypy-play/cba9c66b56fcbfbd383ca559518ebac5)
[Playground URL](https://mypy-play.net/?mypy=latest&python=3.12&flags=strict&gist=cba9c66b56fcbfbd383ca559518ebac5)

```python
from typing import Literal, assert_never

Color = Literal["blue", "green", "red"]


def print_color(color: Color) -> None:
  match color:
      case "blue":
          print("blue")
      case "green" | "kangaroo":
          print("green")
      case _:
          assert_never(color)
```

**Expected Behavior**

I expected mypy to error because I missed checking against `red`
```console
colors.py:13: error: Argument 1 to "assert_never" has incompatible type "Literal['red']"; expected "Never"  [arg-type]
Found 1 error in 1 file (checked 1 source file)
```

**Actual Behavior**

Mypy doesn't raise any errors

```console
Success: no issues found in 1 source file
```

** Extra Context **

This only happens when I use `|`.
I don't use pyright but ran it to see if I was the one missing something but I believe they do flag it correctly
```console
colors.py:13:24 - error: Argument of type "Literal['red']" cannot be assigned to parameter "arg" of type "Never" in function "assert_never"
    Type "Literal['red']" is not assignable to type "Never" (reportArgumentType)
```

**Your Environment**

- Mypy version used: `mypy 1.13.0 (compiled: yes)`
- Mypy command-line flags: `--strict`
- Python version used: `Python 3.11.6`





Functions to edit:
mypy/checkpattern.py:PatternChecker.visit_or_pattern

Patch:
diff --git a/mypy/checkpattern.py b/mypy/checkpattern.py
index 6b4fa35f9c49..4323daa68025 100644
--- a/mypy/checkpattern.py
+++ b/mypy/checkpattern.py
@@ -158,7 +158,8 @@ def visit_or_pattern(self, o: OrPattern) -> PatternType:
         for pattern in o.patterns:
             pattern_type = self.accept(pattern, current_type)
             pattern_types.append(pattern_type)
-            current_type = pattern_type.rest_type
+            if not is_uninhabited(pattern_type.type):
+                current_type = pattern_type.rest_type
 
         #
         # Collect the final type

Test patch: diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test
index 58b70d7b74d8..3ef964d3c0b9 100644
--- a/test-data/unit/check-python310.test
+++ b/test-data/unit/check-python310.test
@@ -1702,6 +1702,22 @@ def f(x: int | str) -> int:
         case str() as s:
             return 1
 
+[case testMatchOrPatternExhaustiveness]
+from typing import NoReturn, Literal
+def assert_never(x: NoReturn) -> None: ...
+
+Color = Literal["blue", "green", "red"]
+c: Color
+
+match c:
+    case "blue":
+        reveal_type(c) # N: Revealed type is "Literal['blue']"
+    case "green" | "notColor":
+        reveal_type(c) # N: Revealed type is "Literal['green']"
+    case _:
+        assert_never(c) # E: Argument 1 to "assert_never" has incompatible type "Literal['red']"; expected "Never"
+[typing fixtures/typing-typeddict.pyi]
+
 [case testMatchAsPatternIntersection-skip]
 class A: pass
 class B: pass