Skip to content

Commit 11915aa

Browse files
committed
Don't assume for loop body index variable assignment executed always
1 parent 75b5604 commit 11915aa

File tree

2 files changed

+20
-3
lines changed

2 files changed

+20
-3
lines changed

mypy/checker.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -585,12 +585,12 @@ def accept_loop(
585585
else_body: Statement | None = None,
586586
*,
587587
exit_condition: Expression | None = None,
588+
on_enter_body: Callable[[], None] | None = None,
588589
) -> None:
589590
"""Repeatedly type check a loop body until the frame doesn't change."""
590591

591592
# The outer frame accumulates the results of all iterations:
592593
with self.binder.frame_context(can_skip=False, conditional_frame=True):
593-
594594
# Check for potential decreases in the number of partial types so as not to stop the
595595
# iteration too early:
596596
partials_old = sum(len(pts.map) for pts in self.partial_types)
@@ -603,6 +603,9 @@ def accept_loop(
603603

604604
while True:
605605
with self.binder.frame_context(can_skip=True, break_frame=2, continue_frame=1):
606+
if on_enter_body is not None:
607+
on_enter_body()
608+
606609
self.accept(body)
607610
partials_new = sum(len(pts.map) for pts in self.partial_types)
608611
if (partials_new == partials_old) and not self.binder.last_pop_changed:
@@ -5119,8 +5122,14 @@ def visit_for_stmt(self, s: ForStmt) -> None:
51195122
iterator_type, item_type = self.analyze_iterable_item_type(s.expr)
51205123
s.inferred_item_type = item_type
51215124
s.inferred_iterator_type = iterator_type
5122-
self.analyze_index_variables(s.index, item_type, s.index_type is None, s)
5123-
self.accept_loop(s.body, s.else_body)
5125+
5126+
self.accept_loop(
5127+
s.body,
5128+
s.else_body,
5129+
on_enter_body=lambda: self.analyze_index_variables(
5130+
s.index, item_type, s.index_type is None, s
5131+
),
5132+
)
51245133

51255134
def analyze_async_iterable_item_type(self, expr: Expression) -> tuple[Type, Type]:
51265135
"""Analyse async iterable expression and return iterator and iterator item types."""

test-data/unit/check-inference.test

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3894,3 +3894,11 @@ foo = [
38943894
]
38953895
reveal_type(foo) # N: Revealed type is "builtins.list[Tuple[builtins.int, typing.Sequence[builtins.str]]]"
38963896
[builtins fixtures/tuple.pyi]
3897+
3898+
[case testForLoopIndexVaribaleNarrowing]
3899+
from typing import Union
3900+
x: Union[int, str]
3901+
x = "abc"
3902+
for x in list[int]():
3903+
reveal_type(x) # N: Revealed type is "builtins.int"
3904+
reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]"

0 commit comments

Comments
 (0)