about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-11-05 22:12:31 +0000
committerbors <bors@rust-lang.org>2021-11-05 22:12:31 +0000
commitd32993afe81a49701edd6f2b8f018020ca50da1a (patch)
tree359ab46c3e67e59e847f579fbb0b7745800ff438
parent0d1754e8bf6942b4c1d24d7c923438782129ba5a (diff)
parentf5f6f73faf7d38b4d4c6cdccd6bd8a12ff23d5be (diff)
downloadrust-d32993afe81a49701edd6f2b8f018020ca50da1a.tar.gz
rust-d32993afe81a49701edd6f2b8f018020ca50da1a.zip
Auto merge of #90631 - matthiaskrgr:rollup-a5tzjh3, r=matthiaskrgr
Rollup of 5 pull requests

Successful merges:

 - #89942 (Reorder `widening_impl`s to make the doc clearer)
 - #90569 (Fix tests using `only-i686` to use the correct `only-x86` directive)
 - #90597 (Warn for variables that are no longer captured)
 - #90623 (Remove more checks for LLVM < 12)
 - #90626 (Properly register text_direction_codepoint_in_comment lint.)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--compiler/rustc_codegen_llvm/src/abi.rs14
-rw-r--r--compiler/rustc_codegen_llvm/src/builder.rs4
-rw-r--r--compiler/rustc_codegen_llvm/src/context.rs3
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm_util.rs5
-rw-r--r--compiler/rustc_lint_defs/src/builtin.rs1
-rw-r--r--compiler/rustc_middle/src/ty/layout.rs7
-rw-r--r--compiler/rustc_session/src/options.rs2
-rw-r--r--compiler/rustc_target/src/abi/call/mod.rs6
-rw-r--r--compiler/rustc_target/src/spec/aarch64_be_unknown_linux_gnu.rs1
-rw-r--r--compiler/rustc_target/src/spec/aarch64_be_unknown_linux_gnu_ilp32.rs1
-rw-r--r--compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu.rs1
-rw-r--r--compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu_ilp32.rs1
-rw-r--r--compiler/rustc_target/src/spec/aarch64_unknown_linux_musl.rs6
-rw-r--r--compiler/rustc_typeck/src/check/upvar.rs231
-rw-r--r--library/core/src/num/mod.rs36
-rw-r--r--src/test/ui/closures/2229_closure_analysis/issue-90465.fixed35
-rw-r--r--src/test/ui/closures/2229_closure_analysis/issue-90465.rs34
-rw-r--r--src/test/ui/closures/2229_closure_analysis/issue-90465.stderr26
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.fixed13
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.rs13
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr19
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.fixed5
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.rs5
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr7
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.fixed26
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.rs26
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.stderr35
-rw-r--r--src/test/ui/extern/extern-methods.rs2
-rw-r--r--src/test/ui/extern/extern-thiscall.rs2
-rw-r--r--src/test/ui/extern/extern-vectorcall.rs2
-rw-r--r--src/test/ui/lint/issue-90614-accept-allow-text-direction-codepoint-in-comment-lint.rs9
-rw-r--r--src/tools/compiletest/src/header/tests.rs2
32 files changed, 384 insertions, 196 deletions
diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs
index dca9c1f04d3..bedd3523d89 100644
--- a/compiler/rustc_codegen_llvm/src/abi.rs
+++ b/compiler/rustc_codegen_llvm/src/abi.rs
@@ -1,7 +1,6 @@
 use crate::builder::Builder;
 use crate::context::CodegenCx;
 use crate::llvm::{self, AttributePlace};
-use crate::llvm_util;
 use crate::type_::Type;
 use crate::type_of::LayoutLlvmExt;
 use crate::value::Value;
