@@ -540,7 +540,7 @@ pub fn opt_loan_path<'tcx>(cmt: &mc::cmt<'tcx>) -> Option<Rc<LoanPath<'tcx>>> {
540
540
// Errors
541
541
542
542
// Errors that can occur
543
- #[ derive( PartialEq ) ]
543
+ #[ derive( Debug , PartialEq ) ]
544
544
pub enum bckerr_code < ' tcx > {
545
545
err_mutbl,
546
546
/// superscope, subscope, loan cause
@@ -550,7 +550,7 @@ pub enum bckerr_code<'tcx> {
550
550
551
551
// Combination of an error code and the categorization of the expression
552
552
// that caused it
553
- #[ derive( PartialEq ) ]
553
+ #[ derive( Debug , PartialEq ) ]
554
554
pub struct BckError < ' tcx > {
555
555
span : Span ,
556
556
cause : AliasableViolationKind ,
@@ -601,12 +601,8 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
601
601
_ => { }
602
602
}
603
603
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) ;
610
606
db. emit ( ) ;
611
607
}
612
608
@@ -771,8 +767,11 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
771
767
self . tcx . sess . span_err_with_code ( s, msg, code) ;
772
768
}
773
769
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 {
776
775
err_mutbl => {
777
776
let descr = match err. cmt . note {
778
777
mc:: NoteClosureEnv ( _) | mc:: NoteUpvarRef ( _) => {
@@ -783,6 +782,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
783
782
format ! ( "{} {}" ,
784
783
err. cmt. mutbl. to_user_str( ) ,
785
784
self . cmt_to_string( & err. cmt) )
785
+
786
786
}
787
787
Some ( lp) => {
788
788
format ! ( "{} {} `{}`" ,
@@ -807,6 +807,19 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
807
807
BorrowViolation ( euv:: AutoUnsafe ) |
808
808
BorrowViolation ( euv:: ForLoop ) |
809
809
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
+
810
823
format ! ( "cannot borrow {} as mutable" , descr)
811
824
}
812
825
BorrowViolation ( euv:: ClosureInvocation ) => {
@@ -830,13 +843,20 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
830
843
its contents can be safely reborrowed",
831
844
descr)
832
845
}
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) ;
833
851
}
852
+ db
834
853
}
835
854
836
855
pub fn report_aliasability_violation ( & self ,
837
856
span : Span ,
838
857
kind : AliasableViolationKind ,
839
- cause : mc:: AliasableReason ) {
858
+ cause : mc:: AliasableReason ,
859
+ cmt : mc:: cmt < ' tcx > ) {
840
860
let mut is_closure = false ;
841
861
let prefix = match kind {
842
862
MutabilityViolation => {
@@ -903,6 +923,9 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
903
923
self . tcx. sess, span, E0389 ,
904
924
"{} in a `&` reference" , prefix) ;
905
925
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
+ }
906
929
e
907
930
}
908
931
} ;
@@ -913,6 +936,55 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
913
936
err. emit ( ) ;
914
937
}
915
938
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
+
916
988
fn report_out_of_scope_escaping_closure_capture ( & self ,
917
989
err : & BckError < ' tcx > ,
918
990
capture_span : Span )
@@ -961,8 +1033,8 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
961
1033
}
962
1034
}
963
1035
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 ( ) ;
966
1038
match err. code {
967
1039
err_mutbl => self . note_and_explain_mutbl_error ( db, & err, & error_span) ,
968
1040
err_out_of_scope( super_scope, sub_scope, cause) => {
@@ -1114,41 +1186,13 @@ before rustc 1.16, this temporary lived longer - see issue #39283 \
1114
1186
}
1115
1187
}
1116
1188
_ => {
1117
- if let Categorization :: Deref ( ref inner_cmt, ..) = err. cmt . cat {
1118
- if let Categorization :: Local ( local_id) = inner_cmt. cat {
1119
- let parent = self . tcx . hir . get_parent_node ( local_id) ;
1120
-
1121
- if let Some ( fn_like) = FnLikeNode :: from_node ( self . tcx . hir . get ( parent) ) {
1122
- if let Some ( i) = self . tcx . hir . body ( fn_like. body ( ) ) . arguments . iter ( )
1123
- . position ( |arg| arg. pat . id == local_id) {
1124
- let arg_ty = & fn_like. decl ( ) . inputs [ i] ;
1125
- if let hir:: TyRptr (
1126
- opt_lifetime,
1127
- hir:: MutTy { mutbl : hir:: Mutability :: MutImmutable , ref ty} ) =
1128
- arg_ty. node {
1129
- if let Some ( lifetime) = opt_lifetime {
1130
- if let Ok ( snippet) = self . tcx . sess . codemap ( )
1131
- . span_to_snippet ( ty. span ) {
1132
- if let Ok ( lifetime_snippet) = self . tcx . sess . codemap ( )
1133
- . span_to_snippet ( lifetime. span ) {
1134
- db. span_label ( arg_ty. span ,
1135
- & format ! ( "use `&{} mut {}` \
1136
- here to make mutable",
1137
- lifetime_snippet,
1138
- snippet) ) ;
1139
- }
1140
- }
1141
- }
1142
- else if let Ok ( snippet) = self . tcx . sess . codemap ( )
1143
- . span_to_snippet ( arg_ty. span ) {
1144
- if snippet. starts_with ( "&" ) {
1145
- db. span_label ( arg_ty. span ,
1146
- & format ! ( "use `{}` here to make mutable" ,
1147
- snippet. replace( "&" , "&mut " ) ) ) ;
1148
- }
1149
- }
1150
- }
1151
- }
1189
+ if let Categorization :: Deref ( ..) = err. cmt . cat {
1190
+ db. span_label ( * error_span, & "cannot borrow as mutable" ) ;
1191
+ if let Some ( local_id) = err. cmt . get_arg_if_immutable ( & self . tcx . hir ) {
1192
+ self . immutable_argument_should_be_mut ( local_id, db) ;
1193
+ } else if let Categorization :: Deref ( ref inner_cmt, ..) = err. cmt . cat {
1194
+ if let Categorization :: Local ( local_id) = inner_cmt. cat {
1195
+ self . immutable_argument_should_be_mut ( local_id, db) ;
1152
1196
}
1153
1197
}
1154
1198
} else if let Categorization :: Local ( local_id) = err. cmt . cat {
0 commit comments