Skip to content

Commit 2900c1a

Browse files
committed
Add note pointing to where a closure and it's captured variables are dropped
1 parent 0e8e89d commit 2900c1a

20 files changed

+373
-30
lines changed

compiler/rustc_typeck/src/check/upvar.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,15 +511,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
511511
.as_str(),
512512
);
513513
for (var_hir_id, diagnostics_info) in need_migrations.iter() {
514+
let mut captured_names = format!("");
514515
for (captured_hir_id, captured_name) in diagnostics_info.iter() {
515516
if let Some(captured_hir_id) = captured_hir_id {
516517
let cause_span = self.tcx.hir().span(*captured_hir_id);
517518
diagnostics_builder.span_label(cause_span, format!("in Rust 2018, closure captures all of `{}`, but in Rust 2021, it only captures `{}`",
518519
self.tcx.hir().name(*var_hir_id),
519520
captured_name,
520521
));
522+
if captured_names == "" {
523+
captured_names = format!("`{}`", captured_name);
524+
} else {
525+
captured_names = format!("{}, `{}`", captured_names, captured_name);
526+
}
521527
}
522528
}
529+
530+
if reasons.contains("drop order") {
531+
let drop_location_span = drop_location_span(self.tcx, &closure_hir_id);
532+
533+
diagnostics_builder.span_label(drop_location_span, format!("in Rust 2018, `{}` would be dropped here, but in Rust 2021, only {} would be dropped here alongside the closure",
534+
self.tcx.hir().name(*var_hir_id),
535+
captured_names,
536+
));
537+
}
523538
}
524539
diagnostics_builder.note("for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>");
525540
let closure_body_span = self.tcx.hir().span(body_id.hir_id);
@@ -1350,6 +1365,31 @@ fn apply_capture_kind_on_capture_ty(
13501365
}
13511366
}
13521367

