@@ -291,10 +291,12 @@ def __init__(self) -> None:
291291 UninhabitedType ,
292292 UnionType ,
293293 UnpackType ,
294+ extend_args_for_prefix_and_suffix ,
294295 find_unpack_in_list ,
295296 flatten_nested_unions ,
296297 get_proper_type ,
297298 get_proper_types ,
299+ get_variadic_item ,
298300 instance_cache ,
299301 is_literal_type ,
300302 is_named_instance ,
@@ -4389,6 +4391,36 @@ def flatten_lvalues(self, lvalues: list[Expression]) -> list[Expression]:
43894391 res .append (lv )
43904392 return res
43914393
4394+ def adjust_rvalue_type_if_possible (
4395+ self , rvalue_type : TupleType , lvalues : list [Lvalue ]
4396+ ) -> TupleType :
4397+ """Adjust type of rvalue to match the shape/structure of lvalues.
4398+
4399+ Currently, we only allow this if the rvalue type has contains *tuple[Any, ...].
4400+ """
4401+ right_variadic = get_variadic_item (rvalue_type )
4402+ if right_variadic is None :
4403+ return rvalue_type
4404+ right_unpack_index , right_item = right_variadic
4405+ if not isinstance (get_proper_type (right_item ), AnyType ):
4406+ return rvalue_type
4407+ left_star_index = next (
4408+ (i for i , lv in enumerate (lvalues ) if isinstance (lv , StarExpr )), None
4409+ )
4410+ if left_star_index is None :
4411+ extra = len (lvalues ) - len (rvalue_type .items ) + 1
4412+ if extra < 0 :
4413+ return rvalue_type
4414+ return rvalue_type .copy_modified (
4415+ items = rvalue_type .items [:right_unpack_index ]
4416+ + [right_item ] * extra
4417+ + rvalue_type .items [right_unpack_index + 1 :]
4418+ )
4419+ new_items = extend_args_for_prefix_and_suffix (
4420+ tuple (rvalue_type .items ), left_star_index , len (lvalues ) - left_star_index - 1
4421+ )
4422+ return rvalue_type .copy_modified (items = list (new_items ))
4423+
43924424 def check_multi_assignment_from_tuple (
43934425 self ,
43944426 lvalues : list [Lvalue ],
@@ -4399,6 +4431,9 @@ def check_multi_assignment_from_tuple(
43994431 infer_lvalue_type : bool = True ,
44004432 ) -> None :
44014433 rvalue_unpack = find_unpack_in_list (rvalue_type .items )
4434+ if rvalue_unpack is not None :
4435+ rvalue_type = self .adjust_rvalue_type_if_possible (rvalue_type , lvalues )
4436+ rvalue_unpack = find_unpack_in_list (rvalue_type .items )
44024437 if self .check_rvalue_count_in_assignment (
44034438 lvalues , len (rvalue_type .items ), context , rvalue_unpack = rvalue_unpack
44044439 ):
@@ -4440,8 +4475,14 @@ def check_multi_assignment_from_tuple(
44404475 if isinstance (reinferred_rvalue_type , TupleType ):
44414476 # This branch will usually be taken, but in some cases context can
44424477 # e.g. select a different overload
4478+ # TODO: reinferred tuple may be of a different (invalid) shape.
44434479 rvalue_type = reinferred_rvalue_type
44444480
4481+ # Reinferring the type can undo the shape adjustment, so do it again.
4482+ rvalue_unpack = find_unpack_in_list (rvalue_type .items )
4483+ if rvalue_unpack is not None :
4484+ rvalue_type = self .adjust_rvalue_type_if_possible (rvalue_type , lvalues )
4485+
44454486 left_rv_types , star_rv_types , right_rv_types = self .split_around_star (
44464487 rvalue_type .items , star_index , len (lvalues )
44474488 )
0 commit comments