Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions mypy/binder.py
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,8 @@ def update_from_options(self, frames: list[Frame]) -> bool:
if (
current_value is None
or not is_same_type(type, current_value.type)
# 新语义下,即使类型语义等价,也要保留合并得到的窄化表示。
or (not old_semantics and type != current_value.type)
# Manually carry over any narrowing from hasattr() from inner frames. This is
# a bit ad-hoc, but our handling of hasattr() is on best effort basis anyway.
or isinstance(p_type := get_proper_type(type), Instance)
Expand Down
3 changes: 2 additions & 1 deletion mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -5477,7 +5477,8 @@ def visit_if_stmt(self, s: IfStmt) -> None:
s.unreachable_else = is_unreachable_map(else_map)

# XXX Issue a warning if condition is always False?
with self.binder.frame_context(can_skip=True, fall_through=2):
# else 分支不可达时,不能把进入 if 前的状态作为可跳过路径合并。
with self.binder.frame_context(can_skip=not s.unreachable_else, fall_through=2):
self.push_type_map(if_map, from_assignment=False)
self.accept(b)

Expand Down
38 changes: 38 additions & 0 deletions test-data/unit/check-redefine2.test
Original file line number Diff line number Diff line change
Expand Up @@ -1297,6 +1297,44 @@ def foo() -> None:
if bool():
name = "a"

[case testNarrowingSurvivesAlwaysTrueBlock]
# flags: --allow-redefinition
class Base: ...
class Sub(Base): ...

def f(x: Sub | Base) -> None:
if True:
assert not isinstance(x, Sub)
reveal_type(x) # N: Revealed type is "__main__.Base"
reveal_type(x) # N: Revealed type is "__main__.Base"
[builtins fixtures/isinstance.pyi]

[case testNarrowingSurvivesAlwaysTrueBlockDefault]
class Base: ...
class Sub(Base): ...

def f(x: Sub | Base) -> None:
if True:
assert not isinstance(x, Sub)
reveal_type(x) # N: Revealed type is "__main__.Base"
reveal_type(x) # N: Revealed type is "__main__.Base"
[builtins fixtures/isinstance.pyi]

[case testNarrowingSurvivesTypeCheckingBlock]
# flags: --allow-redefinition
from typing import TYPE_CHECKING

class Base: ...
class Sub(Base): ...

def f(x: Sub | Base) -> None:
if TYPE_CHECKING:
assert not isinstance(x, Sub)
reveal_type(x) # N: Revealed type is "__main__.Base"
reveal_type(x) # N: Revealed type is "__main__.Base"
[builtins fixtures/isinstance.pyi]
[typing fixtures/typing-full.pyi]

[case testNewRedefinePartialTypeForUnderscore]
# flags: --allow-redefinition

Expand Down
Loading