mypy --warn-unreachable will error on an unreachable return after an if block but not a semantically equivalent else: return ... after an if block. This shows up in platform checking logic, as an example of "unreachable code" which mypy understands and allows.
EDIT: Sorry, I noticed that the unit test example in the "original comment" below isn't type-checked since it doesn't have annotations. I can reproduce on master with this example.
$ mypy --warn-unreachable test.py
test.py:6: error: Statement is unreachable
Found 1 error in 1 file (checked 1 source file)
(Original comment)
I cannot reproduce this on master, so possibly it is already fixed by this PR #10560, which fixed some crash from if-branch with sys.platform.
I'm using this unit test locally:
# This block was marked as being unreachable during semantic analysis.
# It turns out any blocks marked in this way are *intentionally* marked
# as unreachable -- so we don't display an error.
self.binder.unreachable()
return
When reachability.py and its consider_sys_version_info and consider_sys_platform functions get invoked in the first mypy pass semanal_pass1.py, they set this b.is_unreachable property:
Currently the if/else based check works because unreachable is able to mark the else_body or body as unreachable. However, for this feature we would specifically want to mark the ReturnStmt in the outer block as unreachable and then somehow pick that up in checker.py.
I'm gonna try to see if I can come up with a PR to solve this but that's the root cause and problem of the issue.
However, for this feature we would specifically want to mark the ReturnStmt as unreachable and then somehow pick that up in checker.py.
I was thinking it might be easier to have some sort of unreachability kind that frames get marked as (rather than just generally marking them as unreachable) for the binder to check... but I'm not too sure.
if sys.platform == "darwin":
return ..
if sys.platform == "win32":
return ..
return
And this make mypy fail locally on macos with unreachable, and if I put an ignore unreachable, then CI on linux fails with unused-ignore, so it's impossible to get mypy to properly accept the types unless I pin the platform, or make a file non-strict. And really platform should be able to be a list of platform you support.
Activity
hi-ogawa commented on Oct 3, 2021
EDIT: Sorry, I noticed that the unit test example in the "original comment" below isn't type-checked since it doesn't have annotations. I can reproduce on master with this example.
import sys def f() -> int: if sys.platform != "win32": return 1 return 0 def g() -> int: if sys.platform == "win32": return 1 return 0(Original comment)
I cannot reproduce this on master, so possibly it is already fixed by this PR #10560, which fixed some crash from if-branch with sys.platform.
I'm using this unit test locally:
[case testUnreachableBranchBySys1] # flags: --warn-unreachable import sys def f(): if sys.platform != "win32": return 1 return 0 [builtins fixtures/dict.pyi] [case testUnreachableBranchBySys2] # flags: --warn-unreachable import sys def f(): if sys.platform == "win32": return 1 return 0 [builtins fixtures/dict.pyi]intgr commented on Mar 21, 2023
I experienced this issue as well, mypy 1.1.1 with --warn-unreachable
Unreachable error: (Playground link)
No unreachable error: (Playground link)
Have git module use sys.platform to check for Windows
ammaraskar commented on Jan 26, 2025
#5964 is an old bug that is this issue plus a little more functionality.
Currently the way that sys.version_info and sys.platform checks end up not getting flagged by --warn-unreachable is this code block:
mypy/mypy/checker.py
Lines 3020 to 3025 in 1eb9d4c
When reachability.py and its consider_sys_version_info and consider_sys_platform functions get invoked in the first mypy pass semanal_pass1.py, they set this b.is_unreachable property:
mypy/mypy/reachability.py
Lines 327 to 329 in 1eb9d4c
The is_unreachable property is defined at the Block level meaning that the block for those functions looks like:
Currently the if/else based check works because unreachable is able to mark the else_body or body as unreachable. However, for this feature we would specifically want to mark the ReturnStmt in the outer block as unreachable and then somehow pick that up in checker.py.
I'm gonna try to see if I can come up with a PR to solve this but that's the root cause and problem of the issue.
A5rocks commented on Jan 26, 2025
I was thinking it might be easier to have some sort of unreachability kind that frames get marked as (rather than just generally marking them as unreachable) for the binder to check... but I'm not too sure.
Carreau commented on Oct 11, 2025
This is annoying I have a:
And this make mypy fail locally on macos with unreachable, and if I put an ignore unreachable, then CI on linux fails with unused-ignore, so it's impossible to get mypy to properly accept the types unless I pin the platform, or make a file non-strict. And really platform should be able to be a list of platform you support.
A5rocks commented on Oct 11, 2025
You can work around the issue with # type: ignore[unreachable,unused-ignore]. This is unfortunately common :^)