@@ -53,15 +52,10 @@ pub trait ArgAttributesExt {
 }
 
 fn should_use_mutable_noalias(cx: &CodegenCx<'_, '_>) -> bool {
-    // LLVM prior to version 12 has known miscompiles in the presence of
-    // noalias attributes (see #54878). Only enable mutable noalias by
-    // default for versions we believe to be safe.
-    cx.tcx
-        .sess
-        .opts
-        .debugging_opts
-        .mutable_noalias
-        .unwrap_or_else(|| llvm_util::get_version() >= (12, 0, 0))
+    // LLVM prior to version 12 had known miscompiles in the presence of
+    // noalias attributes (see #54878), but we don't support earlier
+    // versions at all anymore. We now enable mutable noalias by default.
+    cx.tcx.sess.opts.debugging_opts.mutable_noalias.unwrap_or(true)
 }
 
 impl ArgAttributesExt for ArgAttributes {
diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs
index 0707faf610c..6c74163fb49 100644
--- a/compiler/rustc_codegen_llvm/src/builder.rs
+++ b/compiler/rustc_codegen_llvm/src/builder.rs
@@ -731,7 +731,7 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
     }
 
     fn fptoui_sat(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> Option<&'ll Value> {
-        if llvm_util::get_version() >= (12, 0, 0) && !self.fptoint_sat_broken_in_llvm() {
+        if !self.fptoint_sat_broken_in_llvm() {
             let src_ty = self.cx.val_ty(val);
             let float_width = self.cx.float_width(src_ty);
             let int_width = self.cx.int_width(dest_ty);
@@ -743,7 +743,7 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
     }
 
     fn fptosi_sat(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> Option<&'ll Value> {
-        if llvm_util::get_version() >= (12, 0, 0) && !self.fptoint_sat_broken_in_llvm() {
+        if !self.fptoint_sat_broken_in_llvm() {
             let src_ty = self.cx.val_ty(val);
             let float_width = self.cx.float_width(src_ty);
             let int_width = self.cx.int_width(dest_ty);
diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs
index cda766039c1..1dba264a961 100644
--- a/compiler/rustc_codegen_llvm/src/context.rs
+++ b/compiler/rustc_codegen_llvm/src/context.rs
@@ -134,9 +134,6 @@ pub unsafe fn create_module(
     let llmod = llvm::LLVMModuleCreateWithNameInContext(mod_name.as_ptr(), llcx);
 
     let mut target_data_layout = sess.target.data_layout.clone();
-    if llvm_util::get_version() < (12, 0, 0) && sess.target.arch == "powerpc64" {
-        target_data_layout = target_data_layout.replace("-v256:256:256-v512:512:512", "");
-    }
     if llvm_util::get_version() < (13, 0, 0) {
         if sess.target.arch == "powerpc64" {
             target_data_layout = target_data_layout.replace("-S128", "");
diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs
index 246bb88885d..3e0ea92ab81 100644
--- a/compiler/rustc_codegen_llvm/src/llvm_util.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs
@@ -406,11 +406,6 @@ pub fn llvm_global_features(sess: &Session) -> Vec<String> {
     // -Ctarget-features
     features.extend(sess.opts.cg.target_feature.split(',').flat_map(&filter));
 
-    // FIXME: Move outline-atomics to target definition when earliest supported LLVM is 12.
-    if get_version() >= (12, 0, 0) && sess.target.llvm_target.contains("aarch64-unknown-linux") {
-        features.push("+outline-atomics".to_string());
-    }
-
     features
 }
 
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index b83d04f3dc2..c1a53c34b7a 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -3054,6 +3054,7 @@ declare_lint_pass! {
         BREAK_WITH_LABEL_AND_LOOP,
         UNUSED_ATTRIBUTES,
         NON_EXHAUSTIVE_OMITTED_PATTERNS,
+        TEXT_DIRECTION_CODEPOINT_IN_COMMENT,
         DEREF_INTO_DYN_SUPERTRAIT,
     ]
 }
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index 8ec5f4c7978..b87e23af72b 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -3060,9 +3060,10 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
                     // LLVM's definition of `noalias` is based solely on memory
                     // dependencies rather than pointer equality
                     //
-                    // Due to miscompiles in LLVM < 12, we apply a separate NoAliasMutRef attribute
-                    // for UniqueBorrowed arguments, so that the codegen backend can decide
-                    // whether or not to actually emit the attribute.
+                    // Due to past miscompiles in LLVM, we apply a separate NoAliasMutRef attribute
+                    // for UniqueBorrowed arguments, so that the codegen backend can decide whether
+                    // or not to actually emit the attribute. It can also be controlled with the
+                    // `-Zmutable-noalias` debugging option.
                     let no_alias = match kind {
                         PointerKind::Shared | PointerKind::UniqueBorrowed => false,
                         PointerKind::UniqueOwned => true,
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index e894e46a301..3add3e86148 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -1193,7 +1193,7 @@ options! {
     move_size_limit: Option<usize> = (None, parse_opt_number, [TRACKED],
         "the size at which the `large_assignments` lint starts to be emitted"),
     mutable_noalias: Option<bool> = (None, parse_opt_bool, [TRACKED],
-        "emit noalias metadata for mutable references (default: yes for LLVM >= 12, otherwise no)"),
+        "emit noalias metadata for mutable references (default: yes)"),
     new_llvm_pass_manager: Option<bool> = (None, parse_opt_bool, [TRACKED],
         "use new LLVM pass manager (default: no)"),
     nll_facts: bool = (false, parse_bool, [UNTRACKED],
diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs
index d9eb299e2fd..4768c9e2db5 100644
--- a/compiler/rustc_target/src/abi/call/mod.rs
+++ b/compiler/rustc_target/src/abi/call/mod.rs
@@ -68,8 +68,10 @@ mod attr_impl {
             const NonNull   = 1 << 3;
             const ReadOnly  = 1 << 4;
             const InReg     = 1 << 5;
-            // NoAlias on &mut arguments can only be used with LLVM >= 12 due to miscompiles
-            // in earlier versions. FIXME: Remove this distinction once possible.
+            // Due to past miscompiles in LLVM, we use a separate attribute for
+            // &mut arguments, so that the codegen backend can decide whether
+            // or not to actually emit the attribute. It can also be controlled
+            // with the `-Zmutable-noalias` debugging option.
             const NoAliasMutRef = 1 << 6;
         }
     }
diff --git a/compiler/rustc_target/src/spec/aarch64_be_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/aarch64_be_unknown_linux_gnu.rs
index 71ee6deb07f..a393858879b 100644
--- a/compiler/rustc_target/src/spec/aarch64_be_unknown_linux_gnu.rs
+++ b/compiler/rustc_target/src/spec/aarch64_be_unknown_linux_gnu.rs
@@ -8,6 +8,7 @@ pub fn target() -> Target {
         data_layout: "E-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".to_string(),
         arch: "aarch64".to_string(),
         options: TargetOptions {
+            features: "+outline-atomics".to_string(),
             max_atomic_width: Some(128),
             mcount: "\u{1}_mcount".to_string(),
             endian: Endian::Big,
diff --git a/compiler/rustc_target/src/spec/aarch64_be_unknown_linux_gnu_ilp32.rs b/compiler/rustc_target/src/spec/aarch64_be_unknown_linux_gnu_ilp32.rs
index e05360ea45c..e75100f1435 100644
--- a/compiler/rustc_target/src/spec/aarch64_be_unknown_linux_gnu_ilp32.rs
+++ b/compiler/rustc_target/src/spec/aarch64_be_unknown_linux_gnu_ilp32.rs
@@ -12,6 +12,7 @@ pub fn target() -> Target {
         arch: "aarch64".to_string(),
         options: TargetOptions {
             abi: "ilp32".to_string(),
+            features: "+outline-atomics".to_string(),
             mcount: "\u{1}_mcount".to_string(),
             endian: Endian::Big,
             ..base
diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu.rs
index c8d46adbfd9..850381f7fb0 100644
--- a/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu.rs
+++ b/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu.rs
@@ -7,6 +7,7 @@ pub fn target() -> Target {
         data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".to_string(),
         arch: "aarch64".to_string(),
         options: TargetOptions {
+            features: "+outline-atomics".to_string(),
             mcount: "\u{1}_mcount".to_string(),
             max_atomic_width: Some(128),
             supported_sanitizers: SanitizerSet::ADDRESS
diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu_ilp32.rs b/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu_ilp32.rs
index 8522405f61f..1c931d5a705 100644
--- a/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu_ilp32.rs
+++ b/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu_ilp32.rs
@@ -8,6 +8,7 @@ pub fn target() -> Target {
         arch: "aarch64".to_string(),
         options: TargetOptions {
             abi: "ilp32".to_string(),
+            features: "+outline-atomics".to_string(),
             max_atomic_width: Some(128),
             mcount: "\u{1}_mcount".to_string(),
             ..super::linux_gnu_base::opts()
diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/aarch64_unknown_linux_musl.rs
index 6a16b4ce419..0770f3496c2 100644
--- a/compiler/rustc_target/src/spec/aarch64_unknown_linux_musl.rs
+++ b/compiler/rustc_target/src/spec/aarch64_unknown_linux_musl.rs
@@ -9,6 +9,10 @@ pub fn target() -> Target {
         pointer_width: 64,
         data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".to_string(),
         arch: "aarch64".to_string(),
-        options: TargetOptions { mcount: "\u{1}_mcount".to_string(), ..base },
+        options: TargetOptions {
+            features: "+outline-atomics".to_string(),
+            mcount: "\u{1}_mcount".to_string(),
+            ..base
+        },
     }
 }
diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs
index 9c7b0b2cacb..774d8078e52 100644
--- a/compiler/rustc_typeck/src/check/upvar.rs
+++ b/compiler/rustc_typeck/src/check/upvar.rs
@@ -86,18 +86,55 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 /// Intermediate format to store the hir_id pointing to the use that resulted in the
 /// corresponding place being captured and a String which contains the captured value's
 /// name (i.e: a.b.c)
-type CapturesInfo = (Option<hir::HirId>, String);
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+enum UpvarMigrationInfo {
+    /// We previously captured all of `x`, but now we capture some sub-path.
+    CapturingPrecise { source_expr: Option<hir::HirId>, var_name: String },
+    CapturingNothing {
+        // where the variable appears in the closure (but is not captured)
+        use_span: Span,
+    },
+}
+
+/// Reasons that we might issue a migration warning.
+#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
+struct MigrationWarningReason {
+    /// When we used to capture `x` in its entirety, we implemented the auto-trait(s)
+    /// in this vec, but now we don't.
+    auto_traits: Vec<&'static str>,
+
+    /// When we used to capture `x` in its entirety, we would execute some destructors
+    /// at a different time.
+    drop_order: bool,
+}
+
+impl MigrationWarningReason {
+    fn migration_message(&self) -> String {
+        let base = "changes to closure capture in Rust 2021 will affect";
+        if !self.auto_traits.is_empty() && self.drop_order {
+            format!("{} drop order and which traits the closure implements", base)
+        } else if self.drop_order {
+            format!("{} drop order", base)
+        } else {
+            format!("{} which traits the closure implements", base)
+        }
+    }
+}
 
-/// Intermediate format to store information needed to generate migration lint. The tuple
-/// contains the hir_id pointing to the use that resulted in the
-/// corresponding place being captured, a String which contains the captured value's
-/// name (i.e: a.b.c) and a String which contains the reason why migration is needed for that
-/// capture
-type MigrationNeededForCapture = (Option<hir::HirId>, String, String);
+/// Intermediate format to store information needed to generate a note in the migration lint.
+struct MigrationLintNote {
+    captures_info: UpvarMigrationInfo,
+
+    /// reasons why migration is needed for this capture
+    reason: MigrationWarningReason,
+}
 
 /// Intermediate format to store the hir id of the root variable and a HashSet containing
 /// information on why the root variable should be fully captured
-type MigrationDiagnosticInfo = (hir::HirId, Vec<MigrationNeededForCapture>);
+struct NeededMigration {
+    var_hir_id: hir::HirId,
+    diagnostics_info: Vec<MigrationLintNote>,
+}
 
 struct InferBorrowKindVisitor<'a, 'tcx> {
     fcx: &'a FnCtxt<'a, 'tcx>,
@@ -707,47 +744,66 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                  closure_head_span,
                 |lint| {
                     let mut diagnostics_builder = lint.build(
-                        format!(
-                            "changes to closure capture in Rust 2021 will affect {}",
-                            reasons
-                        )
-                        .as_str(),
+                        &reasons.migration_message(),
                     );
-                    for (var_hir_id, diagnostics_info) in need_migrations.iter() {
+                    for NeededMigration { var_hir_id, diagnostics_info } in &need_migrations {
                         // Labels all the usage of the captured variable and why they are responsible
                         // for migration being needed
-                        for (captured_hir_id, captured_name, reasons) in diagnostics_info.iter() {
-                            if let Some(captured_hir_id) = captured_hir_id {
-                                let cause_span = self.tcx.hir().span(*captured_hir_id);
-                                diagnostics_builder.span_label(cause_span, format!("in Rust 2018, this closure captures all of `{}`, but in Rust 2021, it will only capture `{}`",
-                                    self.tcx.hir().name(*var_hir_id),
-                                    captured_name,
-                                ));
+                        for lint_note in diagnostics_info.iter() {
+                            match &lint_note.captures_info {
+                                UpvarMigrationInfo::CapturingPrecise { source_expr: Some(capture_expr_id), var_name: captured_name } => {
+                                    let cause_span = self.tcx.hir().span(*capture_expr_id);
+                                    diagnostics_builder.span_label(cause_span, format!("in Rust 2018, this closure captures all of `{}`, but in Rust 2021, it will only capture `{}`",
+                                        self.tcx.hir().name(*var_hir_id),
+                                        captured_name,
+                                    ));
+                                }
+                                UpvarMigrationInfo::CapturingNothing { use_span } => {
+                                    diagnostics_builder.span_label(*use_span, format!("in Rust 2018, this causes the closure to capture `{}`, but in Rust 2021, it has no effect",
+                                        self.tcx.hir().name(*var_hir_id),
+                                    ));
+                                }
+
+                                _ => { }
                             }
 
                             // Add a label pointing to where a captured variable affected by drop order
                             // is dropped
-                            if reasons.contains("drop order") {
+                            if lint_note.reason.drop_order {
                                 let drop_location_span = drop_location_span(self.tcx, &closure_hir_id);
 
-                                diagnostics_builder.span_label(drop_location_span, format!("in Rust 2018, `{}` is dropped here, but in Rust 2021, only `{}` will be dropped here as part of the closure",
-                                    self.tcx.hir().name(*var_hir_id),
-                                    captured_name,
-                                ));
+                                match &lint_note.captures_info {
+                                    UpvarMigrationInfo::CapturingPrecise { var_name: captured_name, .. } => {
+                                        diagnostics_builder.span_label(drop_location_span, format!("in Rust 2018, `{}` is dropped here, but in Rust 2021, only `{}` will be dropped here as part of the closure",
+                                            self.tcx.hir().name(*var_hir_id),
+                                            captured_name,
+                                        ));
+                                    }
+                                    UpvarMigrationInfo::CapturingNothing { use_span: _ } => {
+                                        diagnostics_builder.span_label(drop_location_span, format!("in Rust 2018, `{v}` is dropped here along with the closure, but in Rust 2021 `{v}` is not part of the closure",
+                                            v = self.tcx.hir().name(*var_hir_id),
+                                        ));
+                                    }
+                                }
                             }
 
                             // Add a label explaining why a closure no longer implements a trait
-                            if reasons.contains("trait implementation") {
-                                let missing_trait = &reasons[..reasons.find("trait implementation").unwrap() - 1];
-
-                                diagnostics_builder.span_label(closure_head_span, format!("in Rust 2018, this closure implements {} as `{}` implements {}, but in Rust 2021, this closure will no longer implement {} as `{}` does not implement {}",
-                                    missing_trait,
-                                    self.tcx.hir().name(*var_hir_id),
-                                    missing_trait,
-                                    missing_trait,
-                                    captured_name,
-                                    missing_trait,
-                                ));
+                            for &missing_trait in &lint_note.reason.auto_traits {
+                                // not capturing something anymore cannot cause a trait to fail to be implemented:
+                                match &lint_note.captures_info {
+                                    UpvarMigrationInfo::CapturingPrecise { var_name: captured_name, .. } => {
+                                        let var_name = self.tcx.hir().name(*var_hir_id);
+                                        diagnostics_builder.span_label(closure_head_span, format!("\
+                                        in Rust 2018, this closure implements {missing_trait} \
+                                        as `{var_name}` implements {missing_trait}, but in Rust 2021, \
+                                        this closure will no longer implement {missing_trait} \
+                                        because `{var_name}` is not fully captured \
+                                        and `{captured_name}` does not implement {missing_trait}"));
+                                    }
+
+                                    // Cannot happen: if we don't capture a variable, we impl strictly more traits
+                                    UpvarMigrationInfo::CapturingNothing { use_span } => span_bug!(*use_span, "missing trait from not capturing something"),
+                                }
                             }
                         }
                     }
@@ -840,25 +896,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// Combines all the reasons for 2229 migrations
     fn compute_2229_migrations_reasons(
         &self,
-        auto_trait_reasons: FxHashSet<&str>,
-        drop_reason: bool,
-    ) -> String {
-        let mut reasons = String::new();
-
-        if !auto_trait_reasons.is_empty() {
-            reasons = format!(
-                "{} trait implementation for closure",
-                auto_trait_reasons.clone().into_iter().collect::<Vec<&str>>().join(", ")
-            );
-        }
+        auto_trait_reasons: FxHashSet<&'static str>,
+        drop_order: bool,
+    ) -> MigrationWarningReason {
+        let mut reasons = MigrationWarningReason::default();
 
-        if !auto_trait_reasons.is_empty() && drop_reason {
-            reasons = format!("{} and ", reasons);
+        for auto_trait in auto_trait_reasons {
+            reasons.auto_traits.push(auto_trait);
         }
 
-        if drop_reason {
-            reasons = format!("{}drop order", reasons);
-        }
+        reasons.drop_order = drop_order;
 
         reasons
     }
@@ -874,7 +921,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         min_captures: Option<&ty::RootVariableMinCaptureList<'tcx>>,
         var_hir_id: hir::HirId,
         closure_clause: hir::CaptureBy,
-    ) -> Option<FxHashMap<CapturesInfo, FxHashSet<&str>>> {
+    ) -> Option<FxHashMap<UpvarMigrationInfo, FxHashSet<&'static str>>> {
         let auto_traits_def_id = vec![
             self.tcx.lang_items().clone_trait(),
             self.tcx.lang_items().sync_trait(),
@@ -963,7 +1010,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
             if !capture_problems.is_empty() {
                 problematic_captures.insert(
-                    (capture.info.path_expr_id, capture.to_string(self.tcx)),
+                    UpvarMigrationInfo::CapturingPrecise {
+                        source_expr: capture.info.path_expr_id,
+                        var_name: capture.to_string(self.tcx),
+                    },
                     capture_problems,
                 );
             }
@@ -986,6 +1036,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     ///
     /// This function only returns a HashSet of CapturesInfo for significant drops. If there
     /// are no significant drops than None is returned
+    #[instrument(level = "debug", skip(self))]
     fn compute_2229_migrations_for_drop(
         &self,
         closure_def_id: DefId,
@@ -993,25 +1044,41 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         min_captures: Option<&ty::RootVariableMinCaptureList<'tcx>>,
         closure_clause: hir::CaptureBy,
         var_hir_id: hir::HirId,
-    ) -> Option<FxHashSet<CapturesInfo>> {
+    ) -> Option<FxHashSet<UpvarMigrationInfo>> {
         let ty = self.infcx.resolve_vars_if_possible(self.node_ty(var_hir_id));
 
         if !ty.has_significant_drop(self.tcx, self.tcx.param_env(closure_def_id.expect_local())) {
+            debug!("does not have significant drop");
             return None;
         }
 
         let Some(root_var_min_capture_list) = min_captures.and_then(|m| m.get(&var_hir_id)) else {
             // The upvar is mentioned within the closure but no path starting from it is
-            // used.
+            // used. This occurs when you have (e.g.)
+            //
+            // ```
+            // let x = move || {
+            //     let _ = y;
+            // });
+            // ```
+            debug!("no path starting from it is used");
+
 
             match closure_clause {
                 // Only migrate if closure is a move closure
-                hir::CaptureBy::Value => return Some(FxHashSet::default()),
+                hir::CaptureBy::Value => {
+                    let mut diagnostics_info = FxHashSet::default();
+                    let upvars = self.tcx.upvars_mentioned(closure_def_id).expect("must be an upvar");
+                    let upvar = upvars[&var_hir_id];
+                    diagnostics_info.insert(UpvarMigrationInfo::CapturingNothing { use_span: upvar.span });
+                    return Some(diagnostics_info);
+                }
                 hir::CaptureBy::Ref => {}
             }
 
             return None;
         };
+        debug!(?root_var_min_capture_list);
 
         let mut projections_list = Vec::new();
         let mut diagnostics_info = FxHashSet::default();
@@ -1021,19 +1088,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // Only care about captures that are moved into the closure
                 ty::UpvarCapture::ByValue(..) => {
                     projections_list.push(captured_place.place.projections.as_slice());
-                    diagnostics_info.insert((
-                        captured_place.info.path_expr_id,
-                        captured_place.to_string(self.tcx),
-                    ));
+                    diagnostics_info.insert(UpvarMigrationInfo::CapturingPrecise {
+                        source_expr: captured_place.info.path_expr_id,
+                        var_name: captured_place.to_string(self.tcx),
+                    });
                 }
                 ty::UpvarCapture::ByRef(..) => {}
             }
         }
 
+        debug!(?projections_list);
+        debug!(?diagnostics_info);
+
         let is_moved = !projections_list.is_empty();
+        debug!(?is_moved);
 
         let is_not_completely_captured =
             root_var_min_capture_list.iter().any(|capture| !capture.place.projections.is_empty());
+        debug!(?is_not_completely_captured);
 
         if is_moved
             && is_not_completely_captured
@@ -1066,15 +1138,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// Returns a tuple containing a vector of MigrationDiagnosticInfo, as well as a String
     /// containing the reason why root variables whose HirId is contained in the vector should
     /// be captured
+    #[instrument(level = "debug", skip(self))]
     fn compute_2229_migrations(
         &self,
         closure_def_id: DefId,
         closure_span: Span,
         closure_clause: hir::CaptureBy,
         min_captures: Option<&ty::RootVariableMinCaptureList<'tcx>>,
-    ) -> (Vec<MigrationDiagnosticInfo>, String) {
+    ) -> (Vec<NeededMigration>, MigrationWarningReason) {
         let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) else {
-            return (Vec::new(), String::new());
+            return (Vec::new(), MigrationWarningReason::default());
         };
 
         let mut need_migrations = Vec::new();
@@ -1083,7 +1156,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         // Perform auto-trait analysis
         for (&var_hir_id, _) in upvars.iter() {
-            let mut responsible_captured_hir_ids = Vec::new();
+            let mut diagnostics_info = Vec::new();
 
             let auto_trait_diagnostic = if let Some(diagnostics_info) =
                 self.compute_2229_migrations_for_trait(min_captures, var_hir_id, closure_clause)
@@ -1115,34 +1188,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
             let mut capture_diagnostic = capture_diagnostic.into_iter().collect::<Vec<_>>();
             capture_diagnostic.sort();
-            for captured_info in capture_diagnostic.iter() {
+            for captures_info in capture_diagnostic {
                 // Get the auto trait reasons of why migration is needed because of that capture, if there are any
                 let capture_trait_reasons =
-                    if let Some(reasons) = auto_trait_diagnostic.get(captured_info) {
+                    if let Some(reasons) = auto_trait_diagnostic.get(&captures_info) {
                         reasons.clone()
                     } else {
                         FxHashSet::default()
                     };
 
                 // Check if migration is needed because of drop reorder as a result of that capture
-                let capture_drop_reorder_reason = drop_reorder_diagnostic.contains(captured_info);
+                let capture_drop_reorder_reason = drop_reorder_diagnostic.contains(&captures_info);
 
                 // Combine all the reasons of why the root variable should be captured as a result of
                 // auto trait implementation issues
                 auto_trait_migration_reasons.extend(capture_trait_reasons.clone());
 
-                responsible_captured_hir_ids.push((
-                    captured_info.0,
-                    captured_info.1.clone(),
-                    self.compute_2229_migrations_reasons(
+                diagnostics_info.push(MigrationLintNote {
+                    captures_info,
+                    reason: self.compute_2229_migrations_reasons(
                         capture_trait_reasons,
                         capture_drop_reorder_reason,
                     ),
-                ));
+                });
             }
 
-            if !capture_diagnostic.is_empty() {
-                need_migrations.push((var_hir_id, responsible_captured_hir_ids));
+            if !diagnostics_info.is_empty() {
+                need_migrations.push(NeededMigration { var_hir_id, diagnostics_info });
             }
         }
         (
@@ -2087,6 +2159,7 @@ fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> Symbol {
     tcx.hir().name(var_hir_id)
 }
 
+#[instrument(level = "debug", skip(tcx))]
 fn should_do_rust_2021_incompatible_closure_captures_analysis(
     tcx: TyCtxt<'_>,
     closure_id: hir::HirId,
@@ -2102,10 +2175,12 @@ fn should_do_rust_2021_incompatible_closure_captures_analysis(
 /// - s2: Comma separated names of the variables being migrated.
 fn migration_suggestion_for_2229(
     tcx: TyCtxt<'_>,
-    need_migrations: &Vec<MigrationDiagnosticInfo>,
+    need_migrations: &Vec<NeededMigration>,
 ) -> (String, String) {
-    let need_migrations_variables =
-        need_migrations.iter().map(|(v, _)| var_name(tcx, *v)).collect::<Vec<_>>();
+    let need_migrations_variables = need_migrations
+        .iter()
+        .map(|NeededMigration { var_hir_id: v, .. }| var_name(tcx, *v))
+        .collect::<Vec<_>>();
 
     let migration_ref_concat =
         need_migrations_variables.iter().map(|v| format!("&{}", v)).collect::<Vec<_>>().join(", ");
diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs
index 9861530c194..0c00db5fdf3 100644
--- a/library/core/src/num/mod.rs
+++ b/library/core/src/num/mod.rs
@@ -201,33 +201,33 @@ macro_rules! widening_impl {
 
 #[lang = "i8"]
 impl i8 {
-    widening_impl! { i8, i16, 8, signed }
     int_impl! { i8, i8, u8, 8, 7, -128, 127, 2, "-0x7e", "0xa", "0x12", "0x12", "0x48",
     "[0x12]", "[0x12]", "", "" }
+    widening_impl! { i8, i16, 8, signed }
 }
 
 #[lang = "i16"]
 impl i16 {
-    widening_impl! { i16, i32, 16, signed }
     int_impl! { i16, i16, u16, 16, 15, -32768, 32767, 4, "-0x5ffd", "0x3a", "0x1234", "0x3412",
     "0x2c48", "[0x34, 0x12]", "[0x12, 0x34]", "", "" }
+    widening_impl! { i16, i32, 16, signed }
 }
 
 #[lang = "i32"]
 impl i32 {
-    widening_impl! { i32, i64, 32, signed }
     int_impl! { i32, i32, u32, 32, 31, -2147483648, 2147483647, 8, "0x10000b3", "0xb301",
     "0x12345678", "0x78563412", "0x1e6a2c48", "[0x78, 0x56, 0x34, 0x12]",
     "[0x12, 0x34, 0x56, 0x78]", "", "" }
+    widening_impl! { i32, i64, 32, signed }
 }
 
 #[lang = "i64"]
 impl i64 {
-    widening_impl! { i64, i128, 64, signed }
     int_impl! { i64, i64, u64, 64, 63, -9223372036854775808, 9223372036854775807, 12,
     "0xaa00000000006e1", "0x6e10aa", "0x1234567890123456", "0x5634129078563412",
     "0x6a2c48091e6a2c48", "[0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12]",
     "[0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56]", "", "" }
+    widening_impl! { i64, i128, 64, signed }
 }
 
 #[lang = "i128"]
@@ -245,31 +245,31 @@ impl i128 {
 #[cfg(target_pointer_width = "16")]
 #[lang = "isize"]
 impl isize {
-    widening_impl! { isize, i32, 16, signed }
     int_impl! { isize, i16, usize, 16, 15, -32768, 32767, 4, "-0x5ffd", "0x3a", "0x1234",
     "0x3412", "0x2c48", "[0x34, 0x12]", "[0x12, 0x34]",
     usize_isize_to_xe_bytes_doc!(), usize_isize_from_xe_bytes_doc!() }
+    widening_impl! { isize, i32, 16, signed }
 }
 
 #[cfg(target_pointer_width = "32")]
 #[lang = "isize"]
 impl isize {
-    widening_impl! { isize, i64, 32, signed }
     int_impl! { isize, i32, usize, 32, 31, -2147483648, 2147483647, 8, "0x10000b3", "0xb301",
     "0x12345678", "0x78563412", "0x1e6a2c48", "[0x78, 0x56, 0x34, 0x12]",
     "[0x12, 0x34, 0x56, 0x78]",
     usize_isize_to_xe_bytes_doc!(), usize_isize_from_xe_bytes_doc!() }
+    widening_impl! { isize, i64, 32, signed }
 }
 
 #[cfg(target_pointer_width = "64")]
 #[lang = "isize"]
 impl isize {
-    widening_impl! { isize, i128, 64, signed }
     int_impl! { isize, i64, usize, 64, 63, -9223372036854775808, 9223372036854775807,
     12, "0xaa00000000006e1", "0x6e10aa",  "0x1234567890123456", "0x5634129078563412",
-     "0x6a2c48091e6a2c48", "[0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12]",
-     "[0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56]",
-     usize_isize_to_xe_bytes_doc!(), usize_isize_from_xe_bytes_doc!() }
+    "0x6a2c48091e6a2c48", "[0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12]",
+    "[0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56]",
+    usize_isize_to_xe_bytes_doc!(), usize_isize_from_xe_bytes_doc!() }
+    widening_impl! { isize, i128, 64, signed }
 }
 
 /// If 6th bit set ascii is upper case.
@@ -277,9 +277,9 @@ const ASCII_CASE_MASK: u8 = 0b0010_0000;
 
 #[lang = "u8"]
 impl u8 {
-    widening_impl! { u8, u16, 8, unsigned }
     uint_impl! { u8, u8, i8, 8, 255, 2, "0x82", "0xa", "0x12", "0x12", "0x48", "[0x12]",
     "[0x12]", "", "" }
+    widening_impl! { u8, u16, 8, unsigned }
 
     /// Checks if the value is within the ASCII range.
     ///
@@ -826,26 +826,26 @@ impl u8 {
 
 #[lang = "u16"]
 impl u16 {
-    widening_impl! { u16, u32, 16, unsigned }
     uint_impl! { u16, u16, i16, 16, 65535, 4, "0xa003", "0x3a", "0x1234", "0x3412", "0x2c48",
     "[0x34, 0x12]", "[0x12, 0x34]", "", "" }
+    widening_impl! { u16, u32, 16, unsigned }
 }
 
 #[lang = "u32"]
 impl u32 {
-    widening_impl! { u32, u64, 32, unsigned }
     uint_impl! { u32, u32, i32, 32, 4294967295, 8, "0x10000b3", "0xb301", "0x12345678",
     "0x78563412", "0x1e6a2c48", "[0x78, 0x56, 0x34, 0x12]", "[0x12, 0x34, 0x56, 0x78]", "", "" }
+    widening_impl! { u32, u64, 32, unsigned }
 }
 
 #[lang = "u64"]
 impl u64 {
-    widening_impl! { u64, u128, 64, unsigned }
     uint_impl! { u64, u64, i64, 64, 18446744073709551615, 12, "0xaa00000000006e1", "0x6e10aa",
     "0x1234567890123456", "0x5634129078563412", "0x6a2c48091e6a2c48",
     "[0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12]",
     "[0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56]",
     "", ""}
+    widening_impl! { u64, u128, 64, unsigned }
 }
 
 #[lang = "u128"]
@@ -863,29 +863,29 @@ impl u128 {
 #[cfg(target_pointer_width = "16")]
 #[lang = "usize"]
 impl usize {
-    widening_impl! { usize, u32, 16, unsigned }
     uint_impl! { usize, u16, isize, 16, 65535, 4, "0xa003", "0x3a", "0x1234", "0x3412", "0x2c48",
     "[0x34, 0x12]", "[0x12, 0x34]",
     usize_isize_to_xe_bytes_doc!(), usize_isize_from_xe_bytes_doc!() }
+    widening_impl! { usize, u32, 16, unsigned }
 }
 #[cfg(target_pointer_width = "32")]
 #[lang = "usize"]
 impl usize {
-    widening_impl! { usize, u64, 32, unsigned }
     uint_impl! { usize, u32, isize, 32, 4294967295, 8, "0x10000b3", "0xb301", "0x12345678",
     "0x78563412", "0x1e6a2c48", "[0x78, 0x56, 0x34, 0x12]", "[0x12, 0x34, 0x56, 0x78]",
     usize_isize_to_xe_bytes_doc!(), usize_isize_from_xe_bytes_doc!() }
+    widening_impl! { usize, u64, 32, unsigned }
 }
 
 #[cfg(target_pointer_width = "64")]
 #[lang = "usize"]
 impl usize {
-    widening_impl! { usize, u128, 64, unsigned }
     uint_impl! { usize, u64, isize, 64, 18446744073709551615, 12, "0xaa00000000006e1", "0x6e10aa",
     "0x1234567890123456", "0x5634129078563412", "0x6a2c48091e6a2c48",
     "[0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12]",
-     "[0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56]",
+    "[0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56]",
     usize_isize_to_xe_bytes_doc!(), usize_isize_from_xe_bytes_doc!() }
+    widening_impl! { usize, u128, 64, unsigned }
 }
 
 /// A classification of floating point numbers.
diff --git a/src/test/ui/closures/2229_closure_analysis/issue-90465.fixed b/src/test/ui/closures/2229_closure_analysis/issue-90465.fixed
new file mode 100644
index 00000000000..4e0b18e7233
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/issue-90465.fixed
@@ -0,0 +1,35 @@
+// run-rustfix
+
+#![deny(rust_2021_incompatible_closure_captures)]
+//~^ NOTE lint level is defined here
+
+fn main() {
+    struct Foo(u32);
+    impl Drop for Foo {
+        fn drop(&mut self) {
+            println!("dropped {}", self.0);
+        }
+    }
+
+    let f0 = Foo(0);
+    let f1 = Foo(1);
+
+    let c0 = move || {
+        let _ = &f0;
+        //~^ ERROR changes to closure capture in Rust 2021 will affect drop order
+        //~| NOTE for more information
+        let _ = f0;
+        //~^ NOTE in Rust 2018, this causes the closure to capture `f0`, but in Rust 2021, it has no effect
+    };
+
+    let c1 = move || {
+        let _ = &f1;
+    };
+
+    println!("dropping 0");
+    drop(c0);
+    println!("dropping 1");
+    drop(c1);
+    println!("dropped all");
+}
+//~^ NOTE in Rust 2018, `f0` is dropped here along with the closure, but in Rust 2021 `f0` is not part of the closure
diff --git a/src/test/ui/closures/2229_closure_analysis/issue-90465.rs b/src/test/ui/closures/2229_closure_analysis/issue-90465.rs
new file mode 100644
index 00000000000..466e6dbabc5
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/issue-90465.rs
@@ -0,0 +1,34 @@
+// run-rustfix
+
+#![deny(rust_2021_incompatible_closure_captures)]
+//~^ NOTE lint level is defined here
+
+fn main() {
+    struct Foo(u32);
+    impl Drop for Foo {
+        fn drop(&mut self) {
+            println!("dropped {}", self.0);
+        }
+    }
+
+    let f0 = Foo(0);
+    let f1 = Foo(1);
+
+    let c0 = move || {
+        //~^ ERROR changes to closure capture in Rust 2021 will affect drop order
+        //~| NOTE for more information
+        let _ = f0;
+        //~^ NOTE in Rust 2018, this causes the closure to capture `f0`, but in Rust 2021, it has no effect
+    };
+
+    let c1 = move || {
+        let _ = &f1;
+    };
+
+    println!("dropping 0");
+    drop(c0);
+    println!("dropping 1");
+    drop(c1);
+    println!("dropped all");
+}
+//~^ NOTE in Rust 2018, `f0` is dropped here along with the closure, but in Rust 2021 `f0` is not part of the closure
diff --git a/src/test/ui/closures/2229_closure_analysis/issue-90465.stderr b/src/test/ui/closures/2229_closure_analysis/issue-90465.stderr
new file mode 100644
index 00000000000..3e921dc0f8a
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/issue-90465.stderr
@@ -0,0 +1,26 @@
+error: changes to closure capture in Rust 2021 will affect drop order
+  --> $DIR/issue-90465.rs:17:14
+   |
+LL |     let c0 = move || {
+   |              ^^^^^^^
+...
+LL |         let _ = f0;
+   |                 -- in Rust 2018, this causes the closure to capture `f0`, but in Rust 2021, it has no effect
+...
+LL | }
+   | - in Rust 2018, `f0` is dropped here along with the closure, but in Rust 2021 `f0` is not part of the closure
+   |
+note: the lint level is defined here
+  --> $DIR/issue-90465.rs:3:9
+   |
+LL | #![deny(rust_2021_incompatible_closure_captures)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `f0` to be fully captured
+   |
+LL ~     let c0 = move || {
+LL +         let _ = &f0;
+   |
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.fixed
index b0fc5120f08..26703fbf811 100644
--- a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.fixed
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.fixed
@@ -20,8 +20,8 @@ fn test_send_trait() {
     let mut f = 10;
     let fptr = SendPointer(&mut f as *mut i32);
     thread::spawn(move || { let _ = &fptr; unsafe {
-        //~^ ERROR: `Send` trait implementation for closure
-        //~| NOTE: in Rust 2018, this closure implements `Send` as `fptr` implements `Send`, but in Rust 2021, this closure will no longer implement `Send` as `fptr.0` does not implement `Send`
+        //~^ ERROR: changes to closure capture
+        //~| NOTE: in Rust 2018, this closure implements `Send`
         //~| NOTE: for more information, see
         //~| HELP: add a dummy let to cause `fptr` to be fully captured
         *fptr.0 = 20;
@@ -40,8 +40,9 @@ fn test_sync_trait() {
     let f = CustomInt(&mut f as *mut i32);
     let fptr = SyncPointer(f);
     thread::spawn(move || { let _ = &fptr; unsafe {
-        //~^ ERROR: `Sync`, `Send` trait implementation for closure
-        //~| NOTE: in Rust 2018, this closure implements `Sync`, `Send` as `fptr` implements `Sync`, `Send`, but in Rust 2021, this closure will no longer implement `Sync`, `Send` as `fptr.0.0` does not implement `Sync`, `Send`
+        //~^ ERROR: changes to closure capture
+        //~| NOTE: in Rust 2018, this closure implements `Sync`
+        //~| NOTE: in Rust 2018, this closure implements `Send`
         //~| NOTE: for more information, see
         //~| HELP: add a dummy let to cause `fptr` to be fully captured
         *fptr.0.0 = 20;
@@ -65,8 +66,8 @@ fn test_clone_trait() {
     let f = U(S(Foo(0)), T(0));
     let c = || {
         let _ = &f;
-        //~^ ERROR: `Clone` trait implementation for closure and drop order
-        //~| NOTE: in Rust 2018, this closure implements `Clone` as `f` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` as `f.1` does not implement `Clone`
+        //~^ ERROR: changes to closure capture in Rust 2021 will affect drop order and which traits the closure implements
+        //~| NOTE: in Rust 2018, this closure implements `Clone`
         //~| NOTE: for more information, see
         //~| HELP: add a dummy let to cause `f` to be fully captured
         let f_1 = f.1;
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.rs b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.rs
index 2bcf9a795ed..932db51d437 100644
--- a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.rs
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.rs
@@ -20,8 +20,8 @@ fn test_send_trait() {
     let mut f = 10;
     let fptr = SendPointer(&mut f as *mut i32);
     thread::spawn(move || unsafe {
-        //~^ ERROR: `Send` trait implementation for closure
-        //~| NOTE: in Rust 2018, this closure implements `Send` as `fptr` implements `Send`, but in Rust 2021, this closure will no longer implement `Send` as `fptr.0` does not implement `Send`
+        //~^ ERROR: changes to closure capture
+        //~| NOTE: in Rust 2018, this closure implements `Send`
         //~| NOTE: for more information, see
         //~| HELP: add a dummy let to cause `fptr` to be fully captured
         *fptr.0 = 20;
@@ -40,8 +40,9 @@ fn test_sync_trait() {
     let f = CustomInt(&mut f as *mut i32);
     let fptr = SyncPointer(f);
     thread::spawn(move || unsafe {
-        //~^ ERROR: `Sync`, `Send` trait implementation for closure
-        //~| NOTE: in Rust 2018, this closure implements `Sync`, `Send` as `fptr` implements `Sync`, `Send`, but in Rust 2021, this closure will no longer implement `Sync`, `Send` as `fptr.0.0` does not implement `Sync`, `Send`
+        //~^ ERROR: changes to closure capture
+        //~| NOTE: in Rust 2018, this closure implements `Sync`
+        //~| NOTE: in Rust 2018, this closure implements `Send`
         //~| NOTE: for more information, see
         //~| HELP: add a dummy let to cause `fptr` to be fully captured
         *fptr.0.0 = 20;
@@ -64,8 +65,8 @@ impl Clone for U {
 fn test_clone_trait() {
     let f = U(S(Foo(0)), T(0));
     let c = || {
-        //~^ ERROR: `Clone` trait implementation for closure and drop order
-        //~| NOTE: in Rust 2018, this closure implements `Clone` as `f` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` as `f.1` does not implement `Clone`
+        //~^ ERROR: changes to closure capture in Rust 2021 will affect drop order and which traits the closure implements
+        //~| NOTE: in Rust 2018, this closure implements `Clone`
         //~| NOTE: for more information, see
         //~| HELP: add a dummy let to cause `f` to be fully captured
         let f_1 = f.1;
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr
index 8d2d3553d40..ee4907bb755 100644
--- a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr
@@ -1,8 +1,8 @@
-error: changes to closure capture in Rust 2021 will affect `Send` trait implementation for closure
+error: changes to closure capture in Rust 2021 will affect which traits the closure implements
   --> $DIR/auto_traits.rs:22:19
    |
 LL |     thread::spawn(move || unsafe {
-   |                   ^^^^^^^^^^^^^^ in Rust 2018, this closure implements `Send` as `fptr` implements `Send`, but in Rust 2021, this closure will no longer implement `Send` as `fptr.0` does not implement `Send`
+   |                   ^^^^^^^^^^^^^^ in Rust 2018, this closure implements `Send` as `fptr` implements `Send`, but in Rust 2021, this closure will no longer implement `Send` because `fptr` is not fully captured and `fptr.0` does not implement `Send`
 ...
 LL |         *fptr.0 = 20;
    |         ------- in Rust 2018, this closure captures all of `fptr`, but in Rust 2021, it will only capture `fptr.0`
@@ -23,11 +23,14 @@ LL |
 LL |         *fptr.0 = 20;
  ...
 
-error: changes to closure capture in Rust 2021 will affect `Sync`, `Send` trait implementation for closure
+error: changes to closure capture in Rust 2021 will affect which traits the closure implements
   --> $DIR/auto_traits.rs:42:19
    |
 LL |     thread::spawn(move || unsafe {
-   |                   ^^^^^^^^^^^^^^ in Rust 2018, this closure implements `Sync`, `Send` as `fptr` implements `Sync`, `Send`, but in Rust 2021, this closure will no longer implement `Sync`, `Send` as `fptr.0.0` does not implement `Sync`, `Send`
+   |                   ^^^^^^^^^^^^^^
+   |                   |
+   |                   in Rust 2018, this closure implements `Sync` as `fptr` implements `Sync`, but in Rust 2021, this closure will no longer implement `Sync` because `fptr` is not fully captured and `fptr.0.0` does not implement `Sync`
+   |                   in Rust 2018, this closure implements `Send` as `fptr` implements `Send`, but in Rust 2021, this closure will no longer implement `Send` because `fptr` is not fully captured and `fptr.0.0` does not implement `Send`
 ...
 LL |         *fptr.0.0 = 20;
    |         --------- in Rust 2018, this closure captures all of `fptr`, but in Rust 2021, it will only capture `fptr.0.0`
@@ -40,14 +43,14 @@ LL |
 LL |
 LL |
 LL |
-LL |         *fptr.0.0 = 20;
+LL |
  ...
 
-error: changes to closure capture in Rust 2021 will affect `Clone` trait implementation for closure and drop order
-  --> $DIR/auto_traits.rs:66:13
+error: changes to closure capture in Rust 2021 will affect drop order and which traits the closure implements
+  --> $DIR/auto_traits.rs:67:13
    |
 LL |     let c = || {
-   |             ^^ in Rust 2018, this closure implements `Clone` as `f` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` as `f.1` does not implement `Clone`
+   |             ^^ in Rust 2018, this closure implements `Clone` as `f` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` because `f` is not fully captured and `f.1` does not implement `Clone`
 ...
 LL |         let f_1 = f.1;
    |                   --- in Rust 2018, this closure captures all of `f`, but in Rust 2021, it will only capture `f.1`
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.fixed
index a5652154682..7df0dd76b44 100644
--- a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.fixed
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.fixed
@@ -19,8 +19,9 @@ where
     let f = panic::AssertUnwindSafe(f);
     let result = panic::catch_unwind(move || {
         let _ = &f;
-        //~^ ERROR: `UnwindSafe`, `RefUnwindSafe` trait implementation for closure
-        //~| NOTE: in Rust 2018, this closure implements `UnwindSafe`, `RefUnwindSafe` as `f` implements `UnwindSafe`, `RefUnwindSafe`, but in Rust 2021, this closure will no longer implement `UnwindSafe`, `RefUnwindSafe` as `f.0` does not implement `UnwindSafe`, `RefUnwindSafe`
+        //~^ ERROR: changes to closure capture in Rust 2021 will affect which traits the closure implements [rust_2021_incompatible_closure_captures]
+        //~| NOTE: in Rust 2018, this closure implements `UnwindSafe`
+        //~| NOTE: in Rust 2018, this closure implements `RefUnwindSafe`
         //~| NOTE: for more information, see
         //~| HELP: add a dummy let to cause `f` to be fully captured
         f.0()
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.rs b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.rs
index d9acde073fc..d02fac7c669 100644
--- a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.rs
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.rs
@@ -18,8 +18,9 @@ where
 {
     let f = panic::AssertUnwindSafe(f);
     let result = panic::catch_unwind(move || {
-        //~^ ERROR: `UnwindSafe`, `RefUnwindSafe` trait implementation for closure
-        //~| NOTE: in Rust 2018, this closure implements `UnwindSafe`, `RefUnwindSafe` as `f` implements `UnwindSafe`, `RefUnwindSafe`, but in Rust 2021, this closure will no longer implement `UnwindSafe`, `RefUnwindSafe` as `f.0` does not implement `UnwindSafe`, `RefUnwindSafe`
+        //~^ ERROR: changes to closure capture in Rust 2021 will affect which traits the closure implements [rust_2021_incompatible_closure_captures]
+        //~| NOTE: in Rust 2018, this closure implements `UnwindSafe`
+        //~| NOTE: in Rust 2018, this closure implements `RefUnwindSafe`
         //~| NOTE: for more information, see
         //~| HELP: add a dummy let to cause `f` to be fully captured
         f.0()
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr
index 10816b7bc3a..74f85b6ebaa 100644
--- a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr
@@ -1,8 +1,11 @@
-error: changes to closure capture in Rust 2021 will affect `UnwindSafe`, `RefUnwindSafe` trait implementation for closure
+error: changes to closure capture in Rust 2021 will affect which traits the closure implements
   --> $DIR/mir_calls_to_shims.rs:20:38
    |
 LL |     let result = panic::catch_unwind(move || {
-   |                                      ^^^^^^^ in Rust 2018, this closure implements `UnwindSafe`, `RefUnwindSafe` as `f` implements `UnwindSafe`, `RefUnwindSafe`, but in Rust 2021, this closure will no longer implement `UnwindSafe`, `RefUnwindSafe` as `f.0` does not implement `UnwindSafe`, `RefUnwindSafe`
+   |                                      ^^^^^^^
+   |                                      |
+   |                                      in Rust 2018, this closure implements `UnwindSafe` as `f` implements `UnwindSafe`, but in Rust 2021, this closure will no longer implement `UnwindSafe` because `f` is not fully captured and `f.0` does not implement `UnwindSafe`
+   |                                      in Rust 2018, this closure implements `RefUnwindSafe` as `f` implements `RefUnwindSafe`, but in Rust 2021, this closure will no longer implement `RefUnwindSafe` because `f` is not fully captured and `f.0` does not implement `RefUnwindSafe`
 ...
 LL |         f.0()
    |         --- in Rust 2018, this closure captures all of `f`, but in Rust 2021, it will only capture `f.0`
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.fixed
index 11218eff133..2b86b0ddade 100644
--- a/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.fixed
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.fixed
@@ -18,7 +18,6 @@ impl Foo {
     }
 }
 
-
 struct S(Foo);
 
 #[derive(Clone)]
@@ -37,8 +36,8 @@ fn test_multi_issues() {
     let f2 = U(S(Foo::from("bar")), T(0));
     let c = || {
         let _ = (&f1, &f2);
-        //~^ ERROR: `Clone` trait implementation for closure and drop order
-        //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` as `f1.0` does not implement `Clone`
+        //~^ ERROR: changes to closure capture in Rust 2021
+        //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`
         //~| NOTE: for more information, see
         //~| HELP: add a dummy let to cause `f1`, `f2` to be fully captured
         let _f_1 = f1.0;
@@ -57,8 +56,8 @@ fn test_capturing_all_disjoint_fields_individually() {
     let f1 = U(S(Foo::from("foo")), T(0));
     let c = || {
         let _ = &f1;
-        //~^ ERROR: `Clone` trait implementation for closure
-        //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` as `f1.0` does not implement `Clone`
+        //~^ ERROR: changes to closure capture in Rust 2021 will affect which traits the closure implements [rust_2021_incompatible_closure_captures]
+        //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`
         //~| NOTE: for more information, see
         //~| HELP: add a dummy let to cause `f1` to be fully captured
         let _f_1 = f1.0;
@@ -83,9 +82,9 @@ fn test_capturing_several_disjoint_fields_individually_1() {
     let f1 = U1(S(Foo::from("foo")), T(0), S(Foo::from("bar")));
     let c = || {
         let _ = &f1;
-        //~^ ERROR: `Clone` trait implementation for closure
-        //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` as `f1.0` does not implement `Clone`
-        //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` as `f1.2` does not implement `Clone`
+        //~^ ERROR: changes to closure capture in Rust 2021 will affect which traits the closure implements [rust_2021_incompatible_closure_captures]
+        //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`
+        //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`
         //~| NOTE: for more information, see
         //~| HELP: add a dummy let to cause `f1` to be fully captured
         let _f_0 = f1.0;
@@ -103,8 +102,8 @@ fn test_capturing_several_disjoint_fields_individually_2() {
     let f1 = U1(S(Foo::from("foo")), T(0), S(Foo::from("bar")));
     let c = || {
         let _ = &f1;
-        //~^ ERROR: `Clone` trait implementation for closure and drop order
-        //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` as `f1.0` does not implement `Clone`
+        //~^ ERROR: changes to closure capture in Rust 2021 will affect drop order and which traits the closure implements
+        //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`
         //~| NOTE: for more information, see
         //~| HELP: add a dummy let to cause `f1` to be fully captured
         let _f_0 = f1.0;
@@ -136,9 +135,10 @@ fn test_multi_traits_issues() {
     let mut f2 = 10;
     let fptr2 = SendPointer(&mut f2 as *mut i32);
     thread::spawn(move || { let _ = (&fptr1, &fptr2); unsafe {
-        //~^ ERROR: `Sync`, `Send` trait implementation for closure
-        //~| NOTE: in Rust 2018, this closure implements `Sync`, `Send` as `fptr1` implements `Sync`, `Send`, but in Rust 2021, this closure will no longer implement `Sync`, `Send` as `fptr1.0.0` does not implement `Sync`, `Send`
-        //~| NOTE: in Rust 2018, this closure implements `Send` as `fptr2` implements `Send`, but in Rust 2021, this closure will no longer implement `Send` as `fptr2.0` does not implement `Send`
+        //~^ ERROR: changes to closure capture in Rust 2021
+        //~| NOTE: in Rust 2018, this closure implements `Sync` as `fptr1` implements `Sync`
+        //~| NOTE: in Rust 2018, this closure implements `Send` as `fptr1` implements `Send`
+        //~| NOTE: in Rust 2018, this closure implements `Send` as `fptr2` implements `Send`
         //~| NOTE: for more information, see
         //~| HELP: add a dummy let to cause `fptr1`, `fptr2` to be fully captured
         *fptr1.0.0 = 20;
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.rs b/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.rs
index 02f2faa2e87..3cac4abfad7 100644
--- a/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.rs
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.rs
@@ -18,7 +18,6 @@ impl Foo {
     }
 }
 
-
 struct S(Foo);
 
 #[derive(Clone)]
@@ -36,8 +35,8 @@ fn test_multi_issues() {
     let f1 = U(S(Foo::from("foo")), T(0));
     let f2 = U(S(Foo::from("bar")), T(0));
     let c = || {
-        //~^ ERROR: `Clone` trait implementation for closure and drop order
-        //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` as `f1.0` does not implement `Clone`
+        //~^ ERROR: changes to closure capture in Rust 2021
+        //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`
         //~| NOTE: for more information, see
         //~| HELP: add a dummy let to cause `f1`, `f2` to be fully captured
         let _f_1 = f1.0;
@@ -55,8 +54,8 @@ fn test_multi_issues() {
 fn test_capturing_all_disjoint_fields_individually() {
     let f1 = U(S(Foo::from("foo")), T(0));
     let c = || {
-        //~^ ERROR: `Clone` trait implementation for closure
-        //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` as `f1.0` does not implement `Clone`
+        //~^ ERROR: changes to closure capture in Rust 2021 will affect which traits the closure implements [rust_2021_incompatible_closure_captures]
+        //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`
         //~| NOTE: for more information, see
         //~| HELP: add a dummy let to cause `f1` to be fully captured
         let _f_1 = f1.0;
@@ -80,9 +79,9 @@ impl Clone for U1 {
 fn test_capturing_several_disjoint_fields_individually_1() {
     let f1 = U1(S(Foo::from("foo")), T(0), S(Foo::from("bar")));
     let c = || {
-        //~^ ERROR: `Clone` trait implementation for closure
-        //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` as `f1.0` does not implement `Clone`
-        //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` as `f1.2` does not implement `Clone`
+        //~^ ERROR: changes to closure capture in Rust 2021 will affect which traits the closure implements [rust_2021_incompatible_closure_captures]
+        //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`
+        //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`
         //~| NOTE: for more information, see
         //~| HELP: add a dummy let to cause `f1` to be fully captured
         let _f_0 = f1.0;
@@ -99,8 +98,8 @@ fn test_capturing_several_disjoint_fields_individually_1() {
 fn test_capturing_several_disjoint_fields_individually_2() {
     let f1 = U1(S(Foo::from("foo")), T(0), S(Foo::from("bar")));
     let c = || {
-        //~^ ERROR: `Clone` trait implementation for closure and drop order
-        //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` as `f1.0` does not implement `Clone`
+        //~^ ERROR: changes to closure capture in Rust 2021 will affect drop order and which traits the closure implements
+        //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`
         //~| NOTE: for more information, see
         //~| HELP: add a dummy let to cause `f1` to be fully captured
         let _f_0 = f1.0;
@@ -132,9 +131,10 @@ fn test_multi_traits_issues() {
     let mut f2 = 10;
     let fptr2 = SendPointer(&mut f2 as *mut i32);
     thread::spawn(move || unsafe {
-        //~^ ERROR: `Sync`, `Send` trait implementation for closure
-        //~| NOTE: in Rust 2018, this closure implements `Sync`, `Send` as `fptr1` implements `Sync`, `Send`, but in Rust 2021, this closure will no longer implement `Sync`, `Send` as `fptr1.0.0` does not implement `Sync`, `Send`
-        //~| NOTE: in Rust 2018, this closure implements `Send` as `fptr2` implements `Send`, but in Rust 2021, this closure will no longer implement `Send` as `fptr2.0` does not implement `Send`
+        //~^ ERROR: changes to closure capture in Rust 2021
+        //~| NOTE: in Rust 2018, this closure implements `Sync` as `fptr1` implements `Sync`
+        //~| NOTE: in Rust 2018, this closure implements `Send` as `fptr1` implements `Send`
+        //~| NOTE: in Rust 2018, this closure implements `Send` as `fptr2` implements `Send`
         //~| NOTE: for more information, see
         //~| HELP: add a dummy let to cause `fptr1`, `fptr2` to be fully captured
         *fptr1.0.0 = 20;
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.stderr
index d425db5aa99..0008f1b2c07 100644
--- a/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.stderr
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.stderr
@@ -1,8 +1,8 @@
-error: changes to closure capture in Rust 2021 will affect `Clone` trait implementation for closure and drop order
-  --> $DIR/multi_diagnostics.rs:38:13
+error: changes to closure capture in Rust 2021 will affect drop order and which traits the closure implements
+  --> $DIR/multi_diagnostics.rs:37:13
    |
 LL |     let c = || {
-   |             ^^ in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` as `f1.0` does not implement `Clone`
+   |             ^^ in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` because `f1` is not fully captured and `f1.0` does not implement `Clone`
 ...
 LL |         let _f_1 = f1.0;
    |                    ---- in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.0`
@@ -25,11 +25,11 @@ LL ~     let c = || {
 LL +         let _ = (&f1, &f2);
    |
 
-error: changes to closure capture in Rust 2021 will affect `Clone` trait implementation for closure
-  --> $DIR/multi_diagnostics.rs:57:13
+error: changes to closure capture in Rust 2021 will affect which traits the closure implements
+  --> $DIR/multi_diagnostics.rs:56:13
    |
 LL |     let c = || {
-   |             ^^ in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` as `f1.0` does not implement `Clone`
+   |             ^^ in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` because `f1` is not fully captured and `f1.0` does not implement `Clone`
 ...
 LL |         let _f_1 = f1.0;
    |                    ---- in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.0`
@@ -41,14 +41,14 @@ LL ~     let c = || {
 LL +         let _ = &f1;
    |
 
-error: changes to closure capture in Rust 2021 will affect `Clone` trait implementation for closure
-  --> $DIR/multi_diagnostics.rs:82:13
+error: changes to closure capture in Rust 2021 will affect which traits the closure implements
+  --> $DIR/multi_diagnostics.rs:81:13
    |
 LL |     let c = || {
    |             ^^
    |             |
-   |             in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` as `f1.0` does not implement `Clone`
-   |             in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` as `f1.2` does not implement `Clone`
+   |             in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` because `f1` is not fully captured and `f1.0` does not implement `Clone`
+   |             in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` because `f1` is not fully captured and `f1.2` does not implement `Clone`
 ...
 LL |         let _f_0 = f1.0;
    |                    ---- in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.0`
@@ -63,11 +63,11 @@ LL ~     let c = || {
 LL +         let _ = &f1;
    |
 
-error: changes to closure capture in Rust 2021 will affect `Clone` trait implementation for closure and drop order
-  --> $DIR/multi_diagnostics.rs:101:13
+error: changes to closure capture in Rust 2021 will affect drop order and which traits the closure implements
+  --> $DIR/multi_diagnostics.rs:100:13
    |
 LL |     let c = || {
-   |             ^^ in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` as `f1.0` does not implement `Clone`
+   |             ^^ in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` because `f1` is not fully captured and `f1.0` does not implement `Clone`
 ...
 LL |         let _f_0 = f1.0;
    |                    ---- in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.0`
@@ -88,14 +88,15 @@ LL ~     let c = || {
 LL +         let _ = &f1;
    |
 
-error: changes to closure capture in Rust 2021 will affect `Sync`, `Send` trait implementation for closure
-  --> $DIR/multi_diagnostics.rs:134:19
+error: changes to closure capture in Rust 2021 will affect which traits the closure implements
+  --> $DIR/multi_diagnostics.rs:133:19
    |
 LL |     thread::spawn(move || unsafe {
    |                   ^^^^^^^^^^^^^^
    |                   |
-   |                   in Rust 2018, this closure implements `Sync`, `Send` as `fptr1` implements `Sync`, `Send`, but in Rust 2021, this closure will no longer implement `Sync`, `Send` as `fptr1.0.0` does not implement `Sync`, `Send`
-   |                   in Rust 2018, this closure implements `Send` as `fptr2` implements `Send`, but in Rust 2021, this closure will no longer implement `Send` as `fptr2.0` does not implement `Send`
+   |                   in Rust 2018, this closure implements `Sync` as `fptr1` implements `Sync`, but in Rust 2021, this closure will no longer implement `Sync` because `fptr1` is not fully captured and `fptr1.0.0` does not implement `Sync`
+   |                   in Rust 2018, this closure implements `Send` as `fptr1` implements `Send`, but in Rust 2021, this closure will no longer implement `Send` because `fptr1` is not fully captured and `fptr1.0.0` does not implement `Send`
+   |                   in Rust 2018, this closure implements `Send` as `fptr2` implements `Send`, but in Rust 2021, this closure will no longer implement `Send` because `fptr2` is not fully captured and `fptr2.0` does not implement `Send`
 ...
 LL |         *fptr1.0.0 = 20;
    |         ---------- in Rust 2018, this closure captures all of `fptr1`, but in Rust 2021, it will only capture `fptr1.0.0`
diff --git a/src/test/ui/extern/extern-methods.rs b/src/test/ui/extern/extern-methods.rs
index 3909b5301ad..22792c11366 100644
--- a/src/test/ui/extern/extern-methods.rs
+++ b/src/test/ui/extern/extern-methods.rs
@@ -1,5 +1,5 @@
 // run-pass
-// only-i686
+// only-x86
 
 trait A {
     extern "fastcall" fn test1(i: i32);
diff --git a/src/test/ui/extern/extern-thiscall.rs b/src/test/ui/extern/extern-thiscall.rs
index 8ce94aa71aa..717df57ec48 100644
--- a/src/test/ui/extern/extern-thiscall.rs
+++ b/src/test/ui/extern/extern-thiscall.rs
@@ -1,5 +1,5 @@
 // run-pass
-// only-i686
+// only-x86
 
 #![feature(abi_thiscall)]
 
diff --git a/src/test/ui/extern/extern-vectorcall.rs b/src/test/ui/extern/extern-vectorcall.rs
index f625eb0890f..a283573c9fb 100644
--- a/src/test/ui/extern/extern-vectorcall.rs
+++ b/src/test/ui/extern/extern-vectorcall.rs
@@ -1,7 +1,7 @@
 // run-pass
 // revisions: x64 x32
 // [x64]only-x86_64
-// [x32]only-i686
+// [x32]only-x86
 
 #![feature(abi_vectorcall)]
 
diff --git a/src/test/ui/lint/issue-90614-accept-allow-text-direction-codepoint-in-comment-lint.rs b/src/test/ui/lint/issue-90614-accept-allow-text-direction-codepoint-in-comment-lint.rs
new file mode 100644
index 00000000000..425e2703c94
--- /dev/null
+++ b/src/test/ui/lint/issue-90614-accept-allow-text-direction-codepoint-in-comment-lint.rs
@@ -0,0 +1,9 @@
+// check-pass
+// Allowing the code lint should work without warning and
+// the text flow char in the comment should be ignored.
+
+#![allow(text_direction_codepoint_in_comment)]
+
+fn main() {
+    // U+2066 LEFT-TO-RIGHT ISOLATE follows:⁦⁦
+}
diff --git a/src/tools/compiletest/src/header/tests.rs b/src/tools/compiletest/src/header/tests.rs
index 2485dbadab5..157b42e2d17 100644
--- a/src/tools/compiletest/src/header/tests.rs
+++ b/src/tools/compiletest/src/header/tests.rs
@@ -168,7 +168,7 @@ fn only_target() {
     let mut config = config();
     config.target = "x86_64-pc-windows-gnu".to_owned();
 
-    assert!(check_ignore(&config, "// only-i686"));
+    assert!(check_ignore(&config, "// only-x86"));
     assert!(check_ignore(&config, "// only-linux"));
     assert!(check_ignore(&config, "// only-msvc"));
     assert!(check_ignore(&config, "// only-32bit"));