diff --git a/.mailmap b/.mailmap
index 1019710dc9793..cd617ca2f4285 100644
--- a/.mailmap
+++ b/.mailmap
@@ -43,7 +43,7 @@ Brian Anderson <banderson@mozilla.com> <andersrb@gmail.com>
 Brian Anderson <banderson@mozilla.com> <banderson@mozilla.org>
 Brian Dawn <brian.t.dawn@gmail.com>
 Brian Leibig <brian@brianleibig.com> Brian Leibig <brian.leibig@gmail.com>
-Camelid <camelidcamel@gmail.com> <37223377+camelid@users.noreply.github.com>
+Noah Lev <camelidcamel@gmail.com> <37223377+camelid@users.noreply.github.com>
 Carl-Anton Ingmarsson <mail@carlanton.se> <ca.ingmarsson@gmail.com>
 Carol (Nichols || Goulding) <carol.nichols@gmail.com> <193874+carols10cents@users.noreply.github.com>
 Carol (Nichols || Goulding) <carol.nichols@gmail.com> <carol.nichols@gmail.com>
diff --git a/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs b/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs
index 4458fd686788f..08442c588f879 100644
--- a/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs
+++ b/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs
@@ -49,9 +49,9 @@ impl<'tcx> FunctionCoverage<'tcx> {
     }
 
     fn create(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, is_used: bool) -> Self {
-        let coverageinfo = tcx.coverageinfo(instance.def_id());
+        let coverageinfo = tcx.coverageinfo(instance.def);
         debug!(
-            "FunctionCoverage::new(instance={:?}) has coverageinfo={:?}. is_used={}",
+            "FunctionCoverage::create(instance={:?}) has coverageinfo={:?}. is_used={}",
             instance, coverageinfo, is_used
         );
         Self {
diff --git a/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs b/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs
index 621ec0519c956..a283bf1de763a 100644
--- a/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs
@@ -31,7 +31,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                         bx.add_coverage_counter(instance, id, code_region);
                     }
 
-                    let coverageinfo = bx.tcx().coverageinfo(instance.def_id());
+                    let coverageinfo = bx.tcx().coverageinfo(instance.def);
 
                     let fn_name = bx.get_pgo_func_name_var(instance);
                     let hash = bx.const_u64(function_source_hash);
diff --git a/compiler/rustc_error_codes/src/error_codes/E0277.md b/compiler/rustc_error_codes/src/error_codes/E0277.md
index 9f6db6ed7a225..5f05b59d5a6d4 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0277.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0277.md
@@ -29,16 +29,16 @@ trait Foo {
     fn bar(&self);
 }
 
-fn some_func<T: Foo>(foo: T) {
-    foo.bar(); // we can now use this method since i32 implements the
-               // Foo trait
-}
-
 // we implement the trait on the i32 type
 impl Foo for i32 {
     fn bar(&self) {}
 }
 
+fn some_func<T: Foo>(foo: T) {
+    foo.bar(); // we can now use this method since i32 implements the
+               // Foo trait
+}
+
 fn main() {
     some_func(5i32); // ok!
 }
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index fea2aec38a167..d6c4ac99085e9 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -335,10 +335,9 @@ rustc_queries! {
 
     /// Returns coverage summary info for a function, after executing the `InstrumentCoverage`
     /// MIR pass (assuming the -Zinstrument-coverage option is enabled).
-    query coverageinfo(key: DefId) -> mir::CoverageInfo {
-        desc { |tcx| "retrieving coverage info from MIR for `{}`", tcx.def_path_str(key) }
+    query coverageinfo(key: ty::InstanceDef<'tcx>) -> mir::CoverageInfo {
+        desc { |tcx| "retrieving coverage info from MIR for `{}`", tcx.def_path_str(key.def_id()) }
         storage(ArenaCacheSelector<'tcx>)
-        cache_on_disk_if { key.is_local() }
     }
 
     /// Returns the name of the file that contains the function body, if instrumented for coverage.
diff --git a/compiler/rustc_mir/src/borrow_check/region_infer/mod.rs b/compiler/rustc_mir/src/borrow_check/region_infer/mod.rs
index bbd512fd36050..f4d78ac04cb02 100644
--- a/compiler/rustc_mir/src/borrow_check/region_infer/mod.rs
+++ b/compiler/rustc_mir/src/borrow_check/region_infer/mod.rs
@@ -1241,7 +1241,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     /// it. However, it works pretty well in practice. In particular,
     /// this is needed to deal with projection outlives bounds like
     ///
-    /// ```ignore (internal compiler representation so lifetime syntax is invalid)
+    /// ```text
     /// <T as Foo<'0>>::Item: '1
     /// ```
     ///
diff --git a/compiler/rustc_mir/src/dataflow/move_paths/builder.rs b/compiler/rustc_mir/src/dataflow/move_paths/builder.rs
index 994b403abf3bc..1bfbb843114da 100644
--- a/compiler/rustc_mir/src/dataflow/move_paths/builder.rs
+++ b/compiler/rustc_mir/src/dataflow/move_paths/builder.rs
@@ -137,10 +137,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
                         self.loc,
                         InteriorOfSliceOrArray {
                             ty: place_ty,
-                            is_index: match elem {
-                                ProjectionElem::Index(..) => true,
-                                _ => false,
-                            },
+                            is_index: matches!(elem, ProjectionElem::Index(..)),
                         },
                     ));
                 }
diff --git a/compiler/rustc_mir/src/transform/coverage/debug.rs b/compiler/rustc_mir/src/transform/coverage/debug.rs
index 48361483099f2..2397d627880f3 100644
--- a/compiler/rustc_mir/src/transform/coverage/debug.rs
+++ b/compiler/rustc_mir/src/transform/coverage/debug.rs
@@ -120,6 +120,7 @@ use rustc_index::vec::Idx;
 use rustc_middle::mir::coverage::*;
 use rustc_middle::mir::{self, BasicBlock, TerminatorKind};
 use rustc_middle::ty::TyCtxt;
+use rustc_span::Span;
 
 use std::iter;
 use std::lazy::SyncOnceCell;
@@ -636,6 +637,7 @@ pub(super) fn dump_coverage_spanview(
     mir_body: &mir::Body<'tcx>,
     basic_coverage_blocks: &CoverageGraph,
     pass_name: &str,
+    body_span: Span,
     coverage_spans: &Vec<CoverageSpan>,
 ) {
     let mir_source = mir_body.source;
@@ -647,7 +649,7 @@ pub(super) fn dump_coverage_spanview(
     let crate_name = tcx.crate_name(def_id.krate);
     let item_name = tcx.def_path(def_id).to_filename_friendly_no_crate();
     let title = format!("{}.{} - Coverage Spans", crate_name, item_name);
-    spanview::write_document(tcx, def_id, span_viewables, &title, &mut file)
+    spanview::write_document(tcx, body_span, span_viewables, &title, &mut file)
         .expect("Unexpected IO error dumping coverage spans as HTML");
 }
 
diff --git a/compiler/rustc_mir/src/transform/coverage/mod.rs b/compiler/rustc_mir/src/transform/coverage/mod.rs
index 918d6ee4b0ccd..71c244fdd4a3a 100644
--- a/compiler/rustc_mir/src/transform/coverage/mod.rs
+++ b/compiler/rustc_mir/src/transform/coverage/mod.rs
@@ -95,7 +95,7 @@ impl<'tcx> MirPass<'tcx> for InstrumentCoverage {
 
         trace!("InstrumentCoverage starting for {:?}", mir_source.def_id());
         Instrumentor::new(&self.name(), tcx, mir_body).inject_counters();
-        trace!("InstrumentCoverage starting for {:?}", mir_source.def_id());
+        trace!("InstrumentCoverage done for {:?}", mir_source.def_id());
     }
 }
 
@@ -116,25 +116,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
         let def_id = mir_body.source.def_id();
         let (some_fn_sig, hir_body) = fn_sig_and_body(tcx, def_id);
 
-        let mut body_span = hir_body.value.span;
-
-        if tcx.is_closure(def_id) {
-            // If the MIR function is a closure, and if the closure body span
-            // starts from a macro, but it's content is not in that macro, try
-            // to find a non-macro callsite, and instrument the spans there
-            // instead.
-            loop {
-                let expn_data = body_span.ctxt().outer_expn_data();
-                if expn_data.is_root() {
-                    break;
-                }
-                if let ExpnKind::Macro { .. } = expn_data.kind {
-                    body_span = expn_data.call_site;
-                } else {
-                    break;
-                }
-            }
-        }
+        let body_span = get_body_span(tcx, hir_body, mir_body);
 
         let source_file = source_map.lookup_source_file(body_span.lo());
         let fn_sig_span = match some_fn_sig.filter(|fn_sig| {
@@ -144,6 +126,15 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
             Some(fn_sig) => fn_sig.span.with_hi(body_span.lo()),
             None => body_span.shrink_to_lo(),
         };
+
+        debug!(
+            "instrumenting {}: {:?}, fn sig span: {:?}, body span: {:?}",
+            if tcx.is_closure(def_id) { "closure" } else { "function" },
+            def_id,
+            fn_sig_span,
+            body_span
+        );
+
         let function_source_hash = hash_mir_source(tcx, hir_body);
         let basic_coverage_blocks = CoverageGraph::from_mir(mir_body);
         Self {
@@ -160,19 +151,11 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
 
     fn inject_counters(&'a mut self) {
         let tcx = self.tcx;
-        let source_map = tcx.sess.source_map();
         let mir_source = self.mir_body.source;
         let def_id = mir_source.def_id();
         let fn_sig_span = self.fn_sig_span;
         let body_span = self.body_span;
 
-        debug!(
-            "instrumenting {:?}, fn sig span: {}, body span: {}",
-            def_id,
-            source_map.span_to_diagnostic_string(fn_sig_span),
-            source_map.span_to_diagnostic_string(body_span)
-        );
-
         let mut graphviz_data = debug::GraphvizData::new();
         let mut debug_used_expressions = debug::UsedExpressions::new();
 
@@ -204,6 +187,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
                 self.mir_body,
                 &self.basic_coverage_blocks,
                 self.pass_name,
+                body_span,
                 &coverage_spans,
             );
         }
@@ -560,6 +544,35 @@ fn fn_sig_and_body<'tcx>(
     (hir::map::fn_sig(hir_node), tcx.hir().body(fn_body_id))
 }
 
+fn get_body_span<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    hir_body: &rustc_hir::Body<'tcx>,
+    mir_body: &mut mir::Body<'tcx>,
+) -> Span {
+    let mut body_span = hir_body.value.span;
+    let def_id = mir_body.source.def_id();
+
+    if tcx.is_closure(def_id) {
+        // If the MIR function is a closure, and if the closure body span
+        // starts from a macro, but it's content is not in that macro, try
+        // to find a non-macro callsite, and instrument the spans there
+        // instead.
+        loop {
+            let expn_data = body_span.ctxt().outer_expn_data();
+            if expn_data.is_root() {
+                break;
+            }
+            if let ExpnKind::Macro { .. } = expn_data.kind {
+                body_span = expn_data.call_site;
+            } else {
+                break;
+            }
+        }
+    }
+
+    body_span
+}
+
 fn hash_mir_source<'tcx>(tcx: TyCtxt<'tcx>, hir_body: &'tcx rustc_hir::Body<'tcx>) -> u64 {
     let mut hcx = tcx.create_no_span_stable_hashing_context();
     hash(&mut hcx, &hir_body.value).to_smaller_hash()
diff --git a/compiler/rustc_mir/src/transform/coverage/query.rs b/compiler/rustc_mir/src/transform/coverage/query.rs
index 2ba9d1bdc0c00..760f16eae6b1f 100644
--- a/compiler/rustc_mir/src/transform/coverage/query.rs
+++ b/compiler/rustc_mir/src/transform/coverage/query.rs
@@ -120,8 +120,8 @@ impl CoverageVisitor {
     }
 }
 
