Skip to content

Commit

Permalink
Improve match narrowing and reachability analysis
Browse files Browse the repository at this point in the history
Fixes #12534, #15878
  • Loading branch information
hauntsaninja committed Aug 16, 2023
1 parent 854a9f8 commit 8033909
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 2 deletions.
17 changes: 16 additions & 1 deletion mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -4944,7 +4944,7 @@ def visit_match_stmt(self, s: MatchStmt) -> None:
self.push_type_map(pattern_map)
self.push_type_map(pattern_type.captures)
if g is not None:
with self.binder.frame_context(can_skip=True, fall_through=3):
with self.binder.frame_context(can_skip=False, fall_through=3):
gt = get_proper_type(self.expr_checker.accept(g))

if isinstance(gt, DeletedType):
Expand All @@ -4953,6 +4953,21 @@ def visit_match_stmt(self, s: MatchStmt) -> None:
guard_map, guard_else_map = self.find_isinstance_check(g)
else_map = or_conditional_maps(else_map, guard_else_map)

# If the guard narrowed the subject, copy the narrowed types over
if isinstance(p, AsPattern):
case_target = p.pattern or p.name
if isinstance(case_target, NameExpr):
for type_map in (guard_map, else_map):
if not type_map:
continue
for expr in list(type_map):
if not (
isinstance(expr, NameExpr)
and expr.fullname == case_target.fullname
):
continue
type_map[s.subject] = type_map[expr]

self.push_type_map(guard_map)
self.accept(b)
else:
Expand Down
45 changes: 44 additions & 1 deletion test-data/unit/check-python310.test
Original file line number Diff line number Diff line change
Expand Up @@ -1372,7 +1372,7 @@ match m:
reveal_type(m) # N: Revealed type is "__main__.Medal"

[case testMatchNarrowUsingPatternGuardSpecialCase]
def f(x: int | str) -> int: # E: Missing return statement
def f(x: int | str) -> int:
match x:
case x if isinstance(x, str):
return 0
Expand Down Expand Up @@ -1973,3 +1973,46 @@ def f2(x: T) -> None:
case DataFrame(): # type: ignore[misc]
pass
[builtins fixtures/primitives.pyi]

[case testMatchGuardReachability]
# flags: --warn-unreachable
def f1(e: int) -> int:
match e:
case x if True:
return x
case _:
return 0 # E: Statement is unreachable
e = 0 # E: Statement is unreachable


def f2(e: int) -> int:
match e:
case x if bool():
return x
case _:
return 0
e = 0 # E: Statement is unreachable

def f3(e: int | str | bytes) -> int:
match e:
case x if isinstance(x, int):
return x
case [x]:
return 0 # E: Statement is unreachable
case str(x):
return 0
reveal_type(e) # N: Revealed type is "builtins.bytes"
return 0

def f4(e: int | str | bytes) -> int:
match e:
case int(x):
pass
case [x]:
return 0 # E: Statement is unreachable
case x if isinstance(x, str):
return 0
reveal_type(e) # N: Revealed type is "Union[builtins.int, builtins.bytes]"
return 0

[builtins fixtures/primitives.pyi]

0 comments on commit 8033909

Please sign in to comment.