25
25
from mypy .constraints import SUPERTYPE_OF
26
26
from mypy .erasetype import erase_type , erase_typevars , remove_instance_last_known_values
27
27
from mypy .errorcodes import TYPE_VAR , UNUSED_AWAITABLE , UNUSED_COROUTINE , ErrorCode
28
- from mypy .errors import Errors , ErrorWatcher , report_internal_error
28
+ from mypy .errors import Errors , ErrorWatcher , LoopErrorWatcher , report_internal_error
29
29
from mypy .expandtype import expand_type
30
30
from mypy .literals import Key , extract_var_from_literal_hash , literal , literal_hash
31
31
from mypy .maptype import map_instance_to_supertype
@@ -599,19 +599,27 @@ def accept_loop(
599
599
# on without bound otherwise)
600
600
widened_old = len (self .widened_vars )
601
601
602
- # Disable error types that we cannot safely identify in intermediate iteration steps:
603
- warn_unreachable = self .options .warn_unreachable
604
- warn_redundant = codes .REDUNDANT_EXPR in self .options .enabled_error_codes
605
- self .options .warn_unreachable = False
606
- self .options .enabled_error_codes .discard (codes .REDUNDANT_EXPR )
607
-
602
+ # one set of `unreachable`, `redundant-expr`, and `redundant-casts` errors
603
+ # per iteration step:
604
+ uselessness_errors = []
605
+ # one set of unreachable line numbers per iteration step:
606
+ unreachable_lines = []
607
+ # one set of revealed types per line where `reveal_type` is used (each
608
+ # created set can grow during the iteration):
609
+ revealed_types = defaultdict (set )
608
610
iter = 1
609
611
while True :
610
612
with self .binder .frame_context (can_skip = True , break_frame = 2 , continue_frame = 1 ):
611
613
if on_enter_body is not None :
612
614
on_enter_body ()
613
615
614
- self .accept (body )
616
+ with LoopErrorWatcher (self .msg .errors ) as watcher :
617
+ self .accept (body )
618
+ uselessness_errors .append (watcher .uselessness_errors )
619
+ unreachable_lines .append (watcher .unreachable_lines )
620
+ for key , values in watcher .revealed_types .items ():
621
+ revealed_types [key ].update (values )
622
+
615
623
partials_new = sum (len (pts .map ) for pts in self .partial_types )
616
624
widened_new = len (self .widened_vars )
617
625
# Perform multiple iterations if something changed that might affect
@@ -632,16 +640,29 @@ def accept_loop(
632
640
if iter == 20 :
633
641
raise RuntimeError ("Too many iterations when checking a loop" )
634
642
635
- # If necessary, reset the modified options and make up for the postponed error checks:
636
- self .options .warn_unreachable = warn_unreachable
637
- if warn_redundant :
638
- self .options .enabled_error_codes .add (codes .REDUNDANT_EXPR )
639
- if warn_unreachable or warn_redundant :
640
- with self .binder .frame_context (can_skip = True , break_frame = 2 , continue_frame = 1 ):
641
- if on_enter_body is not None :
642
- on_enter_body ()
643
-
644
- self .accept (body )
643
+ # Report only those `unreachable`, `redundant-expr`, and `redundant-casts`
644
+ # errors that could not be ruled out in any iteration step:
645
+ persistent_uselessness_errors = set ()
646
+ for candidate in set (itertools .chain (* uselessness_errors )):
647
+ if all (
648
+ (candidate in errors ) or (candidate [2 ] in lines )
649
+ for errors , lines in zip (uselessness_errors , unreachable_lines )
650
+ ):
651
+ persistent_uselessness_errors .add (candidate )
652
+ for error_info in persistent_uselessness_errors :
653
+ context = Context (line = error_info [2 ], column = error_info [3 ])
654
+ context .end_line = error_info [4 ]
655
+ context .end_column = error_info [5 ]
656
+ self .msg .fail (error_info [1 ], context , code = error_info [0 ])
657
+
658
+ # Report all types revealed in at least one iteration step:
659
+ for note_info , types in revealed_types .items ():
660
+ sorted_ = sorted (types , key = lambda typ : typ .lower ())
661
+ revealed = sorted_ [0 ] if len (types ) == 1 else f"Union[{ ', ' .join (sorted_ )} ]"
662
+ context = Context (line = note_info [1 ], column = note_info [2 ])
663
+ context .end_line = note_info [3 ]
664
+ context .end_column = note_info [4 ]
665
+ self .note (f'Revealed type is "{ revealed } "' , context )
645
666
646
667
# If exit_condition is set, assume it must be False on exit from the loop:
647
668
if exit_condition :
0 commit comments