Skip to content

Commit 2ba79cb

Browse files
authored
Use union of current context and left side for right side narrowing of binary ops (#19249)
Fixes #12001. Fixes #6898. Fixes #15368. Improves #17790 and #11508. When encountering `a {and,or} b`, we used to use `a` as primary context for `b` inference. This results in weird errors when `a` and `b` are completely unrelated, and in many cases return type/assignment type context can do much better. Inferring to union should be harmless in most cases, so use union of `a` and current context instead.
1 parent 2996c91 commit 2ba79cb

File tree

2 files changed

+34
-1
lines changed

2 files changed

+34
-1
lines changed

mypy/checkexpr.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4288,7 +4288,9 @@ def check_boolean_op(self, e: OpExpr, context: Context) -> Type:
42884288
):
42894289
self.msg.unreachable_right_operand(e.op, e.right)
42904290

4291-
right_type = self.analyze_cond_branch(right_map, e.right, expanded_left_type)
4291+
right_type = self.analyze_cond_branch(
4292+
right_map, e.right, self._combined_context(expanded_left_type)
4293+
)
42924294

42934295
if left_map is None and right_map is None:
42944296
return UninhabitedType()
@@ -5919,6 +5921,16 @@ def analyze_cond_branch(
59195921
self.chk.push_type_map(map)
59205922
return self.accept(node, type_context=context, allow_none_return=allow_none_return)
59215923

5924+
def _combined_context(self, ty: Type | None) -> Type | None:
5925+
ctx_items = []
5926+
if ty is not None:
5927+
ctx_items.append(ty)
5928+
if self.type_context and self.type_context[-1] is not None:
5929+
ctx_items.append(self.type_context[-1])
5930+
if ctx_items:
5931+
return make_simplified_union(ctx_items)
5932+
return None
5933+
59225934
#
59235935
# Helpers
59245936
#

test-data/unit/check-inference-context.test

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1510,3 +1510,24 @@ def mymin(
15101510
def check(paths: Iterable[str], key: Callable[[str], int]) -> Union[str, None]:
15111511
return mymin(paths, key=key, default=None)
15121512
[builtins fixtures/tuple.pyi]
1513+
1514+
[case testBinaryOpInferenceContext]
1515+
from typing import Literal, TypeVar
1516+
1517+
T = TypeVar("T")
1518+
1519+
def identity(x: T) -> T:
1520+
return x
1521+
1522+
def check1(use: bool, val: str) -> "str | Literal[True]":
1523+
return use or identity(val)
1524+
1525+
def check2(use: bool, val: str) -> "str | bool":
1526+
return use or identity(val)
1527+
1528+
def check3(use: bool, val: str) -> "str | Literal[False]":
1529+
return use and identity(val)
1530+
1531+
def check4(use: bool, val: str) -> "str | bool":
1532+
return use and identity(val)
1533+
[builtins fixtures/tuple.pyi]

0 commit comments

Comments
 (0)