-fn coverageinfo<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> CoverageInfo {
-    let mir_body = mir_body(tcx, def_id);
+fn coverageinfo<'tcx>(tcx: TyCtxt<'tcx>, instance_def: ty::InstanceDef<'tcx>) -> CoverageInfo {
+    let mir_body = tcx.instance_mir(instance_def);
 
     let mut coverage_visitor = CoverageVisitor {
         // num_counters always has at least the `ZERO` counter.
diff --git a/compiler/rustc_mir/src/transform/coverage/spans.rs b/compiler/rustc_mir/src/transform/coverage/spans.rs
index 444b4e2ca1914..f62171b3c535c 100644
--- a/compiler/rustc_mir/src/transform/coverage/spans.rs
+++ b/compiler/rustc_mir/src/transform/coverage/spans.rs
@@ -530,17 +530,25 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> {
                     .iter()
                     .enumerate()
                     .filter_map(move |(index, statement)| {
-                        filtered_statement_span(statement, self.body_span).map(
-                            |(span, expn_span)| {
-                                CoverageSpan::for_statement(
-                                    statement, span, expn_span, bcb, bb, index,
-                                )
-                            },
-                        )
+                        filtered_statement_span(statement).map(|span| {
+                            CoverageSpan::for_statement(
+                                statement,
+                                function_source_span(span, self.body_span),
+                                span,
+                                bcb,
+                                bb,
+                                index,
+                            )
+                        })
                     })
-                    .chain(filtered_terminator_span(data.terminator(), self.body_span).map(
-                        |(span, expn_span)| CoverageSpan::for_terminator(span, expn_span, bcb, bb),
-                    ))
+                    .chain(filtered_terminator_span(data.terminator()).map(|span| {
+                        CoverageSpan::for_terminator(
+                            function_source_span(span, self.body_span),
+                            span,
+                            bcb,
+                            bb,
+                        )
+                    }))
             })
             .collect()
     }
@@ -795,13 +803,9 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> {
     }
 }
 
