Skip to content

Use union of current context and left side for right side narrowing of binary ops #19249

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
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
14 changes: 13 additions & 1 deletion mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -4271,7 +4271,9 @@ def check_boolean_op(self, e: OpExpr, context: Context) -> Type:
):
self.msg.unreachable_right_operand(e.op, e.right)

right_type = self.analyze_cond_branch(right_map, e.right, expanded_left_type)
right_type = self.analyze_cond_branch(
right_map, e.right, self._combined_context(expanded_left_type)
)

if left_map is None and right_map is None:
return UninhabitedType()
Expand Down Expand Up @@ -5890,6 +5892,16 @@ def analyze_cond_branch(
self.chk.push_type_map(map)
return self.accept(node, type_context=context, allow_none_return=allow_none_return)

def _combined_context(self, ty: Type | None) -> Type | None:
ctx_items = []
if ty is not None:
ctx_items.append(ty)
if self.type_context and self.type_context[-1] is not None:
ctx_items.append(self.type_context[-1])
if ctx_items:
return make_simplified_union(ctx_items)
return None

#
# Helpers
#
Expand Down
21 changes: 21 additions & 0 deletions test-data/unit/check-inference-context.test
Original file line number Diff line number Diff line change
Expand Up @@ -1510,3 +1510,24 @@ def mymin(
def check(paths: Iterable[str], key: Callable[[str], int]) -> Union[str, None]:
return mymin(paths, key=key, default=None)
[builtins fixtures/tuple.pyi]

[case testBinaryOpInferenceContext]
from typing import Literal, TypeVar

T = TypeVar("T")

def identity(x: T) -> T:
return x

def check1(use: bool, val: str) -> "str | Literal[True]":
return use or identity(val)

def check2(use: bool, val: str) -> "str | bool":
return use or identity(val)

def check3(use: bool, val: str) -> "str | Literal[False]":
return use and identity(val)

def check4(use: bool, val: str) -> "str | bool":
return use and identity(val)
[builtins fixtures/tuple.pyi]