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 :
@@ -3427,7 +3448,9 @@ def check_compatibility_all_supers(self, lvalue: RefExpr, rvalue: Expression) ->
34273448 # store the rvalue type on the variable.
34283449 actual_lvalue_type = None
34293450 if lvalue_node .is_inferred and not lvalue_node .explicit_self_type :
3430- rvalue_type = self .expr_checker .accept (rvalue , lvalue_node .type )
3451+ # Don't use partial types as context, similar to regular code path.
3452+ ctx = lvalue_node .type if not isinstance (lvalue_node .type , PartialType ) else None
3453+ rvalue_type = self .expr_checker .accept (rvalue , ctx )
34313454 actual_lvalue_type = lvalue_node .type
34323455 lvalue_node .type = rvalue_type
34333456 lvalue_type , _ = self .node_type_from_base (lvalue_node .name , lvalue_node .info , lvalue )
@@ -5453,6 +5476,7 @@ def visit_match_stmt(self, s: MatchStmt) -> None:
54535476 inferred_types = self .infer_variable_types_from_type_maps (type_maps )
54545477
54555478 # The second pass narrows down the types and type checks bodies.
5479+ unmatched_types : TypeMap = None
54565480 for p , g , b in zip (s .patterns , s .guards , s .bodies ):
54575481 current_subject_type = self .expr_checker .narrow_type_from_binder (
54585482 named_subject , subject_type
@@ -5509,6 +5533,11 @@ def visit_match_stmt(self, s: MatchStmt) -> None:
55095533 else :
55105534 self .accept (b )
55115535 self .push_type_map (else_map , from_assignment = False )
5536+ unmatched_types = else_map
5537+
5538+ if unmatched_types is not None :
5539+ for typ in list (unmatched_types .values ()):
5540+ self .msg .match_statement_inexhaustive_match (typ , s )
55125541
55135542 # This is needed due to a quirk in frame_context. Without it types will stay narrowed
55145543 # after the match.
@@ -7689,9 +7718,13 @@ def get_isinstance_type(self, expr: Expression) -> list[TypeRange] | None:
76897718 types : list [TypeRange ] = []
76907719 for typ in all_types :
76917720 if isinstance (typ , FunctionLike ) and typ .is_type_obj ():
7692- # Type variables may be present -- erase them, which is the best
7693- # we can do (outside disallowing them here).
7694- 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
76957728 types .append (TypeRange (erased_type , is_upper_bound = False ))
76967729 elif isinstance (typ , TypeType ):
76977730 # Type[A] means "any type that is a subtype of A" rather than "precisely type A"
0 commit comments