@@ -94,6 +94,12 @@ enum GroupedMoveError<'tcx> {
9494 } ,
9595}
9696
97+ struct PatternBindingInfo {
98+ pat_span : Span ,
99+ binding_spans : Vec < Span > ,
100+ has_mutable_by_value_binding : bool ,
101+ }
102+
97103impl < ' infcx , ' tcx > MirBorrowckCtxt < ' _ , ' infcx , ' tcx > {
98104 pub ( crate ) fn report_move_errors ( & mut self ) {
99105 let grouped_errors = self . group_move_errors ( ) ;
@@ -678,8 +684,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
678684 fn add_move_hints ( & self , error : GroupedMoveError < ' tcx > , err : & mut Diag < ' _ > , span : Span ) {
679685 match error {
680686 GroupedMoveError :: MovesFromPlace { mut binds_to, move_from, .. } => {
681- self . add_borrow_suggestions ( err, span, !binds_to. is_empty ( ) ) ;
687+ binds_to. sort ( ) ;
688+ binds_to. dedup ( ) ;
689+
682690 if binds_to. is_empty ( ) {
691+ self . add_borrow_suggestions ( err, span, false ) ;
683692 let place_ty = move_from. ty ( self . body , self . infcx . tcx ) . ty ;
684693 let place_desc = match self . describe_place ( move_from. as_ref ( ) ) {
685694 Some ( desc) => format ! ( "`{desc}`" ) ,
@@ -697,16 +706,30 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
697706 span,
698707 } ) ;
699708 } else {
700- binds_to. sort ( ) ;
701- binds_to. dedup ( ) ;
702-
703- self . add_move_error_details ( err, & binds_to, & [ ] ) ;
709+ let binding_info = self . pattern_binding_info ( & binds_to) ;
710+ let suggest_pattern_binding = binding_info. as_ref ( ) . is_some_and ( |info| {
711+ self . should_suggest_pattern_binding_instead ( span, info)
712+ } ) ;
713+ let desugar_spans = if suggest_pattern_binding {
714+ self . add_move_error_suggestions ( err, & binds_to)
715+ } else {
716+ if self . should_suggest_borrow_instead ( span, binding_info. as_ref ( ) ) {
717+ self . add_borrow_suggestions ( err, span, true ) ;
718+ }
719+ None
720+ } ;
721+ self . add_move_error_details (
722+ err,
723+ & binds_to,
724+ desugar_spans. as_deref ( ) . unwrap_or_default ( ) ,
725+ ) ;
704726 }
705727 }
706728 GroupedMoveError :: MovesFromValue { mut binds_to, .. } => {
707729 binds_to. sort ( ) ;
708730 binds_to. dedup ( ) ;
709- let desugar_spans = self . add_move_error_suggestions ( err, & binds_to) ;
731+ let desugar_spans =
732+ self . add_move_error_suggestions ( err, & binds_to) . unwrap_or_default ( ) ;
710733 self . add_move_error_details ( err, & binds_to, & desugar_spans) ;
711734 }
712735 // No binding. Nothing to suggest.
@@ -861,7 +884,102 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
861884 }
862885 }
863886
864- fn add_move_error_suggestions ( & self , err : & mut Diag < ' _ > , binds_to : & [ Local ] ) -> Vec < Span > {
887+ fn should_suggest_pattern_binding_instead (
888+ & self ,
889+ span : Span ,
890+ binding_info : & PatternBindingInfo ,
891+ ) -> bool {
892+ let Some ( expr) = self . find_expr ( span) else {
893+ return false ;
894+ } ;
895+
896+ let typeck_results = self . infcx . tcx . typeck ( self . mir_def_id ( ) ) ;
897+ let projection_qualifies = match expr. kind {
898+ hir:: ExprKind :: Field ( base, ..) => {
899+ !typeck_results. node_type_opt ( base. hir_id ) . is_some_and ( |base_ty| {
900+ binding_info. has_mutable_by_value_binding
901+ && matches ! ( base_ty. kind( ) , ty:: Ref ( _, _, hir:: Mutability :: Not ) )
902+ } )
903+ }
904+ hir:: ExprKind :: Index ( base, ..) => typeck_results
905+ . node_type_opt ( base. hir_id )
906+ . is_some_and ( |base_ty| match base_ty. kind ( ) {
907+ ty:: Ref ( _, _, hir:: Mutability :: Not ) | ty:: RawPtr ( ..) => false ,
908+ ty:: Ref ( _, _, hir:: Mutability :: Mut ) => {
909+ binding_info. has_mutable_by_value_binding
910+ }
911+ _ => true ,
912+ } ) ,
913+ _ => false ,
914+ } ;
915+ if !projection_qualifies {
916+ return false ;
917+ }
918+
919+ let is_single_binding = binding_info. binding_spans . len ( ) == 1
920+ && binding_info. binding_spans [ 0 ] == binding_info. pat_span ;
921+ !is_single_binding
922+ }
923+
924+ fn should_suggest_borrow_instead (
925+ & self ,
926+ span : Span ,
927+ binding_info : Option < & PatternBindingInfo > ,
928+ ) -> bool {
929+ if !binding_info. is_some_and ( |info| info. has_mutable_by_value_binding ) {
930+ return true ;
931+ }
932+
933+ let Some ( expr) = self . find_expr ( span) else {
934+ return true ;
935+ } ;
936+
937+ let Some ( base) = ( match expr. kind {
938+ hir:: ExprKind :: Field ( base, _) | hir:: ExprKind :: Index ( base, ..) => Some ( base) ,
939+ _ => None ,
940+ } ) else {
941+ return true ;
942+ } ;
943+
944+ !self
945+ . infcx
946+ . tcx
947+ . typeck ( self . mir_def_id ( ) )
948+ . node_type_opt ( base. hir_id )
949+ . is_some_and ( |base_ty| matches ! ( base_ty. kind( ) , ty:: Ref ( _, _, hir:: Mutability :: Not ) ) )
950+ }
951+
952+ fn pattern_binding_info ( & self , binds_to : & [ Local ] ) -> Option < PatternBindingInfo > {
953+ let mut pat_span = None ;
954+ let mut binding_spans = Vec :: new ( ) ;
955+ let mut has_mutable_by_value_binding = false ;
956+ for local in binds_to {
957+ let bind_to = & self . body . local_decls [ * local] ;
958+ if let LocalInfo :: User ( BindingForm :: Var ( VarBindingForm {
959+ pat_span : pat_sp,
960+ binding_mode,
961+ ..
962+ } ) ) = * bind_to. local_info ( )
963+ {
964+ pat_span = Some ( pat_sp) ;
965+ binding_spans. push ( bind_to. source_info . span ) ;
966+ has_mutable_by_value_binding |=
967+ matches ! ( binding_mode, hir:: BindingMode ( hir:: ByRef :: No , hir:: Mutability :: Mut ) ) ;
968+ }
969+ }
970+
971+ Some ( PatternBindingInfo {
972+ pat_span : pat_span?,
973+ binding_spans,
974+ has_mutable_by_value_binding,
975+ } )
976+ }
977+
978+ fn add_move_error_suggestions (
979+ & self ,
980+ err : & mut Diag < ' _ > ,
981+ binds_to : & [ Local ] ,
982+ ) -> Option < Vec < Span > > {
865983 /// A HIR visitor to associate each binding with a `&` or `&mut` that could be removed to
866984 /// make it bind by reference instead (if possible)
867985 struct BindingFinder < ' tcx > {
@@ -964,27 +1082,20 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
9641082 self . has_adjustments = parent_has_adjustments;
9651083 }
9661084 }
967- let mut pat_span = None ;
968- let mut binding_spans = Vec :: new ( ) ;
969- for local in binds_to {
970- let bind_to = & self . body . local_decls [ * local] ;
971- if let LocalInfo :: User ( BindingForm :: Var ( VarBindingForm { pat_span : pat_sp, .. } ) ) =
972- * bind_to. local_info ( )
973- {
974- pat_span = Some ( pat_sp) ;
975- binding_spans. push ( bind_to. source_info . span ) ;
976- }
977- }
978- let Some ( pat_span) = pat_span else { return Vec :: new ( ) } ;
1085+ let Some ( binding_info) = self . pattern_binding_info ( binds_to) else {
1086+ return None ;
1087+ } ;
9791088
9801089 let tcx = self . infcx . tcx ;
981- let Some ( body) = tcx. hir_maybe_body_owned_by ( self . mir_def_id ( ) ) else { return Vec :: new ( ) } ;
1090+ let Some ( body) = tcx. hir_maybe_body_owned_by ( self . mir_def_id ( ) ) else {
1091+ return None ;
1092+ } ;
9821093 let typeck_results = self . infcx . tcx . typeck ( self . mir_def_id ( ) ) ;
9831094 let mut finder = BindingFinder {
9841095 typeck_results,
9851096 tcx,
986- pat_span,
987- binding_spans,
1097+ pat_span : binding_info . pat_span ,
1098+ binding_spans : binding_info . binding_spans ,
9881099 found_pat : false ,
9891100 ref_pat : None ,
9901101 has_adjustments : false ,
@@ -1015,7 +1126,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
10151126 for ( span, msg, suggestion) in suggestions {
10161127 err. span_suggestion_verbose ( span, msg, suggestion, Applicability :: MachineApplicable ) ;
10171128 }
1018- finder. desugar_binding_spans
1129+
1130+ Some ( finder. desugar_binding_spans )
10191131 }
10201132
10211133 fn add_move_error_details (
0 commit comments