Rollup merge of #88543 - m-ou-se:closure-migration-macro-block-fragment, r=estebank
Improve closure dummy capture suggestion in macros. Fixes some cases of https://github.com/rust-lang/rust/issues/88440 Fixes https://crater-reports.s3.amazonaws.com/pr-87190-3/try%23a7a572ce3edd6d476191fbfe92c9c1986e009b34/reg/rcodec-1.0.1/log.txt
This commit is contained in:
commit
ffbce26e24
4 changed files with 112 additions and 9 deletions
|
@ -47,7 +47,7 @@ use rustc_middle::ty::{
|
|||
};
|
||||
use rustc_session::lint;
|
||||
use rustc_span::sym;
|
||||
use rustc_span::{BytePos, MultiSpan, Pos, Span, Symbol, DUMMY_SP};
|
||||
use rustc_span::{BytePos, MultiSpan, Pos, Span, Symbol};
|
||||
use rustc_trait_selection::infer::InferCtxtExt;
|
||||
|
||||
use rustc_data_structures::stable_map::FxHashMap;
|
||||
|
@ -680,15 +680,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
migrated_variables_concat
|
||||
);
|
||||
|
||||
// If the body was entirely expanded from a macro
|
||||
// invocation, i.e. the body is not contained inside the
|
||||
// closure span, then we walk up the expansion until we
|
||||
// find the span before the expansion.
|
||||
let closure_body_span = self.tcx.hir().span(body_id.hir_id)
|
||||
.find_ancestor_inside(closure_span)
|
||||
.unwrap_or(DUMMY_SP);
|
||||
let mut closure_body_span = {
|
||||
// If the body was entirely expanded from a macro
|
||||
// invocation, i.e. the body is not contained inside the
|
||||
// closure span, then we walk up the expansion until we
|
||||
// find the span before the expansion.
|
||||
let s = self.tcx.hir().span(body_id.hir_id);
|
||||
s.find_ancestor_inside(closure_span).unwrap_or(s)
|
||||
};
|
||||
|
||||
if let Ok(mut s) = self.tcx.sess.source_map().span_to_snippet(closure_body_span) {
|
||||
if s.starts_with('$') {
|
||||
// Looks like a macro fragment. Try to find the real block.
|
||||
if let Some(hir::Node::Expr(&hir::Expr {
|
||||
kind: hir::ExprKind::Block(block, ..), ..
|
||||
})) = self.tcx.hir().find(body_id.hir_id) {
|
||||
// If the body is a block (with `{..}`), we use the span of that block.
|
||||
// E.g. with a `|| $body` expanded from a `m!({ .. })`, we use `{ .. }`, and not `$body`.
|
||||
// Since we know it's a block, we know we can insert the `let _ = ..` without
|
||||
// breaking the macro syntax.
|
||||
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(block.span) {
|
||||
closure_body_span = block.span;
|
||||
s = snippet;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(s) = self.tcx.sess.source_map().span_to_snippet(closure_body_span) {
|
||||
let mut lines = s.lines();
|
||||
let line1 = lines.next().unwrap_or_default();
|
||||
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
// run-rustfix
|
||||
// edition:2018
|
||||
// check-pass
|
||||
#![warn(rust_2021_compatibility)]
|
||||
|
||||
macro_rules! m {
|
||||
(@ $body:expr) => {{
|
||||
let f = || $body;
|
||||
//~^ WARNING: drop order
|
||||
f();
|
||||
}};
|
||||
($body:block) => {{
|
||||
m!(@ $body);
|
||||
}};
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let a = (1.to_string(), 2.to_string());
|
||||
m!({
|
||||
let _ = &a;
|
||||
//~^ HELP: add a dummy
|
||||
let x = a.0;
|
||||
println!("{}", x);
|
||||
});
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
// run-rustfix
|
||||
// edition:2018
|
||||
// check-pass
|
||||
#![warn(rust_2021_compatibility)]
|
||||
|
||||
macro_rules! m {
|
||||
(@ $body:expr) => {{
|
||||
let f = || $body;
|
||||
//~^ WARNING: drop order
|
||||
f();
|
||||
}};
|
||||
($body:block) => {{
|
||||
m!(@ $body);
|
||||
}};
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let a = (1.to_string(), 2.to_string());
|
||||
m!({
|
||||
//~^ HELP: add a dummy
|
||||
let x = a.0;
|
||||
println!("{}", x);
|
||||
});
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
warning: changes to closure capture in Rust 2021 will affect drop order
|
||||
--> $DIR/closure-body-macro-fragment.rs:8:17
|
||||
|
|
||||
LL | let f = || $body;
|
||||
| _________________^
|
||||
LL | |
|
||||
LL | | f();
|
||||
LL | | }};
|
||||
| | - in Rust 2018, `a` is dropped here, but in Rust 2021, only `a.0` will be dropped here as part of the closure
|
||||
LL | | ($body:block) => {{
|
||||
LL | | m!(@ $body);
|
||||
| |__________________^
|
||||
...
|
||||
LL | / m!({
|
||||
LL | |
|
||||
LL | | let x = a.0;
|
||||
| | --- in Rust 2018, this closure captures all of `a`, but in Rust 2021, it will only capture `a.0`
|
||||
LL | | println!("{}", x);
|
||||
LL | | });
|
||||
| |_______- in this macro invocation
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/closure-body-macro-fragment.rs:4:9
|
||||
|
|
||||
LL | #![warn(rust_2021_compatibility)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
= note: `#[warn(rust_2021_incompatible_closure_captures)]` implied by `#[warn(rust_2021_compatibility)]`
|
||||
= note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
|
||||
= note: this warning originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
help: add a dummy let to cause `a` to be fully captured
|
||||
|
|
||||
LL ~ m!({
|
||||
LL + let _ = &a;
|
||||
|
|
||||
|
||||
warning: 1 warning emitted
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue