diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs
index 8ec06feb5bf..a58caf2667b 100644
--- a/compiler/rustc_errors/src/emitter.rs
+++ b/compiler/rustc_errors/src/emitter.rs
@@ -195,6 +195,9 @@ pub trait Emitter {
 
     fn emit_future_breakage_report(&mut self, _diags: Vec<(FutureBreakage, Diagnostic)>) {}
 
+    /// Emit list of unused externs
+    fn emit_unused_externs(&mut self, _lint_level: &str, _unused_externs: &[&str]) {}
+
     /// Checks if should show explanations about "rustc --explain"
     fn should_show_explain(&self) -> bool {
         true
diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs
index 2bce1ac3c0a..40277006462 100644
--- a/compiler/rustc_errors/src/json.rs
+++ b/compiler/rustc_errors/src/json.rs
@@ -159,6 +159,19 @@ impl Emitter for JsonEmitter {
         }
     }
 
+    fn emit_unused_externs(&mut self, lint_level: &str, unused_externs: &[&str]) {
+        let data = UnusedExterns { lint_level, unused_extern_names: unused_externs };
+        let result = if self.pretty {
+            writeln!(&mut self.dst, "{}", as_pretty_json(&data))
+        } else {
+            writeln!(&mut self.dst, "{}", as_json(&data))
+        }
+        .and_then(|_| self.dst.flush());
+        if let Err(e) = result {
+            panic!("failed to print unused externs: {:?}", e);
+        }
+    }
+
     fn source_map(&self) -> Option<&Lrc<SourceMap>> {
         Some(&self.sm)
     }
@@ -322,6 +335,18 @@ struct FutureIncompatReport {
     future_incompat_report: Vec<FutureBreakageItem>,
 }
 
+// NOTE: Keep this in sync with the equivalent structs in rustdoc's
+// doctest component (as well as cargo).
+// We could unify this struct the one in rustdoc but they have different
+// ownership semantics, so doing so would create wasteful allocations.
+#[derive(Encodable)]
+struct UnusedExterns<'a, 'b, 'c> {
+    /// The severity level of the unused dependencies lint
+    lint_level: &'a str,
+    /// List of unused externs by their names.
+    unused_extern_names: &'b [&'c str],
+}
+
 impl Diagnostic {
     fn from_errors_diagnostic(diag: &crate::Diagnostic, je: &JsonEmitter) -> Diagnostic {
         let sugg = diag.suggestions.iter().map(|sugg| Diagnostic {
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index ac7353730ad..0d1f55a6b00 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -765,6 +765,10 @@ impl Handler {
         self.inner.borrow_mut().emitter.emit_future_breakage_report(diags)
     }
 
+    pub fn emit_unused_externs(&self, lint_level: &str, unused_externs: &[&str]) {
+        self.inner.borrow_mut().emit_unused_externs(lint_level, unused_externs)
+    }
+
     pub fn delay_as_bug(&self, diagnostic: Diagnostic) {
         self.inner.borrow_mut().delay_as_bug(diagnostic)
     }
@@ -839,6 +843,10 @@ impl HandlerInner {
         self.emitter.emit_artifact_notification(path, artifact_type);
     }
 
+    fn emit_unused_externs(&mut self, lint_level: &str, unused_externs: &[&str]) {
+        self.emitter.emit_unused_externs(lint_level, unused_externs);
+    }
+
     fn treat_err_as_bug(&self) -> bool {
         self.flags.treat_err_as_bug.map_or(false, |c| self.err_count() >= c.get())
     }
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index c693155994f..02e62a2cee9 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -16,6 +16,7 @@ use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
 use rustc_hir::definitions::Definitions;
 use rustc_hir::Crate;
 use rustc_lint::LintStore;
+use rustc_metadata::creader::CStore;
 use rustc_middle::arena::Arena;
 use rustc_middle::dep_graph::DepGraph;
 use rustc_middle::middle;
@@ -831,6 +832,12 @@ fn analysis(tcx: TyCtxt<'_>, cnum: CrateNum) -> Result<()> {
                 });
 
                 sess.time("looking_for_derive_registrar", || proc_macro_decls::find(tcx));
+
+                let cstore = tcx
+                    .cstore_as_any()
+                    .downcast_ref::<CStore>()
+                    .expect("`tcx.cstore` is not a `CStore`");
+                cstore.report_unused_deps(tcx);
             },
             {
                 par_iter(&tcx.hir().krate().modules).for_each(|(&module, _)| {
diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs
index 9578f5d5863..26db3a5f39d 100644
--- a/compiler/rustc_metadata/src/creader.rs
+++ b/compiler/rustc_metadata/src/creader.rs
@@ -46,6 +46,9 @@ pub struct CStore {
     /// This map is used to verify we get no hash conflicts between
     /// `StableCrateId` values.
     stable_crate_ids: FxHashMap<StableCrateId, CrateNum>,
+
+    /// Unused externs of the crate
+    unused_externs: Vec<Symbol>,
 }
 
 pub struct CrateLoader<'a> {
@@ -190,6 +193,27 @@ impl CStore {
     crate fn has_global_allocator(&self) -> bool {
         self.has_global_allocator
     }
+
+    pub fn report_unused_deps(&self, tcx: TyCtxt<'_>) {
+        // We put the check for the option before the lint_level_at_node call
+        // because the call mutates internal state and introducing it
+        // leads to some ui tests failing.
+        if !tcx.sess.opts.json_unused_externs {
+            return;
+        }
+        let level = tcx
+            .lint_level_at_node(lint::builtin::UNUSED_CRATE_DEPENDENCIES, rustc_hir::CRATE_HIR_ID)
+            .0;
+        if level != lint::Level::Allow {
+            let unused_externs =
+                self.unused_externs.iter().map(|ident| ident.to_ident_string()).collect::<Vec<_>>();
+            let unused_externs = unused_externs.iter().map(String::as_str).collect::<Vec<&str>>();
+            tcx.sess
+                .parse_sess
+                .span_diagnostic
+                .emit_unused_externs(level.as_str(), &unused_externs);
+        }
+    }
 }
 
 impl<'a> CrateLoader<'a> {
@@ -217,6 +241,7 @@ impl<'a> CrateLoader<'a> {
                 allocator_kind: None,
                 has_global_allocator: false,
                 stable_crate_ids,
+                unused_externs: Vec::new(),
             },
             used_extern_options: Default::default(),
         }
@@ -904,11 +929,17 @@ impl<'a> CrateLoader<'a> {
                 // Don't worry about pathless `--extern foo` sysroot references
                 continue;
             }
-            if self.used_extern_options.contains(&Symbol::intern(name)) {
+            let name_interned = Symbol::intern(name);
+            if self.used_extern_options.contains(&name_interned) {
                 continue;
             }
 
             // Got a real unused --extern
+            if self.sess.opts.json_unused_externs {
+                self.cstore.unused_externs.push(name_interned);
+                continue;
+            }
+
             let diag = match self.sess.opts.extern_dep_specs.get(name) {
                 Some(loc) => BuiltinLintDiagnostics::ExternDepSpec(name.clone(), loc.into()),
                 None => {
@@ -941,9 +972,9 @@ impl<'a> CrateLoader<'a> {
         self.inject_allocator_crate(krate);
         self.inject_panic_runtime(krate);
 
-        info!("{:?}", CrateDump(&self.cstore));
-
         self.report_unused_deps(krate);
+
+        info!("{:?}", CrateDump(&self.cstore));
     }
 
     pub fn process_extern_crate(
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index b2cf1aab112..a7e9fc468b4 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -1482,7 +1482,7 @@ pub enum StatementKind<'tcx> {
     ///
     /// Note that this also is emitted for regular `let` bindings to ensure that locals that are
     /// never accessed still get some sanity checks for, e.g., `let x: ! = ..;`
-    FakeRead(FakeReadCause, Box<Place<'tcx>>),
+    FakeRead(Box<(FakeReadCause, Place<'tcx>)>),
 
     /// Write the discriminant for a variant to the enum Place.
     SetDiscriminant { place: Box<Place<'tcx>>, variant_index: VariantIdx },
@@ -1575,7 +1575,12 @@ pub enum FakeReadCause {
 
     /// `let x: !; match x {}` doesn't generate any read of x so we need to
     /// generate a read of x to check that it is initialized and safe.
-    ForMatchedPlace,
+    ///
+    /// If a closure pattern matches a Place starting with an Upvar, then we introduce a
+    /// FakeRead for that Place outside the closure, in such a case this option would be
+    /// Some(closure_def_id).
+    /// Otherwise, the value of the optional DefId will be None.
+    ForMatchedPlace(Option<DefId>),
 
     /// A fake read of the RefWithinGuard version of a bind-by-value variable
     /// in a match guard to ensure that it's value hasn't change by the time
@@ -1594,7 +1599,12 @@ pub enum FakeReadCause {
     /// but in some cases it can affect the borrow checker, as in #53695.
     /// Therefore, we insert a "fake read" here to ensure that we get
     /// appropriate errors.
-    ForLet,
+    ///
+    /// If a closure pattern matches a Place starting with an Upvar, then we introduce a
+    /// FakeRead for that Place outside the closure, in such a case this option would be
+    /// Some(closure_def_id).
+    /// Otherwise, the value of the optional DefId will be None.
+    ForLet(Option<DefId>),
 
     /// If we have an index expression like
     ///
@@ -1618,7 +1628,9 @@ impl Debug for Statement<'_> {
         use self::StatementKind::*;
         match self.kind {
             Assign(box (ref place, ref rv)) => write!(fmt, "{:?} = {:?}", place, rv),
-            FakeRead(ref cause, ref place) => write!(fmt, "FakeRead({:?}, {:?})", cause, place),
+            FakeRead(box (ref cause, ref place)) => {
+                write!(fmt, "FakeRead({:?}, {:?})", cause, place)
+            }
             Retag(ref kind, ref place) => write!(
                 fmt,
                 "Retag({}{:?})",
diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs
index 32b4cd665d0..fd504f8c5d5 100644
--- a/compiler/rustc_middle/src/mir/visit.rs
+++ b/compiler/rustc_middle/src/mir/visit.rs
@@ -380,7 +380,7 @@ macro_rules! make_mir_visitor {
                     ) => {
                         self.visit_assign(place, rvalue, location);
                     }
-                    StatementKind::FakeRead(_, place) => {
+                    StatementKind::FakeRead(box (_, place)) => {
                         self.visit_place(
                             place,
                             PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect),
diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs
index eb942b195b2..d5deec82088 100644
--- a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs
@@ -1728,7 +1728,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         impl<'tcx> Visitor<'tcx> for FakeReadCauseFinder<'tcx> {
             fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) {
                 match statement {
-                    Statement { kind: StatementKind::FakeRead(cause, box place), .. }
+                    Statement { kind: StatementKind::FakeRead(box (cause, place)), .. }
                         if *place == self.place =>
                     {
                         self.cause = Some(*cause);
diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/explain_borrow.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/explain_borrow.rs
index 06e3f4b91f6..2a388b8a72b 100644
--- a/compiler/rustc_mir/src/borrow_check/diagnostics/explain_borrow.rs
+++ b/compiler/rustc_mir/src/borrow_check/diagnostics/explain_borrow.rs
@@ -515,7 +515,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 let block = &self.body.basic_blocks()[location.block];
 
                 let kind = if let Some(&Statement {
-                    kind: StatementKind::FakeRead(FakeReadCause::ForLet, _),
+                    kind: StatementKind::FakeRead(box (FakeReadCause::ForLet(_), _)),
                     ..
                 }) = block.statements.get(location.statement_index)
                 {
diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs
index 6ea0ba0a8e1..577d7d53814 100644
--- a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs
+++ b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs
@@ -7,8 +7,8 @@ use rustc_hir::def_id::DefId;
 use rustc_hir::lang_items::LangItemGroup;
 use rustc_hir::GeneratorKind;
 use rustc_middle::mir::{
-    AggregateKind, Constant, Field, Local, LocalInfo, LocalKind, Location, Operand, Place,
-    PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind,
+    AggregateKind, Constant, FakeReadCause, Field, Local, LocalInfo, LocalKind, Location, Operand,
+    Place, PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind,
 };
 use rustc_middle::ty::print::Print;
 use rustc_middle::ty::{self, DefIdTree, Instance, Ty, TyCtxt};
@@ -795,6 +795,24 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             }
         }
 
+        // StatementKind::FakeRead only contains a def_id if they are introduced as a result
+        // of pattern matching within a closure.
+        if let StatementKind::FakeRead(box (cause, ref place)) = stmt.kind {
+            match cause {
+                FakeReadCause::ForMatchedPlace(Some(closure_def_id))
+                | FakeReadCause::ForLet(Some(closure_def_id)) => {
+                    debug!("move_spans: def_id={:?} place={:?}", closure_def_id, place);
+                    let places = &[Operand::Move(*place)];
+                    if let Some((args_span, generator_kind, var_span)) =
+                        self.closure_span(closure_def_id, moved_place, places)
+                    {
+                        return ClosureUse { generator_kind, args_span, var_span };
+                    }
+                }
+                _ => {}
+            }
+        }
+
         let normal_ret =
             if moved_place.projection.iter().any(|p| matches!(p, ProjectionElem::Downcast(..))) {
                 PatUse(stmt.source_info.span)
diff --git a/compiler/rustc_mir/src/borrow_check/invalidation.rs b/compiler/rustc_mir/src/borrow_check/invalidation.rs
index 1055e30a3a4..9374741f837 100644
--- a/compiler/rustc_mir/src/borrow_check/invalidation.rs
+++ b/compiler/rustc_mir/src/borrow_check/invalidation.rs
@@ -63,7 +63,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> {
 
                 self.mutate_place(location, *lhs, Shallow(None), JustWrite);
             }
-            StatementKind::FakeRead(_, _) => {
+            StatementKind::FakeRead(box (_, _)) => {
                 // Only relevant for initialized/liveness/safety checks.
             }
             StatementKind::SetDiscriminant { place, variant_index: _ } => {
diff --git a/compiler/rustc_mir/src/borrow_check/mod.rs b/compiler/rustc_mir/src/borrow_check/mod.rs
index 583f73d5775..71db6abde43 100644
--- a/compiler/rustc_mir/src/borrow_check/mod.rs
+++ b/compiler/rustc_mir/src/borrow_check/mod.rs
@@ -574,7 +574,7 @@ impl<'cx, 'tcx> dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'tc
 
                 self.mutate_place(location, (*lhs, span), Shallow(None), JustWrite, flow_state);
             }
-            StatementKind::FakeRead(_, box ref place) => {
+            StatementKind::FakeRead(box (_, ref place)) => {
                 // Read for match doesn't access any memory and is used to
                 // assert that a place is safe and live. So we don't have to
                 // do any checks here.
diff --git a/compiler/rustc_mir/src/dataflow/move_paths/builder.rs b/compiler/rustc_mir/src/dataflow/move_paths/builder.rs
index 52b6e9f3753..538d8921869 100644
--- a/compiler/rustc_mir/src/dataflow/move_paths/builder.rs
+++ b/compiler/rustc_mir/src/dataflow/move_paths/builder.rs
@@ -293,8 +293,8 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
                 }
                 self.gather_rvalue(rval);
             }
-            StatementKind::FakeRead(_, place) => {
-                self.create_move_path(**place);
+            StatementKind::FakeRead(box (_, place)) => {
+                self.create_move_path(*place);
             }
             StatementKind::LlvmInlineAsm(ref asm) => {
                 for (output, kind) in iter::zip(&*asm.outputs, &asm.asm.outputs) {
diff --git a/compiler/rustc_mir/src/transform/coverage/spans.rs b/compiler/rustc_mir/src/transform/coverage/spans.rs
index e7097ce8619..324d826b375 100644
--- a/compiler/rustc_mir/src/transform/coverage/spans.rs
+++ b/compiler/rustc_mir/src/transform/coverage/spans.rs
@@ -683,10 +683,10 @@ pub(super) fn filtered_statement_span(
         // and `_1` is the `Place` for `somenum`.
         //
         // If and when the Issue is resolved, remove this special case match pattern:
-        StatementKind::FakeRead(cause, _) if cause == FakeReadCause::ForGuardBinding => None,
+        StatementKind::FakeRead(box (cause, _)) if cause == FakeReadCause::ForGuardBinding => None,
 
         // Retain spans from all other statements
-        StatementKind::FakeRead(_, _) // Not including `ForGuardBinding`
+        StatementKind::FakeRead(box (_, _)) // Not including `ForGuardBinding`
         | StatementKind::CopyNonOverlapping(..)
         | StatementKind::Assign(_)
         | StatementKind::SetDiscriminant { .. }
diff --git a/compiler/rustc_mir_build/src/build/cfg.rs b/compiler/rustc_mir_build/src/build/cfg.rs
index e562e52f841..fd4a783d12a 100644
--- a/compiler/rustc_mir_build/src/build/cfg.rs
+++ b/compiler/rustc_mir_build/src/build/cfg.rs
@@ -80,7 +80,7 @@ impl<'tcx> CFG<'tcx> {
         cause: FakeReadCause,
         place: Place<'tcx>,
     ) {
-        let kind = StatementKind::FakeRead(cause, box place);
+        let kind = StatementKind::FakeRead(box (cause, place));
         let stmt = Statement { source_info, kind };
         self.push(block, stmt);
     }
diff --git a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs
index 7f24a41b00b..822fbd91c94 100644
--- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs
+++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs
@@ -179,24 +179,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 //     match x { _ => () } // fake read of `x`
                 // };
                 // ```
-                // FIXME(RFC2229): Remove feature gate once diagnostics are improved
-                if this.tcx.features().capture_disjoint_fields {
-                    for (thir_place, cause, hir_id) in fake_reads.into_iter() {
-                        let place_builder =
-                            unpack!(block = this.as_place_builder(block, thir_place));
+                for (thir_place, cause, hir_id) in fake_reads.into_iter() {
+                    let place_builder = unpack!(block = this.as_place_builder(block, thir_place));
 
-                        if let Ok(place_builder_resolved) =
-                            place_builder.try_upvars_resolved(this.tcx, this.typeck_results)
-                        {
-                            let mir_place =
-                                place_builder_resolved.into_place(this.tcx, this.typeck_results);
-                            this.cfg.push_fake_read(
-                                block,
-                                this.source_info(this.tcx.hir().span(*hir_id)),
-                                *cause,
-                                mir_place,
-                            );
-                        }
+                    if let Ok(place_builder_resolved) =
+                        place_builder.try_upvars_resolved(this.tcx, this.typeck_results)
+                    {
+                        let mir_place =
+                            place_builder_resolved.into_place(this.tcx, this.typeck_results);
+                        this.cfg.push_fake_read(
+                            block,
+                            this.source_info(this.tcx.hir().span(*hir_id)),
+                            *cause,
+                            mir_place,
+                        );
                     }
                 }
 
diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs
index 73fd3f0feb5..0e422dc3c63 100644
--- a/compiler/rustc_mir_build/src/build/matches/mod.rs
+++ b/compiler/rustc_mir_build/src/build/matches/mod.rs
@@ -139,7 +139,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         // uninhabited value. If we get never patterns, those will check that
         // the place is initialized, and so this read would only be used to
         // check safety.
-        let cause_matched_place = FakeReadCause::ForMatchedPlace;
+        let cause_matched_place = FakeReadCause::ForMatchedPlace(None);
         let source_info = self.source_info(scrutinee_span);
 
         if let Ok(scrutinee_builder) =
@@ -400,7 +400,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
 
                 // Inject a fake read, see comments on `FakeReadCause::ForLet`.
                 let source_info = self.source_info(irrefutable_pat.span);
-                self.cfg.push_fake_read(block, source_info, FakeReadCause::ForLet, place);
+                self.cfg.push_fake_read(block, source_info, FakeReadCause::ForLet(None), place);
 
                 self.schedule_drop_for_binding(var, irrefutable_pat.span, OutsideGuard);
                 block.unit()
@@ -435,7 +435,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
 
                 // Inject a fake read, see comments on `FakeReadCause::ForLet`.
                 let pattern_source_info = self.source_info(irrefutable_pat.span);
-                let cause_let = FakeReadCause::ForLet;
+                let cause_let = FakeReadCause::ForLet(None);
                 self.cfg.push_fake_read(block, pattern_source_info, cause_let, place);
 
                 let ty_source_info = self.source_info(user_ty_span);
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index e39b9b9b33e..c7d561f760b 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -456,6 +456,10 @@ impl Externs {
     pub fn iter(&self) -> BTreeMapIter<'_, String, ExternEntry> {
         self.0.iter()
     }
+
+    pub fn len(&self) -> usize {
+        self.0.len()
+    }
 }
 
 impl ExternEntry {
@@ -698,6 +702,7 @@ impl Default for Options {
             remap_path_prefix: Vec::new(),
             edition: DEFAULT_EDITION,
             json_artifact_notifications: false,
+            json_unused_externs: false,
             pretty: None,
         }
     }
@@ -1196,15 +1201,23 @@ pub fn parse_color(matches: &getopts::Matches) -> ColorConfig {
     }
 }
 
+/// Possible json config files
+pub struct JsonConfig {
+    pub json_rendered: HumanReadableErrorType,
+    pub json_artifact_notifications: bool,
+    pub json_unused_externs: bool,
+}
+
 /// Parse the `--json` flag.
 ///
 /// The first value returned is how to render JSON diagnostics, and the second
 /// is whether or not artifact notifications are enabled.
-pub fn parse_json(matches: &getopts::Matches) -> (HumanReadableErrorType, bool) {
+pub fn parse_json(matches: &getopts::Matches) -> JsonConfig {
     let mut json_rendered: fn(ColorConfig) -> HumanReadableErrorType =
         HumanReadableErrorType::Default;
     let mut json_color = ColorConfig::Never;
     let mut json_artifact_notifications = false;
+    let mut json_unused_externs = false;
     for option in matches.opt_strs("json") {
         // For now conservatively forbid `--color` with `--json` since `--json`
         // won't actually be emitting any colors and anything colorized is
@@ -1221,6 +1234,7 @@ pub fn parse_json(matches: &getopts::Matches) -> (HumanReadableErrorType, bool)
                 "diagnostic-short" => json_rendered = HumanReadableErrorType::Short,
                 "diagnostic-rendered-ansi" => json_color = ColorConfig::Always,
                 "artifacts" => json_artifact_notifications = true,
+                "unused-externs" => json_unused_externs = true,
                 s => early_error(
                     ErrorOutputType::default(),
                     &format!("unknown `--json` option `{}`", s),
@@ -1228,7 +1242,12 @@ pub fn parse_json(matches: &getopts::Matches) -> (HumanReadableErrorType, bool)
             }
         }
     }
-    (json_rendered(json_color), json_artifact_notifications)
+
+    JsonConfig {
+        json_rendered: json_rendered(json_color),
+        json_artifact_notifications,
+        json_unused_externs,
+    }
 }
 
 /// Parses the `--error-format` flag.
@@ -1806,7 +1825,8 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
 
     let edition = parse_crate_edition(matches);
 
-    let (json_rendered, json_artifact_notifications) = parse_json(matches);
+    let JsonConfig { json_rendered, json_artifact_notifications, json_unused_externs } =
+        parse_json(matches);
 
     let error_format = parse_error_format(matches, color, json_rendered);
 
@@ -1819,6 +1839,14 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
     let mut debugging_opts = build_debugging_options(matches, error_format);
     check_debug_option_stability(&debugging_opts, error_format, json_rendered);
 
+    if !debugging_opts.unstable_options && json_unused_externs {
+        early_error(
+            error_format,
+            "the `-Z unstable-options` flag must also be passed to enable \
+            the flag `--json=unused-externs`",
+        );
+    }
+
     let output_types = parse_output_types(&debugging_opts, matches, error_format);
 
     let mut cg = build_codegen_options(matches, error_format);
@@ -1979,6 +2007,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
         remap_path_prefix,
         edition,
         json_artifact_notifications,
+        json_unused_externs,
         pretty,
     }
 }
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index 623062dd569..dd35cb97275 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -147,6 +147,9 @@ top_level_options!(
         // by the compiler.
         json_artifact_notifications: bool [TRACKED],
 
+        // `true` if we're emitting a JSON blob containing the unused externs
+        json_unused_externs: bool [UNTRACKED],
+
         pretty: Option<PpMode> [UNTRACKED],
     }
 );
diff --git a/compiler/rustc_typeck/src/expr_use_visitor.rs b/compiler/rustc_typeck/src/expr_use_visitor.rs
index ab286bacd81..02510cb6a44 100644
--- a/compiler/rustc_typeck/src/expr_use_visitor.rs
+++ b/compiler/rustc_typeck/src/expr_use_visitor.rs
@@ -280,9 +280,14 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
                 if needs_to_be_read {
                     self.borrow_expr(&discr, ty::ImmBorrow);
                 } else {
+                    let closure_def_id = match discr_place.place.base {
+                        PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id.to_def_id()),
+                        _ => None,
+                    };
+
                     self.delegate.fake_read(
                         discr_place.place.clone(),
-                        FakeReadCause::ForMatchedPlace,
+                        FakeReadCause::ForMatchedPlace(closure_def_id),
                         discr_place.hir_id,
                     );
 
@@ -578,9 +583,14 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
     }
 
     fn walk_arm(&mut self, discr_place: &PlaceWithHirId<'tcx>, arm: &hir::Arm<'_>) {
+        let closure_def_id = match discr_place.place.base {
+            PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id.to_def_id()),
+            _ => None,
+        };
+
         self.delegate.fake_read(
             discr_place.place.clone(),
-            FakeReadCause::ForMatchedPlace,
+            FakeReadCause::ForMatchedPlace(closure_def_id),
             discr_place.hir_id,
         );
         self.walk_pat(discr_place, &arm.pat);
@@ -595,9 +605,14 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
     /// Walks a pat that occurs in isolation (i.e., top-level of fn argument or
     /// let binding, and *not* a match arm or nested pat.)
     fn walk_irrefutable_pat(&mut self, discr_place: &PlaceWithHirId<'tcx>, pat: &hir::Pat<'_>) {
+        let closure_def_id = match discr_place.place.base {
+            PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id.to_def_id()),
+            _ => None,
+        };
+
         self.delegate.fake_read(
             discr_place.place.clone(),
-            FakeReadCause::ForLet,
+            FakeReadCause::ForLet(closure_def_id),
             discr_place.hir_id,
         );
         self.walk_pat(discr_place, pat);
diff --git a/library/alloc/src/collections/btree/node.rs b/library/alloc/src/collections/btree/node.rs
index 9a7119470f3..f330a1bb3dc 100644
--- a/library/alloc/src/collections/btree/node.rs
+++ b/library/alloc/src/collections/btree/node.rs
@@ -128,106 +128,6 @@ impl<K, V> InternalNode<K, V> {
 /// is not a separate type and has no destructor.
 type BoxedNode<K, V> = NonNull<LeafNode<K, V>>;
 
-/// The root node of an owned tree.
-///
-/// Note that this does not have a destructor, and must be cleaned up manually.
-pub type Root<K, V> = NodeRef<marker::Owned, K, V, marker::LeafOrInternal>;
-
-impl<K, V> Root<K, V> {
-    /// Returns a new owned tree, with its own root node that is initially empty.
-    pub fn new() -> Self {
-        NodeRef::new_leaf().forget_type()
-    }
-}
-
-impl<K, V> NodeRef<marker::Owned, K, V, marker::Leaf> {
-    fn new_leaf() -> Self {
-        Self::from_new_leaf(LeafNode::new())
-    }
-
-    fn from_new_leaf(leaf: Box<LeafNode<K, V>>) -> Self {
-        NodeRef { height: 0, node: NonNull::from(Box::leak(leaf)), _marker: PhantomData }
-    }
-}
-
-impl<K, V> NodeRef<marker::Owned, K, V, marker::Internal> {
-    fn new_internal(child: Root<K, V>) -> Self {
-        let mut new_node = unsafe { InternalNode::new() };
-        new_node.edges[0].write(child.node);
-        unsafe { NodeRef::from_new_internal(new_node, child.height + 1) }
-    }
-
-    /// # Safety
-    /// `height` must not be zero.
-    unsafe fn from_new_internal(internal: Box<InternalNode<K, V>>, height: usize) -> Self {
-        debug_assert!(height > 0);
-        let node = NonNull::from(Box::leak(internal)).cast();
-        let mut this = NodeRef { height, node, _marker: PhantomData };
-        this.borrow_mut().correct_all_childrens_parent_links();
-        this
-    }
-}
-
-impl<K, V, Type> NodeRef<marker::Owned, K, V, Type> {
-    /// Mutably borrows the owned root node. Unlike `reborrow_mut`, this is safe
-    /// because the return value cannot be used to destroy the root, and there
-    /// cannot be other references to the tree.
-    pub fn borrow_mut(&mut self) -> NodeRef<marker::Mut<'_>, K, V, Type> {
-        NodeRef { height: self.height, node: self.node, _marker: PhantomData }
-    }
-
-    /// Slightly mutably borrows the owned root node.
-    pub fn borrow_valmut(&mut self) -> NodeRef<marker::ValMut<'_>, K, V, Type> {
-        NodeRef { height: self.height, node: self.node, _marker: PhantomData }
-    }
-
-    /// Irreversibly transitions to a reference that permits traversal and offers
-    /// destructive methods and little else.
-    pub fn into_dying(self) -> NodeRef<marker::Dying, K, V, Type> {
-        NodeRef { height: self.height, node: self.node, _marker: PhantomData }
-    }
-}
-
-impl<K, V> NodeRef<marker::Owned, K, V, marker::LeafOrInternal> {
-    /// Adds a new internal node with a single edge pointing to the previous root node,
-    /// make that new node the root node, and return it. This increases the height by 1
-    /// and is the opposite of `pop_internal_level`.
-    pub fn push_internal_level(&mut self) -> NodeRef<marker::Mut<'_>, K, V, marker::Internal> {
-        super::mem::take_mut(self, |old_root| NodeRef::new_internal(old_root).forget_type());
-
-        // `self.borrow_mut()`, except that we just forgot we're internal now:
-        NodeRef { height: self.height, node: self.node, _marker: PhantomData }
-    }
-
-    /// Removes the internal root node, using its first child as the new root node.
-    /// As it is intended only to be called when the root node has only one child,
-    /// no cleanup is done on any of the keys, values and other children.
-    /// This decreases the height by 1 and is the opposite of `push_internal_level`.
-    ///
-    /// Requires exclusive access to the `Root` object but not to the root node;
-    /// it will not invalidate other handles or references to the root node.
-    ///
-    /// Panics if there is no internal level, i.e., if the root node is a leaf.
-    pub fn pop_internal_level(&mut self) {
-        assert!(self.height > 0);
-
-        let top = self.node;
-
-        // SAFETY: we asserted to be internal.
-        let internal_self = unsafe { self.borrow_mut().cast_to_internal_unchecked() };
-        // SAFETY: we borrowed `self` exclusively and its borrow type is exclusive.
-        let internal_node = unsafe { &mut *NodeRef::as_internal_ptr(&internal_self) };
-        // SAFETY: the first edge is always initialized.
-        self.node = unsafe { internal_node.edges[0].assume_init_read() };
-        self.height -= 1;
-        self.clear_parent_link();
-
-        unsafe {
-            Global.deallocate(top.cast(), Layout::new::<InternalNode<K, V>>());
-        }
-    }
-}
-
 // N.B. `NodeRef` is always covariant in `K` and `V`, even when the `BorrowType`
 // is `Mut`. This is technically wrong, but cannot result in any unsafety due to
 // internal use of `NodeRef` because we stay completely generic over `K` and `V`.
@@ -292,6 +192,11 @@ pub struct NodeRef<BorrowType, K, V, Type> {
     _marker: PhantomData<(BorrowType, Type)>,
 }
 
+/// The root node of an owned tree.
+///
+/// Note that this does not have a destructor, and must be cleaned up manually.
+pub type Root<K, V> = NodeRef<marker::Owned, K, V, marker::LeafOrInternal>;
+
 impl<'a, K: 'a, V: 'a, Type> Copy for NodeRef<marker::Immut<'a>, K, V, Type> {}
 impl<'a, K: 'a, V: 'a, Type> Clone for NodeRef<marker::Immut<'a>, K, V, Type> {
     fn clone(&self) -> Self {
@@ -307,6 +212,34 @@ unsafe impl<'a, K: Send + 'a, V: Send + 'a, Type> Send for NodeRef<marker::ValMu
 unsafe impl<K: Send, V: Send, Type> Send for NodeRef<marker::Owned, K, V, Type> {}
 unsafe impl<K: Send, V: Send, Type> Send for NodeRef<marker::Dying, K, V, Type> {}
 
+impl<K, V> NodeRef<marker::Owned, K, V, marker::Leaf> {
+    fn new_leaf() -> Self {
+        Self::from_new_leaf(LeafNode::new())
+    }
+
+    fn from_new_leaf(leaf: Box<LeafNode<K, V>>) -> Self {
+        NodeRef { height: 0, node: NonNull::from(Box::leak(leaf)), _marker: PhantomData }
+    }
+}
+
+impl<K, V> NodeRef<marker::Owned, K, V, marker::Internal> {
+    fn new_internal(child: Root<K, V>) -> Self {
+        let mut new_node = unsafe { InternalNode::new() };
+        new_node.edges[0].write(child.node);
+        unsafe { NodeRef::from_new_internal(new_node, child.height + 1) }
+    }
+
+    /// # Safety
+    /// `height` must not be zero.
+    unsafe fn from_new_internal(internal: Box<InternalNode<K, V>>, height: usize) -> Self {
+        debug_assert!(height > 0);
+        let node = NonNull::from(Box::leak(internal)).cast();
+        let mut this = NodeRef { height, node, _marker: PhantomData };
+        this.borrow_mut().correct_all_childrens_parent_links();
+        this
+    }
+}
+
 impl<BorrowType, K, V> NodeRef<BorrowType, K, V, marker::Internal> {
     /// Unpack a node reference that was packed as `NodeRef::parent`.
     fn from_internal(node: NonNull<InternalNode<K, V>>, height: usize) -> Self {
@@ -420,6 +353,19 @@ impl<BorrowType: marker::BorrowType, K, V, Type> NodeRef<BorrowType, K, V, Type>
     }
 }
 
+impl<BorrowType, K, V, Type> NodeRef<BorrowType, K, V, Type> {
+    /// Could be a public implementation of PartialEq, but only used in this module.
+    fn eq(&self, other: &Self) -> bool {
+        let Self { node, height, _marker } = self;
+        if node.eq(&other.node) {
+            debug_assert_eq!(*height, other.height);
+            true
+        } else {
+            false
+        }
+    }
+}
+
 impl<'a, K: 'a, V: 'a, Type> NodeRef<marker::Immut<'a>, K, V, Type> {
     /// Exposes the leaf portion of any leaf or internal node in an immutable tree.
     fn into_leaf(self) -> &'a LeafNode<K, V> {
@@ -461,20 +407,6 @@ impl<K, V> NodeRef<marker::Dying, K, V, marker::LeafOrInternal> {
     }
 }
 
-impl<'a, K, V> NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal> {
-    /// Unsafely asserts to the compiler the static information that this node is a `Leaf`.
-    unsafe fn cast_to_leaf_unchecked(self) -> NodeRef<marker::Mut<'a>, K, V, marker::Leaf> {
-        debug_assert!(self.height == 0);
-        NodeRef { height: self.height, node: self.node, _marker: PhantomData }
-    }
-
-    /// Unsafely asserts to the compiler the static information that this node is an `Internal`.
-    unsafe fn cast_to_internal_unchecked(self) -> NodeRef<marker::Mut<'a>, K, V, marker::Internal> {
-        debug_assert!(self.height > 0);
-        NodeRef { height: self.height, node: self.node, _marker: PhantomData }
-    }
-}
-
 impl<'a, K, V, Type> NodeRef<marker::Mut<'a>, K, V, Type> {
     /// Temporarily takes out another, mutable reference to the same node. Beware, as
     /// this method is very dangerous, doubly so since it may not immediately appear
@@ -577,6 +509,22 @@ impl<'a, K: 'a, V: 'a, Type> NodeRef<marker::Mut<'a>, K, V, Type> {
     }
 }
 
+impl<'a, K, V> NodeRef<marker::Mut<'a>, K, V, marker::Internal> {
+    /// # Safety
+    /// Every item returned by `range` is a valid edge index for the node.
+    unsafe fn correct_childrens_parent_links<R: Iterator<Item = usize>>(&mut self, range: R) {
+        for i in range {
+            debug_assert!(i <= self.len());
+            unsafe { Handle::new_edge(self.reborrow_mut(), i) }.correct_parent_link();
+        }
+    }
+
+    fn correct_all_childrens_parent_links(&mut self) {
+        let len = self.len();
+        unsafe { self.correct_childrens_parent_links(0..=len) };
+    }
+}
+
 impl<'a, K: 'a, V: 'a> NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal> {
     /// Sets the node's link to its parent edge,
     /// without invalidating other references to the node.
@@ -596,6 +544,71 @@ impl<K, V> NodeRef<marker::Owned, K, V, marker::LeafOrInternal> {
     }
 }
 
+impl<K, V> NodeRef<marker::Owned, K, V, marker::LeafOrInternal> {
+    /// Returns a new owned tree, with its own root node that is initially empty.
+    pub fn new() -> Self {
+        NodeRef::new_leaf().forget_type()
+    }
+
+    /// Adds a new internal node with a single edge pointing to the previous root node,
+    /// make that new node the root node, and return it. This increases the height by 1
+    /// and is the opposite of `pop_internal_level`.
+    pub fn push_internal_level(&mut self) -> NodeRef<marker::Mut<'_>, K, V, marker::Internal> {
+        super::mem::take_mut(self, |old_root| NodeRef::new_internal(old_root).forget_type());
+
+        // `self.borrow_mut()`, except that we just forgot we're internal now:
+        NodeRef { height: self.height, node: self.node, _marker: PhantomData }
+    }
+
+    /// Removes the internal root node, using its first child as the new root node.
+    /// As it is intended only to be called when the root node has only one child,
+    /// no cleanup is done on any of the keys, values and other children.
+    /// This decreases the height by 1 and is the opposite of `push_internal_level`.
+    ///
+    /// Requires exclusive access to the `Root` object but not to the root node;
+    /// it will not invalidate other handles or references to the root node.
+    ///
+    /// Panics if there is no internal level, i.e., if the root node is a leaf.
+    pub fn pop_internal_level(&mut self) {
+        assert!(self.height > 0);
+
+        let top = self.node;
+
+        // SAFETY: we asserted to be internal.
+        let internal_self = unsafe { self.borrow_mut().cast_to_internal_unchecked() };
+        // SAFETY: we borrowed `self` exclusively and its borrow type is exclusive.
+        let internal_node = unsafe { &mut *NodeRef::as_internal_ptr(&internal_self) };
+        // SAFETY: the first edge is always initialized.
+        self.node = unsafe { internal_node.edges[0].assume_init_read() };
+        self.height -= 1;
+        self.clear_parent_link();
+
+        unsafe {
+            Global.deallocate(top.cast(), Layout::new::<InternalNode<K, V>>());
+        }
+    }
+}
+
+impl<K, V, Type> NodeRef<marker::Owned, K, V, Type> {
+    /// Mutably borrows the owned root node. Unlike `reborrow_mut`, this is safe
+    /// because the return value cannot be used to destroy the root, and there
+    /// cannot be other references to the tree.
+    pub fn borrow_mut(&mut self) -> NodeRef<marker::Mut<'_>, K, V, Type> {
+        NodeRef { height: self.height, node: self.node, _marker: PhantomData }
+    }
+
+    /// Slightly mutably borrows the owned root node.
+    pub fn borrow_valmut(&mut self) -> NodeRef<marker::ValMut<'_>, K, V, Type> {
+        NodeRef { height: self.height, node: self.node, _marker: PhantomData }
+    }
+
+    /// Irreversibly transitions to a reference that permits traversal and offers
+    /// destructive methods and little else.
+    pub fn into_dying(self) -> NodeRef<marker::Dying, K, V, Type> {
+        NodeRef { height: self.height, node: self.node, _marker: PhantomData }
+    }
+}
+
 impl<'a, K: 'a, V: 'a> NodeRef<marker::Mut<'a>, K, V, marker::Leaf> {
     /// Adds a key-value pair to the end of the node.
     pub fn push(&mut self, key: K, val: V) {
@@ -610,22 +623,6 @@ impl<'a, K: 'a, V: 'a> NodeRef<marker::Mut<'a>, K, V, marker::Leaf> {
     }
 }
 
-impl<'a, K, V> NodeRef<marker::Mut<'a>, K, V, marker::Internal> {
-    /// # Safety
-    /// Every item returned by `range` is a valid edge index for the node.
-    unsafe fn correct_childrens_parent_links<R: Iterator<Item = usize>>(&mut self, range: R) {
-        for i in range {
-            debug_assert!(i <= self.len());
-            unsafe { Handle::new_edge(self.reborrow_mut(), i) }.correct_parent_link();
-        }
-    }
-
-    fn correct_all_childrens_parent_links(&mut self) {
-        let len = self.len();
-        unsafe { self.correct_childrens_parent_links(0..=len) };
-    }
-}
-
 impl<'a, K: 'a, V: 'a> NodeRef<marker::Mut<'a>, K, V, marker::Internal> {
     /// Adds a key-value pair, and an edge to go to the right of that pair,
     /// to the end of the node.
@@ -645,6 +642,20 @@ impl<'a, K: 'a, V: 'a> NodeRef<marker::Mut<'a>, K, V, marker::Internal> {
     }
 }
 
+impl<BorrowType, K, V> NodeRef<BorrowType, K, V, marker::Leaf> {
+    /// Removes any static information asserting that this node is a `Leaf` node.
+    pub fn forget_type(self) -> NodeRef<BorrowType, K, V, marker::LeafOrInternal> {
+        NodeRef { height: self.height, node: self.node, _marker: PhantomData }
+    }
+}
+
+impl<BorrowType, K, V> NodeRef<BorrowType, K, V, marker::Internal> {
+    /// Removes any static information asserting that this node is an `Internal` node.
+    pub fn forget_type(self) -> NodeRef<BorrowType, K, V, marker::LeafOrInternal> {
+        NodeRef { height: self.height, node: self.node, _marker: PhantomData }
+    }
+}
+
 impl<BorrowType, K, V> NodeRef<BorrowType, K, V, marker::LeafOrInternal> {
     /// Checks whether a node is an `Internal` node or a `Leaf` node.
     pub fn force(
@@ -669,6 +680,20 @@ impl<BorrowType, K, V> NodeRef<BorrowType, K, V, marker::LeafOrInternal> {
     }
 }
 
+impl<'a, K, V> NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal> {
+    /// Unsafely asserts to the compiler the static information that this node is a `Leaf`.
+    unsafe fn cast_to_leaf_unchecked(self) -> NodeRef<marker::Mut<'a>, K, V, marker::Leaf> {
+        debug_assert!(self.height == 0);
+        NodeRef { height: self.height, node: self.node, _marker: PhantomData }
+    }
+
+    /// Unsafely asserts to the compiler the static information that this node is an `Internal`.
+    unsafe fn cast_to_internal_unchecked(self) -> NodeRef<marker::Mut<'a>, K, V, marker::Internal> {
+        debug_assert!(self.height > 0);
+        NodeRef { height: self.height, node: self.node, _marker: PhantomData }
+    }
+}
+
 /// A reference to a specific key-value pair or edge within a node. The `Node` parameter
 /// must be a `NodeRef`, while the `Type` can either be `KV` (signifying a handle on a key-value
 /// pair) or `Edge` (signifying a handle on an edge).
@@ -722,19 +747,6 @@ impl<BorrowType, K, V, NodeType> Handle<NodeRef<BorrowType, K, V, NodeType>, mar
     }
 }
 
-impl<BorrowType, K, V, NodeType> NodeRef<BorrowType, K, V, NodeType> {
-    /// Could be a public implementation of PartialEq, but only used in this module.
-    fn eq(&self, other: &Self) -> bool {
-        let Self { node, height, _marker } = self;
-        if node.eq(&other.node) {
-            debug_assert_eq!(*height, other.height);
-            true
-        } else {
-            false
-        }
-    }
-}
-
 impl<BorrowType, K, V, NodeType, HandleType> PartialEq
     for Handle<NodeRef<BorrowType, K, V, NodeType>, HandleType>
 {
@@ -754,16 +766,6 @@ impl<BorrowType, K, V, NodeType, HandleType>
     }
 }
 
-impl<'a, K, V, Type> Handle<NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal>, Type> {
-    /// Unsafely asserts to the compiler the static information that the handle's node is a `Leaf`.
-    pub unsafe fn cast_to_leaf_unchecked(
-        self,
-    ) -> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, Type> {
-        let node = unsafe { self.node.cast_to_leaf_unchecked() };
-        Handle { node, idx: self.idx, _marker: PhantomData }
-    }
-}
-
 impl<'a, K, V, NodeType, HandleType> Handle<NodeRef<marker::Mut<'a>, K, V, NodeType>, HandleType> {
     /// Temporarily takes out another, mutable handle on the same location. Beware, as
     /// this method is very dangerous, doubly so since it may not immediately appear
@@ -1466,20 +1468,6 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> {
     }
 }
 
-impl<BorrowType, K, V> NodeRef<BorrowType, K, V, marker::Leaf> {
-    /// Removes any static information asserting that this node is a `Leaf` node.
-    pub fn forget_type(self) -> NodeRef<BorrowType, K, V, marker::LeafOrInternal> {
-        NodeRef { height: self.height, node: self.node, _marker: PhantomData }
-    }
-}
-
-impl<BorrowType, K, V> NodeRef<BorrowType, K, V, marker::Internal> {
-    /// Removes any static information asserting that this node is an `Internal` node.
-    pub fn forget_type(self) -> NodeRef<BorrowType, K, V, marker::LeafOrInternal> {
-        NodeRef { height: self.height, node: self.node, _marker: PhantomData }
-    }
-}
-
 impl<BorrowType, K, V> Handle<NodeRef<BorrowType, K, V, marker::Leaf>, marker::Edge> {
     pub fn forget_node_type(
         self,
@@ -1531,6 +1519,16 @@ impl<BorrowType, K, V, Type> Handle<NodeRef<BorrowType, K, V, marker::LeafOrInte
     }
 }
 
+impl<'a, K, V, Type> Handle<NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal>, Type> {
+    /// Unsafely asserts to the compiler the static information that the handle's node is a `Leaf`.
+    pub unsafe fn cast_to_leaf_unchecked(
+        self,
+    ) -> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, Type> {
+        let node = unsafe { self.node.cast_to_leaf_unchecked() };
+        Handle { node, idx: self.idx, _marker: PhantomData }
+    }
+}
+
 impl<'a, K, V> Handle<NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal>, marker::Edge> {
     /// Move the suffix after `self` from one node to another one. `right` must be empty.
     /// The first edge of `right` remains unchanged.
diff --git a/library/alloc/tests/vec.rs b/library/alloc/tests/vec.rs
index b926c697d58..4dcc5d30deb 100644
--- a/library/alloc/tests/vec.rs
+++ b/library/alloc/tests/vec.rs
@@ -1004,9 +1004,9 @@ fn test_from_iter_specialization_with_iterator_adapters() {
         .map_while(Option::Some)
         .peekable()
         .skip(1)
-        .map(|e| std::num::NonZeroUsize::new(e));
+        .map(|e| if e != usize::MAX { Ok(std::num::NonZeroUsize::new(e)) } else { Err(()) });
     assert_in_place_trait(&iter);
-    let sink = iter.collect::<Vec<_>>();
+    let sink = iter.collect::<Result<Vec<_>, _>>().unwrap();
     let sinkptr = sink.as_ptr();
     assert_eq!(srcptr, sinkptr as *const usize);
 }
@@ -1078,12 +1078,21 @@ fn test_from_iter_specialization_panic_during_drop_leaks() {
         }
     }
 
+    let mut to_free: *mut Droppable = core::ptr::null_mut();
+    let mut cap = 0;
+
     let _ = std::panic::catch_unwind(AssertUnwindSafe(|| {
-        let v = vec![Droppable::DroppedTwice(Box::new(123)), Droppable::PanicOnDrop];
+        let mut v = vec![Droppable::DroppedTwice(Box::new(123)), Droppable::PanicOnDrop];
+        to_free = v.as_mut_ptr();
+        cap = v.capacity();
         let _ = v.into_iter().take(0).collect::<Vec<_>>();
     }));
 
     assert_eq!(unsafe { DROP_COUNTER }, 1);
+    // clean up the leak to keep miri happy
+    unsafe {
+        drop(Vec::from_raw_parts(to_free, 0, cap));
+    }
 }
 
 #[test]
diff --git a/library/core/src/iter/adapters/mod.rs b/library/core/src/iter/adapters/mod.rs
index ba4050757cb..61d8351d59f 100644
--- a/library/core/src/iter/adapters/mod.rs
+++ b/library/core/src/iter/adapters/mod.rs
@@ -194,3 +194,26 @@ where
         self.try_fold(init, ok(fold)).unwrap()
     }
 }
+
+#[unstable(issue = "none", feature = "inplace_iteration")]
+unsafe impl<S: Iterator, I, E> SourceIter for ResultShunt<'_, I, E>
+where
+    I: SourceIter<Source = S>,
+{
+    type Source = S;
+
+    #[inline]
+    unsafe fn as_inner(&mut self) -> &mut S {
+        // SAFETY: unsafe function forwarding to unsafe function with the same requirements
+        unsafe { SourceIter::as_inner(&mut self.iter) }
+    }
+}
+
+// SAFETY: ResultShunt::next calls I::find, which has to advance `iter` in order to
+// return `Some(_)`. Since `iter` has type `I: InPlaceIterable` it's guaranteed that
+// at least one item will be moved out from the underlying source.
+#[unstable(issue = "none", feature = "inplace_iteration")]
+unsafe impl<I, T, E> InPlaceIterable for ResultShunt<'_, I, E> where
+    I: Iterator<Item = Result<T, E>> + InPlaceIterable
+{
+}
diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs
index 246e0ebbb2b..5e9cfe148f1 100644
--- a/src/librustdoc/config.rs
+++ b/src/librustdoc/config.rs
@@ -154,6 +154,8 @@ crate struct Options {
     /// If this option is set to `true`, rustdoc will only run checks and not generate
     /// documentation.
     crate run_check: bool,
+    /// Whether doctests should emit unused externs
+    crate json_unused_externs: bool,
 }
 
 impl fmt::Debug for Options {
@@ -352,7 +354,8 @@ impl Options {
         }
 
         let color = config::parse_color(&matches);
-        let (json_rendered, _artifacts) = config::parse_json(&matches);
+        let config::JsonConfig { json_rendered, json_unused_externs, .. } =
+            config::parse_json(&matches);
         let error_format = config::parse_error_format(&matches, color, json_rendered);
 
         let codegen_options = build_codegen_options(matches, error_format);
@@ -507,7 +510,6 @@ impl Options {
         let edition = config::parse_crate_edition(&matches);
 
         let mut id_map = html::markdown::IdMap::new();
-        id_map.populate(&html::render::INITIAL_IDS);
         let external_html = match ExternalHtml::load(
             &matches.opt_strs("html-in-header"),
             &matches.opt_strs("html-before-content"),
@@ -687,6 +689,7 @@ impl Options {
             },
             crate_name,
             output_format,
+            json_unused_externs,
         })
     }
 
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index 3d0ef028902..6f6ed0eb684 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -1,5 +1,5 @@
 use rustc_ast as ast;
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::{ColorConfig, ErrorReported};
 use rustc_hir as hir;
@@ -23,6 +23,8 @@ use std::panic;
 use std::path::PathBuf;
 use std::process::{self, Command, Stdio};
 use std::str;
+use std::sync::atomic::{AtomicUsize, Ordering};
+use std::sync::{Arc, Mutex};
 
 use crate::clean::Attributes;
 use crate::config::Options;
@@ -104,8 +106,10 @@ crate fn run(options: Options) -> Result<(), ErrorReported> {
 
     let mut test_args = options.test_args.clone();
     let display_warnings = options.display_warnings;
+    let externs = options.externs.clone();
+    let json_unused_externs = options.json_unused_externs;
 
-    let tests = interface::run_compiler(config, |compiler| {
+    let res = interface::run_compiler(config, |compiler| {
         compiler.enter(|queries| {
             let _lower_to_hir = queries.lower_to_hir()?;
 
@@ -151,12 +155,15 @@ crate fn run(options: Options) -> Result<(), ErrorReported> {
             });
             compiler.session().abort_if_errors();
 
-            let ret: Result<_, ErrorReported> = Ok(collector.tests);
+            let unused_extern_reports = collector.unused_extern_reports.clone();
+            let compiling_test_count = collector.compiling_test_count.load(Ordering::SeqCst);
+            let ret: Result<_, ErrorReported> =
+                Ok((collector.tests, unused_extern_reports, compiling_test_count));
             ret
         })
     });
-    let tests = match tests {
-        Ok(tests) => tests,
+    let (tests, unused_extern_reports, compiling_test_count) = match res {
+        Ok(res) => res,
         Err(ErrorReported) => return Err(ErrorReported),
     };
 
@@ -168,6 +175,44 @@ crate fn run(options: Options) -> Result<(), ErrorReported> {
         Some(testing::Options::new().display_output(display_warnings)),
     );
 
+    // Collect and warn about unused externs, but only if we've gotten
+    // reports for each doctest
+    if json_unused_externs {
+        let unused_extern_reports: Vec<_> =
+            std::mem::take(&mut unused_extern_reports.lock().unwrap());
+        if unused_extern_reports.len() == compiling_test_count {
+            let extern_names = externs.iter().map(|(name, _)| name).collect::<FxHashSet<&String>>();
+            let mut unused_extern_names = unused_extern_reports
+                .iter()
+                .map(|uexts| uexts.unused_extern_names.iter().collect::<FxHashSet<&String>>())
+                .fold(extern_names, |uextsa, uextsb| {
+                    uextsa.intersection(&uextsb).map(|v| *v).collect::<FxHashSet<&String>>()
+                })
+                .iter()
+                .map(|v| (*v).clone())
+                .collect::<Vec<String>>();
+            unused_extern_names.sort();
+            // Take the most severe lint level
+            let lint_level = unused_extern_reports
+                .iter()
+                .map(|uexts| uexts.lint_level.as_str())
+                .max_by_key(|v| match *v {
+                    "warn" => 1,
+                    "deny" => 2,
+                    "forbid" => 3,
+                    // The allow lint level is not expected,
+                    // as if allow is specified, no message
+                    // is to be emitted.
+                    v => unreachable!("Invalid lint level '{}'", v),
+                })
+                .unwrap_or("warn")
+                .to_string();
+            let uext = UnusedExterns { lint_level, unused_extern_names };
+            let unused_extern_json = serde_json::to_string(&uext).unwrap();
+            eprintln!("{}", unused_extern_json);
+        }
+    }
+
     Ok(())
 }
 
@@ -235,6 +280,18 @@ impl DirState {
     }
 }
 
+// NOTE: Keep this in sync with the equivalent structs in rustc
+// and cargo.
+// We could unify this struct the one in rustc but they have different
+// ownership semantics, so doing so would create wasteful allocations.
+#[derive(serde::Serialize, serde::Deserialize)]
+struct UnusedExterns {
+    /// Lint level of the unused_crate_dependencies lint
+    lint_level: String,
+    /// List of unused externs by their names.
+    unused_extern_names: Vec<String>,
+}
+
 fn run_test(
     test: &str,
     cratename: &str,
@@ -253,6 +310,7 @@ fn run_test(
     outdir: DirState,
     path: PathBuf,
     test_id: &str,
+    report_unused_externs: impl Fn(UnusedExterns),
 ) -> Result<(), TestFailure> {
     let (test, line_offset, supports_color) =
         make_test(test, Some(cratename), as_test_harness, opts, edition, Some(test_id));
@@ -278,6 +336,12 @@ fn run_test(
     if as_test_harness {
         compiler.arg("--test");
     }
+    if options.json_unused_externs && !compile_fail {
+        compiler.arg("--error-format=json");
+        compiler.arg("--json").arg("unused-externs");
+        compiler.arg("-Z").arg("unstable-options");
+        compiler.arg("-W").arg("unused_crate_dependencies");
+    }
     for lib_str in &options.lib_strs {
         compiler.arg("-L").arg(&lib_str);
     }
@@ -337,7 +401,26 @@ fn run_test(
             eprint!("{}", self.0);
         }
     }
-    let out = str::from_utf8(&output.stderr).unwrap();
+    let mut out_lines = str::from_utf8(&output.stderr)
+        .unwrap()
+        .lines()
+        .filter(|l| {
+            if let Ok(uext) = serde_json::from_str::<UnusedExterns>(l) {
+                report_unused_externs(uext);
+                false
+            } else {
+                true
+            }
+        })
+        .collect::<Vec<_>>();
+
+    // Add a \n to the end to properly terminate the last line,
+    // but only if there was output to be printed
+    if out_lines.len() > 0 {
+        out_lines.push("");
+    }
+
+    let out = out_lines.join("\n");
     let _bomb = Bomb(&out);
     match (output.status.success(), compile_fail) {
         (true, true) => {
@@ -721,6 +804,8 @@ crate struct Collector {
     source_map: Option<Lrc<SourceMap>>,
     filename: Option<PathBuf>,
     visited_tests: FxHashMap<(String, usize), usize>,
+    unused_extern_reports: Arc<Mutex<Vec<UnusedExterns>>>,
+    compiling_test_count: AtomicUsize,
 }
 
 impl Collector {
@@ -745,6 +830,8 @@ impl Collector {
             source_map,
             filename,
             visited_tests: FxHashMap::default(),
+            unused_extern_reports: Default::default(),
+            compiling_test_count: AtomicUsize::new(0),
         }
     }
 
@@ -791,6 +878,10 @@ impl Tester for Collector {
         let runtool_args = self.options.runtool_args.clone();
         let target = self.options.target.clone();
         let target_str = target.to_string();
+        let unused_externs = self.unused_extern_reports.clone();
+        if !config.compile_fail {
+            self.compiling_test_count.fetch_add(1, Ordering::SeqCst);
+        }
 
         // FIXME(#44940): if doctests ever support path remapping, then this filename
         // needs to be the result of `SourceMap::span_to_unmapped_path`.
@@ -846,6 +937,9 @@ impl Tester for Collector {
                 test_type: testing::TestType::DocTest,
             },
             testfn: testing::DynTestFn(box move || {
+                let report_unused_externs = |uext| {
+                    unused_externs.lock().unwrap().push(uext);
+                };
                 let res = run_test(
                     &test,
                     &cratename,
@@ -864,6 +958,7 @@ impl Tester for Collector {
                     outdir,
                     path,
                     &test_id,
+                    report_unused_externs,
                 );
 
                 if let Err(err) = res {
diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs
index 7e50d72e60f..3a4319d5d9a 100644
--- a/src/librustdoc/html/highlight.rs
+++ b/src/librustdoc/html/highlight.rs
@@ -189,7 +189,9 @@ impl<'a> Classifier<'a> {
             // leading identifier.
             TokenKind::Bang if self.in_macro => {
                 self.in_macro = false;
-                Class::Macro
+                sink(Highlight::Token { text, class: None });
+                sink(Highlight::ExitSpan);
+                return;
             }
 
             // Assume that '&' or '*' is the reference or dereference operator
@@ -298,7 +300,9 @@ impl<'a> Classifier<'a> {
             },
             TokenKind::Ident | TokenKind::RawIdent if lookahead == Some(TokenKind::Bang) => {
                 self.in_macro = true;
-                Class::Macro
+                sink(Highlight::EnterSpan { class: Class::Macro });
+                sink(Highlight::Token { text, class: None });
+                return;
             }
             TokenKind::Ident => match text {
                 "ref" | "mut" => Class::RefKeyWord,
diff --git a/src/librustdoc/html/highlight/fixtures/dos_line.html b/src/librustdoc/html/highlight/fixtures/dos_line.html
index 4400f85681d..1c8dbffe78c 100644
--- a/src/librustdoc/html/highlight/fixtures/dos_line.html
+++ b/src/librustdoc/html/highlight/fixtures/dos_line.html
@@ -1,3 +1,3 @@
 <span class="kw">pub</span> <span class="kw">fn</span> <span class="ident">foo</span>() {
-<span class="macro">println</span><span class="macro">!</span>(<span class="string">&quot;foo&quot;</span>);
+<span class="macro">println!</span>(<span class="string">&quot;foo&quot;</span>);
 }
diff --git a/src/librustdoc/html/highlight/fixtures/sample.html b/src/librustdoc/html/highlight/fixtures/sample.html
index d937246f466..4966e0ac6bb 100644
--- a/src/librustdoc/html/highlight/fixtures/sample.html
+++ b/src/librustdoc/html/highlight/fixtures/sample.html
@@ -17,11 +17,11 @@
     <span class="kw">let</span> <span class="kw">_</span> <span class="op">=</span> <span class="kw-2">&amp;</span><span class="ident">foo</span>;
     <span class="kw">let</span> <span class="kw">_</span> <span class="op">=</span> <span class="op">&amp;&amp;</span><span class="ident">foo</span>;
     <span class="kw">let</span> <span class="kw">_</span> <span class="op">=</span> <span class="kw-2">*</span><span class="ident">foo</span>;
-    <span class="macro">mac</span><span class="macro">!</span>(<span class="ident">foo</span>, <span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">bar</span>);
-    <span class="macro">assert</span><span class="macro">!</span>(<span class="self">self</span>.<span class="ident">length</span> <span class="op">&lt;</span> <span class="ident">N</span> <span class="op">&amp;&amp;</span> <span class="ident">index</span> <span class="op">&lt;</span><span class="op">=</span> <span class="self">self</span>.<span class="ident">length</span>);
+    <span class="macro">mac!</span>(<span class="ident">foo</span>, <span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">bar</span>);
+    <span class="macro">assert!</span>(<span class="self">self</span>.<span class="ident">length</span> <span class="op">&lt;</span> <span class="ident">N</span> <span class="op">&amp;&amp;</span> <span class="ident">index</span> <span class="op">&lt;</span><span class="op">=</span> <span class="self">self</span>.<span class="ident">length</span>);
 }
 
-<span class="macro">macro_rules</span><span class="macro">!</span> <span class="ident">bar</span> {
+<span class="macro">macro_rules!</span> <span class="ident">bar</span> {
     (<span class="macro-nonterminal">$</span><span class="macro-nonterminal">foo</span>:<span class="ident">tt</span>) <span class="op">=</span><span class="op">&gt;</span> {};
 }
 </code></pre>
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index cdccb1c8581..509f1730557 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -1356,6 +1356,9 @@ fn init_id_map() -> FxHashMap<String, usize> {
     map.insert("rustdoc-vars".to_owned(), 1);
     map.insert("sidebar-vars".to_owned(), 1);
     map.insert("copy-path".to_owned(), 1);
+    map.insert("help".to_owned(), 1);
+    map.insert("TOC".to_owned(), 1);
+    map.insert("render-detail".to_owned(), 1);
     // This is the list of IDs used by rustdoc sections.
     map.insert("fields".to_owned(), 1);
     map.insert("variants".to_owned(), 1);
@@ -1365,6 +1368,12 @@ fn init_id_map() -> FxHashMap<String, usize> {
     map.insert("trait-implementations".to_owned(), 1);
     map.insert("synthetic-implementations".to_owned(), 1);
     map.insert("blanket-implementations".to_owned(), 1);
+    map.insert("associated-types".to_owned(), 1);
+    map.insert("associated-const".to_owned(), 1);
+    map.insert("required-methods".to_owned(), 1);
+    map.insert("provided-methods".to_owned(), 1);
+    map.insert("implementors".to_owned(), 1);
+    map.insert("synthetic-implementors".to_owned(), 1);
     map
 }
 
@@ -1373,12 +1382,6 @@ impl IdMap {
         IdMap { map: init_id_map() }
     }
 
-    crate fn populate<I: IntoIterator<Item = S>, S: AsRef<str> + ToString>(&mut self, ids: I) {
-        for id in ids {
-            let _ = self.derive(id);
-        }
-    }
-
     crate fn derive<S: AsRef<str> + ToString>(&mut self, candidate: S) -> String {
         let id = match self.map.get_mut(candidate.as_ref()) {
             None => candidate.to_string(),
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index 07c850a20a9..1a993f360a1 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -18,7 +18,7 @@ use super::print_item::{full_path, item_path, print_item};
 use super::write_shared::write_shared;
 use super::{
     print_sidebar, settings, AllTypes, NameDoc, SharedContext, StylePath, BASIC_KEYWORDS,
-    CURRENT_DEPTH, INITIAL_IDS,
+    CURRENT_DEPTH,
 };
 
 use crate::clean::{self, AttributesExt};
@@ -423,14 +423,11 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
     }
 
     fn make_child_renderer(&self) -> Self {
-        let mut id_map = IdMap::new();
-        id_map.populate(&INITIAL_IDS);
-
         Self {
             current: self.current.clone(),
             dst: self.dst.clone(),
             render_redirect_pages: self.render_redirect_pages,
-            id_map: RefCell::new(id_map),
+            id_map: RefCell::new(IdMap::new()),
             deref_id_map: RefCell::new(FxHashMap::default()),
             shared: Rc::clone(&self.shared),
             cache: Rc::clone(&self.cache),
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index a8a08fb23e0..7b656baa1b4 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -283,24 +283,6 @@ crate struct StylePath {
 
 thread_local!(crate static CURRENT_DEPTH: Cell<usize> = Cell::new(0));
 
-crate const INITIAL_IDS: [&'static str; 15] = [
-    "main",
-    "search",
-    "help",
-    "TOC",
-    "render-detail",
-    "associated-types",
-    "associated-const",
-    "required-methods",
-    "provided-methods",
-    "implementors",
-    "synthetic-implementors",
-    "implementors-list",
-    "synthetic-implementors-list",
-    "methods",
-    "implementations",
-];
-
 fn write_srclink(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer) {
     if let Some(l) = cx.src_href(item) {
         write!(buf, "<a class=\"srclink\" href=\"{}\" title=\"goto source code\">[src]</a>", l)
diff --git a/src/test/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir b/src/test/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir
index a9568112620..9fa478f8a82 100644
--- a/src/test/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir
+++ b/src/test/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir
@@ -130,12 +130,12 @@ fn address_of_reborrow() -> () {
         StorageLive(_2);                 // scope 0 at $DIR/address-of.rs:4:14: 4:21
         _2 = [const 0_i32; 10];          // scope 0 at $DIR/address-of.rs:4:14: 4:21
         _1 = &_2;                        // scope 0 at $DIR/address-of.rs:4:13: 4:21
-        FakeRead(ForLet, _1);            // scope 0 at $DIR/address-of.rs:4:9: 4:10
+        FakeRead(ForLet(None), _1);      // scope 0 at $DIR/address-of.rs:4:9: 4:10
         StorageLive(_3);                 // scope 1 at $DIR/address-of.rs:5:9: 5:14
         StorageLive(_4);                 // scope 1 at $DIR/address-of.rs:5:22: 5:29
         _4 = [const 0_i32; 10];          // scope 1 at $DIR/address-of.rs:5:22: 5:29
         _3 = &mut _4;                    // scope 1 at $DIR/address-of.rs:5:17: 5:29
-        FakeRead(ForLet, _3);            // scope 1 at $DIR/address-of.rs:5:9: 5:14
+        FakeRead(ForLet(None), _3);      // scope 1 at $DIR/address-of.rs:5:9: 5:14
         StorageLive(_5);                 // scope 2 at $DIR/address-of.rs:7:5: 7:18
         StorageLive(_6);                 // scope 2 at $DIR/address-of.rs:7:5: 7:18
         _6 = &raw const (*_1);           // scope 2 at $DIR/address-of.rs:7:5: 7:6
@@ -170,25 +170,25 @@ fn address_of_reborrow() -> () {
         StorageDead(_13);                // scope 2 at $DIR/address-of.rs:11:20: 11:21
         StorageLive(_15);                // scope 2 at $DIR/address-of.rs:13:9: 13:10
         _15 = &raw const (*_1);          // scope 2 at $DIR/address-of.rs:13:23: 13:24
-        FakeRead(ForLet, _15);           // scope 2 at $DIR/address-of.rs:13:9: 13:10
+        FakeRead(ForLet(None), _15);     // scope 2 at $DIR/address-of.rs:13:9: 13:10
         AscribeUserType(_15, o, UserTypeProjection { base: UserType(3), projs: [] }); // scope 2 at $DIR/address-of.rs:13:12: 13:20
         StorageLive(_16);                // scope 3 at $DIR/address-of.rs:14:9: 14:10
         _16 = &raw const (*_1);          // scope 3 at $DIR/address-of.rs:14:31: 14:32
-        FakeRead(ForLet, _16);           // scope 3 at $DIR/address-of.rs:14:9: 14:10
+        FakeRead(ForLet(None), _16);     // scope 3 at $DIR/address-of.rs:14:9: 14:10
         AscribeUserType(_16, o, UserTypeProjection { base: UserType(5), projs: [] }); // scope 3 at $DIR/address-of.rs:14:12: 14:28
         StorageLive(_17);                // scope 4 at $DIR/address-of.rs:15:9: 15:10
         StorageLive(_18);                // scope 4 at $DIR/address-of.rs:15:30: 15:31
         _18 = &raw const (*_1);          // scope 4 at $DIR/address-of.rs:15:30: 15:31
         _17 = move _18 as *const dyn std::marker::Send (Pointer(Unsize)); // scope 4 at $DIR/address-of.rs:15:30: 15:31
         StorageDead(_18);                // scope 4 at $DIR/address-of.rs:15:30: 15:31
-        FakeRead(ForLet, _17);           // scope 4 at $DIR/address-of.rs:15:9: 15:10
+        FakeRead(ForLet(None), _17);     // scope 4 at $DIR/address-of.rs:15:9: 15:10
         AscribeUserType(_17, o, UserTypeProjection { base: UserType(7), projs: [] }); // scope 4 at $DIR/address-of.rs:15:12: 15:27
         StorageLive(_19);                // scope 5 at $DIR/address-of.rs:16:9: 16:10
         StorageLive(_20);                // scope 5 at $DIR/address-of.rs:16:27: 16:28
         _20 = &raw const (*_1);          // scope 5 at $DIR/address-of.rs:16:27: 16:28
         _19 = move _20 as *const [i32] (Pointer(Unsize)); // scope 5 at $DIR/address-of.rs:16:27: 16:28
         StorageDead(_20);                // scope 5 at $DIR/address-of.rs:16:27: 16:28
-        FakeRead(ForLet, _19);           // scope 5 at $DIR/address-of.rs:16:9: 16:10
+        FakeRead(ForLet(None), _19);     // scope 5 at $DIR/address-of.rs:16:9: 16:10
         AscribeUserType(_19, o, UserTypeProjection { base: UserType(9), projs: [] }); // scope 5 at $DIR/address-of.rs:16:12: 16:24
         StorageLive(_21);                // scope 6 at $DIR/address-of.rs:18:5: 18:18
         StorageLive(_22);                // scope 6 at $DIR/address-of.rs:18:5: 18:18
@@ -218,25 +218,25 @@ fn address_of_reborrow() -> () {
         StorageDead(_27);                // scope 6 at $DIR/address-of.rs:21:22: 21:23
         StorageLive(_29);                // scope 6 at $DIR/address-of.rs:23:9: 23:10
         _29 = &raw const (*_3);          // scope 6 at $DIR/address-of.rs:23:23: 23:24
-        FakeRead(ForLet, _29);           // scope 6 at $DIR/address-of.rs:23:9: 23:10
+        FakeRead(ForLet(None), _29);     // scope 6 at $DIR/address-of.rs:23:9: 23:10
         AscribeUserType(_29, o, UserTypeProjection { base: UserType(13), projs: [] }); // scope 6 at $DIR/address-of.rs:23:12: 23:20
         StorageLive(_30);                // scope 7 at $DIR/address-of.rs:24:9: 24:10
         _30 = &raw const (*_3);          // scope 7 at $DIR/address-of.rs:24:31: 24:32
-        FakeRead(ForLet, _30);           // scope 7 at $DIR/address-of.rs:24:9: 24:10
+        FakeRead(ForLet(None), _30);     // scope 7 at $DIR/address-of.rs:24:9: 24:10
         AscribeUserType(_30, o, UserTypeProjection { base: UserType(15), projs: [] }); // scope 7 at $DIR/address-of.rs:24:12: 24:28
         StorageLive(_31);                // scope 8 at $DIR/address-of.rs:25:9: 25:10
         StorageLive(_32);                // scope 8 at $DIR/address-of.rs:25:30: 25:31
         _32 = &raw const (*_3);          // scope 8 at $DIR/address-of.rs:25:30: 25:31
         _31 = move _32 as *const dyn std::marker::Send (Pointer(Unsize)); // scope 8 at $DIR/address-of.rs:25:30: 25:31
         StorageDead(_32);                // scope 8 at $DIR/address-of.rs:25:30: 25:31
-        FakeRead(ForLet, _31);           // scope 8 at $DIR/address-of.rs:25:9: 25:10
+        FakeRead(ForLet(None), _31);     // scope 8 at $DIR/address-of.rs:25:9: 25:10
         AscribeUserType(_31, o, UserTypeProjection { base: UserType(17), projs: [] }); // scope 8 at $DIR/address-of.rs:25:12: 25:27
         StorageLive(_33);                // scope 9 at $DIR/address-of.rs:26:9: 26:10
         StorageLive(_34);                // scope 9 at $DIR/address-of.rs:26:27: 26:28
         _34 = &raw const (*_3);          // scope 9 at $DIR/address-of.rs:26:27: 26:28
         _33 = move _34 as *const [i32] (Pointer(Unsize)); // scope 9 at $DIR/address-of.rs:26:27: 26:28
         StorageDead(_34);                // scope 9 at $DIR/address-of.rs:26:27: 26:28
-        FakeRead(ForLet, _33);           // scope 9 at $DIR/address-of.rs:26:9: 26:10
+        FakeRead(ForLet(None), _33);     // scope 9 at $DIR/address-of.rs:26:9: 26:10
         AscribeUserType(_33, o, UserTypeProjection { base: UserType(19), projs: [] }); // scope 9 at $DIR/address-of.rs:26:12: 26:24
         StorageLive(_35);                // scope 10 at $DIR/address-of.rs:28:5: 28:16
         StorageLive(_36);                // scope 10 at $DIR/address-of.rs:28:5: 28:16
@@ -266,25 +266,25 @@ fn address_of_reborrow() -> () {
         StorageDead(_41);                // scope 10 at $DIR/address-of.rs:31:20: 31:21
         StorageLive(_43);                // scope 10 at $DIR/address-of.rs:33:9: 33:10
         _43 = &raw mut (*_3);            // scope 10 at $DIR/address-of.rs:33:21: 33:22
-        FakeRead(ForLet, _43);           // scope 10 at $DIR/address-of.rs:33:9: 33:10
+        FakeRead(ForLet(None), _43);     // scope 10 at $DIR/address-of.rs:33:9: 33:10
         AscribeUserType(_43, o, UserTypeProjection { base: UserType(23), projs: [] }); // scope 10 at $DIR/address-of.rs:33:12: 33:18
         StorageLive(_44);                // scope 11 at $DIR/address-of.rs:34:9: 34:10
         _44 = &raw mut (*_3);            // scope 11 at $DIR/address-of.rs:34:29: 34:30
-        FakeRead(ForLet, _44);           // scope 11 at $DIR/address-of.rs:34:9: 34:10
+        FakeRead(ForLet(None), _44);     // scope 11 at $DIR/address-of.rs:34:9: 34:10
         AscribeUserType(_44, o, UserTypeProjection { base: UserType(25), projs: [] }); // scope 11 at $DIR/address-of.rs:34:12: 34:26
         StorageLive(_45);                // scope 12 at $DIR/address-of.rs:35:9: 35:10
         StorageLive(_46);                // scope 12 at $DIR/address-of.rs:35:28: 35:29
         _46 = &raw mut (*_3);            // scope 12 at $DIR/address-of.rs:35:28: 35:29
         _45 = move _46 as *mut dyn std::marker::Send (Pointer(Unsize)); // scope 12 at $DIR/address-of.rs:35:28: 35:29
         StorageDead(_46);                // scope 12 at $DIR/address-of.rs:35:28: 35:29
-        FakeRead(ForLet, _45);           // scope 12 at $DIR/address-of.rs:35:9: 35:10
+        FakeRead(ForLet(None), _45);     // scope 12 at $DIR/address-of.rs:35:9: 35:10
         AscribeUserType(_45, o, UserTypeProjection { base: UserType(27), projs: [] }); // scope 12 at $DIR/address-of.rs:35:12: 35:25
         StorageLive(_47);                // scope 13 at $DIR/address-of.rs:36:9: 36:10
         StorageLive(_48);                // scope 13 at $DIR/address-of.rs:36:25: 36:26
         _48 = &raw mut (*_3);            // scope 13 at $DIR/address-of.rs:36:25: 36:26
         _47 = move _48 as *mut [i32] (Pointer(Unsize)); // scope 13 at $DIR/address-of.rs:36:25: 36:26
         StorageDead(_48);                // scope 13 at $DIR/address-of.rs:36:25: 36:26
-        FakeRead(ForLet, _47);           // scope 13 at $DIR/address-of.rs:36:9: 36:10
+        FakeRead(ForLet(None), _47);     // scope 13 at $DIR/address-of.rs:36:9: 36:10
         AscribeUserType(_47, o, UserTypeProjection { base: UserType(29), projs: [] }); // scope 13 at $DIR/address-of.rs:36:12: 36:22
         _0 = const ();                   // scope 0 at $DIR/address-of.rs:3:26: 37:2
         StorageDead(_47);                // scope 13 at $DIR/address-of.rs:37:1: 37:2
diff --git a/src/test/mir-opt/address_of.borrow_and_cast.SimplifyCfg-initial.after.mir b/src/test/mir-opt/address_of.borrow_and_cast.SimplifyCfg-initial.after.mir
index e058b0aaa8f..195f3e2e65c 100644
--- a/src/test/mir-opt/address_of.borrow_and_cast.SimplifyCfg-initial.after.mir
+++ b/src/test/mir-opt/address_of.borrow_and_cast.SimplifyCfg-initial.after.mir
@@ -24,19 +24,19 @@ fn borrow_and_cast(_1: i32) -> () {
         StorageLive(_3);                 // scope 0 at $DIR/address-of.rs:42:13: 42:15
         _3 = &_1;                        // scope 0 at $DIR/address-of.rs:42:13: 42:15
         _2 = &raw const (*_3);           // scope 0 at $DIR/address-of.rs:42:13: 42:15
-        FakeRead(ForLet, _2);            // scope 0 at $DIR/address-of.rs:42:9: 42:10
+        FakeRead(ForLet(None), _2);      // scope 0 at $DIR/address-of.rs:42:9: 42:10
         StorageDead(_3);                 // scope 0 at $DIR/address-of.rs:42:29: 42:30
         StorageLive(_4);                 // scope 1 at $DIR/address-of.rs:43:9: 43:10
         StorageLive(_5);                 // scope 1 at $DIR/address-of.rs:43:13: 43:19
         _5 = &mut _1;                    // scope 1 at $DIR/address-of.rs:43:13: 43:19
         _4 = &raw const (*_5);           // scope 1 at $DIR/address-of.rs:43:13: 43:19
-        FakeRead(ForLet, _4);            // scope 1 at $DIR/address-of.rs:43:9: 43:10
+        FakeRead(ForLet(None), _4);      // scope 1 at $DIR/address-of.rs:43:9: 43:10
         StorageDead(_5);                 // scope 1 at $DIR/address-of.rs:43:33: 43:34
         StorageLive(_6);                 // scope 2 at $DIR/address-of.rs:44:9: 44:10
         StorageLive(_7);                 // scope 2 at $DIR/address-of.rs:44:13: 44:19
         _7 = &mut _1;                    // scope 2 at $DIR/address-of.rs:44:13: 44:19
         _6 = &raw mut (*_7);             // scope 2 at $DIR/address-of.rs:44:13: 44:19
-        FakeRead(ForLet, _6);            // scope 2 at $DIR/address-of.rs:44:9: 44:10
+        FakeRead(ForLet(None), _6);      // scope 2 at $DIR/address-of.rs:44:9: 44:10
         StorageDead(_7);                 // scope 2 at $DIR/address-of.rs:44:31: 44:32
         _0 = const ();                   // scope 0 at $DIR/address-of.rs:41:32: 45:2
         StorageDead(_6);                 // scope 2 at $DIR/address-of.rs:45:1: 45:2
diff --git a/src/test/mir-opt/basic_assignment.main.SimplifyCfg-initial.after.mir b/src/test/mir-opt/basic_assignment.main.SimplifyCfg-initial.after.mir
index 7e0ca3dea4b..e751b825c05 100644
--- a/src/test/mir-opt/basic_assignment.main.SimplifyCfg-initial.after.mir
+++ b/src/test/mir-opt/basic_assignment.main.SimplifyCfg-initial.after.mir
@@ -28,7 +28,7 @@ fn main() -> () {
     bb0: {
         StorageLive(_1);                 // scope 0 at $DIR/basic_assignment.rs:11:9: 11:17
         _1 = const false;                // scope 0 at $DIR/basic_assignment.rs:11:20: 11:25
-        FakeRead(ForLet, _1);            // scope 0 at $DIR/basic_assignment.rs:11:9: 11:17
+        FakeRead(ForLet(None), _1);      // scope 0 at $DIR/basic_assignment.rs:11:9: 11:17
         StorageLive(_2);                 // scope 1 at $DIR/basic_assignment.rs:12:9: 12:17
         StorageLive(_3);                 // scope 2 at $DIR/basic_assignment.rs:16:16: 16:24
         _3 = _1;                         // scope 2 at $DIR/basic_assignment.rs:16:16: 16:24
@@ -36,7 +36,7 @@ fn main() -> () {
         StorageDead(_3);                 // scope 2 at $DIR/basic_assignment.rs:16:23: 16:24
         StorageLive(_4);                 // scope 2 at $DIR/basic_assignment.rs:18:9: 18:15
         _4 = Option::<Box<u32>>::None;   // scope 2 at $DIR/basic_assignment.rs:18:36: 18:40
-        FakeRead(ForLet, _4);            // scope 2 at $DIR/basic_assignment.rs:18:9: 18:15
+        FakeRead(ForLet(None), _4);      // scope 2 at $DIR/basic_assignment.rs:18:9: 18:15
         AscribeUserType(_4, o, UserTypeProjection { base: UserType(1), projs: [] }); // scope 2 at $DIR/basic_assignment.rs:18:17: 18:33
         StorageLive(_5);                 // scope 3 at $DIR/basic_assignment.rs:19:9: 19:15
         StorageLive(_6);                 // scope 4 at $DIR/basic_assignment.rs:23:14: 23:20
diff --git a/src/test/mir-opt/exponential_or.match_tuple.SimplifyCfg-initial.after.mir b/src/test/mir-opt/exponential_or.match_tuple.SimplifyCfg-initial.after.mir
index aa4f996c4b4..93507879a6f 100644
--- a/src/test/mir-opt/exponential_or.match_tuple.SimplifyCfg-initial.after.mir
+++ b/src/test/mir-opt/exponential_or.match_tuple.SimplifyCfg-initial.after.mir
@@ -18,7 +18,7 @@ fn match_tuple(_1: (u32, bool, Option<i32>, u32)) -> u32 {
     }
 
     bb0: {
-        FakeRead(ForMatchedPlace, _1);   // scope 0 at $DIR/exponential-or.rs:5:11: 5:12
+        FakeRead(ForMatchedPlace(None), _1); // scope 0 at $DIR/exponential-or.rs:5:11: 5:12
         switchInt((_1.0: u32)) -> [1_u32: bb2, 4_u32: bb2, otherwise: bb1]; // scope 0 at $DIR/exponential-or.rs:6:15: 6:16
     }
 
diff --git a/src/test/mir-opt/issue_38669.main.SimplifyCfg-initial.after.mir b/src/test/mir-opt/issue_38669.main.SimplifyCfg-initial.after.mir
index e9e5a101a64..8355b2d195e 100644
--- a/src/test/mir-opt/issue_38669.main.SimplifyCfg-initial.after.mir
+++ b/src/test/mir-opt/issue_38669.main.SimplifyCfg-initial.after.mir
@@ -14,7 +14,7 @@ fn main() -> () {
     bb0: {
         StorageLive(_1);                 // scope 0 at $DIR/issue-38669.rs:5:9: 5:25
         _1 = const false;                // scope 0 at $DIR/issue-38669.rs:5:28: 5:33
-        FakeRead(ForLet, _1);            // scope 0 at $DIR/issue-38669.rs:5:9: 5:25
+        FakeRead(ForLet(None), _1);      // scope 0 at $DIR/issue-38669.rs:5:9: 5:25
         goto -> bb1;                     // scope 1 at $DIR/issue-38669.rs:6:5: 11:6
     }
 
diff --git a/src/test/mir-opt/issue_49232.main.mir_map.0.mir b/src/test/mir-opt/issue_49232.main.mir_map.0.mir
index 79f5495c788..06fbbda3d9e 100644
--- a/src/test/mir-opt/issue_49232.main.mir_map.0.mir
+++ b/src/test/mir-opt/issue_49232.main.mir_map.0.mir
@@ -24,7 +24,7 @@ fn main() -> () {
         StorageLive(_2);                 // scope 0 at $DIR/issue-49232.rs:7:13: 7:19
         StorageLive(_3);                 // scope 0 at $DIR/issue-49232.rs:8:19: 8:23
         _3 = const true;                 // scope 0 at $DIR/issue-49232.rs:8:19: 8:23
-        FakeRead(ForMatchedPlace, _3);   // scope 0 at $DIR/issue-49232.rs:8:19: 8:23
+        FakeRead(ForMatchedPlace(None), _3); // scope 0 at $DIR/issue-49232.rs:8:19: 8:23
         switchInt(_3) -> [false: bb3, otherwise: bb4]; // scope 0 at $DIR/issue-49232.rs:9:17: 9:22
     }
 
@@ -51,7 +51,7 @@ fn main() -> () {
     }
 
     bb8: {
-        FakeRead(ForLet, _2);            // scope 0 at $DIR/issue-49232.rs:7:13: 7:19
+        FakeRead(ForLet(None), _2);      // scope 0 at $DIR/issue-49232.rs:7:13: 7:19
         StorageDead(_3);                 // scope 0 at $DIR/issue-49232.rs:12:10: 12:11
         StorageLive(_5);                 // scope 1 at $DIR/issue-49232.rs:13:9: 13:22
         StorageLive(_6);                 // scope 1 at $DIR/issue-49232.rs:13:14: 13:21
diff --git a/src/test/mir-opt/issue_72181.main.mir_map.0.32bit.mir b/src/test/mir-opt/issue_72181.main.mir_map.0.32bit.mir
index cf66a501e35..2e6783b7f3c 100644
--- a/src/test/mir-opt/issue_72181.main.mir_map.0.32bit.mir
+++ b/src/test/mir-opt/issue_72181.main.mir_map.0.32bit.mir
@@ -38,7 +38,7 @@ fn main() -> () {
         _2 = [move _3, move _4];         // scope 1 at $DIR/issue-72181.rs:26:13: 26:43
         StorageDead(_4);                 // scope 1 at $DIR/issue-72181.rs:26:42: 26:43
         StorageDead(_3);                 // scope 1 at $DIR/issue-72181.rs:26:42: 26:43
-        FakeRead(ForLet, _2);            // scope 1 at $DIR/issue-72181.rs:26:9: 26:10
+        FakeRead(ForLet(None), _2);      // scope 1 at $DIR/issue-72181.rs:26:9: 26:10
         StorageLive(_5);                 // scope 2 at $DIR/issue-72181.rs:27:13: 27:30
         StorageLive(_6);                 // scope 4 at $DIR/issue-72181.rs:27:24: 27:25
         _6 = const 0_usize;              // scope 4 at $DIR/issue-72181.rs:27:24: 27:25
diff --git a/src/test/mir-opt/issue_72181.main.mir_map.0.64bit.mir b/src/test/mir-opt/issue_72181.main.mir_map.0.64bit.mir
index cf66a501e35..2e6783b7f3c 100644
--- a/src/test/mir-opt/issue_72181.main.mir_map.0.64bit.mir
+++ b/src/test/mir-opt/issue_72181.main.mir_map.0.64bit.mir
@@ -38,7 +38,7 @@ fn main() -> () {
         _2 = [move _3, move _4];         // scope 1 at $DIR/issue-72181.rs:26:13: 26:43
         StorageDead(_4);                 // scope 1 at $DIR/issue-72181.rs:26:42: 26:43
         StorageDead(_3);                 // scope 1 at $DIR/issue-72181.rs:26:42: 26:43
-        FakeRead(ForLet, _2);            // scope 1 at $DIR/issue-72181.rs:26:9: 26:10
+        FakeRead(ForLet(None), _2);      // scope 1 at $DIR/issue-72181.rs:26:9: 26:10
         StorageLive(_5);                 // scope 2 at $DIR/issue-72181.rs:27:13: 27:30
         StorageLive(_6);                 // scope 4 at $DIR/issue-72181.rs:27:24: 27:25
         _6 = const 0_usize;              // scope 4 at $DIR/issue-72181.rs:27:24: 27:25
diff --git a/src/test/mir-opt/issue_72181_1.f.mir_map.0.mir b/src/test/mir-opt/issue_72181_1.f.mir_map.0.mir
index 7571d7bb94f..7def08ece22 100644
--- a/src/test/mir-opt/issue_72181_1.f.mir_map.0.mir
+++ b/src/test/mir-opt/issue_72181_1.f.mir_map.0.mir
@@ -9,7 +9,7 @@ fn f(_1: Void) -> ! {
     bb0: {
         StorageLive(_2);                 // scope 0 at $DIR/issue-72181-1.rs:10:20: 12:2
         StorageLive(_3);                 // scope 0 at $DIR/issue-72181-1.rs:11:5: 11:15
-        FakeRead(ForMatchedPlace, _1);   // scope 0 at $DIR/issue-72181-1.rs:11:11: 11:12
+        FakeRead(ForMatchedPlace(None), _1); // scope 0 at $DIR/issue-72181-1.rs:11:11: 11:12
         unreachable;                     // scope 0 at $DIR/issue-72181-1.rs:11:11: 11:12
     }
 
diff --git a/src/test/mir-opt/issue_72181_1.main.mir_map.0.mir b/src/test/mir-opt/issue_72181_1.main.mir_map.0.mir
index 1fd91c2056b..3c26b20c35e 100644
--- a/src/test/mir-opt/issue_72181_1.main.mir_map.0.mir
+++ b/src/test/mir-opt/issue_72181_1.main.mir_map.0.mir
@@ -29,7 +29,7 @@ fn main() -> () {
 
     bb1: {
         StorageDead(_3);                 // scope 2 at $DIR/issue-72181-1.rs:17:43: 17:44
-        FakeRead(ForLet, _2);            // scope 0 at $DIR/issue-72181-1.rs:16:9: 16:10
+        FakeRead(ForLet(None), _2);      // scope 0 at $DIR/issue-72181-1.rs:16:9: 16:10
         AscribeUserType(_2, o, UserTypeProjection { base: UserType(1), projs: [] }); // scope 0 at $DIR/issue-72181-1.rs:16:12: 16:16
         StorageLive(_4);                 // scope 1 at $DIR/issue-72181-1.rs:20:5: 20:9
         StorageLive(_5);                 // scope 1 at $DIR/issue-72181-1.rs:20:7: 20:8
diff --git a/src/test/mir-opt/loop_test.main.SimplifyCfg-promote-consts.after.mir b/src/test/mir-opt/loop_test.main.SimplifyCfg-promote-consts.after.mir
index f109937dbf9..99c7ac8d5b7 100644
--- a/src/test/mir-opt/loop_test.main.SimplifyCfg-promote-consts.after.mir
+++ b/src/test/mir-opt/loop_test.main.SimplifyCfg-promote-consts.after.mir
@@ -41,7 +41,7 @@ fn main() -> () {
     bb4: {
         StorageLive(_6);                 // scope 0 at $DIR/loop_test.rs:14:13: 14:14
         _6 = const 1_i32;                // scope 0 at $DIR/loop_test.rs:14:17: 14:18
-        FakeRead(ForLet, _6);            // scope 0 at $DIR/loop_test.rs:14:13: 14:14
+        FakeRead(ForLet(None), _6);      // scope 0 at $DIR/loop_test.rs:14:13: 14:14
         StorageDead(_6);                 // scope 0 at $DIR/loop_test.rs:16:5: 16:6
         goto -> bb3;                     // scope 0 at $DIR/loop_test.rs:1:1: 1:1
     }
diff --git a/src/test/mir-opt/match_arm_scopes.complicated_match.SimplifyCfg-initial.after-ElaborateDrops.after.diff b/src/test/mir-opt/match_arm_scopes.complicated_match.SimplifyCfg-initial.after-ElaborateDrops.after.diff
index 95beab2ec9a..3395cbfbdfb 100644
--- a/src/test/mir-opt/match_arm_scopes.complicated_match.SimplifyCfg-initial.after-ElaborateDrops.after.diff
+++ b/src/test/mir-opt/match_arm_scopes.complicated_match.SimplifyCfg-initial.after-ElaborateDrops.after.diff
@@ -31,7 +31,7 @@
       }
   
       bb0: {
--         FakeRead(ForMatchedPlace, _2);   // scope 0 at $DIR/match-arm-scopes.rs:14:11: 14:16
+-         FakeRead(ForMatchedPlace(None), _2); // scope 0 at $DIR/match-arm-scopes.rs:14:11: 14:16
 -         switchInt((_2.0: bool)) -> [false: bb1, otherwise: bb2]; // scope 0 at $DIR/match-arm-scopes.rs:15:10: 15:15
 +         switchInt((_2.0: bool)) -> [false: bb5, otherwise: bb1]; // scope 0 at $DIR/match-arm-scopes.rs:15:10: 15:15
       }
diff --git a/src/test/mir-opt/match_false_edges.full_tested_match.PromoteTemps.after.mir b/src/test/mir-opt/match_false_edges.full_tested_match.PromoteTemps.after.mir
index 1921b935941..5af242376c9 100644
--- a/src/test/mir-opt/match_false_edges.full_tested_match.PromoteTemps.after.mir
+++ b/src/test/mir-opt/match_false_edges.full_tested_match.PromoteTemps.after.mir
@@ -27,7 +27,7 @@ fn full_tested_match() -> () {
         StorageLive(_1);                 // scope 0 at $DIR/match_false_edges.rs:15:13: 19:6
         StorageLive(_2);                 // scope 0 at $DIR/match_false_edges.rs:15:19: 15:27
         _2 = Option::<i32>::Some(const 42_i32); // scope 0 at $DIR/match_false_edges.rs:15:19: 15:27
-        FakeRead(ForMatchedPlace, _2);   // scope 0 at $DIR/match_false_edges.rs:15:19: 15:27
+        FakeRead(ForMatchedPlace(None), _2); // scope 0 at $DIR/match_false_edges.rs:15:19: 15:27
         _3 = discriminant(_2);           // scope 0 at $DIR/match_false_edges.rs:16:9: 16:16
         switchInt(move _3) -> [0_isize: bb1, 1_isize: bb2, otherwise: bb4]; // scope 0 at $DIR/match_false_edges.rs:16:9: 16:16
     }
diff --git a/src/test/mir-opt/match_false_edges.full_tested_match2.PromoteTemps.before.mir b/src/test/mir-opt/match_false_edges.full_tested_match2.PromoteTemps.before.mir
index c7b1cce061b..a4ebf8a0246 100644
--- a/src/test/mir-opt/match_false_edges.full_tested_match2.PromoteTemps.before.mir
+++ b/src/test/mir-opt/match_false_edges.full_tested_match2.PromoteTemps.before.mir
@@ -26,7 +26,7 @@ fn full_tested_match2() -> () {
         StorageLive(_1);                 // scope 0 at $DIR/match_false_edges.rs:26:13: 30:6
         StorageLive(_2);                 // scope 0 at $DIR/match_false_edges.rs:26:19: 26:27
         _2 = Option::<i32>::Some(const 42_i32); // scope 0 at $DIR/match_false_edges.rs:26:19: 26:27
-        FakeRead(ForMatchedPlace, _2);   // scope 0 at $DIR/match_false_edges.rs:26:19: 26:27
+        FakeRead(ForMatchedPlace(None), _2); // scope 0 at $DIR/match_false_edges.rs:26:19: 26:27
         _3 = discriminant(_2);           // scope 0 at $DIR/match_false_edges.rs:27:9: 27:16
         switchInt(move _3) -> [0_isize: bb1, 1_isize: bb2, otherwise: bb4]; // scope 0 at $DIR/match_false_edges.rs:27:9: 27:16
     }
diff --git a/src/test/mir-opt/match_false_edges.main.PromoteTemps.before.mir b/src/test/mir-opt/match_false_edges.main.PromoteTemps.before.mir
index 9b8ce2c1ed0..5de52b324f4 100644
--- a/src/test/mir-opt/match_false_edges.main.PromoteTemps.before.mir
+++ b/src/test/mir-opt/match_false_edges.main.PromoteTemps.before.mir
@@ -37,7 +37,7 @@ fn main() -> () {
         StorageLive(_1);                 // scope 0 at $DIR/match_false_edges.rs:35:13: 40:6
         StorageLive(_2);                 // scope 0 at $DIR/match_false_edges.rs:35:19: 35:26
         _2 = Option::<i32>::Some(const 1_i32); // scope 0 at $DIR/match_false_edges.rs:35:19: 35:26
-        FakeRead(ForMatchedPlace, _2);   // scope 0 at $DIR/match_false_edges.rs:35:19: 35:26
+        FakeRead(ForMatchedPlace(None), _2); // scope 0 at $DIR/match_false_edges.rs:35:19: 35:26
         _4 = discriminant(_2);           // scope 0 at $DIR/match_false_edges.rs:36:9: 36:17
         switchInt(move _4) -> [1_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/match_false_edges.rs:36:9: 36:17
     }
diff --git a/src/test/mir-opt/match_test.main.SimplifyCfg-initial.after.mir b/src/test/mir-opt/match_test.main.SimplifyCfg-initial.after.mir
index e3bc4f80f27..5bb910947ca 100644
--- a/src/test/mir-opt/match_test.main.SimplifyCfg-initial.after.mir
+++ b/src/test/mir-opt/match_test.main.SimplifyCfg-initial.after.mir
@@ -21,12 +21,12 @@ fn main() -> () {
     bb0: {
         StorageLive(_1);                 // scope 0 at $DIR/match_test.rs:7:9: 7:10
         _1 = const 3_i32;                // scope 0 at $DIR/match_test.rs:7:13: 7:14
-        FakeRead(ForLet, _1);            // scope 0 at $DIR/match_test.rs:7:9: 7:10
+        FakeRead(ForLet(None), _1);      // scope 0 at $DIR/match_test.rs:7:9: 7:10
         StorageLive(_2);                 // scope 1 at $DIR/match_test.rs:8:9: 8:10
         _2 = const true;                 // scope 1 at $DIR/match_test.rs:8:13: 8:17
-        FakeRead(ForLet, _2);            // scope 1 at $DIR/match_test.rs:8:9: 8:10
+        FakeRead(ForLet(None), _2);      // scope 1 at $DIR/match_test.rs:8:9: 8:10
         StorageLive(_3);                 // scope 2 at $DIR/match_test.rs:12:5: 17:6
-        FakeRead(ForMatchedPlace, _1);   // scope 2 at $DIR/match_test.rs:12:11: 12:12
+        FakeRead(ForMatchedPlace(None), _1); // scope 2 at $DIR/match_test.rs:12:11: 12:12
         _6 = Le(const 0_i32, _1);        // scope 2 at $DIR/match_test.rs:13:9: 13:14
         switchInt(move _6) -> [false: bb4, otherwise: bb1]; // scope 2 at $DIR/match_test.rs:13:9: 13:14
     }
diff --git a/src/test/mir-opt/nll/region_subtyping_basic.main.nll.0.32bit.mir b/src/test/mir-opt/nll/region_subtyping_basic.main.nll.0.32bit.mir
index 8c939d5fc3d..39e6cee11b4 100644
--- a/src/test/mir-opt/nll/region_subtyping_basic.main.nll.0.32bit.mir
+++ b/src/test/mir-opt/nll/region_subtyping_basic.main.nll.0.32bit.mir
@@ -46,7 +46,7 @@ fn main() -> () {
     bb0: {
         StorageLive(_1);                 // bb0[0]: scope 0 at $DIR/region-subtyping-basic.rs:17:9: 17:14
         _1 = [const Const(Value(Scalar(0x00000001)): usize), const Const(Value(Scalar(0x00000002)): usize), const Const(Value(Scalar(0x00000003)): usize)]; // bb0[1]: scope 0 at $DIR/region-subtyping-basic.rs:17:17: 17:26
-        FakeRead(ForLet, _1);            // bb0[2]: scope 0 at $DIR/region-subtyping-basic.rs:17:9: 17:14
+        FakeRead(ForLet(None), _1);      // bb0[2]: scope 0 at $DIR/region-subtyping-basic.rs:17:9: 17:14
         StorageLive(_2);                 // bb0[3]: scope 1 at $DIR/region-subtyping-basic.rs:18:9: 18:10
         StorageLive(_3);                 // bb0[4]: scope 1 at $DIR/region-subtyping-basic.rs:18:16: 18:17
         _3 = const Const(Value(Scalar(0x00000000)): usize); // bb0[5]: scope 1 at $DIR/region-subtyping-basic.rs:18:16: 18:17
@@ -57,10 +57,10 @@ fn main() -> () {
 
     bb1: {
         _2 = &'_#3r _1[_3];              // bb1[0]: scope 1 at $DIR/region-subtyping-basic.rs:18:13: 18:18
-        FakeRead(ForLet, _2);            // bb1[1]: scope 1 at $DIR/region-subtyping-basic.rs:18:9: 18:10
+        FakeRead(ForLet(None), _2);      // bb1[1]: scope 1 at $DIR/region-subtyping-basic.rs:18:9: 18:10
         StorageLive(_6);                 // bb1[2]: scope 2 at $DIR/region-subtyping-basic.rs:19:9: 19:10
         _6 = _2;                         // bb1[3]: scope 2 at $DIR/region-subtyping-basic.rs:19:13: 19:14
-        FakeRead(ForLet, _6);            // bb1[4]: scope 2 at $DIR/region-subtyping-basic.rs:19:9: 19:10
+        FakeRead(ForLet(None), _6);      // bb1[4]: scope 2 at $DIR/region-subtyping-basic.rs:19:9: 19:10
         StorageLive(_7);                 // bb1[5]: scope 3 at $DIR/region-subtyping-basic.rs:20:8: 20:12
         _7 = const Const(Value(Scalar(0x01)): bool); // bb1[6]: scope 3 at $DIR/region-subtyping-basic.rs:20:8: 20:12
         switchInt(move _7) -> [Const(Value(Scalar(0x00)): bool): bb3, otherwise: bb2]; // bb1[7]: scope 3 at $DIR/region-subtyping-basic.rs:20:5: 24:6
diff --git a/src/test/mir-opt/nll/region_subtyping_basic.main.nll.0.64bit.mir b/src/test/mir-opt/nll/region_subtyping_basic.main.nll.0.64bit.mir
index 00704baa6c1..6021b6529f9 100644
--- a/src/test/mir-opt/nll/region_subtyping_basic.main.nll.0.64bit.mir
+++ b/src/test/mir-opt/nll/region_subtyping_basic.main.nll.0.64bit.mir
@@ -46,7 +46,7 @@ fn main() -> () {
     bb0: {
         StorageLive(_1);                 // bb0[0]: scope 0 at $DIR/region-subtyping-basic.rs:17:9: 17:14
         _1 = [const Const(Value(Scalar(0x0000000000000001)): usize), const Const(Value(Scalar(0x0000000000000002)): usize), const Const(Value(Scalar(0x0000000000000003)): usize)]; // bb0[1]: scope 0 at $DIR/region-subtyping-basic.rs:17:17: 17:26
-        FakeRead(ForLet, _1);            // bb0[2]: scope 0 at $DIR/region-subtyping-basic.rs:17:9: 17:14
+        FakeRead(ForLet(None), _1);      // bb0[2]: scope 0 at $DIR/region-subtyping-basic.rs:17:9: 17:14
         StorageLive(_2);                 // bb0[3]: scope 1 at $DIR/region-subtyping-basic.rs:18:9: 18:10
         StorageLive(_3);                 // bb0[4]: scope 1 at $DIR/region-subtyping-basic.rs:18:16: 18:17
         _3 = const Const(Value(Scalar(0x0000000000000000)): usize); // bb0[5]: scope 1 at $DIR/region-subtyping-basic.rs:18:16: 18:17
@@ -57,10 +57,10 @@ fn main() -> () {
 
     bb1: {
         _2 = &'_#3r _1[_3];              // bb1[0]: scope 1 at $DIR/region-subtyping-basic.rs:18:13: 18:18
-        FakeRead(ForLet, _2);            // bb1[1]: scope 1 at $DIR/region-subtyping-basic.rs:18:9: 18:10
+        FakeRead(ForLet(None), _2);      // bb1[1]: scope 1 at $DIR/region-subtyping-basic.rs:18:9: 18:10
         StorageLive(_6);                 // bb1[2]: scope 2 at $DIR/region-subtyping-basic.rs:19:9: 19:10
         _6 = _2;                         // bb1[3]: scope 2 at $DIR/region-subtyping-basic.rs:19:13: 19:14
-        FakeRead(ForLet, _6);            // bb1[4]: scope 2 at $DIR/region-subtyping-basic.rs:19:9: 19:10
+        FakeRead(ForLet(None), _6);      // bb1[4]: scope 2 at $DIR/region-subtyping-basic.rs:19:9: 19:10
         StorageLive(_7);                 // bb1[5]: scope 3 at $DIR/region-subtyping-basic.rs:20:8: 20:12
         _7 = const Const(Value(Scalar(0x01)): bool); // bb1[6]: scope 3 at $DIR/region-subtyping-basic.rs:20:8: 20:12
         switchInt(move _7) -> [Const(Value(Scalar(0x00)): bool): bb3, otherwise: bb2]; // bb1[7]: scope 3 at $DIR/region-subtyping-basic.rs:20:5: 24:6
diff --git a/src/test/mir-opt/receiver_ptr_mutability.main.mir_map.0.mir b/src/test/mir-opt/receiver_ptr_mutability.main.mir_map.0.mir
index d2d96ff468d..f54c8f8ab4a 100644
--- a/src/test/mir-opt/receiver_ptr_mutability.main.mir_map.0.mir
+++ b/src/test/mir-opt/receiver_ptr_mutability.main.mir_map.0.mir
@@ -36,7 +36,7 @@ fn main() -> () {
     }
 
     bb1: {
-        FakeRead(ForLet, _1);            // scope 0 at $DIR/receiver-ptr-mutability.rs:14:9: 14:12
+        FakeRead(ForLet(None), _1);      // scope 0 at $DIR/receiver-ptr-mutability.rs:14:9: 14:12
         AscribeUserType(_1, o, UserTypeProjection { base: UserType(1), projs: [] }); // scope 0 at $DIR/receiver-ptr-mutability.rs:14:14: 14:23
         StorageLive(_2);                 // scope 1 at $DIR/receiver-ptr-mutability.rs:15:5: 15:12
         StorageLive(_3);                 // scope 1 at $DIR/receiver-ptr-mutability.rs:15:5: 15:8
@@ -63,7 +63,7 @@ fn main() -> () {
         _7 = &_8;                        // scope 1 at $DIR/receiver-ptr-mutability.rs:18:35: 18:41
         _6 = &_7;                        // scope 1 at $DIR/receiver-ptr-mutability.rs:18:34: 18:41
         _5 = &(*_6);                     // scope 1 at $DIR/receiver-ptr-mutability.rs:18:34: 18:41
-        FakeRead(ForLet, _5);            // scope 1 at $DIR/receiver-ptr-mutability.rs:18:9: 18:16
+        FakeRead(ForLet(None), _5);      // scope 1 at $DIR/receiver-ptr-mutability.rs:18:9: 18:16
         AscribeUserType(_5, o, UserTypeProjection { base: UserType(3), projs: [] }); // scope 1 at $DIR/receiver-ptr-mutability.rs:18:18: 18:31
         StorageDead(_6);                 // scope 1 at $DIR/receiver-ptr-mutability.rs:18:41: 18:42
         StorageLive(_10);                // scope 2 at $DIR/receiver-ptr-mutability.rs:19:5: 19:16
diff --git a/src/test/mir-opt/remove_fake_borrows.match_guard.CleanupNonCodegenStatements.diff b/src/test/mir-opt/remove_fake_borrows.match_guard.CleanupNonCodegenStatements.diff
index 47027311b47..4aa388fc67b 100644
--- a/src/test/mir-opt/remove_fake_borrows.match_guard.CleanupNonCodegenStatements.diff
+++ b/src/test/mir-opt/remove_fake_borrows.match_guard.CleanupNonCodegenStatements.diff
@@ -13,7 +13,7 @@
       let mut _8: bool;                    // in scope 0 at $DIR/remove_fake_borrows.rs:8:20: 8:21
   
       bb0: {
--         FakeRead(ForMatchedPlace, _1);   // scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12
+-         FakeRead(ForMatchedPlace(None), _1); // scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12
 +         nop;                             // scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12
           _3 = discriminant(_1);           // scope 0 at $DIR/remove_fake_borrows.rs:8:9: 8:16
           switchInt(move _3) -> [1_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/remove_fake_borrows.rs:8:9: 8:16
diff --git a/src/test/mir-opt/simple_match.match_bool.mir_map.0.32bit.mir b/src/test/mir-opt/simple_match.match_bool.mir_map.0.32bit.mir
index 5bcb20ca72a..841cca7c381 100644
--- a/src/test/mir-opt/simple_match.match_bool.mir_map.0.32bit.mir
+++ b/src/test/mir-opt/simple_match.match_bool.mir_map.0.32bit.mir
@@ -5,7 +5,7 @@ fn match_bool(_1: bool) -> usize {
     let mut _0: usize;                   // return place in scope 0 at $DIR/simple-match.rs:5:27: 5:32
 
     bb0: {
-        FakeRead(ForMatchedPlace, _1);   // scope 0 at $DIR/simple-match.rs:6:11: 6:12
+        FakeRead(ForMatchedPlace(None), _1); // scope 0 at $DIR/simple-match.rs:6:11: 6:12
         switchInt(_1) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/simple-match.rs:7:9: 7:13
     }
 
diff --git a/src/test/mir-opt/simple_match.match_bool.mir_map.0.64bit.mir b/src/test/mir-opt/simple_match.match_bool.mir_map.0.64bit.mir
index 5bcb20ca72a..841cca7c381 100644
--- a/src/test/mir-opt/simple_match.match_bool.mir_map.0.64bit.mir
+++ b/src/test/mir-opt/simple_match.match_bool.mir_map.0.64bit.mir
@@ -5,7 +5,7 @@ fn match_bool(_1: bool) -> usize {
     let mut _0: usize;                   // return place in scope 0 at $DIR/simple-match.rs:5:27: 5:32
 
     bb0: {
-        FakeRead(ForMatchedPlace, _1);   // scope 0 at $DIR/simple-match.rs:6:11: 6:12
+        FakeRead(ForMatchedPlace(None), _1); // scope 0 at $DIR/simple-match.rs:6:11: 6:12
         switchInt(_1) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/simple-match.rs:7:9: 7:13
     }
 
diff --git a/src/test/mir-opt/storage_ranges.main.nll.0.mir b/src/test/mir-opt/storage_ranges.main.nll.0.mir
index 6fa83d3de62..e02580135af 100644
--- a/src/test/mir-opt/storage_ranges.main.nll.0.mir
+++ b/src/test/mir-opt/storage_ranges.main.nll.0.mir
@@ -39,7 +39,7 @@ fn main() -> () {
     bb0: {
         StorageLive(_1);                 // scope 0 at $DIR/storage_ranges.rs:4:9: 4:10
         _1 = const 0_i32;                // scope 0 at $DIR/storage_ranges.rs:4:13: 4:14
-        FakeRead(ForLet, _1);            // scope 0 at $DIR/storage_ranges.rs:4:9: 4:10
+        FakeRead(ForLet(None), _1);      // scope 0 at $DIR/storage_ranges.rs:4:9: 4:10
         StorageLive(_2);                 // scope 1 at $DIR/storage_ranges.rs:5:5: 7:6
         StorageLive(_3);                 // scope 1 at $DIR/storage_ranges.rs:6:13: 6:14
         StorageLive(_4);                 // scope 1 at $DIR/storage_ranges.rs:6:18: 6:25
@@ -48,14 +48,14 @@ fn main() -> () {
         _4 = Option::<i32>::Some(move _5); // scope 1 at $DIR/storage_ranges.rs:6:18: 6:25
         StorageDead(_5);                 // scope 1 at $DIR/storage_ranges.rs:6:24: 6:25
         _3 = &_4;                        // scope 1 at $DIR/storage_ranges.rs:6:17: 6:25
-        FakeRead(ForLet, _3);            // scope 1 at $DIR/storage_ranges.rs:6:13: 6:14
+        FakeRead(ForLet(None), _3);      // scope 1 at $DIR/storage_ranges.rs:6:13: 6:14
         _2 = const ();                   // scope 1 at $DIR/storage_ranges.rs:5:5: 7:6
         StorageDead(_4);                 // scope 1 at $DIR/storage_ranges.rs:7:5: 7:6
         StorageDead(_3);                 // scope 1 at $DIR/storage_ranges.rs:7:5: 7:6
         StorageDead(_2);                 // scope 1 at $DIR/storage_ranges.rs:7:5: 7:6
         StorageLive(_6);                 // scope 1 at $DIR/storage_ranges.rs:8:9: 8:10
         _6 = const 1_i32;                // scope 1 at $DIR/storage_ranges.rs:8:13: 8:14
-        FakeRead(ForLet, _6);            // scope 1 at $DIR/storage_ranges.rs:8:9: 8:10
+        FakeRead(ForLet(None), _6);      // scope 1 at $DIR/storage_ranges.rs:8:9: 8:10
         _0 = const ();                   // scope 0 at $DIR/storage_ranges.rs:3:11: 9:2
         StorageDead(_6);                 // scope 1 at $DIR/storage_ranges.rs:9:1: 9:2
         StorageDead(_1);                 // scope 0 at $DIR/storage_ranges.rs:9:1: 9:2
diff --git a/src/test/mir-opt/uniform_array_move_out.move_out_by_subslice.mir_map.0.mir b/src/test/mir-opt/uniform_array_move_out.move_out_by_subslice.mir_map.0.mir
index d18f6308ded..7f81d9fc482 100644
--- a/src/test/mir-opt/uniform_array_move_out.move_out_by_subslice.mir_map.0.mir
+++ b/src/test/mir-opt/uniform_array_move_out.move_out_by_subslice.mir_map.0.mir
@@ -48,7 +48,7 @@ fn move_out_by_subslice() -> () {
 
     bb4: {
         StorageDead(_2);                 // scope 0 at $DIR/uniform_array_move_out.rs:11:26: 11:27
-        FakeRead(ForLet, _1);            // scope 0 at $DIR/uniform_array_move_out.rs:11:9: 11:10
+        FakeRead(ForLet(None), _1);      // scope 0 at $DIR/uniform_array_move_out.rs:11:9: 11:10
         StorageLive(_6);                 // scope 1 at $DIR/uniform_array_move_out.rs:12:10: 12:17
         _6 = move _1[0..2];              // scope 1 at $DIR/uniform_array_move_out.rs:12:10: 12:17
         _0 = const ();                   // scope 0 at $DIR/uniform_array_move_out.rs:10:27: 13:2
diff --git a/src/test/mir-opt/uniform_array_move_out.move_out_from_end.mir_map.0.mir b/src/test/mir-opt/uniform_array_move_out.move_out_from_end.mir_map.0.mir
index eda8e5fd3af..62ab494c066 100644
--- a/src/test/mir-opt/uniform_array_move_out.move_out_from_end.mir_map.0.mir
+++ b/src/test/mir-opt/uniform_array_move_out.move_out_from_end.mir_map.0.mir
@@ -48,7 +48,7 @@ fn move_out_from_end() -> () {
 
     bb4: {
         StorageDead(_2);                 // scope 0 at $DIR/uniform_array_move_out.rs:5:26: 5:27
-        FakeRead(ForLet, _1);            // scope 0 at $DIR/uniform_array_move_out.rs:5:9: 5:10
+        FakeRead(ForLet(None), _1);      // scope 0 at $DIR/uniform_array_move_out.rs:5:9: 5:10
         StorageLive(_6);                 // scope 1 at $DIR/uniform_array_move_out.rs:6:14: 6:16
         _6 = move _1[1 of 2];            // scope 1 at $DIR/uniform_array_move_out.rs:6:14: 6:16
         _0 = const ();                   // scope 0 at $DIR/uniform_array_move_out.rs:4:24: 7:2
diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
index 1391f7505e2..dac4d93499d 100644
--- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
+++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
@@ -212,7 +212,7 @@ fn check_statement(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, statemen
             check_rvalue(tcx, body, def_id, rval, span)
         }
 
-        StatementKind::FakeRead(_, place) |
+        StatementKind::FakeRead(box (_, place)) => check_place(tcx, *place, span, body),
         // just an assignment
         StatementKind::SetDiscriminant { place, .. } => check_place(tcx, **place, span, body),
 
diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs
index 1d4b5e1247d..48091601861 100644
--- a/src/tools/compiletest/src/main.rs
+++ b/src/tools/compiletest/src/main.rs
@@ -909,7 +909,8 @@ fn extract_gdb_version(full_version_line: &str) -> Option<u32> {
     // This particular form is documented in the GNU coding standards:
     // https://www.gnu.org/prep/standards/html_node/_002d_002dversion.html#g_t_002d_002dversion
 
-    let mut splits = full_version_line.rsplit(' ');
+    let unbracketed_part = full_version_line.split('[').next().unwrap();
+    let mut splits = unbracketed_part.trim_end().rsplit(' ');
     let version_string = splits.next().unwrap();
 
     let mut splits = version_string.split('.');
diff --git a/src/tools/compiletest/src/tests.rs b/src/tools/compiletest/src/tests.rs
index 233f2e648dc..e6725dba260 100644
--- a/src/tools/compiletest/src/tests.rs
+++ b/src/tools/compiletest/src/tests.rs
@@ -39,6 +39,9 @@ fn test_extract_gdb_version() {
         7012000: "GNU gdb (GDB) 7.12",
         7012000: "GNU gdb (GDB) 7.12.20161027-git",
         7012050: "GNU gdb (GDB) 7.12.50.20161027-git",
+
+        9002000: "GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2",
+        10001000: "GNU gdb (GDB) 10.1 [GDB v10.1 for FreeBSD]",
     }
 }