diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 9cf741bc63f..b28c4d9b5ee 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -47,7 +47,7 @@ use rustc_middle::ty::{ }; use rustc_session::lint; use rustc_span::sym; -use rustc_span::{MultiSpan, Span, Symbol}; +use rustc_span::{MultiSpan, Span, Symbol, DUMMY_SP}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_data_structures::stable_map::FxHashMap; @@ -644,8 +644,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } diagnostics_builder.note("for more information, see "); - let closure_body_span = self.tcx.hir().span(body_id.hir_id); - let (sugg, app) = + + let mut closure_body_span = self.tcx.hir().span(body_id.hir_id); + + // 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. + while !closure_body_span.is_dummy() && !closure_span.contains(closure_body_span) { + closure_body_span = closure_body_span.parent().unwrap_or(DUMMY_SP); + } + + let (span, sugg, app) = match self.tcx.sess.source_map().span_to_snippet(closure_body_span) { Ok(s) => { let trimmed = s.trim_start(); @@ -666,9 +676,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { format!("{{ {}; {} }}", migration_string, s) }; - (sugg, Applicability::MachineApplicable) + (closure_body_span, sugg, Applicability::MachineApplicable) } - Err(_) => (migration_string.clone(), Applicability::HasPlaceholders), + Err(_) => (closure_span, migration_string.clone(), Applicability::HasPlaceholders), }; let diagnostic_msg = format!( @@ -677,7 +687,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); diagnostics_builder.span_suggestion( - closure_body_span, + span, &diagnostic_msg, sugg, app, diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/macro.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/macro.fixed new file mode 100644 index 00000000000..3d9797e6579 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/macro.fixed @@ -0,0 +1,16 @@ +// run-rustfix + +// See https://github.com/rust-lang/rust/issues/87955 + +#![deny(rust_2021_incompatible_closure_captures)] +//~^ NOTE: the lint level is defined here + +fn main() { + let a = ("hey".to_string(), "123".to_string()); + let _ = || { let _ = &a; dbg!(a.0) }; + //~^ ERROR: drop order + //~| NOTE: only captures `a.0` + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `a` to be fully captured +} +//~^ NOTE: dropped here diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/macro.rs b/src/test/ui/closures/2229_closure_analysis/migrations/macro.rs new file mode 100644 index 00000000000..ffceaf0dd22 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/macro.rs @@ -0,0 +1,16 @@ +// run-rustfix + +// See https://github.com/rust-lang/rust/issues/87955 + +#![deny(rust_2021_incompatible_closure_captures)] +//~^ NOTE: the lint level is defined here + +fn main() { + let a = ("hey".to_string(), "123".to_string()); + let _ = || dbg!(a.0); + //~^ ERROR: drop order + //~| NOTE: only captures `a.0` + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `a` to be fully captured +} +//~^ NOTE: dropped here diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/macro.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/macro.stderr new file mode 100644 index 00000000000..8ce5844d490 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/macro.stderr @@ -0,0 +1,24 @@ +error: changes to closure capture in Rust 2021 will affect drop order + --> $DIR/macro.rs:10:13 + | +LL | let _ = || dbg!(a.0); + | ^^^^^^^^---^ + | | + | in Rust 2018, closure captures all of `a`, but in Rust 2021, it only captures `a.0` +... +LL | } + | - in Rust 2018, `a` would be dropped here, but in Rust 2021, only `a.0` would be dropped here alongside the closure + | +note: the lint level is defined here + --> $DIR/macro.rs:5:9 + | +LL | #![deny(rust_2021_incompatible_closure_captures)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: for more information, see +help: add a dummy let to cause `a` to be fully captured + | +LL | let _ = || { let _ = &a; dbg!(a.0) }; + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to previous error +