Closed
Description
Bug Report
(A clear and concise description of what the bug is.)
To Reproduce
Gist: https://mypy-play.net/?mypy=latest&python=3.12&gist=61f3db46cdaf7782c6679322e8809f47&flags=strict%2Ccheck-untyped-defs
import enum
import random
class Test(enum.Enum):
A = 1
B = 2
val = list(Test)[random.randint(0, 2)]
reveal_type(val)
match val:
case Test.A:
print("A")
# case Test.B:
# print("B")
val2 = random.randint(0, 2)
reveal_type(val2)
match val2:
case 1:
print("1")
Expected Behavior
Expected both of the match statements to raise errors due to not exhaustively matching on the enum/int values
Actual Behavior
File is passed as ok when it should raise errors
Pyright is able to catch both cases in strict mode as expected in case this helps: pyright playground link
Your Environment
- Mypy version used: tested on 1.15.0, 1.14.1, 1.10.1
- Mypy command-line flags:
--strict
- Python version used: 3.12 & 3.10
Possibly Related Issues?
Below are the most relevant issues I could spot, but don't seem to quite align with the case here to the best of my understanding
Activity
A5rocks commentedon May 23, 2025
mypy doesn't have this feature, you need to add
case _: typing.assert_never(val)
to get exhaustiveness checking.Don-Burns commentedon May 23, 2025
Ah apologies then, I could have sworn I had seen this working in the past, but must have just been from my IDE's checking rather than running mypy (VsCode with pylance for IDE)
Would be a great feature though! Understand if this gets closed though
A5rocks commentedon May 23, 2025
Yeah maybe opt-in (what mypy currently has) is worse than opt-out (what pyright currently has) for strict mode at least. But I'm not too good with knowing what features mypy should add!
Don-Burns commentedon May 23, 2025
If the maintainers are open to it, I can try to take a stab at implementing this, but admittedly I am not very familiar with mypy's code base to know how hard/easy this may be to tackle.
A5rocks commentedon May 23, 2025
Implementation wise, I think this is simple: at the end of this loop:
mypy/mypy/checker.py
Line 5455 in a8ec893
check whether mypy is in strict mode (maybe add a new mode?) and whether the narrowed type (copy how
current_subject_type
is done, I think?) isUninhabitedType
.sterliakov commentedon May 23, 2025
It should be simple to add, but I don't think it's really a sensible check to enable globally. Python's match statement is not as good as Rust's (mostly because it's a statement and not a statement, so has no "return value"). Enforcing that
match
is always exhaustive is equivalent to requiring that anyif
has a correspondingelse
, which isn't something commonly enforced in python code. There's a simplecase other: assert_never(other)
option to enforce exhaustiveness of every match stmt separately.Don-Burns commentedon May 23, 2025
I agree partly here, while not quite as powerful as some other languages like Rust's or Scala's match statements, there is still a lot of power in python's version. For me one great benefit of being able to turn this on would be to flag any areas that were previously thought to be exhaustive or the dev thought they were exhaustive and didn't realise otherwise. If a new pattern becomes possible and there isn't a defined path, it feels like a code smell to me. Having an option at least in mypy to catch this would be great IMO.
You can still "opt-out" if no match causing no effect is the intended behaviour with something like
case _: pass
in the final clause. I can totally see how this comes down to which scenario you want to be explicit in though.sterliakov commentedon May 23, 2025
Hm,
case_: pass
is a good explicit opt-out, quite in line with_ => {}
in Rust to express "yes, it's non-exhaustive, I know, stfu". That's a good argument in favour of this proposal. It's especially nice because exhaustive matches should still occur more often than non-exhaustive, andcase _: pass
is shorter, needs no extra imports and is more immediately obvious compared tocase other: assert_never(other)
. Andmatch
predatesassert_never
by one version, so that import may not even come from stdlibtyping
.I'm +1 on adding this check, except for one problem: it shouldn't be on by default (and even in strict mode IMO), and everything else is seldom ever enabled by users, be it a config flag or
--enable-error-code
.Add flag to raise error if match statement does not match exaustively (…