@@ -218,6 +218,38 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
218218        debug ! ( "report_mutability_error: act={:?}, acted_on={:?}" ,  act,  acted_on) ; 
219219
220220        match  the_place_err { 
221+             // Suggest making an existing shared borrow in a struct definition a mutable borrow. 
222+             // 
223+             // This is applicable when we have a deref of a field access to a deref of a local - 
224+             // something like `*((*_1).0`. The local that we get will be a reference to the 
225+             // struct we've got a field access of (it must be a reference since there's a deref 
226+             // after the field access). 
227+             Place :: Projection ( box Projection  { 
228+                 base :  Place :: Projection ( box Projection  { 
229+                     base :  Place :: Projection ( box Projection  { 
230+                         base, 
231+                         elem :  ProjectionElem :: Deref , 
232+                     } ) , 
233+                     elem :  ProjectionElem :: Field ( field,  _) , 
234+                 } ) , 
235+                 elem :  ProjectionElem :: Deref , 
236+             } )  => { 
237+                 err. span_label ( span,  format ! ( "cannot {ACT}" ,  ACT  = act) ) ; 
238+ 
239+                 if  let  Some ( ( span,  message) )  = annotate_struct_field ( 
240+                     self . infcx . tcx , 
241+                     base. ty ( self . mir ,  self . infcx . tcx ) . to_ty ( self . infcx . tcx ) , 
242+                     field, 
243+                 )  { 
244+                     err. span_suggestion_with_applicability ( 
245+                         span, 
246+                         "consider changing this to be mutable" , 
247+                         message, 
248+                         Applicability :: MaybeIncorrect , 
249+                     ) ; 
250+                 } 
251+             } , 
252+ 
221253            // Suggest removing a `&mut` from the use of a mutable reference. 
222254            Place :: Local ( local) 
223255                if  { 
@@ -592,3 +624,54 @@ fn suggest_ampmut<'cx, 'gcx, 'tcx>(
592624fn  is_closure_or_generator ( ty :  ty:: Ty )  -> bool  { 
593625    ty. is_closure ( )  || ty. is_generator ( ) 
594626} 
627+ 
628+ /// Add a suggestion to a struct definition given a field access to a local. 
629+ /// This function expects the local to be a reference to a struct in order to produce a suggestion. 
630+ /// 
631+ /// ```text 
632+ /// LL |     s: &'a String 
633+ ///    |        ---------- use `&'a mut String` here to make mutable 
634+ /// ``` 
635+ fn  annotate_struct_field ( 
636+     tcx :  TyCtxt < ' cx ,  ' gcx ,  ' tcx > , 
637+     ty :  ty:: Ty < ' tcx > , 
638+     field :  & mir:: Field , 
639+ )  -> Option < ( Span ,  String ) >  { 
640+     // Expect our local to be a reference to a struct of some kind. 
641+     if  let  ty:: TyKind :: Ref ( _,  ty,  _)  = ty. sty  { 
642+         if  let  ty:: TyKind :: Adt ( def,  _)  = ty. sty  { 
643+             let  field = def. all_fields ( ) . nth ( field. index ( ) ) ?; 
644+             // Use the HIR types to construct the diagnostic message. 
645+             let  node_id = tcx. hir . as_local_node_id ( field. did ) ?; 
646+             let  node = tcx. hir . find ( node_id) ?; 
647+             // Now we're dealing with the actual struct that we're going to suggest a change to, 
648+             // we can expect a field that is an immutable reference to a type. 
649+             if  let  hir:: Node :: Field ( field)  = node { 
650+                 if  let  hir:: TyKind :: Rptr ( lifetime,  hir:: MutTy  { 
651+                     mutbl :  hir:: Mutability :: MutImmutable , 
652+                     ref  ty
653+                 } )  = field. ty . node  { 
654+                     // Get the snippets in two parts - the named lifetime (if there is one) and 
655+                     // type being referenced, that way we can reconstruct the snippet without loss 
656+                     // of detail. 
657+                     let  type_snippet = tcx. sess . source_map ( ) . span_to_snippet ( ty. span ) . ok ( ) ?; 
658+                     let  lifetime_snippet = if  !lifetime. is_elided ( )  { 
659+                         format ! ( "{} " ,  tcx. sess. source_map( ) . span_to_snippet( lifetime. span) . ok( ) ?) 
660+                     }  else  { 
661+                         String :: new ( ) 
662+                     } ; 
663+ 
664+                     return  Some ( ( 
665+                         field. ty . span , 
666+                         format ! ( 
667+                             "&{}mut {}" , 
668+                             lifetime_snippet,  & * type_snippet, 
669+                         ) , 
670+                     ) ) ; 
671+                 } 
672+             } 
673+         } 
674+     } 
675+ 
676+     None 
677+ } 
0 commit comments