@@ -85,6 +85,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
8585
8686        self . annotate_expected_due_to_let_ty ( err,  expr,  error) ; 
8787        self . annotate_loop_expected_due_to_inference ( err,  expr,  error) ; 
88+         if  self . annotate_mut_binding_to_immutable_binding ( err,  expr,  error)  { 
89+             return ; 
90+         } 
8891
8992        // FIXME(#73154): For now, we do leak check when coercing function 
9093        // pointers in typeck, instead of only during borrowck. This can lead 
@@ -795,6 +798,98 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
795798        } 
796799    } 
797800
801+     /// Detect the following case 
802+      /// 
803+      /// ```text 
804+      /// fn change_object(mut a: &Ty) { 
805+      ///     let a = Ty::new(); 
806+      ///     b = a; 
807+      /// } 
808+      /// ``` 
809+      /// 
810+      /// where the user likely meant to modify the value behind there reference, use `a` as an out 
811+      /// parameter, instead of mutating the local binding. When encountering this we suggest: 
812+      /// 
813+      /// ```text 
814+      /// fn change_object(a: &'_ mut Ty) { 
815+      ///     let a = Ty::new(); 
816+      ///     *b = a; 
817+      /// } 
818+      /// ``` 
819+      fn  annotate_mut_binding_to_immutable_binding ( 
820+         & self , 
821+         err :  & mut  Diag < ' _ > , 
822+         expr :  & hir:: Expr < ' _ > , 
823+         error :  Option < TypeError < ' tcx > > , 
824+     )  -> bool  { 
825+         if  let  Some ( TypeError :: Sorts ( ExpectedFound  {  expected,  found } ) )  = error
826+             && let  ty:: Ref ( _,  inner,  hir:: Mutability :: Not )  = expected. kind ( ) 
827+ 
828+             // The difference between the expected and found values is one level of borrowing. 
829+             && self . can_eq ( self . param_env ,  * inner,  found) 
830+ 
831+             // We have an `ident = expr;` assignment. 
832+             && let  hir:: Node :: Expr ( hir:: Expr  {  kind :  hir:: ExprKind :: Assign ( lhs,  rhs,  _) ,  .. } )  =
833+                 self . tcx . parent_hir_node ( expr. hir_id ) 
834+             && rhs. hir_id  == expr. hir_id 
835+ 
836+             // We are assigning to some binding. 
837+             && let  hir:: ExprKind :: Path ( hir:: QPath :: Resolved ( 
838+                 None , 
839+                 hir:: Path  {  res :  hir:: def:: Res :: Local ( hir_id) ,  .. } , 
840+             ) )  = lhs. kind 
841+             && let  hir:: Node :: Pat ( pat)  = self . tcx . hir_node ( * hir_id) 
842+ 
843+             // The pattern we have is an fn argument. 
844+             && let  hir:: Node :: Param ( hir:: Param  {  ty_span,  .. } )  =
845+                 self . tcx . parent_hir_node ( pat. hir_id ) 
846+             && let  item = self . tcx . hir ( ) . get_parent_item ( pat. hir_id ) 
847+             && let  item = self . tcx . hir_owner_node ( item) 
848+             && let  Some ( fn_decl)  = item. fn_decl ( ) 
849+ 
850+             // We have a mutable binding in the argument. 
851+             && let  hir:: PatKind :: Binding ( hir:: BindingMode :: MUT ,  _hir_id,  ident,  _)  = pat. kind 
852+ 
853+             // Look for the type corresponding to the argument pattern we have in the argument list. 
854+             && let  Some ( ty_sugg)  = fn_decl
855+                 . inputs 
856+                 . iter ( ) 
857+                 . filter_map ( |ty| { 
858+                     if  ty. span  == * ty_span
859+                         && let  hir:: TyKind :: Ref ( lt,  x)  = ty. kind 
860+                     { 
861+                         // `&'name Ty` -> `&'name mut Ty` or `&Ty` -> `&mut Ty` 
862+                         Some ( ( 
863+                             x. ty . span . shrink_to_lo ( ) , 
864+                             format ! ( 
865+                                 "{}mut " , 
866+                                 if  lt. ident. span. lo( )  == lt. ident. span. hi( )  {  ""  }  else {  " "  } 
867+                             ) , 
868+                         ) ) 
869+                     }  else  { 
870+                         None 
871+                     } 
872+                 } ) 
873+                 . next ( ) 
874+         { 
875+             let  sugg = vec ! [ 
876+                 ty_sugg, 
877+                 ( pat. span. until( ident. span) ,  String :: new( ) ) , 
878+                 ( lhs. span. shrink_to_lo( ) ,  "*" . to_string( ) ) , 
879+             ] ; 
880+             // We suggest changing the argument from `mut ident: &Ty` to `ident: &'_ mut Ty` and the 
881+             // assignment from `ident = val;` to `*ident = val;`. 
882+             err. multipart_suggestion_verbose ( 
883+                 "you might have meant to mutate the pointed at value being passed in, instead of \  
884+                  changing the reference in the local binding", 
885+                 sugg, 
886+                 Applicability :: MaybeIncorrect , 
887+             ) ; 
888+             return  true ; 
889+         } 
890+         false 
891+     } 
892+ 
798893    fn  annotate_alternative_method_deref ( 
799894        & self , 
800895        err :  & mut  Diag < ' _ > , 
0 commit comments