@@ -218,6 +218,33 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
218
218
debug ! ( "report_mutability_error: act={:?}, acted_on={:?}" , act, acted_on) ;
219
219
220
220
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_label ( span, message) ;
245
+ }
246
+ } ,
247
+
221
248
// Suggest removing a `&mut` from the use of a mutable reference.
222
249
Place :: Local ( local)
223
250
if {
@@ -592,3 +619,56 @@ fn suggest_ampmut<'cx, 'gcx, 'tcx>(
592
619
fn is_closure_or_generator ( ty : ty:: Ty ) -> bool {
593
620
ty. is_closure ( ) || ty. is_generator ( )
594
621
}
622
+
623
+ /// Add a suggestion to a struct definition given a field access to a local.
624
+ /// This function expects the local to be a reference to a struct in order to produce a suggestion.
625
+ ///
626
+ /// ```text
627
+ /// LL | s: &'a String
628
+ /// | ---------- use `&'a mut String` here to make mutable
629
+ /// ```
630
+ fn annotate_struct_field (
631
+ tcx : TyCtxt < ' cx , ' gcx , ' tcx > ,
632
+ ty : ty:: Ty < ' tcx > ,
633
+ field : & mir:: Field ,
634
+ ) -> Option < ( Span , String ) > {
635
+ // Expect our local to be a reference to a struct of some kind.
636
+ if let ty:: TyKind :: Ref ( _, ty, _) = ty. sty {
637
+ if let ty:: TyKind :: Adt ( def, _) = ty. sty {
638
+ let field = def. all_fields ( ) . nth ( field. index ( ) ) ?;
639
+ let span = tcx. def_span ( field. did ) ;
640
+
641
+ // Use the HIR types to construct the diagnostic message.
642
+ let node_id = tcx. hir . as_local_node_id ( field. did ) ?;
643
+ let node = tcx. hir . find ( node_id) ?;
644
+ // Now we're dealing with the actual struct that we're going to suggest a change to,
645
+ // we can expect a field that is an immutable reference to a type.
646
+ if let hir:: Node :: Field ( field) = node {
647
+ if let hir:: TyKind :: Rptr ( lifetime, hir:: MutTy {
648
+ mutbl : hir:: Mutability :: MutImmutable ,
649
+ ref ty
650
+ } ) = field. ty . node {
651
+ // Get the snippets in two parts - the named lifetime (if there is one) and
652
+ // type being referenced, that way we can reconstruct the snippet without loss
653
+ // of detail.
654
+ let type_snippet = tcx. sess . source_map ( ) . span_to_snippet ( ty. span ) . ok ( ) ?;
655
+ let lifetime_snippet = if !lifetime. is_elided ( ) {
656
+ format ! ( "{} " , tcx. sess. source_map( ) . span_to_snippet( lifetime. span) . ok( ) ?)
657
+ } else {
658
+ String :: new ( )
659
+ } ;
660
+
661
+ return Some ( (
662
+ span,
663
+ format ! (
664
+ "use `&{}mut {}` here to make mutable" ,
665
+ lifetime_snippet, & * type_snippet,
666
+ ) ,
667
+ ) ) ;
668
+ }
669
+ }
670
+ }
671
+ }
672
+
673
+ None
674
+ }
0 commit comments