@@ -540,7 +540,7 @@ pub fn opt_loan_path<'tcx>(cmt: &mc::cmt<'tcx>) -> Option<Rc<LoanPath<'tcx>>> {
540540// Errors
541541
542542// Errors that can occur
543- #[ derive( PartialEq ) ]
543+ #[ derive( Debug , PartialEq ) ]
544544pub enum bckerr_code < ' tcx > {
545545 err_mutbl,
546546 /// superscope, subscope, loan cause
@@ -550,7 +550,7 @@ pub enum bckerr_code<'tcx> {
550550
551551// Combination of an error code and the categorization of the expression
552552// that caused it
553- #[ derive( PartialEq ) ]
553+ #[ derive( Debug , PartialEq ) ]
554554pub struct BckError < ' tcx > {
555555 span : Span ,
556556 cause : AliasableViolationKind ,
@@ -601,12 +601,8 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
601601 _ => { }
602602 }
603603
604- // General fallback.
605- let span = err. span . clone ( ) ;
606- let mut db = self . struct_span_err (
607- err. span ,
608- & self . bckerr_to_string ( & err) ) ;
609- self . note_and_explain_bckerr ( & mut db, err, span) ;
604+ let mut db = self . bckerr_to_diag ( & err) ;
605+ self . note_and_explain_bckerr ( & mut db, err) ;
610606 db. emit ( ) ;
611607 }
612608
@@ -771,8 +767,11 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
771767 self . tcx . sess . span_err_with_code ( s, msg, code) ;
772768 }
773769
774- pub fn bckerr_to_string ( & self , err : & BckError < ' tcx > ) -> String {
775- match err. code {
770+ pub fn bckerr_to_diag ( & self , err : & BckError < ' tcx > ) -> DiagnosticBuilder < ' a > {
771+ let span = err. span . clone ( ) ;
772+ let mut immutable_field = None ;
773+
774+ let msg = & match err. code {
776775 err_mutbl => {
777776 let descr = match err. cmt . note {
778777 mc:: NoteClosureEnv ( _) | mc:: NoteUpvarRef ( _) => {
@@ -783,6 +782,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
783782 format ! ( "{} {}" ,
784783 err. cmt. mutbl. to_user_str( ) ,
785784 self . cmt_to_string( & err. cmt) )
785+
786786 }
787787 Some ( lp) => {
788788 format ! ( "{} {} `{}`" ,
@@ -807,6 +807,19 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
807807 BorrowViolation ( euv:: AutoUnsafe ) |
808808 BorrowViolation ( euv:: ForLoop ) |
809809 BorrowViolation ( euv:: MatchDiscriminant ) => {
810+ // Check for this field's definition to see if it is an immutable reference
811+ // and suggest making it mutable if that is the case.
812+ immutable_field = err. cmt . get_field_name ( )
813+ . and_then ( |name| err. cmt . get_field ( name) )
814+ . and_then ( |did| self . tcx . hir . as_local_node_id ( did) )
815+ . and_then ( |nid| {
816+ if let hir_map:: Node :: NodeField ( ref field) = self . tcx . hir . get ( nid) {
817+ return self . suggest_mut_for_immutable ( & field. ty )
818+ . map ( |msg| ( self . tcx . hir . span ( nid) , msg) ) ;
819+ }
820+ None
821+ } ) ;
822+
810823 format ! ( "cannot borrow {} as mutable" , descr)
811824 }
812825 BorrowViolation ( euv:: ClosureInvocation ) => {
@@ -830,13 +843,20 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
830843 its contents can be safely reborrowed",
831844 descr)
832845 }
846+ } ;
847+
848+ let mut db = self . struct_span_err ( span, msg) ;
849+ if let Some ( ( span, msg) ) = immutable_field {
850+ db. span_label ( span, & msg) ;
833851 }
852+ db
834853 }
835854
836855 pub fn report_aliasability_violation ( & self ,
837856 span : Span ,
838857 kind : AliasableViolationKind ,
839- cause : mc:: AliasableReason ) {
858+ cause : mc:: AliasableReason ,
859+ cmt : mc:: cmt < ' tcx > ) {
840860 let mut is_closure = false ;
841861 let prefix = match kind {
842862 MutabilityViolation => {
@@ -903,6 +923,9 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
903923 self . tcx. sess, span, E0389 ,
904924 "{} in a `&` reference" , prefix) ;
905925 e. span_label ( span, & "assignment into an immutable reference" ) ;
926+ if let Some ( nid) = cmt. get_arg_if_immutable ( & self . tcx . hir ) {
927+ self . immutable_argument_should_be_mut ( nid, & mut e) ;
928+ }
906929 e
907930 }
908931 } ;
@@ -913,6 +936,55 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
913936 err. emit ( ) ;
914937 }
915938
939+ /// Given a type, if it is an immutable reference, return a suggestion to make it mutable
940+ fn suggest_mut_for_immutable ( & self , pty : & hir:: Ty ) -> Option < String > {
941+ // Check wether the argument is an immutable reference
942+ if let hir:: TyRptr ( opt_lifetime, hir:: MutTy {
943+ mutbl : hir:: Mutability :: MutImmutable ,
944+ ref ty
945+ } ) = pty. node {
946+ // Account for existing lifetimes when generating the message
947+ if let Some ( lifetime) = opt_lifetime {
948+ if let Ok ( snippet) = self . tcx . sess . codemap ( ) . span_to_snippet ( ty. span ) {
949+ if let Ok ( lifetime_snippet) = self . tcx . sess . codemap ( )
950+ . span_to_snippet ( lifetime. span ) {
951+ return Some ( format ! ( "use `&{} mut {}` here to make mutable" ,
952+ lifetime_snippet,
953+ snippet) ) ;
954+ }
955+ }
956+ } else if let Ok ( snippet) = self . tcx . sess . codemap ( ) . span_to_snippet ( pty. span ) {
957+ if snippet. starts_with ( "&" ) {
958+ return Some ( format ! ( "use `{}` here to make mutable" ,
959+ snippet. replace( "&" , "&mut " ) ) ) ;
960+ }
961+ } else {
962+ bug ! ( "couldn't find a snippet for span: {:?}" , pty. span) ;
963+ }
964+ }
965+ None
966+ }
967+
968+ fn immutable_argument_should_be_mut ( & self , nid : ast:: NodeId , db : & mut DiagnosticBuilder ) {
969+ let parent = self . tcx . hir . get_parent_node ( nid) ;
970+ let parent_node = self . tcx . hir . get ( parent) ;
971+
972+ // The parent node is like a fn
973+ if let Some ( fn_like) = FnLikeNode :: from_node ( parent_node) {
974+ // `nid`'s parent's `Body`
975+ let fn_body = self . tcx . hir . body ( fn_like. body ( ) ) ;
976+ // Get the position of `nid` in the arguments list
977+ let arg_pos = fn_body. arguments . iter ( ) . position ( |arg| arg. pat . id == nid) ;
978+ if let Some ( i) = arg_pos {
979+ // The argument's `Ty`
980+ let arg_ty = & fn_like. decl ( ) . inputs [ i] ;
981+ if let Some ( msg) = self . suggest_mut_for_immutable ( & arg_ty) {
982+ db. span_label ( arg_ty. span , & msg) ;
983+ }
984+ }
985+ }
986+ }
987+
916988 fn report_out_of_scope_escaping_closure_capture ( & self ,
917989 err : & BckError < ' tcx > ,
918990 capture_span : Span )
@@ -961,8 +1033,8 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
9611033 }
9621034 }
9631035
964- pub fn note_and_explain_bckerr ( & self , db : & mut DiagnosticBuilder , err : BckError < ' tcx > ,
965- error_span : Span ) {
1036+ pub fn note_and_explain_bckerr ( & self , db : & mut DiagnosticBuilder , err : BckError < ' tcx > ) {
1037+ let error_span = err . span . clone ( ) ;
9661038 match err. code {
9671039 err_mutbl => self . note_and_explain_mutbl_error ( db, & err, & error_span) ,
9681040 err_out_of_scope( super_scope, sub_scope, cause) => {
@@ -1103,41 +1175,13 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
11031175 }
11041176 }
11051177 _ => {
1106- if let Categorization :: Deref ( ref inner_cmt, ..) = err. cmt . cat {
1107- if let Categorization :: Local ( local_id) = inner_cmt. cat {
1108- let parent = self . tcx . hir . get_parent_node ( local_id) ;
1109-
1110- if let Some ( fn_like) = FnLikeNode :: from_node ( self . tcx . hir . get ( parent) ) {
1111- if let Some ( i) = self . tcx . hir . body ( fn_like. body ( ) ) . arguments . iter ( )
1112- . position ( |arg| arg. pat . id == local_id) {
1113- let arg_ty = & fn_like. decl ( ) . inputs [ i] ;
1114- if let hir:: TyRptr (
1115- opt_lifetime,
1116- hir:: MutTy { mutbl : hir:: Mutability :: MutImmutable , ref ty} ) =
1117- arg_ty. node {
1118- if let Some ( lifetime) = opt_lifetime {
1119- if let Ok ( snippet) = self . tcx . sess . codemap ( )
1120- . span_to_snippet ( ty. span ) {
1121- if let Ok ( lifetime_snippet) = self . tcx . sess . codemap ( )
1122- . span_to_snippet ( lifetime. span ) {
1123- db. span_label ( arg_ty. span ,
1124- & format ! ( "use `&{} mut {}` \
1125- here to make mutable",
1126- lifetime_snippet,
1127- snippet) ) ;
1128- }
1129- }
1130- }
1131- else if let Ok ( snippet) = self . tcx . sess . codemap ( )
1132- . span_to_snippet ( arg_ty. span ) {
1133- if snippet. starts_with ( "&" ) {
1134- db. span_label ( arg_ty. span ,
1135- & format ! ( "use `{}` here to make mutable" ,
1136- snippet. replace( "&" , "&mut " ) ) ) ;
1137- }
1138- }
1139- }
1140- }
1178+ if let Categorization :: Deref ( ..) = err. cmt . cat {
1179+ db. span_label ( * error_span, & "cannot borrow as mutable" ) ;
1180+ if let Some ( local_id) = err. cmt . get_arg_if_immutable ( & self . tcx . hir ) {
1181+ self . immutable_argument_should_be_mut ( local_id, db) ;
1182+ } else if let Categorization :: Deref ( ref inner_cmt, ..) = err. cmt . cat {
1183+ if let Categorization :: Local ( local_id) = inner_cmt. cat {
1184+ self . immutable_argument_should_be_mut ( local_id, db) ;
11411185 }
11421186 }
11431187 } else if let Categorization :: Local ( local_id) = err. cmt . cat {
0 commit comments