1
+ use crate :: diagnostics:: mutability_errors:: mut_borrow_of_mutable_ref;
1
2
use either:: Either ;
3
+ use hir:: Closure ;
2
4
use rustc_const_eval:: util:: CallKind ;
3
5
use rustc_data_structures:: captures:: Captures ;
4
6
use rustc_data_structures:: fx:: FxHashSet ;
@@ -20,7 +22,7 @@ use rustc_middle::ty::{self, suggest_constraining_type_params, PredicateKind, Ty
20
22
use rustc_mir_dataflow:: move_paths:: { InitKind , MoveOutIndex , MovePathIndex } ;
21
23
use rustc_span:: def_id:: LocalDefId ;
22
24
use rustc_span:: hygiene:: DesugaringKind ;
23
- use rustc_span:: symbol:: sym;
25
+ use rustc_span:: symbol:: { kw , sym} ;
24
26
use rustc_span:: { BytePos , Span , Symbol } ;
25
27
use rustc_trait_selection:: infer:: InferCtxtExt ;
26
28
@@ -39,6 +41,8 @@ use super::{
39
41
DescribePlaceOpt , RegionName , RegionNameSource , UseSpans ,
40
42
} ;
41
43
44
+ use rustc_hir:: def:: Res ;
45
+
42
46
#[ derive( Debug ) ]
43
47
struct MoveSite {
44
48
/// Index of the "move out" that we found. The `MoveData` can
@@ -356,7 +360,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
356
360
if let Some ( hir:: Node :: Item ( hir:: Item {
357
361
kind : hir:: ItemKind :: Fn ( _, _, body_id) ,
358
362
..
359
- } ) ) = hir. find ( hir . local_def_id_to_hir_id ( self . mir_def_id ( ) ) )
363
+ } ) ) = hir. find ( self . mir_hir_id ( ) )
360
364
&& let Some ( hir:: Node :: Expr ( expr) ) = hir. find ( body_id. hir_id )
361
365
{
362
366
let place = & self . move_data . move_paths [ mpi] . place ;
@@ -948,7 +952,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
948
952
}
949
953
( BorrowKind :: Mut { .. } , BorrowKind :: Shared ) => {
950
954
first_borrow_desc = "immutable " ;
951
- self . cannot_reborrow_already_borrowed (
955
+ let mut err = self . cannot_reborrow_already_borrowed (
952
956
span,
953
957
& desc_place,
954
958
& msg_place,
@@ -958,7 +962,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
958
962
"immutable" ,
959
963
& msg_borrow,
960
964
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
962
972
}
963
973
964
974
( BorrowKind :: Mut { .. } , BorrowKind :: Mut { .. } ) => {
@@ -1240,6 +1250,138 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
1240
1250
}
1241
1251
}
1242
1252
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
+
1243
1385
/// Returns the description of the root place for a conflicting borrow and the full
1244
1386
/// descriptions of the places that caused the conflict.
1245
1387
///
0 commit comments