Thanks to visit codestin.com
Credit goes to github.com

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

2 files changed

Lines changed: 34 additions & 1 deletion

File tree

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)