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_session::lint;
|
||||||
use rustc_span::sym;
|
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_trait_selection::infer::InferCtxtExt;
|
||||||
|
|
||||||
use rustc_data_structures::stable_map::FxHashMap;
|
use rustc_data_structures::stable_map::FxHashMap;
|
||||||
|
@ -680,15 +680,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
migrated_variables_concat
|
migrated_variables_concat
|
||||||
);
|
);
|
||||||
|
|
||||||
// If the body was entirely expanded from a macro
|
let mut closure_body_span = {
|
||||||
// invocation, i.e. the body is not contained inside the
|
// If the body was entirely expanded from a macro
|
||||||
// closure span, then we walk up the expansion until we
|
// invocation, i.e. the body is not contained inside the
|
||||||
// find the span before the expansion.
|
// closure span, then we walk up the expansion until we
|
||||||
let closure_body_span = self.tcx.hir().span(body_id.hir_id)
|
// find the span before the expansion.
|
||||||
.find_ancestor_inside(closure_span)
|
let s = self.tcx.hir().span(body_id.hir_id);
|
||||||
.unwrap_or(DUMMY_SP);
|
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 mut lines = s.lines();
|
||||||
let line1 = lines.next().unwrap_or_default();
|
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