2525from mypy .constraints import SUPERTYPE_OF
2626from mypy .erasetype import erase_type , erase_typevars , remove_instance_last_known_values
2727from 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
2929from mypy .expandtype import expand_type
3030from mypy .literals import Key , extract_var_from_literal_hash , literal , literal_hash
3131from mypy .maptype import map_instance_to_supertype
@@ -599,19 +599,27 @@ def accept_loop(
599599 # on without bound otherwise)
600600 widened_old = len (self .widened_vars )
601601
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 )
608610 iter = 1
609611 while True :
610612 with self .binder .frame_context (can_skip = True , break_frame = 2 , continue_frame = 1 ):
611613 if on_enter_body is not None :
612614 on_enter_body ()
613615
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+
615623 partials_new = sum (len (pts .map ) for pts in self .partial_types )
616624 widened_new = len (self .widened_vars )
617625 # Perform multiple iterations if something changed that might affect
@@ -632,16 +640,29 @@ def accept_loop(
632640 if iter == 20 :
633641 raise RuntimeError ("Too many iterations when checking a loop" )
634642
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 )
645666
646667 # If exit_condition is set, assume it must be False on exit from the loop:
647668 if exit_condition :
@@ -2264,7 +2285,7 @@ def check_method_override_for_base_with_name(
22642285 original_type ,
22652286 defn .name ,
22662287 name ,
2267- base .name ,
2288+ base .name if base . module_name == self . tree . fullname else base . fullname ,
22682289 original_class_or_static ,
22692290 override_class_or_static ,
22702291 context ,
@@ -2449,7 +2470,7 @@ def erase_override(t: Type) -> Type:
24492470 if not is_subtype (original_arg_type , erase_override (override_arg_type )):
24502471 context : Context = node
24512472 if isinstance (node , FuncDef ) and not node .is_property :
2452- arg_node = node .arguments [i + len ( override .bound_args )]
2473+ arg_node = node .arguments [i + override .bound ( )]
24532474 if arg_node .line != - 1 :
24542475 context = arg_node
24552476 self .msg .argument_incompatible_with_supertype (
@@ -2664,7 +2685,7 @@ def check_typevar_defaults(self, tvars: Sequence[TypeVarLikeType]) -> None:
26642685 continue
26652686 if not is_subtype (tv .default , tv .upper_bound ):
26662687 self .fail ("TypeVar default must be a subtype of the bound type" , tv )
2667- if tv .values and not any (tv .default == value for value in tv .values ):
2688+ if tv .values and not any (is_same_type ( tv .default , value ) for value in tv .values ):
26682689 self .fail ("TypeVar default must be one of the constraint types" , tv )
26692690
26702691 def check_enum (self , defn : ClassDef ) -> None :
@@ -7697,9 +7718,13 @@ def get_isinstance_type(self, expr: Expression) -> list[TypeRange] | None:
76977718 types : list [TypeRange ] = []
76987719 for typ in all_types :
76997720 if isinstance (typ , FunctionLike ) and typ .is_type_obj ():
7700- # Type variables may be present -- erase them, which is the best
7701- # we can do (outside disallowing them here).
7702- erased_type = erase_typevars (typ .items [0 ].ret_type )
7721+ # If a type is generic, `isinstance` can only narrow its variables to Any.
7722+ any_parameterized = fill_typevars_with_any (typ .type_object ())
7723+ # Tuples may have unattended type variables among their items
7724+ if isinstance (any_parameterized , TupleType ):
7725+ erased_type = erase_typevars (any_parameterized )
7726+ else :
7727+ erased_type = any_parameterized
77037728 types .append (TypeRange (erased_type , is_upper_bound = False ))
77047729 elif isinstance (typ , TypeType ):
77057730 # Type[A] means "any type that is a subtype of A" rather than "precisely type A"
0 commit comments