8000 Typecheck unreachable code by A5rocks · Pull Request #18707 · python/mypy · GitHub
[go: up one dir, main page]

Skip to content

Typecheck unreachable code #18707

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 46 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
be1071e
Start type checking unreachable code
A5rocks Jan 24, 2025
d996918
Pass self-check
A5rocks Feb 19, 2025
07de5bf
Hacky fix to narrow unreachable literals
A5rocks Feb 19, 2025
bac268b
20% of tests
A5rocks Feb 19, 2025
2da4b7c
25% of tests
A5rocks Feb 19, 2025
ba9dab2
30% of tests
A5rocks Feb 19, 2025
7c39efa
40% of tests
A5rocks Feb 19, 2025
18642d0
50% of tests
A5rocks Feb 19, 2025
879b274
60% of tests
A5rocks Feb 19, 2025
c62093e
Pass all tests
A5rocks Feb 19, 2025
e242b5e
Narrow `isinstance` checks to `Never`
A5rocks Feb 20, 2025
69efb41
Don't treat `reveal_type(<Never>)` as a no-op
A5rocks Feb 20, 2025
f846af7
Ensure functions with typevar values don't get false positives
A5rocks Feb 21, 2025
390ade8
Ensure that `<Never>.x` is `Never`
A5rocks Feb 21, 2025
f3ba149
Ensure that `<Never>()` is `Never`
A5rocks Feb 21, 2025
92a4428
Only report unreachability once
A5rocks Feb 24, 2025
c76d422
Narrow `callable` checks to `Never`
A5rocks Feb 24, 2025
be1d524
Address CI failures
A5rocks Feb 24, 2025
13ad1cb
Narrow `match` captures to `Never`
A5rocks Feb 24, 2025
53553e7
Don't check unreachable code with partial types
A5rocks Feb 24, 2025
6cc9930
Avoid strange behavior regarding "narrowing" expressions for unreacha…
A5rocks Feb 24, 2025
4ddd34e
Unpack `Never` iterables
A5rocks Feb 24, 2025
3cda7db
Narrow disjoint narrows to `Never`
A5rocks Mar 3, 2025
6e97289
Fix `redundant-expr` for comprehensions
A5rocks Mar 3, 2025
28894ce
Narrow `issubclass` to `Never`
A5rocks Mar 3, 2025
be6573e
Narrow things based on key types even if the type is `Never`
A5rocks Mar 3, 2025
4718cfd
Update some tests
A5rocks Mar 4, 2025
475f8b1
`typing.assert_never(5)` is fine in unreachable code
A5rocks Mar 4, 2025
72349c0
Non-ambiguous `Never` is fine as inference result
A5rocks Mar 4, 2025
ca11151
Implement or-ing typemaps for `Never`-based unreachability
A5rocks Mar 6, 2025
22e73d5
Tuple length checks should narrow to `Never`
A5rocks Mar 6, 2025
2bd0802
Narrow walrus to `Never` if unreachable
A5rocks Mar 6, 2025
8fb6fab
Narrow unreachable captures due to guards in `match` to `Never`
A5rocks Mar 6, 2025
0f41d33
Narrow impossible values in mapping pattern to `Never`
A5rocks Mar 6, 2025
b641ed4
Smooth out edges
A5rocks Mar 6, 2025
dcc4f29
Propagate suppressing unreachability warnings
A5rocks Mar 7, 2025
98abde4
Fix typemap handling for ternaries
A5rocks Mar 9, 2025
390388a
Ensure `--strict-equality` works correctly with partial types
A5rocks Mar 9, 2025
dc8ed71
Support `Never` callables in plugins
A5rocks Mar 13, 2025
102ac11
Add a test about narrowing behavior
A5rocks Mar 13, 2025
f7f8915
Merge remote-tracking branch 'upstream/master' into experiments/typec…
A5rocks Mar 17, 2025
bc8cb90
Merge branch 'master' into experiments/typecheck-unreachable
A5rocks Mar 31, 2025
ac03c88
Address PR review
A5rocks Apr 9, 2025
4f319bb
Add an option to enable checking unreachable code
A5rocks Apr 9, 2025
d5e4a47
Update test cases to pass
A5rocks Apr 9, 2025
730d9ee
Don't typecheck unreachable noops
A5rocks Apr 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Only report unreachability once
  • Loading branch information
A5rocks committed Feb 24, 2025
commit 92a4428bf4f221810eefb7d197bbf4776aaf3908
7 changes: 7 additions & 0 deletions mypy/binder.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ def __init__(self, id: int, conditional_frame: bool = False) -> None:
self.unreachable = False
self.conditional_frame = conditional_frame
self.suppress_unreachable_warnings = False
self.unreachable_warning_emitted = False

def __repr__(self) -> str:
return f"Frame({self.id}, {self.types}, {self.unreachable}, {self.conditional_frame})"
Expand Down Expand Up @@ -186,6 +187,9 @@ def unreachable(self) -> None:
def suppress_unreachable_warnings(self) -> None:
self.frames[-1].suppress_unreachable_warnings = True

def emitted_unreachable_warning(self) -> None:
self.frames[-1].unreachable_warning_emitted = True

def get(self, expr: Expression) -> Type | None:
key = literal_hash(expr)
assert key is not None, "Internal error: binder tried to get non-literal"
Expand All @@ -202,6 +206,9 @@ def is_unreachable(self) -> bool:
def is_unreachable_warning_suppressed(self) -> bool:
return any(f.suppress_unreachable_warnings for f in self.frames)

def is_unreachable_warning_emitted(self) -> bool:
return any(f.unreachable_warning_emitted for f in self.frames)

def cleanse(self, expr: Expression) -> None:
"""Remove all references to a Node from the binder."""
key = literal_hash(expr)
Expand Down
3 changes: 3 additions & 0 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,7 @@ def check_first_pass(self) -> None:
reported_unreachable = True
elif not self.is_noop_for_reachability(d):
self.msg.unreachable_statement(d)
self.binder.emitted_unreachable_warning()
reported_unreachable = True
self.accept(d)

Expand Down Expand Up @@ -3060,6 +3061,7 @@ def visit_block(self, b: Block) -> None:
reported_unreachable = True
elif not self.is_noop_for_reachability(s):
self.msg.unreachable_statement(s)
self.binder.emitted_unreachable_warning()
reported_unreachable = True
self.accept(s)

Expand All @@ -3069,6 +3071,7 @@ def should_report_unreachable_issues(self) -> bool:
and self.options.warn_unreachable
and not self.current_node_deferred
and not self.binder.is_unreachable_warning_suppressed()
and not self.binder.is_unreachable_warning_emitted()
)

def is_noop_for_reachability(self, s: Statement) -> bool:
Expand Down
5 changes: 2 additions & 3 deletions test-data/unit/check-isinstance.test
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ while 2:
x.b # E: "A" has no attribute "b"
break
_ = "unreachable" # E: Statement is unreachable
x.b # E: Statement is unreachable
x.b

[case testUnionTryFinally6]
class A: pass
Expand Down Expand Up @@ -733,9 +733,8 @@ while bool():
if isinstance(x, int):
x + 1
break
# TODO: only report unreachability once
elif isinstance(x, str): # E: Statement is unreachable
_ = "unreachable" # E: Statement is unreachable
_ = "unreachable"
x + 'a'
break
_ = "unreachable" # E: Statement is unreachable
Expand Down
0