about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMara Bos <m-ou.se@m-ou.se>2021-09-02 19:10:17 +0200
committerGitHub <noreply@github.com>2021-09-02 19:10:17 +0200
commitffbce26e24cb1cf955f07cee0d0e1c59d1692edb (patch)
treec233f502d6ceb184a34468b3ce5af474f75dc043
parentafdaa2e8f58958149fd76c16b89a54bbc8dbd8d1 (diff)
parent7d18052b1bfc31a96c2482e8919ba824c0e4100c (diff)
downloadrust-ffbce26e24cb1cf955f07cee0d0e1c59d1692edb.tar.gz
rust-ffbce26e24cb1cf955f07cee0d0e1c59d1692edb.zip
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
-rw-r--r--compiler/rustc_typeck/src/check/upvar.rs35
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.fixed25
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.rs24
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.stderr37
4 files changed, 112 insertions, 9 deletions
diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs
index a25d0f80644..702f69a9fcf 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::{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();
 
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.fixed
new file mode 100644
index 00000000000..f91454aa211
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.fixed
@@ -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);
+    });
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.rs b/src/test/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.rs
new file mode 100644
index 00000000000..5a1026d0433
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.rs
@@ -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);
+    });
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.stderr
new file mode 100644
index 00000000000..e6e5598f6d2
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.stderr
@@ -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
+