Skip to content

Commit 57b371a

Browse files
authored
Rollup merge of #106641 - chenyukang:yukang/fix-105761-segguest-this, r=estebank
Provide help on closures capturing self causing borrow checker errors Fixes #105761 r? ````@estebank````
2 parents c6e3a47 + eafbca9 commit 57b371a

File tree

5 files changed

+250
-5
lines changed

5 files changed

+250
-5
lines changed

compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs

+144-4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use rustc_errors::{
66
struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan,
77
};
88
use rustc_hir as hir;
9+
use rustc_hir::def::Res;
910
use rustc_hir::intravisit::{walk_block, walk_expr, Visitor};
1011
use rustc_hir::{AsyncGeneratorKind, GeneratorKind, LangItem};
1112
use rustc_infer::infer::TyCtxtInferExt;
@@ -20,7 +21,7 @@ use rustc_middle::ty::{self, suggest_constraining_type_params, PredicateKind, Ty
2021
use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex};
2122
use rustc_span::def_id::LocalDefId;
2223
use rustc_span::hygiene::DesugaringKind;
23-
use rustc_span::symbol::sym;
24+
use rustc_span::symbol::{kw, sym};
2425
use rustc_span::{BytePos, Span, Symbol};
2526
use rustc_trait_selection::infer::InferCtxtExt;
2627

@@ -29,6 +30,7 @@ use crate::borrowck_errors;
2930

3031
use crate::diagnostics::conflict_errors::StorageDeadOrDrop::LocalStorageDead;
3132
use crate::diagnostics::find_all_local_uses;
33+
use crate::diagnostics::mutability_errors::mut_borrow_of_mutable_ref;
3234
use 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
///

compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1094,7 +1094,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
10941094
}
10951095
}
10961096

1097-
fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option<Symbol>) -> bool {
1097+
pub fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option<Symbol>) -> bool {
10981098
debug!("local_info: {:?}, ty.kind(): {:?}", local_decl.local_info, local_decl.ty.kind());
10991099

11001100
match local_decl.local_info.as_deref() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//run-rustfix
2+
#![allow(unused)]
3+
4+
struct S;
5+
impl S {
6+
fn foo(&mut self) {
7+
let x = |this: &Self, v: i32| {
8+
this.bar();
9+
this.hel();
10+
};
11+
self.qux(); //~ ERROR cannot borrow `*self` as mutable because it is also borrowed as immutable
12+
x(self, 1);
13+
x(self, 3);
14+
}
15+
fn bar(&self) {}
16+
fn hel(&self) {}
17+
fn qux(&mut self) {}
18+
19+
fn hello(&mut self) {
20+
let y = |this: &Self| {
21+
this.bar();
22+
};
23+
self.qux(); //~ ERROR cannot borrow `*self` as mutable because it is also borrowed as immutable
24+
y(self);
25+
}
26+
}
27+
28+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//run-rustfix
2+
#![allow(unused)]
3+
4+
struct S;
5+
impl S {
6+
fn foo(&mut self) {
7+
let x = |v: i32| {
8+
self.bar();
9+
self.hel();
10+
};
11+
self.qux(); //~ ERROR cannot borrow `*self` as mutable because it is also borrowed as immutable
12+
x(1);
13+
x(3);
14+
}
15+
fn bar(&self) {}
16+
fn hel(&self) {}
17+
fn qux(&mut self) {}
18+
19+
fn hello(&mut self) {
20+
let y = || {
21+
self.bar();
22+
};
23+
self.qux(); //~ ERROR cannot borrow `*self` as mutable because it is also borrowed as immutable
24+
y();
25+
}
26+
}
27+
28+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
2+
--> $DIR/issue-105761-suggest-self-for-closure.rs:11:9
3+
|
4+
LL | let x = |v: i32| {
5+
| -------- immutable borrow occurs here
6+
LL | self.bar();
7+
| ---- first borrow occurs due to use of `self` in closure
8+
...
9+
LL | self.qux();
10+
| ^^^^^^^^^^ mutable borrow occurs here
11+
LL | x(1);
12+
| - immutable borrow later used here
13+
|
14+
help: try explicitly pass `&Self` into the Closure as an argument
15+
|
16+
LL ~ let x = |this: &Self, v: i32| {
17+
LL ~ this.bar();
18+
LL ~ this.hel();
19+
LL | };
20+
LL | self.qux();
21+
LL ~ x(self, 1);
22+
LL ~ x(self, 3);
23+
|
24+
25+
error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
26+
--> $DIR/issue-105761-suggest-self-for-closure.rs:23:9
27+
|
28+
LL | let y = || {
29+
| -- immutable borrow occurs here
30+
LL | self.bar();
31+
| ---- first borrow occurs due to use of `self` in closure
32+
LL | };
33+
LL | self.qux();
34+
| ^^^^^^^^^^ mutable borrow occurs here
35+
LL | y();
36+
| - immutable borrow later used here
37+
|
38+
help: try explicitly pass `&Self` into the Closure as an argument
39+
|
40+
LL ~ let y = |this: &Self| {
41+
LL ~ this.bar();
42+
LL | };
43+
LL | self.qux();
44+
LL ~ y(self);
45+
|
46+
47+
error: aborting due to 2 previous errors
48+
49+
For more information about this error, try `rustc --explain E0502`.

0 commit comments

Comments
 (0)