@@ -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