10000 Suppress errors for unreachable branches in conditional expressions (… · python/mypy@7959a20 · GitHub
[go: up one dir, main page]

Skip to content

Commit 7959a20

Browse files
Suppress errors for unreachable branches in conditional expressions (#18295)
Fixes #4134 Fixes #9195 Suppress errors when analyzing unreachable conditional expression branches. Same idea as what's done when analyzing the right-hand operand of `and`/`or`: https://github.com/python/mypy/blob/973618a6bfa88398e08dc250c8427b381b3a0fce/mypy/checkexpr.py#L4252-L4256 This PR originally added filters of the same form to the places where `analyze_cond_branch` is called in `ExpressionChecker.visit_conditional_expr`. However, since 5 out of the 6 `analyze_cond_branch` call sites now use `filter_errors` for the case when `map is None`, I decided to move the error filtering logic to inside `analyze_cond_branch`. **Given:** ```python from typing import TypeVar T = TypeVar("T", int, str) def foo(x: T) -> T: return x + 1 if isinstance(x, int) else x + "a" ``` **Before:** ```none main.py:5:16: error: Unsupported operand types for + ("str" and "int") [operator] main.py:5:49: error: Unsupported operand types for + ("int" and "str") [operator] Found 2 errors in 1 file (checked 1 source file) ``` **After:** ``` Success: no issues found in 1 source file ```
1 parent d33cef8 commit 7959a20

File tree

3 files changed

+16
-8
lines changed

3 files changed

+16
-8
lines changed

mypy/checker.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4793,7 +4793,9 @@ def visit_assert_stmt(self, s: AssertStmt) -> None:
47934793
# If this is asserting some isinstance check, bind that type in the following code
47944794
true_map, else_map = self.find_isinstance_check(s.expr)
47954795
if s.msg is not None:
4796-
self.expr_checker.analyze_cond_branch(else_map, s.msg, None)
4796+
self.expr_checker.analyze_cond_branch(
4797+
else_map, s.msg, None, suppress_unreachable_errors=False
4798+
)
47974799
self.push_type_map(true_map)
47984800

47994801
def visit_raise_stmt(self, s: RaiseStmt) -> None:

mypy/checkexpr.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4249,11 +4249,7 @@ def check_boolean_op(self, e: OpExpr, context: Context) -> Type:
42494249
):
42504250
self.msg.unreachable_right_operand(e.op, e.right)
42514251

4252-
# If right_map is None then we know mypy considers the right branch
4253-
# to be unreachable and therefore any errors found in the right branch
4254-
# should be suppressed.
4255-
with self.msg.filter_errors(filter_errors=right_map is None):
4256-
right_type = self.analyze_cond_branch(right_map, e.right, expanded_left_type)
4252+
right_type = self.analyze_cond_branch(right_map, e.right, expanded_left_type)
42574253

42584254
if left_map is None and right_map is None:
42594255
return UninhabitedType()
@@ -5851,12 +5847,15 @@ def analyze_cond_branch(
58515847
node: Expression,
58525848
context: Type | None,
58535849
allow_none_return: bool = False,
5850+
suppress_unreachable_errors: bool = True,
58545851
) -> Type:
58555852
with self.chk.binder.frame_context(can_skip=True, fall_through=0):
58565853
if map is None:
58575854
# We still need to type check node, in case we want to
5858-
# process it for isinstance checks later
5859-
self.accept(node, type_context=context, allow_none_return=allow_none_return)
5855+
# process it for isinstance checks later. Since the branch was
5856+
# determined to be unreachable, any errors should be suppressed.
5857+
with self.msg.filter_errors(filter_errors=suppress_unreachable_errors):
5858+
self.accept(node, type_context=context, allow_none_return=allow_none_return)
58605859
return UninhabitedType()
58615860
self.chk.push_type_map(map)
58625861
return self.accept(node, type_context=context, allow_none_return=allow_none_return)

test-data/unit/check-expressions.test

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1506,6 +1506,13 @@ x.append(y) if bool() else x.append(y)
15061506
z = x.append(y) if bool() else x.append(y) # E: "append" of "list" does not return a value (it only ever returns None)
15071507
[builtins fixtures/list.pyi]
15081508

1509+
[case testConditionalExpressionWithUnreachableBranches]
1510+
from typing import TypeVar
1511+
T = TypeVar("T", int, str)
1512+
def foo(x: T) -> T:
1513+
return x + 1 if isinstance(x, int) else x + "a"
1514+
[builtins fixtures/isinstancelist.pyi]
1515+
15091516
-- Special cases
15101517
-- -------------
15111518

0 commit comments

Comments
 (0)
0