1368+
/// Returns the Span of where the value with the provided HirId would be dropped
1369+
fn drop_location_span(tcx: TyCtxt<'tcx>, hir_id: &hir::HirId) -> Span {
1370+
let owner_id = tcx.hir().get_enclosing_scope(*hir_id).unwrap();
1371+
1372+
let owner_node = tcx.hir().get(owner_id);
1373+
match owner_node {
1374+
hir::Node::Item(item) => match item.kind {
1375+
hir::ItemKind::Fn(_, _, owner_id) => {
1376+
let owner_span = tcx.hir().span(owner_id.hir_id);
1377+
tcx.sess.source_map().end_point(owner_span)
1378+
}
1379+
_ => {
1380+
bug!("Drop location span error: need to handle more ItemKind {:?}", item.kind);
1381+
}
1382+
},
1383+
hir::Node::Block(block) => {
1384+
let owner_span = tcx.hir().span(block.hir_id);
1385+
tcx.sess.source_map().end_point(owner_span)
1386+
}
1387+
_ => {
1388+
bug!("Drop location span error: need to handle more Node {:?}", owner_node);
1389+
}
1390+
}
1391+
}
1392+
13531393
struct InferBorrowKind<'a, 'tcx> {
13541394
fcx: &'a FnCtxt<'a, 'tcx>,
13551395

src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.fixed

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// run-rustfix
22
#![deny(rust_2021_incompatible_closure_captures)]
3+
//~^ NOTE: the lint level is defined here
34

45
use std::thread;
56

@@ -12,8 +13,10 @@ fn test_send_trait() {
1213
let fptr = SendPointer(&mut f as *mut i32);
1314
thread::spawn(move || { let _ = &fptr; unsafe {
1415
//~^ ERROR: `Send` closure trait implementation
16+
//~| NOTE: for more information, see
1517
//~| HELP: add a dummy let to cause `fptr` to be fully captured
1618
*fptr.0 = 20;
19+
//~^ NOTE: in Rust 2018, closure captures all of `fptr`, but in Rust 2021, it only captures `fptr.0`
1720
} });
1821
}
1922

@@ -29,8 +32,10 @@ fn test_sync_trait() {
2932
let fptr = SyncPointer(f);
3033
thread::spawn(move || { let _ = &fptr; unsafe {
3134
//~^ ERROR: `Sync`, `Send` closure trait implementation
35+
//~| NOTE: for more information, see
3236
//~| HELP: add a dummy let to cause `fptr` to be fully captured
3337
*fptr.0.0 = 20;
38+
//~^ NOTE: in Rust 2018, closure captures all of `fptr`, but in Rust 2021, it only captures `fptr.0.0`
3439
} });
3540
}
3641

@@ -50,15 +55,18 @@ fn test_clone_trait() {
5055
let f = U(S(String::from("Hello World")), T(0));
5156
let c = || { let _ = &f;
5257
//~^ ERROR: `Clone` closure trait implementation, and drop order
58+
//~| NOTE: for more information, see
5359
//~| HELP: add a dummy let to cause `f` to be fully captured
5460
let f_1 = f.1;
61+
//~^ NOTE: in Rust 2018, closure captures all of `f`, but in Rust 2021, it only captures `f.1`
5562
println!("{:?}", f_1.0);
5663
};
5764

5865
let c_clone = c.clone();
5966

6067
c_clone();
6168
}
69+
//~^ NOTE: in Rust 2018, `f` would be dropped here, but in Rust 2021, only `f.1` would be dropped here alongside the closure
6270

6371
fn main() {
6472
test_send_trait();

src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// run-rustfix
22
#![deny(rust_2021_incompatible_closure_captures)]
3+
//~^ NOTE: the lint level is defined here
34

45
use std::thread;
56

@@ -12,8 +13,10 @@ fn test_send_trait() {
1213
let fptr = SendPointer(&mut f as *mut i32);
1314
thread::spawn(move || unsafe {
1415
//~^ ERROR: `Send` closure trait implementation
16+
//~| NOTE: for more information, see
1517
//~| HELP: add a dummy let to cause `fptr` to be fully captured
1618
*fptr.0 = 20;
19+
//~^ NOTE: in Rust 2018, closure captures all of `fptr`, but in Rust 2021, it only captures `fptr.0`
1720
});
1821
}
1922

@@ -29,8 +32,10 @@ fn test_sync_trait() {
2932
let fptr = SyncPointer(f);
3033
thread::spawn(move || unsafe {
3134
//~^ ERROR: `Sync`, `Send` closure trait implementation
35+
//~| NOTE: for more information, see
3236
//~| HELP: add a dummy let to cause `fptr` to be fully captured
3337
*fptr.0.0 = 20;
38+
//~^ NOTE: in Rust 2018, closure captures all of `fptr`, but in Rust 2021, it only captures `fptr.0.0`
3439
});
3540
}
3641

@@ -50,15 +55,18 @@ fn test_clone_trait() {
5055
let f = U(S(String::from("Hello World")), T(0));
5156
let c = || {
5257
//~^ ERROR: `Clone` closure trait implementation, and drop order
58+
//~| NOTE: for more information, see
5359
//~| HELP: add a dummy let to cause `f` to be fully captured
5460
let f_1 = f.1;
61+
//~^ NOTE: in Rust 2018, closure captures all of `f`, but in Rust 2021, it only captures `f.1`
5562
println!("{:?}", f_1.0);
5663
};
5764

5865
let c_clone = c.clone();
5966

6067
c_clone();
6168
}
69+
//~^ NOTE: in Rust 2018, `f` would be dropped here, but in Rust 2021, only `f.1` would be dropped here alongside the closure
6270

6371
fn main() {
6472
test_send_trait();
Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
error: changes to closure capture in Rust 2021 will affect `Send` closure trait implementation
2-
--> $DIR/auto_traits.rs:13:19
2+
--> $DIR/auto_traits.rs:14:19
33
|
44
LL | thread::spawn(move || unsafe {
55
| ___________________^
66
LL | |
77
LL | |
8+
LL | |
89
LL | | *fptr.0 = 20;
910
| | ------- in Rust 2018, closure captures all of `fptr`, but in Rust 2021, it only captures `fptr.0`
11+
LL | |
1012
LL | | });
1113
| |_____^
1214
|
@@ -21,19 +23,22 @@ help: add a dummy let to cause `fptr` to be fully captured
2123
LL | thread::spawn(move || { let _ = &fptr; unsafe {
2224
LL |
2325
LL |
26+
LL |
2427
LL | *fptr.0 = 20;
25-
LL | } });
26-
|
28+
LL |
29+
...
2730

2831
error: changes to closure capture in Rust 2021 will affect `Sync`, `Send` closure trait implementation
29-
--> $DIR/auto_traits.rs:30:19
32+
--> $DIR/auto_traits.rs:33:19
3033
|
3134
LL | thread::spawn(move || unsafe {
3235
| ___________________^
3336
LL | |
3437
LL | |
38+
LL | |
3539
LL | | *fptr.0.0 = 20;
3640
| | --------- in Rust 2018, closure captures all of `fptr`, but in Rust 2021, it only captures `fptr.0.0`
41+
LL | |
3742
LL | | });
3843
| |_____^
3944
|
@@ -43,33 +48,39 @@ help: add a dummy let to cause `fptr` to be fully captured
4348
LL | thread::spawn(move || { let _ = &fptr; unsafe {
4449
LL |
4550
LL |
51+
LL |
4652
LL | *fptr.0.0 = 20;
47-
LL | } });
48-
|
53+
LL |
54+
...
4955

5056
error: changes to closure capture in Rust 2021 will affect `Clone` closure trait implementation, and drop order
51-
--> $DIR/auto_traits.rs:51:13
57+
--> $DIR/auto_traits.rs:56:13
5258
|
5359
LL | let c = || {
5460
| _____________^
5561
LL | |
5662
LL | |
63+
LL | |
5764
LL | | let f_1 = f.1;
5865
| | --- in Rust 2018, closure captures all of `f`, but in Rust 2021, it only captures `f.1`
66+
LL | |
5967
LL | | println!("{:?}", f_1.0);
6068
LL | | };
6169
| |_____^
70+
...
71+
LL | }
72+
| - in Rust 2018, `f` would be dropped here, but in Rust 2021, only `f.1` would be dropped here alongside the closure
6273
|
6374
= note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
6475
help: add a dummy let to cause `f` to be fully captured
6576
|
6677
LL | let c = || { let _ = &f;
6778
LL |
6879
LL |
80+
LL |
6981
LL | let f_1 = f.1;
70-
LL | println!("{:?}", f_1.0);
71-
LL | };
72-
|
82+
LL |
83+
...
7384

7485
error: aborting due to 3 previous errors
7586

src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.fixed

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ fn test1_all_need_migration() {
2727

2828
c();
2929
}
30+
//~^ in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure
31+
//~| in Rust 2018, `t1` would be dropped here, but in Rust 2021, only `t1.0` would be dropped here alongside the closure
32+
//~| in Rust 2018, `t2` would be dropped here, but in Rust 2021, only `t2.0` would be dropped here alongside the closure
3033

3134
// String implements drop and therefore should be migrated.
3235
// But in this test cases, `t2` is completely captured and when it is dropped won't be affected
@@ -48,6 +51,8 @@ fn test2_only_precise_paths_need_migration() {
4851

4952
c();
5053
}
54+
//~^ in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure
55+
//~| in Rust 2018, `t1` would be dropped here, but in Rust 2021, only `t1.0` would be dropped here alongside the closure
5156

5257
// If a variable would've not been captured by value then it would've not been
5358
// dropped with the closure and therefore doesn't need migration.
@@ -65,6 +70,7 @@ fn test3_only_by_value_need_migration() {
6570

6671
c();
6772
}
73+
//~^ in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure
6874

6975
// Copy types get copied into the closure instead of move. Therefore we don't need to
7076
// migrate then as their drop order isn't tied to the closure.
@@ -85,6 +91,7 @@ fn test4_only_non_copy_types_need_migration() {
8591

8692
c();
8793
}
94+
//~^ in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure
8895

8996
fn test5_only_drop_types_need_migration() {
9097
struct S(i32, i32);
@@ -105,6 +112,7 @@ fn test5_only_drop_types_need_migration() {
105112

106113
c();
107114
}
115+
//~^ in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure
108116

109117
// Since we are using a move closure here, both `t` and `t1` get moved
110118
// even though they are being used by ref inside the closure.
@@ -122,6 +130,8 @@ fn test6_move_closures_non_copy_types_might_need_migration() {
122130

123131
c();
124132
}
133+
//~^ in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.1` would be dropped here alongside the closure
134+
//~| in Rust 2018, `t1` would be dropped here, but in Rust 2021, only `t1.1` would be dropped here alongside the closure
125135

126136
// Test migration analysis in case of Drop + Non Drop aggregates.
127137
// Note we need migration here only because the non-copy (because Drop type) is captured,
@@ -139,6 +149,7 @@ fn test7_drop_non_drop_aggregate_need_migration() {
139149

140150
c();
141151
}
152+
//~^ in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure
142153

143154
fn main() {
144155
test1_all_need_migration();

src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ fn test1_all_need_migration() {
2727

2828
c();
2929
}
30+
//~^ in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure
31+
//~| in Rust 2018, `t1` would be dropped here, but in Rust 2021, only `t1.0` would be dropped here alongside the closure
32+
//~| in Rust 2018, `t2` would be dropped here, but in Rust 2021, only `t2.0` would be dropped here alongside the closure
3033

3134
// String implements drop and therefore should be migrated.
3235
// But in this test cases, `t2` is completely captured and when it is dropped won't be affected
@@ -48,6 +51,8 @@ fn test2_only_precise_paths_need_migration() {
4851

4952
c();
5053
}
54+
//~^ in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure
55+
//~| in Rust 2018, `t1` would be dropped here, but in Rust 2021, only `t1.0` would be dropped here alongside the closure
5156

5257
// If a variable would've not been captured by value then it would've not been
5358
// dropped with the closure and therefore doesn't need migration.
@@ -65,6 +70,7 @@ fn test3_only_by_value_need_migration() {
6570

6671
c();
6772
}
73+
//~^ in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure
6874

6975
// Copy types get copied into the closure instead of move. Therefore we don't need to
7076
// migrate then as their drop order isn't tied to the closure.
@@ -85,6 +91,7 @@ fn test4_only_non_copy_types_need_migration() {
8591

8692
c();
8793
}
94+
//~^ in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure
8895

8996
fn test5_only_drop_types_need_migration() {
9097
struct S(i32, i32);
@@ -105,6 +112,7 @@ fn test5_only_drop_types_need_migration() {
105112

106113
c();
107114
}
115+
//~^ in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure
108116

109117
// Since we are using a move closure here, both `t` and `t1` get moved
110118
// even though they are being used by ref inside the closure.
@@ -122,6 +130,8 @@ fn test6_move_closures_non_copy_types_might_need_migration() {
122130

123131
c();
124132
}
133+
//~^ in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.1` would be dropped here alongside the closure
134+
//~| in Rust 2018, `t1` would be dropped here, but in Rust 2021, only `t1.1` would be dropped here alongside the closure
125135

126136
// Test migration analysis in case of Drop + Non Drop aggregates.
127137
// Note we need migration here only because the non-copy (because Drop type) is captured,
@@ -139,6 +149,7 @@ fn test7_drop_non_drop_aggregate_need_migration() {
139149

140150
c();
141151
}
152+
//~^ in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure
142153

143154
fn main() {
144155
test1_all_need_migration();

0 commit comments

Comments
 (0)