@@ -218,6 +218,38 @@ 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_suggestion_with_applicability (
245
+ span,
246
+ "consider changing this to be mutable" ,
247
+ message,
248
+ Applicability :: MaybeIncorrect ,
249
+ ) ;
250
+ }
251
+ } ,
252
+
221
253
// Suggest removing a `&mut` from the use of a mutable reference.
222
254
Place :: Local ( local)
223
255
if {
@@ -592,3 +624,54 @@ fn suggest_ampmut<'cx, 'gcx, 'tcx>(
592
624
fn is_closure_or_generator ( ty : ty:: Ty ) -> bool {
593
625
ty. is_closure ( ) || ty. is_generator ( )
594
626
}
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