@@ -6,6 +6,7 @@ use rustc_errors::{
66 struct_span_err, Applicability , Diagnostic , DiagnosticBuilder , ErrorGuaranteed , MultiSpan ,
77} ;
88use rustc_hir as hir;
9+ use rustc_hir:: def:: Res ;
910use rustc_hir:: intravisit:: { walk_block, walk_expr, Visitor } ;
1011use rustc_hir:: { AsyncGeneratorKind , GeneratorKind , LangItem } ;
1112use rustc_infer:: infer:: TyCtxtInferExt ;
@@ -20,7 +21,7 @@ use rustc_middle::ty::{self, suggest_constraining_type_params, PredicateKind, Ty
2021use rustc_mir_dataflow:: move_paths:: { InitKind , MoveOutIndex , MovePathIndex } ;
2122use rustc_span:: def_id:: LocalDefId ;
2223use rustc_span:: hygiene:: DesugaringKind ;
23- use rustc_span:: symbol:: sym;
24+ use rustc_span:: symbol:: { kw , sym} ;
2425use rustc_span:: { BytePos , Span , Symbol } ;
2526use rustc_trait_selection:: infer:: InferCtxtExt ;
2627
@@ -29,6 +30,7 @@ use crate::borrowck_errors;
2930
3031use crate :: diagnostics:: conflict_errors:: StorageDeadOrDrop :: LocalStorageDead ;
3132use crate :: diagnostics:: find_all_local_uses;
33+ use crate :: diagnostics:: mutability_errors:: mut_borrow_of_mutable_ref;
3234use crate :: {
3335 borrow_set:: BorrowData , diagnostics:: Instance , prefixes:: IsPrefixOf ,
3436 InitializationRequiringAction , MirBorrowckCtxt , PrefixSet , WriteKind ,
@@ -356,7 +358,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
356358 if let Some ( hir:: Node :: Item ( hir:: Item {
357359 kind : hir:: ItemKind :: Fn ( _, _, body_id) ,
358360 ..
359- } ) ) = hir. find ( hir . local_def_id_to_hir_id ( self . mir_def_id ( ) ) )
361+ } ) ) = hir. find ( self . mir_hir_id ( ) )
360362 && let Some ( hir:: Node :: Expr ( expr) ) = hir. find ( body_id. hir_id )
361363 {
362364 let place = & self . move_data . move_paths [ mpi] . place ;
@@ -948,7 +950,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
948950 }
949951 ( BorrowKind :: Mut { .. } , BorrowKind :: Shared ) => {
950952 first_borrow_desc = "immutable " ;
951- self . cannot_reborrow_already_borrowed (
953+ let mut err = self . cannot_reborrow_already_borrowed (
952954 span,
953955 & desc_place,
954956 & msg_place,
@@ -958,7 +960,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
958960 "immutable" ,
959961 & msg_borrow,
960962 None ,
961- )
963+ ) ;
964+ self . suggest_binding_for_closure_capture_self (
965+ & mut err,
966+ issued_borrow. borrowed_place ,
967+ & issued_spans,
968+ ) ;
969+ err
962970 }
963971
964972 ( BorrowKind :: Mut { .. } , BorrowKind :: Mut { .. } ) => {
@@ -1240,6 +1248,138 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
12401248 }
12411249 }
12421250
1251+ fn suggest_binding_for_closure_capture_self (
1252+ & self ,
1253+ err : & mut Diagnostic ,
1254+ borrowed_place : Place < ' tcx > ,
1255+ issued_spans : & UseSpans < ' tcx > ,
1256+ ) {
1257+ let UseSpans :: ClosureUse { capture_kind_span, .. } = issued_spans else { return } ;
1258+ let hir = self . infcx . tcx . hir ( ) ;
1259+
1260+ // check whether the borrowed place is capturing `self` by mut reference
1261+ let local = borrowed_place. local ;
1262+ let Some ( _) = self
1263+ . body
1264+ . local_decls
1265+ . get ( local)
1266+ . map ( |l| mut_borrow_of_mutable_ref ( l, self . local_names [ local] ) ) else { return } ;
1267+
1268+ struct ExpressionFinder < ' hir > {
1269+ capture_span : Span ,
1270+ closure_change_spans : Vec < Span > ,
1271+ closure_arg_span : Option < Span > ,
1272+ in_closure : bool ,
1273+ suggest_arg : String ,
1274+ hir : rustc_middle:: hir:: map:: Map < ' hir > ,
1275+ closure_local_id : Option < hir:: HirId > ,
1276+ closure_call_changes : Vec < ( Span , String ) > ,
1277+ }
1278+ impl < ' hir > Visitor < ' hir > for ExpressionFinder < ' hir > {
1279+ fn visit_expr ( & mut self , e : & ' hir hir:: Expr < ' hir > ) {
1280+ if e. span . contains ( self . capture_span ) {
1281+ if let hir:: ExprKind :: Closure ( & hir:: Closure {
1282+ movability : None ,
1283+ body,
1284+ fn_arg_span,
1285+ fn_decl : hir:: FnDecl { inputs, .. } ,
1286+ ..
1287+ } ) = e. kind &&
1288+ let Some ( hir:: Node :: Expr ( body ) ) = self . hir . find ( body. hir_id ) {
1289+ self . suggest_arg = "this: &Self" . to_string ( ) ;
1290+ if inputs. len ( ) > 0 {
1291+ self . suggest_arg . push_str ( ", " ) ;
1292+ }
1293+ self . in_closure = true ;
1294+ self . closure_arg_span = fn_arg_span;
1295+ self . visit_expr ( body) ;
1296+ self . in_closure = false ;
1297+ }
1298+ }
1299+ if let hir:: Expr { kind : hir:: ExprKind :: Path ( path) , .. } = e {
1300+ if let hir:: QPath :: Resolved ( _, hir:: Path { segments : [ seg] , ..} ) = path &&
1301+ seg. ident . name == kw:: SelfLower && self . in_closure {
1302+ self . closure_change_spans . push ( e. span ) ;
1303+ }
1304+ }
1305+ hir:: intravisit:: walk_expr ( self , e) ;
1306+ }
1307+
1308+ fn visit_local ( & mut self , local : & ' hir hir:: Local < ' hir > ) {
1309+ if let hir:: Pat { kind : hir:: PatKind :: Binding ( _, hir_id, _ident, _) , .. } = local. pat &&
1310+ let Some ( init) = local. init
1311+ {
1312+ if let hir:: Expr { kind : hir:: ExprKind :: Closure ( & hir:: Closure {
1313+ movability : None ,
1314+ ..
1315+ } ) , .. } = init &&
1316+ init. span . contains ( self . capture_span ) {
1317+ self . closure_local_id = Some ( * hir_id) ;
1318+ }
1319+ }
1320+ hir:: intravisit:: walk_local ( self , local) ;
1321+ }
1322+
1323+ fn visit_stmt ( & mut self , s : & ' hir hir:: Stmt < ' hir > ) {
1324+ if let hir:: StmtKind :: Semi ( e) = s. kind &&
1325+ let hir:: ExprKind :: Call ( hir:: Expr { kind : hir:: ExprKind :: Path ( path) , ..} , args) = e. kind &&
1326+ let hir:: QPath :: Resolved ( _, hir:: Path { segments : [ seg] , ..} ) = path &&
1327+ let Res :: Local ( hir_id) = seg. res &&
1328+ Some ( hir_id) == self . closure_local_id {
1329+ let ( span, arg_str) = if args. len ( ) > 0 {
1330+ ( args[ 0 ] . span . shrink_to_lo ( ) , "self, " . to_string ( ) )
1331+ } else {
1332+ let span = e. span . trim_start ( seg. ident . span ) . unwrap_or ( e. span ) ;
1333+ ( span, "(self)" . to_string ( ) )
1334+ } ;
1335+ self . closure_call_changes . push ( ( span, arg_str) ) ;
1336+ }
1337+ hir:: intravisit:: walk_stmt ( self , s) ;
1338+ }
1339+ }
1340+
1341+ if let Some ( hir:: Node :: ImplItem (
1342+ hir:: ImplItem { kind : hir:: ImplItemKind :: Fn ( _fn_sig, body_id) , .. }
1343+ ) ) = hir. find ( self . mir_hir_id ( ) ) &&
1344+ let Some ( hir:: Node :: Expr ( expr) ) = hir. find ( body_id. hir_id ) {
1345+ let mut finder = ExpressionFinder {
1346+ capture_span : * capture_kind_span,
1347+ closure_change_spans : vec ! [ ] ,
1348+ closure_arg_span : None ,
1349+ in_closure : false ,
1350+ suggest_arg : String :: new ( ) ,
1351+ closure_local_id : None ,
1352+ closure_call_changes : vec ! [ ] ,
1353+ hir,
1354+ } ;
1355+ finder. visit_expr ( expr) ;
1356+
1357+ if finder. closure_change_spans . is_empty ( ) || finder. closure_call_changes . is_empty ( ) {
1358+ return ;
1359+ }
1360+
1361+ let mut sugg = vec ! [ ] ;
1362+ let sm = self . infcx . tcx . sess . source_map ( ) ;
1363+
1364+ if let Some ( span) = finder. closure_arg_span {
1365+ sugg. push ( ( sm. next_point ( span. shrink_to_lo ( ) ) . shrink_to_hi ( ) , finder. suggest_arg ) ) ;
1366+ }
1367+ for span in finder. closure_change_spans {
1368+ sugg. push ( ( span, "this" . to_string ( ) ) ) ;
1369+ }
1370+
1371+ for ( span, suggest) in finder. closure_call_changes {
1372+ sugg. push ( ( span, suggest) ) ;
1373+ }
1374+
1375+ err. multipart_suggestion_verbose (
1376+ "try explicitly pass `&Self` into the Closure as an argument" ,
1377+ sugg,
1378+ Applicability :: MachineApplicable ,
1379+ ) ;
1380+ }
1381+ }
1382+
12431383 /// Returns the description of the root place for a conflicting borrow and the full
12441384 /// descriptions of the places that caused the conflict.
12451385 ///
0 commit comments