Skip to content

Commit 1c815c2

Browse files
authored
Fix narrowing with chained comparison (#21150)
Fixes #21149 (the regression part of it, still working on the bad behaviour present in previous mypy versions)
1 parent e70166a commit 1c815c2

File tree

2 files changed

+35
-2
lines changed

2 files changed

+35
-2
lines changed

mypy/checker.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6676,8 +6676,8 @@ def comparison_type_narrowing_helper(self, node: ComparisonExpr) -> tuple[TypeMa
66766676
# If we have found non-trivial restrictions from the regular comparisons,
66776677
# then return soon. Otherwise try to infer restrictions involving `len(x)`.
66786678
# TODO: support regular and len() narrowing in the same chain.
6679-
if any(m != ({}, {}) for m in partial_type_maps):
6680-
return reduce_conditional_maps(partial_type_maps)
6679+
if any(len(m[0]) or len(m[1]) for m in partial_type_maps):
6680+
return reduce_conditional_maps(partial_type_maps, use_meet=True)
66816681
else:
66826682
# Use meet for `and` maps to get correct results for chained checks
66836683
# like `if 1 < len(x) < 4: ...`

test-data/unit/check-narrowing.test

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3264,6 +3264,39 @@ def bad_but_should_pass(has_key: bool, key: bool, s: tuple[bool, ...]) -> None:
32643264
reveal_type(key) # N: Revealed type is "builtins.bool"
32653265
[builtins fixtures/primitives.pyi]
32663266

3267+
[case testNarrowChainedComparisonMeet]
3268+
# flags: --strict-equality --warn-unreachable
3269+
from __future__ import annotations
3270+
from typing import Any
3271+
3272+
def f1(a: str | None, b: str | None) -> None:
3273+
if None is not a == b:
3274+
reveal_type(a) # N: Revealed type is "builtins.str"
3275+
reveal_type(b) # N: Revealed type is "builtins.str | None"
3276+
3277+
if (None is not a) and (a == b):
3278+
reveal_type(a) # N: Revealed type is "builtins.str"
3279+
reveal_type(b) # N: Revealed type is "builtins.str"
3280+
3281+
def f2(a: Any | None, b: str | None) -> None:
3282+
if None is not a == b:
3283+
reveal_type(a) # N: Revealed type is "Any"
3284+
reveal_type(b) # N: Revealed type is "builtins.str | None"
3285+
3286+
if (None is not a) and (a == b):
3287+
reveal_type(a) # N: Revealed type is "Any"
3288+
reveal_type(b) # N: Revealed type is "builtins.str | None"
3289+
3290+
def f3(a: str | None, b: Any | None) -> None:
3291+
if None is not a == b:
3292+
reveal_type(a) # N: Revealed type is "builtins.str"
3293+
reveal_type(b) # N: Revealed type is "Any | builtins.str | None"
3294+
3295+
if (None is not a) and (a == b):
3296+
reveal_type(a) # N: Revealed type is "builtins.str"
3297+
reveal_type(b) # N: Revealed type is "Any | builtins.str"
3298+
[builtins fixtures/primitives.pyi]
3299+
32673300
[case testNarrowTypeObject]
32683301
# flags: --strict-equality --warn-unreachable
32693302
from typing import Any

0 commit comments

Comments
 (0)