-/// See `function_source_span()` for a description of the two returned spans.
-/// If the MIR `Statement` is not contributive to computing coverage spans,
-/// returns `None`.
-pub(super) fn filtered_statement_span(
-    statement: &'a Statement<'tcx>,
-    body_span: Span,
-) -> Option<(Span, Span)> {
+/// If the MIR `Statement` has a span contributive to computing coverage spans,
+/// return it; otherwise return `None`.
+pub(super) fn filtered_statement_span(statement: &'a Statement<'tcx>) -> Option<Span> {
     match statement.kind {
         // These statements have spans that are often outside the scope of the executed source code
         // for their parent `BasicBlock`.
@@ -838,18 +842,14 @@ pub(super) fn filtered_statement_span(
         | StatementKind::LlvmInlineAsm(_)
         | StatementKind::Retag(_, _)
         | StatementKind::AscribeUserType(_, _) => {
-            Some(function_source_span(statement.source_info.span, body_span))
+            Some(statement.source_info.span)
         }
     }
 }
 
-/// See `function_source_span()` for a description of the two returned spans.
-/// If the MIR `Terminator` is not contributive to computing coverage spans,
-/// returns `None`.
-pub(super) fn filtered_terminator_span(
-    terminator: &'a Terminator<'tcx>,
-    body_span: Span,
-) -> Option<(Span, Span)> {
+/// If the MIR `Terminator` has a span contributive to computing coverage spans,
+/// return it; otherwise return `None`.
+pub(super) fn filtered_terminator_span(terminator: &'a Terminator<'tcx>) -> Option<Span> {
     match terminator.kind {
         // These terminators have spans that don't positively contribute to computing a reasonable
         // span of actually executed source code. (For example, SwitchInt terminators extracted from
@@ -873,7 +873,7 @@ pub(super) fn filtered_terminator_span(
                     span = span.with_lo(constant.span.lo());
                 }
             }
-            Some(function_source_span(span, body_span))
+            Some(span)
         }
 
         // Retain spans from all other terminators
@@ -884,28 +884,20 @@ pub(super) fn filtered_terminator_span(
         | TerminatorKind::GeneratorDrop
         | TerminatorKind::FalseUnwind { .. }
         | TerminatorKind::InlineAsm { .. } => {
-            Some(function_source_span(terminator.source_info.span, body_span))
+            Some(terminator.source_info.span)
         }
     }
 }
 
-/// Returns two spans from the given span (the span associated with a
-/// `Statement` or `Terminator`):
-///
-///   1. An extrapolated span (pre-expansion[^1]) corresponding to a range within
-///      the function's body source. This span is guaranteed to be contained
-///      within, or equal to, the `body_span`. If the extrapolated span is not
-///      contained within the `body_span`, the `body_span` is returned.
-///   2. The actual `span` value from the `Statement`, before expansion.
-///
-/// Only the first span is used when computing coverage code regions. The second
-/// span is useful if additional expansion data is needed (such as to look up
-/// the macro name for a composed span within that macro).)
+/// Returns an extrapolated span (pre-expansion[^1]) corresponding to a range
+/// within the function's body source. This span is guaranteed to be contained
+/// within, or equal to, the `body_span`. If the extrapolated span is not
+/// contained within the `body_span`, the `body_span` is returned.
 ///
-/// [^1]Expansions result from Rust syntax including macros, syntactic
-/// sugar, etc.).
+/// [^1]Expansions result from Rust syntax including macros, syntactic sugar,
+/// etc.).
 #[inline]
-fn function_source_span(span: Span, body_span: Span) -> (Span, Span) {
+pub(super) fn function_source_span(span: Span, body_span: Span) -> Span {
     let original_span = original_sp(span, body_span).with_ctxt(body_span.ctxt());
-    (if body_span.contains(original_span) { original_span } else { body_span }, span)
+    if body_span.contains(original_span) { original_span } else { body_span }
 }
diff --git a/compiler/rustc_mir/src/transform/coverage/tests.rs b/compiler/rustc_mir/src/transform/coverage/tests.rs
index 9b84173c8a293..b04c2d542d459 100644
--- a/compiler/rustc_mir/src/transform/coverage/tests.rs
+++ b/compiler/rustc_mir/src/transform/coverage/tests.rs
@@ -683,12 +683,10 @@ fn test_make_bcb_counters() {
         let mut basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body);
         let mut coverage_spans = Vec::new();
         for (bcb, data) in basic_coverage_blocks.iter_enumerated() {
-            if let Some((span, expn_span)) =
-                spans::filtered_terminator_span(data.terminator(&mir_body), body_span)
-            {
+            if let Some(span) = spans::filtered_terminator_span(data.terminator(&mir_body)) {
                 coverage_spans.push(spans::CoverageSpan::for_terminator(
+                    spans::function_source_span(span, body_span),
                     span,
-                    expn_span,
                     bcb,
                     data.last_bb(),
                 ));
diff --git a/compiler/rustc_mir/src/transform/early_otherwise_branch.rs b/compiler/rustc_mir/src/transform/early_otherwise_branch.rs
index f7ea9faec4728..7934d4ba8499c 100644
--- a/compiler/rustc_mir/src/transform/early_otherwise_branch.rs
+++ b/compiler/rustc_mir/src/transform/early_otherwise_branch.rs
@@ -170,10 +170,7 @@ impl<'tcx> MirPass<'tcx> for EarlyOtherwiseBranch {
 }
 
 fn is_switch<'tcx>(terminator: &Terminator<'tcx>) -> bool {
-    match terminator.kind {
-        TerminatorKind::SwitchInt { .. } => true,
-        _ => false,
-    }
+    matches!(terminator.kind, TerminatorKind::SwitchInt { .. })
 }
 
 struct Helper<'a, 'tcx> {
diff --git a/compiler/rustc_mir/src/transform/simplify_try.rs b/compiler/rustc_mir/src/transform/simplify_try.rs
index b42543c04eb3d..89fddc95c98f7 100644
--- a/compiler/rustc_mir/src/transform/simplify_try.rs
+++ b/compiler/rustc_mir/src/transform/simplify_try.rs
@@ -628,10 +628,7 @@ impl<'a, 'tcx> SimplifyBranchSameOptimizationFinder<'a, 'tcx> {
                     // But `asm!(...)` could abort the program,
                     // so we cannot assume that the `unreachable` terminator itself is reachable.
                     // FIXME(Centril): use a normalization pass instead of a check.
-                    || bb.statements.iter().any(|stmt| match stmt.kind {
-                        StatementKind::LlvmInlineAsm(..) => true,
-                        _ => false,
-                    })
+                    || bb.statements.iter().any(|stmt| matches!(stmt.kind, StatementKind::LlvmInlineAsm(..)))
                     })
                     .peekable();
 
diff --git a/compiler/rustc_mir/src/util/spanview.rs b/compiler/rustc_mir/src/util/spanview.rs
index 2103f2f093407..42683dac426e3 100644
--- a/compiler/rustc_mir/src/util/spanview.rs
+++ b/compiler/rustc_mir/src/util/spanview.rs
@@ -131,7 +131,7 @@ where
             }
         }
     }
-    write_document(tcx, def_id, span_viewables, title, w)?;
+    write_document(tcx, fn_span(tcx, def_id), span_viewables, title, w)?;
     Ok(())
 }
 
@@ -139,7 +139,7 @@ where
 /// list `SpanViewable`s.
 pub fn write_document<'tcx, W>(
     tcx: TyCtxt<'tcx>,
-    def_id: DefId,
+    spanview_span: Span,
     mut span_viewables: Vec<SpanViewable>,
     title: &str,
     w: &mut W,
@@ -147,16 +147,16 @@ pub fn write_document<'tcx, W>(
 where
     W: Write,
 {
-    let fn_span = fn_span(tcx, def_id);
-    let mut from_pos = fn_span.lo();
-    let end_pos = fn_span.hi();
+    let mut from_pos = spanview_span.lo();
+    let end_pos = spanview_span.hi();
     let source_map = tcx.sess.source_map();
     let start = source_map.lookup_char_pos(from_pos);
     let indent_to_initial_start_col = " ".repeat(start.col.to_usize());
     debug!(
-        "fn_span source is:\n{}{}",
+        "spanview_span={:?}; source is:\n{}{}",
+        spanview_span,
         indent_to_initial_start_col,
-        source_map.span_to_snippet(fn_span).expect("function should have printable source")
+        source_map.span_to_snippet(spanview_span).expect("function should have printable source")
     );
     writeln!(w, "{}", HEADER)?;
     writeln!(w, "<title>{}</title>", title)?;
diff --git a/compiler/rustc_trait_selection/src/opaque_types.rs b/compiler/rustc_trait_selection/src/opaque_types.rs
index fb4a8ce687c4d..7e67bc118ec1e 100644
--- a/compiler/rustc_trait_selection/src/opaque_types.rs
+++ b/compiler/rustc_trait_selection/src/opaque_types.rs
@@ -46,6 +46,7 @@ pub struct OpaqueTypeDecl<'tcx> {
     /// type Foo = impl Baz;
     /// fn bar() -> Foo {
     /// //          ^^^ This is the span we are looking for!
+    /// }
     /// ```
     ///
     /// In cases where the fn returns `(impl Trait, impl Trait)` or
diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs
index 7a2b5b26ef437..7643d0d011714 100644
--- a/compiler/rustc_typeck/src/check/upvar.rs
+++ b/compiler/rustc_typeck/src/check/upvar.rs
@@ -323,7 +323,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     ///
     /// InferBorrowKind results in a structure like this:
     ///
-    /// ```
+    /// ```text
     /// {
     ///       Place(base: hir_id_s, projections: [], ....) -> {
     ///                                                            capture_kind_expr: hir_id_L5,
@@ -348,7 +348,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// ```
     ///
     /// After the min capture analysis, we get:
-    /// ```
+    /// ```text
     /// {
     ///       hir_id_s -> [
     ///            Place(base: hir_id_s, projections: [], ....) -> {
diff --git a/library/std/src/macros.rs b/library/std/src/macros.rs
index b729349cf530f..b2c5df5410dca 100644
--- a/library/std/src/macros.rs
+++ b/library/std/src/macros.rs
@@ -185,9 +185,10 @@ macro_rules! eprintln {
 /// builds or when debugging in release mode is significantly faster.
 ///
 /// Note that the macro is intended as a debugging tool and therefore you
-/// should avoid having uses of it in version control for long periods.
-/// Use cases involving debug output that should be added to version control
-/// are better served by macros such as [`debug!`] from the [`log`] crate.
+/// should avoid having uses of it in version control for long periods
+/// (other than in tests and similar).
+/// Debug output from production code is better done with other facilities
+/// such as the [`debug!`] macro from the [`log`] crate.
 ///
 /// # Stability
 ///
diff --git a/library/std/src/path.rs b/library/std/src/path.rs
index ed0987064e8f7..cbe14767bd3e7 100644
--- a/library/std/src/path.rs
+++ b/library/std/src/path.rs
@@ -569,7 +569,7 @@ pub struct Components<'a> {
     prefix: Option<Prefix<'a>>,
 
     // true if path *physically* has a root separator; for most Windows
-    // prefixes, it may have a "logical" rootseparator for the purposes of
+    // prefixes, it may have a "logical" root separator for the purposes of
     // normalization, e.g.,  \\server\share == \\server\share\.
     has_physical_root: bool,
 
diff --git a/src/doc/rustdoc/src/lints.md b/src/doc/rustdoc/src/lints.md
index a6626679a7d60..16b091eb255b0 100644
--- a/src/doc/rustdoc/src/lints.md
+++ b/src/doc/rustdoc/src/lints.md
@@ -294,6 +294,50 @@ warning: unclosed HTML tag `h1`
 warning: 2 warnings emitted
 ```
 
+## invalid_rust_codeblocks
+
+This lint **warns by default**. It detects Rust code blocks in documentation
+examples that are invalid (e.g. empty, not parsable as Rust). For example:
+
+```rust
+/// Empty code blocks (with and without the `rust` marker):
+///
+/// ```rust
+/// ```
+///
+/// Invalid syntax in code blocks:
+///
+/// ```rust
+/// '<
+/// ```
+pub fn foo() {}
+```
+
+Which will give:
+
+```text
+warning: Rust code block is empty
+ --> lint.rs:3:5
+  |
+3 |   /// ```rust
+  |  _____^
+4 | | /// ```
+  | |_______^
+  |
+  = note: `#[warn(rustdoc::invalid_rust_codeblocks)]` on by default
+
+warning: could not parse code block as Rust code
+  --> lint.rs:8:5
+   |
+8  |   /// ```rust
+   |  _____^
+9  | | /// '<
+10 | | /// ```
+   | |_______^
+   |
+   = note: error from rustc: unterminated character literal
+```
+
 ## bare_urls
 
 This lint is **warn-by-default**. It detects URLs which are not links.
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index e1dde8eeaf84a..feeb03b1b6700 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -533,8 +533,7 @@ impl Clean<Generics> for hir::Generics<'_> {
                 match param.kind {
                     GenericParamDefKind::Lifetime => unreachable!(),
                     GenericParamDefKind::Type { did, ref bounds, .. } => {
-                        cx.impl_trait_bounds
-                            .insert(FakeDefId::new_real(did).into(), bounds.clone());
+                        cx.impl_trait_bounds.insert(did.into(), bounds.clone());
                     }
                     GenericParamDefKind::Const { .. } => unreachable!(),
                 }
@@ -615,7 +614,7 @@ impl<'a, 'tcx> Clean<Generics> for (&'a ty::Generics, ty::GenericPredicates<'tcx
             .collect::<Vec<GenericParamDef>>();
 
         // param index -> [(DefId of trait, associated type name, type)]
-        let mut impl_trait_proj = FxHashMap::<u32, Vec<(FakeDefId, Symbol, Ty<'tcx>)>>::default();
+        let mut impl_trait_proj = FxHashMap::<u32, Vec<(DefId, Symbol, Ty<'tcx>)>>::default();
 
         let where_predicates = preds
             .predicates
@@ -687,13 +686,7 @@ impl<'a, 'tcx> Clean<Generics> for (&'a ty::Generics, ty::GenericPredicates<'tcx
                 if let Some(proj) = impl_trait_proj.remove(&idx) {
                     for (trait_did, name, rhs) in proj {
                         let rhs = rhs.clean(cx);
-                        simplify::merge_bounds(
-                            cx,
-                            &mut bounds,
-                            trait_did.expect_real(),
-                            name,
-                            &rhs,
-                        );
+                        simplify::merge_bounds(cx, &mut bounds, trait_did, name, &rhs);
                     }
                 }
             } else {
@@ -1183,8 +1176,7 @@ fn clean_qpath(hir_ty: &hir::Ty<'_>, cx: &mut DocContext<'_>) -> Type {
                 if let Some(new_ty) = cx.ty_substs.get(&did).cloned() {
                     return new_ty;
                 }
-                if let Some(bounds) = cx.impl_trait_bounds.remove(&FakeDefId::new_real(did).into())
-                {
+                if let Some(bounds) = cx.impl_trait_bounds.remove(&did.into()) {
                     return ImplTrait(bounds);
                 }
             }
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index f0e0401824265..edd3d77eeb780 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -73,10 +73,6 @@ impl FakeDefId {
         Self::Fake(DefIndex::from(id), krate)
     }
 
-    crate fn new_real(id: DefId) -> Self {
-        Self::Real(id)
-    }
-
     #[inline]
     crate fn is_local(self) -> bool {
         match self {
@@ -470,7 +466,7 @@ impl Item {
             .filter_map(|ItemLink { link: s, link_text, did, ref fragment }| {
                 match did {
                     Some(did) => {
-                        if let Some((mut href, ..)) = href(did.expect_real(), cx) {
+                        if let Some((mut href, ..)) = href(did.clone(), cx) {
                             if let Some(ref fragment) = *fragment {
                                 href.push('#');
                                 href.push_str(fragment);
@@ -972,7 +968,7 @@ crate struct ItemLink {
     /// This may not be the same as `link` if there was a disambiguator
     /// in an intra-doc link (e.g. \[`fn@f`\])
     pub(crate) link_text: String,
-    pub(crate) did: Option<FakeDefId>,
+    pub(crate) did: Option<DefId>,
     /// The url fragment to append to the link
     pub(crate) fragment: Option<String>,
 }
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index b43916f9f332c..2930c3c5fb7ec 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -576,12 +576,12 @@ impl<'tcx> Visitor<'tcx> for EmitIgnoredResolutionErrors<'tcx> {
 /// for `impl Trait` in argument position.
 #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
 crate enum ImplTraitParam {
-    DefId(FakeDefId),
+    DefId(DefId),
     ParamIndex(u32),
 }
 
-impl From<FakeDefId> for ImplTraitParam {
-    fn from(did: FakeDefId) -> Self {
+impl From<DefId> for ImplTraitParam {
+    fn from(did: DefId) -> Self {
         ImplTraitParam::DefId(did)
     }
 }
diff --git a/src/librustdoc/fold.rs b/src/librustdoc/fold.rs
index 37d11d4ed47c4..723792381ef09 100644
--- a/src/librustdoc/fold.rs
+++ b/src/librustdoc/fold.rs
@@ -1,17 +1,10 @@
 use crate::clean::*;
 
-crate struct StripItem(pub Item);
-
-impl StripItem {
-    crate fn strip(self) -> Item {
-        match self.0 {
-            Item { kind: box StrippedItem(..), .. } => self.0,
-            mut i => {
-                i.kind = box StrippedItem(i.kind);
-                i
-            }
-        }
+crate fn strip_item(mut item: Item) -> Item {
+    if !matches!(*item.kind, StrippedItem(..)) {
+        item.kind = box StrippedItem(item.kind);
     }
+    item
 }
 
 crate trait DocFolder: Sized {
diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs
index 50496f320096c..5734a4a98e2b5 100644
--- a/src/librustdoc/formats/cache.rs
+++ b/src/librustdoc/formats/cache.rs
@@ -67,7 +67,7 @@ crate struct Cache {
     /// When rendering traits, it's often useful to be able to list all
     /// implementors of the trait, and this mapping is exactly, that: a mapping
     /// of trait ids to the list of known implementors of the trait
-    crate implementors: FxHashMap<FakeDefId, Vec<Impl>>,
+    crate implementors: FxHashMap<DefId, Vec<Impl>>,
 
     /// Cache of where external crate documentation can be found.
     crate extern_locations: FxHashMap<CrateNum, ExternalLocation>,
@@ -299,7 +299,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
                             desc: item
                                 .doc_value()
                                 .map_or_else(String::new, |x| short_markdown_summary(&x.as_str())),
-                            parent: parent.map(FakeDefId::new_real),
+                            parent,
                             parent_idx: None,
                             search_type: get_index_search_type(&item, &self.empty_cache, self.tcx),
                             aliases: item.attrs.get_doc_aliases(),
diff --git a/src/librustdoc/html/render/cache.rs b/src/librustdoc/html/render/cache.rs
index 57520a1a1fb46..3e056c4b67a70 100644
--- a/src/librustdoc/html/render/cache.rs
+++ b/src/librustdoc/html/render/cache.rs
@@ -7,7 +7,7 @@ use serde::ser::{Serialize, SerializeStruct, Serializer};
 
 use crate::clean;
 use crate::clean::types::{
-    FakeDefId, FnDecl, FnRetTy, GenericBound, Generics, GetDefId, Type, WherePredicate,
+    FnDecl, FnRetTy, GenericBound, Generics, GetDefId, Type, WherePredicate,
 };
 use crate::formats::cache::Cache;
 use crate::formats::item_type::ItemType;
@@ -82,7 +82,7 @@ crate fn build_index<'tcx>(krate: &clean::Crate, cache: &mut Cache, tcx: TyCtxt<
                 defid_to_pathid.insert(defid, pathid);
                 lastpathid += 1;
 
-                if let Some(&(ref fqp, short)) = paths.get(&defid.expect_real()) {
+                if let Some(&(ref fqp, short)) = paths.get(&defid) {
                     crate_paths.push((short, fqp.last().unwrap().clone()));
                     Some(pathid)
                 } else {
@@ -214,7 +214,7 @@ crate fn get_index_search_type<'tcx>(
 
 fn get_index_type(clean_type: &clean::Type, cache: &Cache) -> RenderType {
     RenderType {
-        ty: clean_type.def_id_full(cache).map(FakeDefId::new_real),
+        ty: clean_type.def_id_full(cache),
         idx: None,
         name: get_index_type_name(clean_type, true).map(|s| s.as_str().to_ascii_lowercase()),
         generics: get_generics(clean_type, cache),
@@ -256,7 +256,7 @@ fn get_generics(clean_type: &clean::Type, cache: &Cache) -> Option<Vec<Generic>>
             .filter_map(|t| {
                 get_index_type_name(t, false).map(|name| Generic {
                     name: name.as_str().to_ascii_lowercase(),
-                    defid: t.def_id_full(cache).map(FakeDefId::new_real),
+                    defid: t.def_id_full(cache),
                     idx: None,
                 })
             })
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 5b54b32e4ddea..e4dbf14586629 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -87,7 +87,7 @@ crate struct IndexItem {
     crate name: String,
     crate path: String,
     crate desc: String,
-    crate parent: Option<FakeDefId>,
+    crate parent: Option<DefId>,
     crate parent_idx: Option<usize>,
     crate search_type: Option<IndexItemFunctionType>,
     crate aliases: Box<[String]>,
@@ -96,7 +96,7 @@ crate struct IndexItem {
 /// A type used for the search index.
 #[derive(Debug)]
 crate struct RenderType {
-    ty: Option<FakeDefId>,
+    ty: Option<DefId>,
     idx: Option<usize>,
     name: Option<String>,
     generics: Option<Vec<Generic>>,
@@ -128,7 +128,7 @@ impl Serialize for RenderType {
 #[derive(Debug)]
 crate struct Generic {
     name: String,
-    defid: Option<FakeDefId>,
+    defid: Option<DefId>,
     idx: Option<usize>,
 }
 
@@ -2137,7 +2137,7 @@ fn sidebar_trait(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, t: &clean
         "</div>",
     );
 
-    if let Some(implementors) = cx.cache.implementors.get(&it.def_id) {
+    if let Some(implementors) = cx.cache.implementors.get(&it.def_id.expect_real()) {
         let cache = cx.cache();
         let mut res = implementors
             .iter()
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 20e82cf2caf38..ff639cb292462 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -642,7 +642,7 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
     // If there are methods directly on this trait object, render them here.
     render_assoc_items(w, cx, it, it.def_id.expect_real(), AssocItemRender::All);
 
-    if let Some(implementors) = cx.cache.implementors.get(&it.def_id) {
+    if let Some(implementors) = cx.cache.implementors.get(&it.def_id.expect_real()) {
         // The DefId is for the first Type found with that name. The bool is
         // if any Types with the same name but different DefId have been found.
         let mut implementor_dups: FxHashMap<Symbol, (DefId, bool)> = FxHashMap::default();
diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs
index d0518cb6862fe..c25a73f58eb60 100644
--- a/src/librustdoc/html/render/write_shared.rs
+++ b/src/librustdoc/html/render/write_shared.rs
@@ -465,8 +465,6 @@ pub(super) fn write_shared(
     // Update the list of all implementors for traits
     let dst = cx.dst.join("implementors");
     for (&did, imps) in &cx.cache.implementors {
-        let did = did.expect_real();
-
         // Private modules can leak through to this phase of rustdoc, which
         // could contain implementations for otherwise private types. In some
         // rare cases we could find an implementation for an item which wasn't
diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js
index ebc9ada4451e7..be30871ea4c70 100644
--- a/src/librustdoc/html/static/main.js
+++ b/src/librustdoc/html/static/main.js
@@ -1,6 +1,6 @@
 // Local js definitions:
-/* global addClass, getSettingValue, hasClass */
-/* global onEach, onEachLazy, hasOwnProperty, removeClass, updateLocalStorage */
+/* global addClass, getSettingValue, hasClass, searchState */
+/* global onEach, onEachLazy, removeClass */
 /* global switchTheme, useSystemTheme */
 
 if (!String.prototype.startsWith) {
@@ -347,10 +347,6 @@ function hideThemeButtonState() {
         document.getElementsByTagName("body")[0].style.marginTop = "";
     }
 
-    function isHidden(elem) {
-        return elem.offsetHeight === 0;
-    }
-
     var toggleAllDocsId = "toggle-all-docs";
     var main = document.getElementById("main");
     var savedHash = "";
@@ -553,7 +549,7 @@ function hideThemeButtonState() {
                     len = window.rootPath.match(/\.\.\//g).length + 1;
 
                 for (i = 0; i < len; ++i) {
-                    match = url.match(/\/[^\/]*$/);
+                    match = url.match(/\/[^/]*$/);
                     if (i < len - 1) {
                         stripped = match[0] + stripped;
                     }
@@ -952,13 +948,11 @@ function hideThemeButtonState() {
         });
 
         var currentType = document.getElementsByClassName("type-decl")[0];
-        var className = null;
         if (currentType) {
             currentType = currentType.getElementsByClassName("rust")[0];
             if (currentType) {
                 onEachLazy(currentType.classList, function(item) {
                     if (item !== "main") {
-                        className = item;
                         return true;
                     }
                 });
@@ -1047,7 +1041,7 @@ function hideThemeButtonState() {
         };
     }
 
-    function buildHelperPopup() {
+    var buildHelperPopup = function() {
         var popup = document.createElement("aside");
         addClass(popup, "hidden");
         popup.id = "help";
@@ -1114,7 +1108,7 @@ function hideThemeButtonState() {
         insertAfter(popup, searchState.outputElement());
         // So that it's only built once and then it'll do nothing when called!
         buildHelperPopup = function() {};
-    }
+    };
 
     onHashChange(null);
     window.addEventListener("hashchange", onHashChange);
diff --git a/src/librustdoc/html/static/rustdoc.css b/src/librustdoc/html/static/rustdoc.css
index 1b7eff4604f5f..e2e1aefa4a842 100644
--- a/src/librustdoc/html/static/rustdoc.css
+++ b/src/librustdoc/html/static/rustdoc.css
@@ -542,7 +542,7 @@ h4 > code, h3 > code, .invisible > code {
 	font-size: 0.8em;
 }
 
-.content .methods > div:not(.notable-traits) {
+.content .methods > div:not(.notable-traits):not(.methods) {
 	margin-left: 40px;
 	margin-bottom: 15px;
 }
@@ -1293,6 +1293,201 @@ h4 > .notable-traits {
 	border-top: 1px solid;
 }
 
+
+
+h3.notable {
+	margin: 0;
+	margin-bottom: 13px;
+	font-size: 19px;
+}
+
+kbd {
+	display: inline-block;
+	padding: 3px 5px;
+	font: 15px monospace;
+	line-height: 10px;
+	vertical-align: middle;
+	border: solid 1px;
+	border-radius: 3px;
+	box-shadow: inset 0 -1px 0;
+	cursor: default;
+}
+
+.hidden-by-impl-hider,
+.hidden-by-usual-hider {
+	/* important because of conflicting rule for small screens */
+	display: none !important;
+}
+
+#implementations-list > h3 > span.in-band {
+	width: 100%;
+}
+
+.table-display {
+	width: 100%;
+	border: 0;
+	border-collapse: collapse;
+	border-spacing: 0;
+	font-size: 16px;
+}
+
+.table-display tr td:first-child {
+	padding-right: 0;
+}
+
+.table-display tr td:last-child {
+	float: right;
+}
+.table-display .out-of-band {
+	position: relative;
+	font-size: 19px;
+	display: block;
+}
+#implementors-list > .impl-items .table-display .out-of-band {
+	font-size: 17px;
+}
+
+.table-display td:hover .anchor {
+	display: block;
+	top: 2px;
+	left: -5px;
+}
+
+#main > ul {
+	padding-left: 10px;
+}
+#main > ul > li {
+	list-style: none;
+}
+
+.non-exhaustive {
+	margin-bottom: 1em;
+}
+
+div.children {
+	padding-left: 27px;
+	display: none;
+}
+div.name {
+	cursor: pointer;
+	position: relative;
+	margin-left: 16px;
+}
+div.files > a {
+	display: block;
+	padding: 0 3px;
+}
+div.files > a:hover, div.name:hover {
+	background-color: #a14b4b;
+}
+div.name.expand + .children {
+	display: block;
+}
+div.name::before {
+	content: "\25B6";
+	padding-left: 4px;
+	font-size: 0.7em;
+	position: absolute;
+	left: -16px;
+	top: 4px;
+}
+div.name.expand::before {
+	transform: rotate(90deg);
+	left: -15px;
+	top: 2px;
+}
+
+/* The hideme class is used on summary tags that contain a span with
+	placeholder text shown only when the toggle is closed. For instance,
+	"Expand description" or "Show methods". */
+details.rustdoc-toggle > summary.hideme {
+	cursor: pointer;
+}
+
+details.rustdoc-toggle > summary, details.undocumented > summary {
+	list-style: none;
+}
+details.rustdoc-toggle > summary::-webkit-details-marker,
+details.rustdoc-toggle > summary::marker,
+details.undocumented > summary::-webkit-details-marker,
+details.undocumented > summary::marker {
+	display: none;
+}
+
+details.rustdoc-toggle > summary.hideme > span {
+	margin-left: 9px;
+}
+
+details.rustdoc-toggle > summary::before {
+	content: "[+]";
+	font-weight: 300;
+	font-size: 0.8em;
+	letter-spacing: 1px;
+	cursor: pointer;
+}
+
+details.rustdoc-toggle.top-doc > summary,
+details.rustdoc-toggle.top-doc > summary::before,
+details.rustdoc-toggle.non-exhaustive > summary,
+details.rustdoc-toggle.non-exhaustive > summary::before {
+	font-family: 'Fira Sans';
+	font-size: 16px;
+}
+
+details.non-exhaustive {
+	margin-bottom: 8px;
+}
+
+details.rustdoc-toggle > summary.hideme::before {
+	position: relative;
+}
+
+details.rustdoc-toggle > summary:not(.hideme)::before {
+	position: absolute;
+	left: -23px;
+	top: initial;
+}
+
+.impl-items > details.rustdoc-toggle > summary:not(.hideme)::before,
+.undocumented > details.rustdoc-toggle > summary:not(.hideme)::before {
+	position: absolute;
+	top: 3px;
+	left: -2px;
+}
+
+/* When a "hideme" summary is open and the "Expand description" or "Show
+	methods" text is hidden, we want the [-] toggle that remains to not
+	affect the layout of the items to its right. To do that, we use
+	absolute positioning. Note that we also set position: relative
+	on the parent <details> to make this work properly. */
+details.rustdoc-toggle[open] > summary.hideme {
+	position: absolute;
+}
+
+details.rustdoc-toggle, details.undocumented {
+	position: relative;
+}
+
+details.rustdoc-toggle[open] > summary.hideme > span {
+	display: none;
+}
+
+details.rustdoc-toggle[open] > summary::before {
+	content: "[−]";
+	display: inline;
+}
+
+details.undocumented > summary::before {
+	content: "[+] Show hidden undocumented items";
+	cursor: pointer;
+	font-size: 16px;
+	font-weight: 300;
+}
+
+details.undocumented[open] > summary::before {
+	content: "[−] Hide undocumented items";
+}
+
 /* Media Queries */
 
 @media (min-width: 701px) {
@@ -1492,8 +1687,9 @@ h4 > .notable-traits {
 		border-bottom: 1px solid;
 	}
 
-	.item-list > details.rustdoc-toggle > summary:not(.hideme)::before {
-		left: -10px;
+	#main > details.rustdoc-toggle > summary::before,
+	#main > div > details.rustdoc-toggle > summary::before {
+		left: -11px;
 	}
 
 	#all-types {
@@ -1587,193 +1783,3 @@ h4 > .notable-traits {
 		margin-left: 12px;
 	}
 }
-
-h3.notable {
-	margin: 0;
-	margin-bottom: 13px;
-	font-size: 19px;
-}
-
-kbd {
-	display: inline-block;
-	padding: 3px 5px;
-	font: 15px monospace;
-	line-height: 10px;
-	vertical-align: middle;
-	border: solid 1px;
-	border-radius: 3px;
-	box-shadow: inset 0 -1px 0;
-	cursor: default;
-}
-
-.hidden-by-impl-hider,
-.hidden-by-usual-hider {
-	/* important because of conflicting rule for small screens */
-	display: none !important;
-}
-
-#implementations-list > h3 > span.in-band {
-	width: 100%;
-}
-
-.table-display {
-	width: 100%;
-	border: 0;
-	border-collapse: collapse;
-	border-spacing: 0;
-	font-size: 16px;
-}
-
-.table-display tr td:first-child {
-	padding-right: 0;
-}
-
-.table-display tr td:last-child {
-	float: right;
-}
-.table-display .out-of-band {
-	position: relative;
-	font-size: 19px;
-	display: block;
-}
-#implementors-list > .impl-items .table-display .out-of-band {
-	font-size: 17px;
-}
-
-.table-display td:hover .anchor {
-	display: block;
-	top: 2px;
-	left: -5px;
-}
-
-#main > ul {
-	padding-left: 10px;
-}
-#main > ul > li {
-	list-style: none;
-}
-
-.non-exhaustive {
-	margin-bottom: 1em;
-}
-
-div.children {
-	padding-left: 27px;
-	display: none;
-}
-div.name {
-	cursor: pointer;
-	position: relative;
-	margin-left: 16px;
-}
-div.files > a {
-	display: block;
-	padding: 0 3px;
-}
-div.files > a:hover, div.name:hover {
-	background-color: #a14b4b;
-}
-div.name.expand + .children {
-	display: block;
-}
-div.name::before {
-	content: "\25B6";
-	padding-left: 4px;
-	font-size: 0.7em;
-	position: absolute;
-	left: -16px;
-	top: 4px;
-}
-div.name.expand::before {
-	transform: rotate(90deg);
-	left: -15px;
-	top: 2px;
-}
-
-/* The hideme class is used on summary tags that contain a span with
-	placeholder text shown only when the toggle is closed. For instance,
-	"Expand description" or "Show methods". */
-details.rustdoc-toggle > summary.hideme {
-	cursor: pointer;
-}
-
-details.rustdoc-toggle > summary::-webkit-details-marker,
-details.rustdoc-toggle > summary::marker,
-details.undocumented > summary::-webkit-details-marker,
-details.undocumented > summary::marker {
-	display: none;
-}
-
-details.rustdoc-toggle > summary.hideme > span {
-	margin-left: 9px;
-}
-
-details.rustdoc-toggle > summary::before {
-	content: "[+]";
-	font-weight: 300;
-	font-size: 0.8em;
-	letter-spacing: 1px;
-	cursor: pointer;
-}
-
-details.rustdoc-toggle.top-doc > summary,
-details.rustdoc-toggle.top-doc > summary::before,
-details.rustdoc-toggle.non-exhaustive > summary,
-details.rustdoc-toggle.non-exhaustive > summary::before {
-	font-family: 'Fira Sans';
-	font-size: 16px;
-}
-
-details.non-exhaustive {
-	margin-bottom: 8px;
-}
-
-details.rustdoc-toggle > summary.hideme::before {
-	position: relative;
-}
-
-details.rustdoc-toggle > summary:not(.hideme)::before {
-	position: absolute;
-	left: -23px;
-	top: initial;
-}
-
-.impl-items > details.rustdoc-toggle > summary:not(.hideme)::before,
-.undocumented > details.rustdoc-toggle > summary:not(.hideme)::before {
-	position: absolute;
-	top: 3px;
-	left: -2px;
-}
-
-/* When a "hideme" summary is open and the "Expand description" or "Show
-	methods" text is hidden, we want the [-] toggle that remains to not
-	affect the layout of the items to its right. To do that, we use
-	absolute positioning. Note that we also set position: relative
-	on the parent <details> to make this work properly. */
-details.rustdoc-toggle[open] > summary.hideme {
-	position: absolute;
-}
-
-details.rustdoc-toggle, details.undocumented {
-	position: relative;
-}
-
-details.rustdoc-toggle[open] > summary.hideme > span {
-	display: none;
-}
-
-details.rustdoc-toggle[open] > summary::before {
-	content: "[−]";
-	display: inline;
-}
-
-details.undocumented > summary::before {
-	content: "[+] Show hidden undocumented items";
-	cursor: pointer;
-	font-size: 16px;
-	font-weight: 300;
-}
-
-details.undocumented[open] > summary::before {
-	content: "[-] Hide undocumented items";
-}
diff --git a/src/librustdoc/html/static/search.js b/src/librustdoc/html/static/search.js
index a09d3eb1796b3..c53b3d5f14bc6 100644
--- a/src/librustdoc/html/static/search.js
+++ b/src/librustdoc/html/static/search.js
@@ -1,3 +1,6 @@
+/* global addClass, getNakedUrl, getSettingValue, hasOwnPropertyRustdoc, initSearch, onEach */
+/* global onEachLazy, removeClass, searchState, updateLocalStorage */
+
 (function() {
 // This mapping table should match the discriminants of
 // `rustdoc::html::item_type::ItemType` type in Rust.
@@ -170,7 +173,7 @@ window.initSearch = function(rawSearchIndex) {
         function sortResults(results, isType) {
             var ar = [];
             for (var entry in results) {
-                if (hasOwnProperty(results, entry)) {
+                if (hasOwnPropertyRustdoc(results, entry)) {
                     ar.push(results[entry]);
                 }
             }
@@ -254,7 +257,7 @@ window.initSearch = function(rawSearchIndex) {
             });
 
             for (i = 0, len = results.length; i < len; ++i) {
-                var result = results[i];
+                result = results[i];
 
                 // this validation does not make sense when searching by types
                 if (result.dontValidate) {
@@ -301,7 +304,7 @@ window.initSearch = function(rawSearchIndex) {
                 if (obj.length > GENERICS_DATA &&
                       obj[GENERICS_DATA].length >= val.generics.length) {
                     var elems = Object.create(null);
-                    var elength = object[GENERICS_DATA].length;
+                    var elength = obj[GENERICS_DATA].length;
                     for (var x = 0; x < elength; ++x) {
                         elems[getObjectNameFromId(obj[GENERICS_DATA][x])] += 1;
                     }
@@ -717,7 +720,7 @@ window.initSearch = function(rawSearchIndex) {
             query.output = val;
             query.search = val;
             // gather matching search results up to a certain maximum
-            val = val.replace(/\_/g, "");
+            val = val.replace(/_/g, "");
 
             var valGenerics = extractGenerics(val);
 
@@ -1242,7 +1245,9 @@ window.initSearch = function(rawSearchIndex) {
     function getFilterCrates() {
         var elem = document.getElementById("crate-search");
 
-        if (elem && elem.value !== "All crates" && hasOwnProperty(rawSearchIndex, elem.value)) {
+        if (elem && elem.value !== "All crates" &&
+            hasOwnPropertyRustdoc(rawSearchIndex, elem.value))
+        {
             return elem.value;
         }
         return undefined;
@@ -1293,14 +1298,13 @@ window.initSearch = function(rawSearchIndex) {
         var id = 0;
 
         for (var crate in rawSearchIndex) {
-            if (!hasOwnProperty(rawSearchIndex, crate)) { continue; }
+            if (!hasOwnPropertyRustdoc(rawSearchIndex, crate)) {
+                continue;
+            }
 
             var crateSize = 0;
 
             searchWords.push(crate);
-            var normalizedName = crate.indexOf("_") === -1
-                ? crate
-                : crate.replace(/_/g, "");
             // This object should have exactly the same set of fields as the "row"
             // object defined below. Your JavaScript runtime will thank you.
             // https://mathiasbynens.be/notes/shapes-ics
@@ -1313,7 +1317,7 @@ window.initSearch = function(rawSearchIndex) {
                 parent: undefined,
                 type: null,
                 id: id,
-                normalizedName: normalizedName,
+                normalizedName: crate.indexOf("_") === -1 ? crate : crate.replace(/_/g, ""),
             };
             id += 1;
             searchIndex.push(crateRow);
@@ -1363,9 +1367,6 @@ window.initSearch = function(rawSearchIndex) {
                     word = "";
                     searchWords.push("");
                 }
-                var normalizedName = word.indexOf("_") === -1
-                    ? word
-                    : word.replace(/_/g, "");
                 var row = {
                     crate: crate,
                     ty: itemTypes[i],
@@ -1375,7 +1376,7 @@ window.initSearch = function(rawSearchIndex) {
                     parent: itemParentIdxs[i] > 0 ? paths[itemParentIdxs[i] - 1] : undefined,
                     type: itemFunctionSearchTypes[i],
                     id: id,
-                    normalizedName: normalizedName,
+                    normalizedName: word.indexOf("_") === -1 ? word : word.replace(/_/g, ""),
                 };
                 id += 1;
                 searchIndex.push(row);
@@ -1387,9 +1388,11 @@ window.initSearch = function(rawSearchIndex) {
                 ALIASES[crate] = {};
                 var j, local_aliases;
                 for (var alias_name in aliases) {
-                    if (!aliases.hasOwnProperty(alias_name)) { continue; }
+                    if (!hasOwnPropertyRustdoc(aliases, alias_name)) {
+                        continue;
+                    }
 
-                    if (!ALIASES[crate].hasOwnProperty(alias_name)) {
+                    if (!hasOwnPropertyRustdoc(ALIASES[crate], alias_name)) {
                         ALIASES[crate][alias_name] = [];
                     }
                     local_aliases = aliases[alias_name];
diff --git a/src/librustdoc/html/static/source-script.js b/src/librustdoc/html/static/source-script.js
index 81cf437c7dbb0..4d9a59f836b9e 100644
--- a/src/librustdoc/html/static/source-script.js
+++ b/src/librustdoc/html/static/source-script.js
@@ -2,7 +2,8 @@
 /* global search, sourcesIndex */
 
 // Local js definitions:
-/* global addClass, getCurrentValue, hasClass, removeClass, updateLocalStorage */
+/* global addClass, getCurrentValue, hasClass, onEachLazy, removeClass, searchState */
+/* global updateLocalStorage */
 (function() {
 
 function getCurrentFilePath() {
@@ -153,7 +154,7 @@ function createSourceSidebar() {
 
 var lineNumbersRegex = /^#?(\d+)(?:-(\d+))?$/;
 
-function highlightSourceLines(match, ev) {
+function highlightSourceLines(scrollTo, match) {
     if (typeof match === "undefined") {
         match = window.location.hash.match(lineNumbersRegex);
     }
@@ -174,7 +175,7 @@ function highlightSourceLines(match, ev) {
     if (!elem) {
         return;
     }
-    if (!ev) {
+    if (scrollTo) {
         var x = document.getElementById(from);
         if (x) {
             x.scrollIntoView();
@@ -202,7 +203,7 @@ var handleSourceHighlight = (function() {
             y = window.scrollY;
         if (searchState.browserSupportsHistoryApi()) {
             history.replaceState(null, null, "#" + name);
-            highlightSourceLines();
+            highlightSourceLines(true);
         } else {
             location.replace("#" + name);
         }
@@ -234,7 +235,7 @@ var handleSourceHighlight = (function() {
 window.addEventListener("hashchange", function() {
     var match = window.location.hash.match(lineNumbersRegex);
     if (match) {
-        return highlightSourceLines(match, ev);
+        return highlightSourceLines(false, match);
     }
 });
 
@@ -242,7 +243,7 @@ onEachLazy(document.getElementsByClassName("line-numbers"), function(el) {
     el.addEventListener("click", handleSourceHighlight);
 });
 
-highlightSourceLines();
+highlightSourceLines(true);
 
 window.createSourceSidebar = createSourceSidebar;
 })();
diff --git a/src/librustdoc/html/static/storage.js b/src/librustdoc/html/static/storage.js
index 208afd2e732dd..2eaa81a97d8c5 100644
--- a/src/librustdoc/html/static/storage.js
+++ b/src/librustdoc/html/static/storage.js
@@ -84,7 +84,7 @@ function onEachLazy(lazyArray, func, reversed) {
 }
 
 // eslint-disable-next-line no-unused-vars
-function hasOwnProperty(obj, property) {
+function hasOwnPropertyRustdoc(obj, property) {
     return Object.prototype.hasOwnProperty.call(obj, property);
 }
 
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index d2349b42ae930..5ac43c7364622 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -14,9 +14,8 @@ use rustc_span::Pos;
 
 use rustdoc_json_types::*;
 
-use crate::clean;
 use crate::clean::utils::print_const_expr;
-use crate::clean::FakeDefId;
+use crate::clean::{self, FakeDefId};
 use crate::formats::item_type::ItemType;
 use crate::json::JsonRenderer;
 use std::collections::HashSet;
@@ -31,7 +30,7 @@ impl JsonRenderer<'_> {
             .into_iter()
             .flatten()
             .filter_map(|clean::ItemLink { link, did, .. }| {
-                did.map(|did| (link.clone(), from_def_id(did)))
+                did.map(|did| (link.clone(), from_def_id(did.into())))
             })
             .collect();
         let docs = item.attrs.collapsed_doc_value();
diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs
index d56acad60c00f..f8bd971081395 100644
--- a/src/librustdoc/json/mod.rs
+++ b/src/librustdoc/json/mod.rs
@@ -12,13 +12,14 @@ use std::path::PathBuf;
 use std::rc::Rc;
 
 use rustc_data_structures::fx::FxHashMap;
+use rustc_hir::def_id::DefId;
 use rustc_middle::ty::TyCtxt;
 use rustc_session::Session;
 
 use rustdoc_json_types as types;
 
 use crate::clean;
-use crate::clean::{ExternalCrate, FakeDefId};
+use crate::clean::ExternalCrate;
 use crate::config::RenderOptions;
 use crate::error::Error;
 use crate::formats::cache::Cache;
@@ -42,7 +43,7 @@ impl JsonRenderer<'tcx> {
         self.tcx.sess
     }
 
-    fn get_trait_implementors(&mut self, id: FakeDefId) -> Vec<types::Id> {
+    fn get_trait_implementors(&mut self, id: DefId) -> Vec<types::Id> {
         Rc::clone(&self.cache)
             .implementors
             .get(&id)
@@ -59,10 +60,10 @@ impl JsonRenderer<'tcx> {
             .unwrap_or_default()
     }
 
-    fn get_impls(&mut self, id: FakeDefId) -> Vec<types::Id> {
+    fn get_impls(&mut self, id: DefId) -> Vec<types::Id> {
         Rc::clone(&self.cache)
             .impls
-            .get(&id.expect_real())
+            .get(&id)
             .map(|impls| {
                 impls
                     .iter()
@@ -163,11 +164,11 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
         let id = item.def_id;
         if let Some(mut new_item) = self.convert_item(item) {
             if let types::ItemEnum::Trait(ref mut t) = new_item.inner {
-                t.implementors = self.get_trait_implementors(id)
+                t.implementors = self.get_trait_implementors(id.expect_real())
             } else if let types::ItemEnum::Struct(ref mut s) = new_item.inner {
-                s.impls = self.get_impls(id)
+                s.impls = self.get_impls(id.expect_real())
             } else if let types::ItemEnum::Enum(ref mut e) = new_item.inner {
-                e.impls = self.get_impls(id)
+                e.impls = self.get_impls(id.expect_real())
             }
             let removed = self.index.borrow_mut().insert(from_def_id(id), new_item.clone());
 
diff --git a/src/librustdoc/lint.rs b/src/librustdoc/lint.rs
index 1b79811d4b075..376c83b1a6ea8 100644
--- a/src/librustdoc/lint.rs
+++ b/src/librustdoc/lint.rs
@@ -157,6 +157,18 @@ declare_rustdoc_lint! {
     "detects URLs that are not hyperlinks"
 }
 
+declare_rustdoc_lint! {
+   /// The `invalid_rust_codeblocks` lint detects Rust code blocks in
+   /// documentation examples that are invalid (e.g. empty, not parsable as
+   /// Rust code). This is a `rustdoc` only lint, see the documentation in the
+   /// [rustdoc book].
+   ///
+   /// [rustdoc book]: ../../../rustdoc/lints.html#invalid_rust_codeblocks
+   INVALID_RUST_CODEBLOCKS,
+   Warn,
+   "codeblock could not be parsed as valid Rust or is empty"
+}
+
 crate static RUSTDOC_LINTS: Lazy<Vec<&'static Lint>> = Lazy::new(|| {
     vec![
         BROKEN_INTRA_DOC_LINKS,
@@ -164,6 +176,7 @@ crate static RUSTDOC_LINTS: Lazy<Vec<&'static Lint>> = Lazy::new(|| {
         MISSING_DOC_CODE_EXAMPLES,
         PRIVATE_DOC_TESTS,
         INVALID_CODEBLOCK_ATTRIBUTES,
+        INVALID_RUST_CODEBLOCKS,
         INVALID_HTML_TAGS,
         BARE_URLS,
         MISSING_CRATE_LEVEL_DOCS,
diff --git a/src/librustdoc/passes/check_code_block_syntax.rs b/src/librustdoc/passes/check_code_block_syntax.rs
index 8d07cde51880c..0b94a74eeef97 100644
--- a/src/librustdoc/passes/check_code_block_syntax.rs
+++ b/src/librustdoc/passes/check_code_block_syntax.rs
@@ -1,5 +1,6 @@
 use rustc_data_structures::sync::{Lock, Lrc};
 use rustc_errors::{emitter::Emitter, Applicability, Diagnostic, Handler};
+use rustc_middle::lint::LintDiagnosticBuilder;
 use rustc_parse::parse_stream_from_source_str;
 use rustc_session::parse::ParseSess;
 use rustc_span::source_map::{FilePathMapping, SourceMap};
@@ -47,55 +48,68 @@ impl<'a, 'tcx> SyntaxChecker<'a, 'tcx> {
         .unwrap_or(false);
         let buffer = buffer.borrow();
 
-        if buffer.has_errors || is_empty {
-            let mut diag = if let Some(sp) = super::source_span_for_markdown_range(
-                self.cx.tcx,
-                &dox,
-                &code_block.range,
-                &item.attrs,
-            ) {
-                let (warning_message, suggest_using_text) = if buffer.has_errors {
-                    ("could not parse code block as Rust code", true)
-                } else {
-                    ("Rust code block is empty", false)
-                };
-
-                let mut diag = self.cx.sess().struct_span_warn(sp, warning_message);
-
-                if code_block.syntax.is_none() && code_block.is_fenced {
-                    let sp = sp.from_inner(InnerSpan::new(0, 3));
-                    diag.span_suggestion(
-                        sp,
-                        "mark blocks that do not contain Rust code as text",
-                        String::from("```text"),
-                        Applicability::MachineApplicable,
+        if !buffer.has_errors && !is_empty {
+            // No errors in a non-empty program.
+            return;
+        }
+
+        let local_id = match item.def_id.as_local() {
+            Some(id) => id,
+            // We don't need to check the syntax for other crates so returning
+            // without doing anything should not be a problem.
+            None => return,
+        };
+
+        let hir_id = self.cx.tcx.hir().local_def_id_to_hir_id(local_id);
+        let empty_block = code_block.syntax.is_none() && code_block.is_fenced;
+        let is_ignore = code_block.is_ignore;
+
+        // The span and whether it is precise or not.
+        let (sp, precise_span) = match super::source_span_for_markdown_range(
+            self.cx.tcx,
+            &dox,
+            &code_block.range,
+            &item.attrs,
+        ) {
+            Some(sp) => (sp, true),
+            None => (item.attr_span(self.cx.tcx), false),
+        };
+
+        // lambda that will use the lint to start a new diagnostic and add
+        // a suggestion to it when needed.
+        let diag_builder = |lint: LintDiagnosticBuilder<'_>| {
+            let explanation = if is_ignore {
+                "`ignore` code blocks require valid Rust code for syntax highlighting; \
+                    mark blocks that do not contain Rust code as text"
+            } else {
+                "mark blocks that do not contain Rust code as text"
+            };
+            let msg = if buffer.has_errors {
+                "could not parse code block as Rust code"
+            } else {
+                "Rust code block is empty"
+            };
+            let mut diag = lint.build(msg);
+
+            if precise_span {
+                if is_ignore {
+                    // giving an accurate suggestion is hard because `ignore` might not have come first in the list.
+                    // just give a `help` instead.
+                    diag.span_help(
+                        sp.from_inner(InnerSpan::new(0, 3)),
+                        &format!("{}: ```text", explanation),
                     );
-                } else if suggest_using_text && code_block.is_ignore {
-                    let sp = sp.from_inner(InnerSpan::new(0, 3));
+                } else if empty_block {
                     diag.span_suggestion(
-                        sp,
-                        "`ignore` code blocks require valid Rust code for syntax highlighting. \
-                         Mark blocks that do not contain Rust code as text",
-                        String::from("```text,"),
+                        sp.from_inner(InnerSpan::new(0, 3)),
+                        explanation,
+                        String::from("```text"),
                         Applicability::MachineApplicable,
                     );
                 }
-
-                diag
-            } else {
-                // We couldn't calculate the span of the markdown block that had the error, so our
-                // diagnostics are going to be a bit lacking.
-                let mut diag = self.cx.sess().struct_span_warn(
-                    item.attr_span(self.cx.tcx),
-                    "doc comment contains an invalid Rust code block",
-                );
-
-                if code_block.syntax.is_none() && code_block.is_fenced {
-                    diag.help("mark blocks that do not contain Rust code as text: ```text");
-                }
-
-                diag
-            };
+            } else if empty_block || is_ignore {
+                diag.help(&format!("{}: ```text", explanation));
+            }
 
             // FIXME(#67563): Provide more context for these errors by displaying the spans inline.
             for message in buffer.messages.iter() {
@@ -103,7 +117,17 @@ impl<'a, 'tcx> SyntaxChecker<'a, 'tcx> {
             }
 
             diag.emit();
-        }
+        };
+
+        // Finally build and emit the completed diagnostic.
+        // All points of divergence have been handled earlier so this can be
+        // done the same way whether the span is precise or not.
+        self.cx.tcx.struct_span_lint_hir(
+            crate::lint::INVALID_RUST_CODEBLOCKS,
+            hir_id,
+            sp,
+            diag_builder,
+        );
     }
 }
 
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index 8838dc57d5a05..88207b67743cf 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -30,9 +30,7 @@ use std::convert::{TryFrom, TryInto};
 use std::mem;
 use std::ops::Range;
 
