@@ -6,6 +6,7 @@ use rustc_errors::{
6
6
struct_span_err, Applicability , Diagnostic , DiagnosticBuilder , ErrorGuaranteed , MultiSpan ,
7
7
} ;
8
8
use rustc_hir as hir;
9
+ use rustc_hir:: def:: Res ;
9
10
use rustc_hir:: intravisit:: { walk_block, walk_expr, Visitor } ;
10
11
use rustc_hir:: { AsyncGeneratorKind , GeneratorKind , LangItem } ;
11
12
use rustc_infer:: infer:: TyCtxtInferExt ;
@@ -20,7 +21,7 @@ use rustc_middle::ty::{self, suggest_constraining_type_params, PredicateKind, Ty
20
21
use rustc_mir_dataflow:: move_paths:: { InitKind , MoveOutIndex , MovePathIndex } ;
21
22
use rustc_span:: def_id:: LocalDefId ;
22
23
use rustc_span:: hygiene:: DesugaringKind ;
23
- use rustc_span:: symbol:: sym;
24
+ use rustc_span:: symbol:: { kw , sym} ;
24
25
use rustc_span:: { BytePos , Span , Symbol } ;
25
26
use rustc_trait_selection:: infer:: InferCtxtExt ;
26
27
@@ -29,6 +30,7 @@ use crate::borrowck_errors;
29
30
30
31
use crate :: diagnostics:: conflict_errors:: StorageDeadOrDrop :: LocalStorageDead ;
31
32
use crate :: diagnostics:: find_all_local_uses;
33
+ use crate :: diagnostics:: mutability_errors:: mut_borrow_of_mutable_ref;
32
34
use crate :: {
33
35
borrow_set:: BorrowData , diagnostics:: Instance , prefixes:: IsPrefixOf ,
34
36
InitializationRequiringAction , MirBorrowckCtxt , PrefixSet , WriteKind ,
@@ -356,7 +358,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
356
358
if let Some ( hir:: Node :: Item ( hir:: Item {
357
359
kind : hir:: ItemKind :: Fn ( _, _, body_id) ,
358
360
..
359
- } ) ) = hir. find ( hir . local_def_id_to_hir_id ( self . mir_def_id ( ) ) )
361
+ } ) ) = hir. find ( self . mir_hir_id ( ) )
360
362
&& let Some ( hir:: Node :: Expr ( expr) ) = hir. find ( body_id. hir_id )
361
363
{
362
364
let place = & self . move_data . move_paths [ mpi] . place ;
@@ -948,7 +950,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
948
950
}
949
951
( BorrowKind :: Mut { .. } , BorrowKind :: Shared ) => {
950
952
first_borrow_desc = "immutable " ;
951
- self . cannot_reborrow_already_borrowed (
953
+ let mut err = self . cannot_reborrow_already_borrowed (
952
954
span,
953
955
& desc_place,
954
956
& msg_place,
@@ -958,7 +960,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
958
960
"immutable" ,
959
961
& msg_borrow,
960
962
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
962
970
}
963
971
964
972
( BorrowKind :: Mut { .. } , BorrowKind :: Mut { .. } ) => {
@@ -1240,6 +1248,138 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
1240
1248
}
1241
1249
}
1242
1250
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
+
1243
1383
/// Returns the description of the root place for a conflicting borrow and the full
1244
1384
/// descriptions of the places that caused the conflict.
1245
1385
///
0 commit comments