@@ -85,6 +85,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
85
85
86
86
self . annotate_expected_due_to_let_ty ( err, expr, error) ;
87
87
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
+ }
88
91
89
92
// FIXME(#73154): For now, we do leak check when coercing function
90
93
// pointers in typeck, instead of only during borrowck. This can lead
@@ -795,6 +798,98 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
795
798
}
796
799
}
797
800
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
+
798
893
fn annotate_alternative_method_deref (
799
894
& self ,
800
895
err : & mut Diag < ' _ > ,
0 commit comments