-use crate::clean::{
-    self, utils::find_nearest_parent_module, Crate, FakeDefId, Item, ItemLink, PrimitiveType,
-};
+use crate::clean::{self, utils::find_nearest_parent_module, Crate, Item, ItemLink, PrimitiveType};
 use crate::core::DocContext;
 use crate::fold::DocFolder;
 use crate::html::markdown::{markdown_links, MarkdownLink};
@@ -248,7 +246,7 @@ enum AnchorFailure {
 
 #[derive(Clone, Debug, Hash, PartialEq, Eq)]
 struct ResolutionInfo {
-    module_id: FakeDefId,
+    module_id: DefId,
     dis: Option<Disambiguator>,
     path_str: String,
     extra_fragment: Option<String>,
@@ -274,7 +272,7 @@ struct LinkCollector<'a, 'tcx> {
     ///
     /// The last module will be used if the parent scope of the current item is
     /// unknown.
-    mod_ids: Vec<FakeDefId>,
+    mod_ids: Vec<DefId>,
     /// This is used to store the kind of associated items,
     /// because `clean` and the disambiguator code expect them to be different.
     /// See the code for associated items on inherent impls for details.
@@ -861,7 +859,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
         let inner_docs = item.inner_docs(self.cx.tcx);
 
         if item.is_mod() && inner_docs {
-            self.mod_ids.push(item.def_id);
+            self.mod_ids.push(item.def_id.expect_real());
         }
 
         // We want to resolve in the lexical scope of the documentation.
@@ -888,7 +886,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
 
         Some(if item.is_mod() {
             if !inner_docs {
-                self.mod_ids.push(item.def_id);
+                self.mod_ids.push(item.def_id.expect_real());
             }
 
             let ret = self.fold_item_recur(item);
@@ -1070,11 +1068,8 @@ impl LinkCollector<'_, '_> {
         // we've already pushed this node onto the resolution stack but
         // for outer comments we explicitly try and resolve against the
         // parent_node first.
-        let base_node = if item.is_mod() && inner_docs {
-            self.mod_ids.last().copied()
-        } else {
-            parent_node.map(|id| FakeDefId::new_real(id))
-        };
+        let base_node =
+            if item.is_mod() && inner_docs { self.mod_ids.last().copied() } else { parent_node };
 
         let mut module_id = if let Some(id) = base_node {
             id
@@ -1119,7 +1114,7 @@ impl LinkCollector<'_, '_> {
                 resolved_self = format!("self::{}", &path_str["crate::".len()..]);
                 path_str = &resolved_self;
             }
-            module_id = FakeDefId::new_real(DefId { krate, index: CRATE_DEF_INDEX });
+            module_id = DefId { krate, index: CRATE_DEF_INDEX };
         }
 
         let (mut res, mut fragment) = self.resolve_with_disambiguator_cached(
@@ -1180,8 +1175,8 @@ impl LinkCollector<'_, '_> {
             report_diagnostic(self.cx.tcx, BROKEN_INTRA_DOC_LINKS, &msg, &diag_info, callback);
         };
 
-        let verify = |kind: DefKind, id: FakeDefId| {
-            let (kind, id) = self.kind_side_channel.take().unwrap_or((kind, id.expect_real()));
+        let verify = |kind: DefKind, id: DefId| {
+            let (kind, id) = self.kind_side_channel.take().unwrap_or((kind, id));
             debug!("intra-doc link to {} resolved to {:?} (id: {:?})", path_str, res, id);
 
             // Disallow e.g. linking to enums with `struct@`
@@ -1345,7 +1340,7 @@ impl LinkCollector<'_, '_> {
 
         match disambiguator.map(Disambiguator::ns) {
             Some(expected_ns @ (ValueNS | TypeNS)) => {
-                match self.resolve(path_str, expected_ns, base_node.expect_real(), extra_fragment) {
+                match self.resolve(path_str, expected_ns, base_node, extra_fragment) {
                     Ok(res) => Some(res),
                     Err(ErrorKind::Resolve(box mut kind)) => {
                         // We only looked in one namespace. Try to give a better error if possible.
@@ -1354,12 +1349,9 @@ impl LinkCollector<'_, '_> {
                             // FIXME: really it should be `resolution_failure` that does this, not `resolve_with_disambiguator`
                             // See https://github.com/rust-lang/rust/pull/76955#discussion_r493953382 for a good approach
                             for &new_ns in &[other_ns, MacroNS] {
-                                if let Some(res) = self.check_full_res(
-                                    new_ns,
-                                    path_str,
-                                    base_node.expect_real(),
-                                    extra_fragment,
-                                ) {
+                                if let Some(res) =
+                                    self.check_full_res(new_ns, path_str, base_node, extra_fragment)
+                                {
                                     kind = ResolutionFailure::WrongNamespace { res, expected_ns };
                                     break;
                                 }
@@ -1381,14 +1373,9 @@ impl LinkCollector<'_, '_> {
                 // Try everything!
                 let mut candidates = PerNS {
                     macro_ns: self
-                        .resolve_macro(path_str, base_node.expect_real())
+                        .resolve_macro(path_str, base_node)
                         .map(|res| (res, extra_fragment.clone())),
-                    type_ns: match self.resolve(
-                        path_str,
-                        TypeNS,
-                        base_node.expect_real(),
-                        extra_fragment,
-                    ) {
+                    type_ns: match self.resolve(path_str, TypeNS, base_node, extra_fragment) {
                         Ok(res) => {
                             debug!("got res in TypeNS: {:?}", res);
                             Ok(res)
@@ -1399,12 +1386,7 @@ impl LinkCollector<'_, '_> {
                         }
                         Err(ErrorKind::Resolve(box kind)) => Err(kind),
                     },
-                    value_ns: match self.resolve(
-                        path_str,
-                        ValueNS,
-                        base_node.expect_real(),
-                        extra_fragment,
-                    ) {
+                    value_ns: match self.resolve(path_str, ValueNS, base_node, extra_fragment) {
                         Ok(res) => Ok(res),
                         Err(ErrorKind::AnchorFailure(msg)) => {
                             anchor_failure(self.cx, diag, msg);
@@ -1460,17 +1442,14 @@ impl LinkCollector<'_, '_> {
                 }
             }
             Some(MacroNS) => {
-                match self.resolve_macro(path_str, base_node.expect_real()) {
+                match self.resolve_macro(path_str, base_node) {
                     Ok(res) => Some((res, extra_fragment.clone())),
                     Err(mut kind) => {
                         // `resolve_macro` only looks in the macro namespace. Try to give a better error if possible.
                         for &ns in &[TypeNS, ValueNS] {
-                            if let Some(res) = self.check_full_res(
-                                ns,
-                                path_str,
-                                base_node.expect_real(),
-                                extra_fragment,
-                            ) {
+                            if let Some(res) =
+                                self.check_full_res(ns, path_str, base_node, extra_fragment)
+                            {
                                 kind =
                                     ResolutionFailure::WrongNamespace { res, expected_ns: MacroNS };
                                 break;
diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs
index 90b797da24915..9b7e10b2688bb 100644
--- a/src/librustdoc/passes/collect_trait_impls.rs
+++ b/src/librustdoc/passes/collect_trait_impls.rs
@@ -126,7 +126,7 @@ crate fn collect_trait_impls(krate: Crate, cx: &mut DocContext<'_>) -> Crate {
         // Since only the `DefId` portion of the `Type` instances is known to be same for both the
         // `Deref` target type and the impl for type positions, this map of types is keyed by
         // `DefId` and for convenience uses a special cleaner that accepts `DefId`s directly.
-        if cleaner.keep_impl_with_def_id(&FakeDefId::new_real(*type_did)) {
+        if cleaner.keep_impl_with_def_id(FakeDefId::Real(*type_did)) {
             add_deref_target(&type_did_to_deref_target, &mut cleaner, type_did);
         }
     }
@@ -206,13 +206,13 @@ impl BadImplStripper {
         } else if let Some(prim) = ty.primitive_type() {
             self.prims.contains(&prim)
         } else if let Some(did) = ty.def_id() {
-            self.keep_impl_with_def_id(&did.into())
+            self.keep_impl_with_def_id(did.into())
         } else {
             false
         }
     }
 
-    fn keep_impl_with_def_id(&self, did: &FakeDefId) -> bool {
-        self.items.contains(did)
+    fn keep_impl_with_def_id(&self, did: FakeDefId) -> bool {
+        self.items.contains(&did)
     }
 }
diff --git a/src/librustdoc/passes/strip_hidden.rs b/src/librustdoc/passes/strip_hidden.rs
index e5910d081a5d0..87573e8e037a9 100644
--- a/src/librustdoc/passes/strip_hidden.rs
+++ b/src/librustdoc/passes/strip_hidden.rs
@@ -4,7 +4,7 @@ use std::mem;
 use crate::clean;
 use crate::clean::{FakeDefIdSet, Item, NestedAttributesExt};
 use crate::core::DocContext;
-use crate::fold::{DocFolder, StripItem};
+use crate::fold::{strip_item, DocFolder};
 use crate::passes::{ImplStripper, Pass};
 
 crate const STRIP_HIDDEN: Pass = Pass {
@@ -44,7 +44,7 @@ impl<'a> DocFolder for Stripper<'a> {
                     // strip things like impl methods but when doing so
                     // we must not add any items to the `retained` set.
                     let old = mem::replace(&mut self.update_retained, false);
-                    let ret = StripItem(self.fold_item_recur(i)).strip();
+                    let ret = strip_item(self.fold_item_recur(i));
                     self.update_retained = old;
                     return Some(ret);
                 }
diff --git a/src/librustdoc/passes/stripper.rs b/src/librustdoc/passes/stripper.rs
index 87399256292a8..528518410aa06 100644
--- a/src/librustdoc/passes/stripper.rs
+++ b/src/librustdoc/passes/stripper.rs
@@ -3,7 +3,7 @@ use rustc_middle::middle::privacy::AccessLevels;
 use std::mem;
 
 use crate::clean::{self, FakeDefIdSet, GetDefId, Item};
-use crate::fold::{DocFolder, StripItem};
+use crate::fold::{strip_item, DocFolder};
 
 crate struct Stripper<'a> {
     crate retained: &'a mut FakeDefIdSet,
@@ -51,7 +51,7 @@ impl<'a> DocFolder for Stripper<'a> {
 
             clean::StructFieldItem(..) => {
                 if !i.visibility.is_public() {
-                    return Some(StripItem(i).strip());
+                    return Some(strip_item(i));
                 }
             }
 
@@ -59,7 +59,7 @@ impl<'a> DocFolder for Stripper<'a> {
                 if i.def_id.is_local() && !i.visibility.is_public() {
                     debug!("Stripper: stripping module {:?}", i.name);
                     let old = mem::replace(&mut self.update_retained, false);
-                    let ret = StripItem(self.fold_item_recur(i)).strip();
+                    let ret = strip_item(self.fold_item_recur(i));
                     self.update_retained = old;
                     return Some(ret);
                 }
diff --git a/src/test/rustdoc-gui/toggle-docs-mobile.goml b/src/test/rustdoc-gui/toggle-docs-mobile.goml
new file mode 100644
index 0000000000000..fcdfc0344db80
--- /dev/null
+++ b/src/test/rustdoc-gui/toggle-docs-mobile.goml
@@ -0,0 +1,21 @@
+goto: file://|DOC_PATH|/test_docs/struct.Foo.html
+size: (433, 600)
+assert: (".top-doc", "open", "")
+click: (4, 280) // This is the position of the top doc comment toggle
+assert-false: (".top-doc", "open", "")
+click: (4, 280)
+assert: (".top-doc", "open", "")
+// To ensure that the toggle isn't over the text, we check that the toggle isn't clicked.
+click: (3, 280)
+assert: (".top-doc", "open", "")
+
+// Now we do the same but with a little bigger width
+size: (600, 600)
+assert: (".top-doc", "open", "")
+click: (4, 240) // New Y position since all search elements are back on one line.
+assert-false: (".top-doc", "open", "")
+click: (4, 240)
+assert: (".top-doc", "open", "")
+// To ensure that the toggle isn't over the text, we check that the toggle isn't clicked.
+click: (3, 240)
+assert: (".top-doc", "open", "")
diff --git a/src/test/rustdoc-ui/ignore-block-help.rs b/src/test/rustdoc-ui/ignore-block-help.rs
index c22dddd11dffa..86f6a2868fb56 100644
--- a/src/test/rustdoc-ui/ignore-block-help.rs
+++ b/src/test/rustdoc-ui/ignore-block-help.rs
@@ -3,5 +3,8 @@
 /// ```ignore (to-prevent-tidy-error)
 /// let heart = '❤️';
 /// ```
-//~^^^ WARN
+//~^^^ WARNING could not parse code block
+//~| NOTE on by default
+//~| NOTE character literal may only contain one codepoint
+//~| HELP `ignore` code blocks require valid Rust code
 pub struct X;
diff --git a/src/test/rustdoc-ui/ignore-block-help.stderr b/src/test/rustdoc-ui/ignore-block-help.stderr
index d45cd92d2d106..9c02ff11d19c4 100644
--- a/src/test/rustdoc-ui/ignore-block-help.stderr
+++ b/src/test/rustdoc-ui/ignore-block-help.stderr
@@ -7,11 +7,13 @@ LL | | /// let heart = '❤️';
 LL | | /// ```
    | |_______^
    |
-   = note: error from rustc: character literal may only contain one codepoint
-help: `ignore` code blocks require valid Rust code for syntax highlighting. Mark blocks that do not contain Rust code as text
+   = note: `#[warn(rustdoc::invalid_rust_codeblocks)]` on by default
+help: `ignore` code blocks require valid Rust code for syntax highlighting; mark blocks that do not contain Rust code as text: ```text
+  --> $DIR/ignore-block-help.rs:3:5
    |
-LL | /// ```text,ignore (to-prevent-tidy-error)
-   |     ^^^^^^^^
+LL | /// ```ignore (to-prevent-tidy-error)
+   |     ^^^
+   = note: error from rustc: character literal may only contain one codepoint
 
 warning: 1 warning emitted
 
diff --git a/src/test/rustdoc-ui/invalid-syntax.rs b/src/test/rustdoc-ui/invalid-syntax.rs
index c395a8ef3d41a..b503d1093fda8 100644
--- a/src/test/rustdoc-ui/invalid-syntax.rs
+++ b/src/test/rustdoc-ui/invalid-syntax.rs
@@ -71,7 +71,7 @@ pub fn blargh() {}
 /// \_
 #[doc = "```"]
 pub fn crazy_attrs() {}
-//~^^^^ WARNING doc comment contains an invalid Rust code block
+//~^^^^ WARNING could not parse code block
 
 /// ```rust
 /// ```
diff --git a/src/test/rustdoc-ui/invalid-syntax.stderr b/src/test/rustdoc-ui/invalid-syntax.stderr
index 75acdc5ab5f26..82eac9bd68b21 100644
--- a/src/test/rustdoc-ui/invalid-syntax.stderr
+++ b/src/test/rustdoc-ui/invalid-syntax.stderr
@@ -7,6 +7,7 @@ LL | | /// \__________pkt->size___________/          \_result->size_/ \__pkt->si
 LL | | /// ```
    | |_______^
    |
+   = note: `#[warn(rustdoc::invalid_rust_codeblocks)]` on by default
    = note: error from rustc: unknown start of token: \
    = note: error from rustc: unknown start of token: \
    = note: error from rustc: unknown start of token: \
@@ -90,7 +91,7 @@ LL | | /// ```
    |
    = note: error from rustc: unknown start of token: \
 
-warning: doc comment contains an invalid Rust code block
+warning: could not parse code block as Rust code
   --> $DIR/invalid-syntax.rs:70:1
    |
 LL | / #[doc = "```"]
diff --git a/src/tools/rustdoc-js/tester.js b/src/tools/rustdoc-js/tester.js
index e583bd225a9eb..86b16f8b0e60d 100644
--- a/src/tools/rustdoc-js/tester.js
+++ b/src/tools/rustdoc-js/tester.js
@@ -267,9 +267,10 @@ function loadSearchJsAndIndex(searchJs, searchIndex, storageJs, crate) {
                            "handleAliases", "getQuery", "buildIndex", "execQuery", "execSearch",
                            "removeEmptyStringsFromArray"];
 
+    const functions = ["hasOwnPropertyRustdoc", "onEach"];
     ALIASES = {};
     finalJS += 'window = { "currentCrate": "' + crate + '", rootPath: "../" };\n';
-    finalJS += loadThings(["hasOwnProperty", "onEach"], 'function', extractFunction, storageJs);
+    finalJS += loadThings(functions, 'function', extractFunction, storageJs);
     finalJS += loadThings(arraysToLoad, 'array', extractArrayVariable, searchJs);
     finalJS += loadThings(variablesToLoad, 'variable', extractVariable, searchJs);
     finalJS += loadThings(functionsToLoad, 'function', extractFunction, searchJs);