Skip to content

Commit 825f334

Browse files
Rollup merge of rust-lang#88543 - m-ou-se:closure-migration-macro-block-fragment, r=estebank
Improve closure dummy capture suggestion in macros. Fixes some cases of rust-lang#88440 Fixes https://crater-reports.s3.amazonaws.com/pr-87190-3/try%23a7a572ce3edd6d476191fbfe92c9c1986e009b34/reg/rcodec-1.0.1/log.txt
2 parents 5362856 + 7d18052 commit 825f334

File tree

4 files changed

+112
-9
lines changed

4 files changed

+112
-9
lines changed

compiler/rustc_typeck/src/check/upvar.rs

+26-9
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ use rustc_middle::ty::{
4747
};
4848
use rustc_session::lint;
4949
use rustc_span::sym;
50-
use rustc_span::{BytePos, MultiSpan, Pos, Span, Symbol, DUMMY_SP};
50+
use rustc_span::{BytePos, MultiSpan, Pos, Span, Symbol};
5151
use rustc_trait_selection::infer::InferCtxtExt;
5252

5353
use rustc_data_structures::stable_map::FxHashMap;
@@ -680,15 +680,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
680680
migrated_variables_concat
681681
);
682682

683-
// If the body was entirely expanded from a macro
684-
// invocation, i.e. the body is not contained inside the
685-
// closure span, then we walk up the expansion until we
686-
// find the span before the expansion.
687-
let closure_body_span = self.tcx.hir().span(body_id.hir_id)
688-
.find_ancestor_inside(closure_span)
689-
.unwrap_or(DUMMY_SP);
683+
let mut closure_body_span = {
684+
// If the body was entirely expanded from a macro
685+
// invocation, i.e. the body is not contained inside the
686+
// closure span, then we walk up the expansion until we
687+
// find the span before the expansion.
688+
let s = self.tcx.hir().span(body_id.hir_id);
689+
s.find_ancestor_inside(closure_span).unwrap_or(s)
690+
};
691+
692+
if let Ok(mut s) = self.tcx.sess.source_map().span_to_snippet(closure_body_span) {
693+
if s.starts_with('$') {
694+
// Looks like a macro fragment. Try to find the real block.
695+
if let Some(hir::Node::Expr(&hir::Expr {
696+
kind: hir::ExprKind::Block(block, ..), ..
697+
})) = self.tcx.hir().find(body_id.hir_id) {
698+
// If the body is a block (with `{..}`), we use the span of that block.
699+
// E.g. with a `|| $body` expanded from a `m!({ .. })`, we use `{ .. }`, and not `$body`.
700+
// Since we know it's a block, we know we can insert the `let _ = ..` without
701+
// breaking the macro syntax.
702+
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(block.span) {
703+
closure_body_span = block.span;
704+
s = snippet;
705+
}
706+
}
707+
}
690708

691-
if let Ok(s) = self.tcx.sess.source_map().span_to_snippet(closure_body_span) {
692709
let mut lines = s.lines();
693710
let line1 = lines.next().unwrap_or_default();
694711

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// run-rustfix
2+
// edition:2018
3+
// check-pass
4+
#![warn(rust_2021_compatibility)]
5+
6+
macro_rules! m {
7+
(@ $body:expr) => {{
8+
let f = || $body;
9+
//~^ WARNING: drop order
10+
f();
11+
}};
12+
($body:block) => {{
13+
m!(@ $body);
14+
}};
15+
}
16+
17+
fn main() {
18+
let a = (1.to_string(), 2.to_string());
19+
m!({
20+
let _ = &a;
21+
//~^ HELP: add a dummy
22+
let x = a.0;
23+
println!("{}", x);
24+
});
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// run-rustfix
2+
// edition:2018
3+
// check-pass
4+
#![warn(rust_2021_compatibility)]
5+
6+
macro_rules! m {
7+
(@ $body:expr) => {{
8+
let f = || $body;
9+
//~^ WARNING: drop order
10+
f();
11+
}};
12+
($body:block) => {{
13+
m!(@ $body);
14+
}};
15+
}
16+
17+
fn main() {
18+
let a = (1.to_string(), 2.to_string());
19+
m!({
20+
//~^ HELP: add a dummy
21+
let x = a.0;
22+
println!("{}", x);
23+
});
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
warning: changes to closure capture in Rust 2021 will affect drop order
2+
--> $DIR/closure-body-macro-fragment.rs:8:17
3+
|
4+
LL | let f = || $body;
5+
| _________________^
6+
LL | |
7+
LL | | f();
8+
LL | | }};
9+
| | - in Rust 2018, `a` is dropped here, but in Rust 2021, only `a.0` will be dropped here as part of the closure
10+
LL | | ($body:block) => {{
11+
LL | | m!(@ $body);
12+
| |__________________^
13+
...
14+
LL | / m!({
15+
LL | |
16+
LL | | let x = a.0;
17+
| | --- in Rust 2018, this closure captures all of `a`, but in Rust 2021, it will only capture `a.0`
18+
LL | | println!("{}", x);
19+
LL | | });
20+
| |_______- in this macro invocation
21+
|
22+
note: the lint level is defined here
23+
--> $DIR/closure-body-macro-fragment.rs:4:9
24+
|
25+
LL | #![warn(rust_2021_compatibility)]
26+
| ^^^^^^^^^^^^^^^^^^^^^^^
27+
= note: `#[warn(rust_2021_incompatible_closure_captures)]` implied by `#[warn(rust_2021_compatibility)]`
28+
= note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
29+
= note: this warning originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
30+
help: add a dummy let to cause `a` to be fully captured
31+
|
32+
LL ~ m!({
33+
LL + let _ = &a;
34+
|
35+
36+
warning: 1 warning emitted
37+

0 commit comments

Comments
 (0)