Skip to content

Commit 98abde4

Browse files
committed
Fix typemap handling for ternaries
1 parent dcc4f29 commit 98abde4

File tree

4 files changed

+34
-10
lines changed

4 files changed

+34
-10
lines changed

mypy/checker.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8134,8 +8134,9 @@ def and_conditional_maps(m1: TypeMap, m2: TypeMap, use_meet: bool = False) -> Ty
81348134
result = m2.copy()
81358135
m2_keys = {literal_hash(n2) for n2 in m2}
81368136
for n1 in m1:
8137-
# TODO: should this check for UninhabitedType as well as AnyType?
8138-
if literal_hash(n1) not in m2_keys or isinstance(get_proper_type(m1[n1]), AnyType):
8137+
if literal_hash(n1) not in m2_keys or isinstance(
8138+
get_proper_type(m1[n1]), (UninhabitedType, AnyType)
8139+
):
81398140
result[n1] = m1[n1]
81408141
if use_meet:
81418142
# For now, meet common keys only if specifically requested.

mypy/checkexpr.py

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5836,9 +5836,13 @@ def visit_conditional_expr(self, e: ConditionalExpr, allow_none_return: bool = F
58365836
# but only for the current expression
58375837
if_map, else_map = self.chk.find_isinstance_check(e.cond)
58385838
if codes.REDUNDANT_EXPR in self.chk.options.enabled_error_codes:
5839-
if if_map is None:
5839+
if if_map is None or any(
5840+
isinstance(get_proper_type(t), UninhabitedType) for t in if_map.values()
5841+
):
58405842
self.msg.redundant_condition_in_if(False, e.cond)
5841-
elif else_map is None:
5843+
elif else_map is None or any(
5844+
isinstance(get_proper_type(t), UninhabitedType) for t in else_map.values()
5845+
):
58425846
self.msg.redundant_condition_in_if(True, e.cond)
58435847

58445848
if_type = self.analyze_cond_branch(
@@ -5915,17 +5919,28 @@ def analyze_cond_branch(
59155919
node: Expression,
59165920
context: Type | None,
59175921
allow_none_return: bool = False,
5918-
suppress_unreachable_errors: bool = True,
5922+
suppress_unreachable_errors: bool | None = None,
59195923
) -> Type:
5924+
# TODO: default based on flag (default to `True` if flag is not passed)
5925+
unreachable_errors_suppressed = (
5926+
suppress_unreachable_errors
5927+
if suppress_unreachable_errors is not None
5928+
else self.chk.binder.is_unreachable_warning_suppressed()
5929+
)
59205930
with self.chk.binder.frame_context(can_skip=True, fall_through=0):
5921-
if map is None:
5931+
self.chk.push_type_map(map)
5932+
5933+
if map is None or any(
5934+
isinstance(get_proper_type(t), UninhabitedType) for t in map.values()
5935+
):
59225936
# We still need to type check node, in case we want to
59235937
# process it for isinstance checks later. Since the branch was
59245938
# determined to be unreachable, any errors should be suppressed.
5925-
with self.msg.filter_errors(filter_errors=suppress_unreachable_errors):
5939+
5940+
with self.msg.filter_errors(filter_errors=unreachable_errors_suppressed):
59265941
self.accept(node, type_context=context, allow_none_return=allow_none_return)
59275942
return UninhabitedType()
5928-
self.chk.push_type_map(map)
5943+
59295944
return self.accept(node, type_context=context, allow_none_return=allow_none_return)
59305945

59315946
#

test-data/unit/check-isinstance.test

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1444,7 +1444,8 @@ if isinstance(x2, A) or isinstance(x2, C): # E: Right operand of "or" is never
14441444
f = x2.flag # E: "A" has no attribute "flag"
14451445
else:
14461446
# unreachable
1447-
_ = "unreachable" # E: Statement is unreachable
1447+
reveal_type(x2) # E: Statement is unreachable \
1448+
# N: Revealed type is "Never"
14481449
reveal_type(x2) # N: Revealed type is "__main__.A"
14491450
[builtins fixtures/isinstance.pyi]
14501451

test-data/unit/check-unreachable-code.test

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -962,7 +962,8 @@ k = [x for x in lst if isinstance(x, int) or foo()] # E: Right operand of "or"
962962

963963
class Case1:
964964
def test1(self) -> bool:
965-
return False and self.missing() # E: Right operand of "and" is never evaluated
965+
return False and self.missing() # E: Right operand of "and" is never evaluated \
966+
# E: "Case1" has no attribute "missing"
966967

967968
def test2(self) -> bool:
968969
return not self.property_decorator_missing and self.missing() # E: Function "property_decorator_missing" could always be true in boolean context \
@@ -1557,3 +1558,9 @@ if isinstance(x, int):
15571558
reveal_type(y) # N: Revealed type is "Never"
15581559
reveal_type(ys) # N: Revealed type is "builtins.list[Never]"
15591560
[builtins fixtures/isinstancelist.pyi]
1561+
1562+
[case testUnusedConditionalBranchesDoNotAffectType]
1563+
# flags: --enable-error-code redundant-expr
1564+
def foo(var: str) -> str:
1565+
return "aa" if isinstance(var, str) else 0 # E: If condition is always true
1566+
[builtins fixtures/isinstancelist.pyi]

0 commit comments

Comments
 (0)