1+ use crate :: diagnostics:: mutability_errors:: mut_borrow_of_mutable_ref;
12use either:: Either ;
3+ use hir:: Closure ;
24use rustc_const_eval:: util:: CallKind ;
35use rustc_data_structures:: captures:: Captures ;
46use rustc_data_structures:: fx:: FxHashSet ;
@@ -20,7 +22,7 @@ use rustc_middle::ty::{self, suggest_constraining_type_params, PredicateKind, Ty
2022use rustc_mir_dataflow:: move_paths:: { InitKind , MoveOutIndex , MovePathIndex } ;
2123use rustc_span:: def_id:: LocalDefId ;
2224use rustc_span:: hygiene:: DesugaringKind ;
23- use rustc_span:: symbol:: sym;
25+ use rustc_span:: symbol:: { kw , sym} ;
2426use rustc_span:: { BytePos , Span , Symbol } ;
2527use rustc_trait_selection:: infer:: InferCtxtExt ;
2628
@@ -39,6 +41,8 @@ use super::{
3941 DescribePlaceOpt , RegionName , RegionNameSource , UseSpans ,
4042} ;
4143
44+ use rustc_hir:: def:: Res ;
45+
4246#[ derive( Debug ) ]
4347struct MoveSite {
4448 /// Index of the "move out" that we found. The `MoveData` can
@@ -356,7 +360,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
356360 if let Some ( hir:: Node :: Item ( hir:: Item {
357361 kind : hir:: ItemKind :: Fn ( _, _, body_id) ,
358362 ..
359- } ) ) = hir. find ( hir . local_def_id_to_hir_id ( self . mir_def_id ( ) ) )
363+ } ) ) = hir. find ( self . mir_hir_id ( ) )
360364 && let Some ( hir:: Node :: Expr ( expr) ) = hir. find ( body_id. hir_id )
361365 {
362366 let place = & self . move_data . move_paths [ mpi] . place ;
@@ -948,7 +952,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
948952 }
949953 ( BorrowKind :: Mut { .. } , BorrowKind :: Shared ) => {
950954 first_borrow_desc = "immutable " ;
951- self . cannot_reborrow_already_borrowed (
955+ let mut err = self . cannot_reborrow_already_borrowed (
952956 span,
953957 & desc_place,
954958 & msg_place,
@@ -958,7 +962,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
958962 "immutable" ,
959963 & msg_borrow,
960964 None ,
961- )
965+ ) ;
966+ self . suggest_binding_for_closure_capture_self (
967+ & mut err,
968+ issued_borrow. borrowed_place ,
969+ & issued_spans,
970+ ) ;
971+ err
962972 }
963973
964974 ( BorrowKind :: Mut { .. } , BorrowKind :: Mut { .. } ) => {
@@ -1240,6 +1250,138 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
12401250 }
12411251 }
12421252
1253+ fn suggest_binding_for_closure_capture_self (
1254+ & self ,
1255+ err : & mut Diagnostic ,
1256+ borrowed_place : Place < ' tcx > ,
1257+ issued_spans : & UseSpans < ' tcx > ,
1258+ ) {
1259+ let UseSpans :: ClosureUse { capture_kind_span, .. } = issued_spans else { return } ;
1260+ let hir = self . infcx . tcx . hir ( ) ;
1261+
1262+ // check whether the borrowed place is capturing `self` by mut reference
1263+ let local = borrowed_place. local ;
1264+ let Some ( _) = self
1265+ . body
1266+ . local_decls
1267+ . get ( local)
1268+ . map ( |l| mut_borrow_of_mutable_ref ( l, self . local_names [ local] ) ) else { return } ;
1269+
1270+ struct ExpressionFinder < ' hir > {
1271+ capture_span : Span ,
1272+ closure_change_spans : Vec < Span > ,
1273+ closure_arg_span : Option < Span > ,
1274+ in_closure : bool ,
1275+ suggest_arg : String ,
1276+ hir : rustc_middle:: hir:: map:: Map < ' hir > ,
1277+ closure_local_id : Option < hir:: HirId > ,
1278+ closure_call_changes : Vec < ( Span , String ) > ,
1279+ }
1280+ impl < ' hir > Visitor < ' hir > for ExpressionFinder < ' hir > {
1281+ fn visit_expr ( & mut self , e : & ' hir hir:: Expr < ' hir > ) {
1282+ if e. span . contains ( self . capture_span ) {
1283+ if let hir:: ExprKind :: Closure ( & Closure {
1284+ movability : None ,
1285+ body,
1286+ fn_arg_span,
1287+ fn_decl : hir:: FnDecl { inputs, .. } ,
1288+ ..
1289+ } ) = e. kind &&
1290+ let Some ( hir:: Node :: Expr ( body ) ) = self . hir . find ( body. hir_id ) {
1291+ self . suggest_arg = "this: &Self" . to_string ( ) ;
1292+ if inputs. len ( ) > 0 {
1293+ self . suggest_arg . push_str ( ", " ) ;
1294+ }
1295+ self . in_closure = true ;
1296+ self . closure_arg_span = fn_arg_span;
1297+ self . visit_expr ( body) ;
1298+ self . in_closure = false ;
1299+ }
1300+ }
1301+ if let hir:: Expr { kind : hir:: ExprKind :: Path ( path) , .. } = e {
1302+ if let hir:: QPath :: Resolved ( _, hir:: Path { segments : [ seg] , ..} ) = path &&
1303+ seg. ident . name == kw:: SelfLower && self . in_closure {
1304+ self . closure_change_spans . push ( e. span ) ;
1305+ }
1306+ }
1307+ hir:: intravisit:: walk_expr ( self , e) ;
1308+ }
1309+
1310+ fn visit_local ( & mut self , local : & ' hir hir:: Local < ' hir > ) {
1311+ if let hir:: Pat { kind : hir:: PatKind :: Binding ( _, hir_id, _ident, _) , .. } = local. pat &&
1312+ let Some ( init) = local. init
1313+ {
1314+ if let hir:: Expr { kind : hir:: ExprKind :: Closure ( & Closure {
1315+ movability : None ,
1316+ ..
1317+ } ) , .. } = init &&
1318+ init. span . contains ( self . capture_span ) {
1319+ self . closure_local_id = Some ( * hir_id) ;
1320+ }
1321+ }
1322+ hir:: intravisit:: walk_local ( self , local) ;
1323+ }
1324+
1325+ fn visit_stmt ( & mut self , s : & ' hir hir:: Stmt < ' hir > ) {
1326+ if let hir:: StmtKind :: Semi ( e) = s. kind &&
1327+ let hir:: ExprKind :: Call ( hir:: Expr { kind : hir:: ExprKind :: Path ( path) , ..} , args) = e. kind &&
1328+ let hir:: QPath :: Resolved ( _, hir:: Path { segments : [ seg] , ..} ) = path &&
1329+ let Res :: Local ( hir_id) = seg. res &&
1330+ Some ( hir_id) == self . closure_local_id {
1331+ let mut arg_str = "self" . to_string ( ) ;
1332+ if args. len ( ) > 0 {
1333+ arg_str. push_str ( ", " ) ;
1334+ }
1335+ self . closure_call_changes . push ( ( seg. ident . 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+ if let Ok ( span) = sm. span_extend_while ( span, |c| c != '(' ) {
1373+ sugg. push ( ( sm. next_point ( span) . shrink_to_hi ( ) , suggest) ) ;
1374+ }
1375+ }
1376+
1377+ err. multipart_suggestion_verbose (
1378+ "try explicitly pass `&Self` into the Closure as an argument" ,
1379+ sugg,
1380+ Applicability :: MachineApplicable ,
1381+ ) ;
1382+ }
1383+ }
1384+
12431385 /// Returns the description of the root place for a conflicting borrow and the full
12441386 /// descriptions of the places that caused the conflict.
12451387 ///
0 commit comments