diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index 74e35afc87d..63a11877333 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -574,6 +574,11 @@ pub trait LintContext: Sized {
     fn sess(&self) -> &Session;
     fn lints(&self) -> &LintStore;
 
+    /// Emit a lint at the appropriate level, with an optional associated span and an existing diagnostic.
+    ///
+    /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation.
+    ///
+    /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature
     fn lookup_with_diagnostics(
         &self,
         lint: &'static Lint,
@@ -872,6 +877,11 @@ pub trait LintContext: Sized {
 
     // FIXME: These methods should not take an Into<MultiSpan> -- instead, callers should need to
     // set the span in their `decorate` function (preferably using set_span).
+    /// Emit a lint at the appropriate level, with an optional associated span.
+    ///
+    /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation.
+    ///
+    /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature
     fn lookup<S: Into<MultiSpan>>(
         &self,
         lint: &'static Lint,
@@ -893,6 +903,11 @@ pub trait LintContext: Sized {
         self.lookup(lint, Some(span), decorator.msg(), |diag| decorator.decorate_lint(diag));
     }
 
+    /// Emit a lint at the appropriate level, with an associated span.
+    ///
+    /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation.
+    ///
+    /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature
     fn struct_span_lint<S: Into<MultiSpan>>(
         &self,
         lint: &'static Lint,
@@ -914,6 +929,10 @@ pub trait LintContext: Sized {
     }
 
     /// Emit a lint at the appropriate level, with no associated span.
+    ///
+    /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation.
+    ///
+    /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature
     fn lint(
         &self,
         lint: &'static Lint,
diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs
index be1d7d98aa6..d3879ff487d 100644
--- a/compiler/rustc_lint/src/levels.rs
+++ b/compiler/rustc_lint/src/levels.rs
@@ -1069,6 +1069,10 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
 
     /// Used to emit a lint-related diagnostic based on the current state of
     /// this lint context.
+    ///
+    /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation.
+    ///
+    /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature
     pub(crate) fn struct_lint(
         &self,
         lint: &'static Lint,
diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs
index d95c5cbd654..79522bd0b2b 100644
--- a/compiler/rustc_middle/src/lint.rs
+++ b/compiler/rustc_middle/src/lint.rs
@@ -274,6 +274,39 @@ pub fn explain_lint_level_source(
     }
 }
 
+/// The innermost function for emitting lints.
+///
+/// If you are loocking to implement a lint, look for higher level functions,
+/// for example:
+/// - [`TyCtxt::emit_spanned_lint`]
+/// - [`TyCtxt::struct_span_lint_hir`]
+/// - [`TyCtxt::emit_lint`]
+/// - [`TyCtxt::struct_lint_node`]
+/// - `LintContext::lookup`
+///
+/// ## `decorate` signature
+///
+/// The return value of `decorate` is ignored by this function. So what is the
+/// point of returning `&'b mut DiagnosticBuilder<'a, ()>`?
+///
+/// There are 2 reasons for this signature.
+///
+/// First of all, it prevents accidental use of `.emit()` -- it's clear that the
+/// builder will be later used and shouldn't be emitted right away (this is
+/// especially important because the old API expected you to call `.emit()` in
+/// the closure).
+///
+/// Second of all, it makes the most common case of adding just a single label
+/// /suggestion much nicer, since [`DiagnosticBuilder`] methods return
+/// `&mut DiagnosticBuilder`, you can just chain methods, without needed
+/// awkward `{ ...; }`:
+/// ```ignore pseudo-code
+/// struct_lint_level(
+///     ...,
+///     |lint| lint.span_label(sp, "lbl")
+///     //          ^^^^^^^^^^^^^^^^^^^^^ returns `&mut DiagnosticBuilder` by default
+/// )
+/// ```
 pub fn struct_lint_level(
     sess: &Session,
     lint: &'static Lint,
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index ad63f6dbb54..8636c4465d4 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -2823,6 +2823,11 @@ impl<'tcx> TyCtxt<'tcx> {
         })
     }
 
+    /// Emit a lint at the appropriate level for a hir node, with an associated span.
+    ///
+    /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation.
+    ///
+    /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature
     pub fn struct_span_lint_hir(
         self,
         lint: &'static Lint,
@@ -2848,6 +2853,11 @@ impl<'tcx> TyCtxt<'tcx> {
         self.struct_lint_node(lint, id, decorator.msg(), |diag| decorator.decorate_lint(diag))
     }
 
+    /// Emit a lint at the appropriate level for a hir node.
+    ///
+    /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation.
+    ///
+    /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature
     pub fn struct_lint_node(
         self,
         lint: &'static Lint,
diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version
index 7fd0b1e8e6f..679934348d0 100644
--- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version
+++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version
@@ -1 +1 @@
-0.12.4
\ No newline at end of file
+0.12.5
\ No newline at end of file
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index 68eb5008583..5ce62224d35 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -1433,6 +1433,7 @@ static DEFAULT_ID_MAP: Lazy<FxHashMap<Cow<'static, str>, usize>> = Lazy::new(||
 fn init_id_map() -> FxHashMap<Cow<'static, str>, usize> {
     let mut map = FxHashMap::default();
     // This is the list of IDs used in Javascript.
+    map.insert("help".into(), 1);
     map.insert("settings".into(), 1);
     map.insert("not-displayed".into(), 1);
     map.insert("alternative-display".into(), 1);
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index 902b9522429..e303dd8bdaf 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -581,6 +581,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
         let crate_name = self.tcx().crate_name(LOCAL_CRATE);
         let final_file = self.dst.join(crate_name.as_str()).join("all.html");
         let settings_file = self.dst.join("settings.html");
+        let help_file = self.dst.join("help.html");
         let scrape_examples_help_file = self.dst.join("scrape-examples-help.html");
 
         let mut root_path = self.dst.to_str().expect("invalid path").to_owned();
@@ -657,6 +658,39 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
         );
         shared.fs.write(settings_file, v)?;
 
+        // Generating help page.
+        page.title = "Rustdoc help";
+        page.description = "Documentation for Rustdoc";
+        page.root_path = "./";
+
+        let sidebar = "<h2 class=\"location\">Help</h2><div class=\"sidebar-elems\"></div>";
+        let v = layout::render(
+            &shared.layout,
+            &page,
+            sidebar,
+            |buf: &mut Buffer| {
+                write!(
+                    buf,
+                    "<div class=\"main-heading\">\
+                     <h1 class=\"fqn\">Rustdoc help</h1>\
+                     <span class=\"out-of-band\">\
+                         <a id=\"back\" href=\"javascript:void(0)\" onclick=\"history.back();\">\
+                            Back\
+                        </a>\
+                     </span>\
+                     </div>\
+                     <noscript>\
+                        <section>\
+                            <p>You need to enable Javascript to use keyboard commands or search.</p>\
+                            <p>For more information, browse the <a href=\"https://doc.rust-lang.org/rustdoc/\">rustdoc handbook</a>.</p>\
+                        </section>\
+                     </noscript>",
+                )
+            },
+            &shared.style_files,
+        );
+        shared.fs.write(help_file, v)?;
+
         if shared.layout.scrape_examples_extension {
             page.title = "About scraped examples";
             page.description = "How the scraped examples feature works in Rustdoc";
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index 7989c521774..271dd177a4b 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -199,7 +199,7 @@ h1, h2, h3, h4, h5, h6,
 .out-of-band,
 span.since,
 a.srclink,
-#help-button > button,
+#help-button > a,
 details.rustdoc-toggle.top-doc > summary,
 details.rustdoc-toggle.non-exhaustive > summary,
 .scraped-example-title,
@@ -974,32 +974,33 @@ so that we can apply CSS-filters to change the arrow color in themes */
 	color: var(--main-color);
 }
 
-#help-button .popover {
+/* use larger max-width for help popover, but not for help.html */
+#help.popover {
 	max-width: 600px;
 }
 
-#help-button .popover::before {
+#help.popover::before {
 	right: 48px;
 }
 
-#help-button dt {
+#help dt {
 	float: left;
 	clear: left;
 	display: block;
 	margin-right: 0.5rem;
 }
-#help-button span.top, #help-button span.bottom {
+#help span.top, #help span.bottom {
 	text-align: center;
 	display: block;
 	font-size: 1.125rem;
 }
-#help-button span.top {
+#help span.top {
 	margin: 10px 0;
 	border-bottom: 1px solid var(--border-color);
 	padding-bottom: 4px;
 	margin-bottom: 6px;
 }
-#help-button span.bottom {
+#help span.bottom {
 	clear: both;
 	border-top: 1px solid var(--border-color);
 }
@@ -1433,7 +1434,7 @@ h3.variant {
 	outline: none;
 }
 
-#settings-menu > a, #help-button > button, #copy-path {
+#settings-menu > a, #help-button > a, #copy-path {
 	padding: 5px;
 	width: 33px;
 	border: 1px solid var(--border-color);
@@ -1442,7 +1443,7 @@ h3.variant {
 	line-height: 1.5;
 }
 
-#settings-menu > a, #help-button > button {
+#settings-menu > a, #help-button > a {
 	padding: 5px;
 	height: 100%;
 	display: block;
@@ -1490,7 +1491,7 @@ input:checked + .slider {
 	background-color: var(--settings-input-color);
 }
 
-#help-button > button {
+#help-button > a {
 	text-align: center;
 	/* Rare exception to specifying font sizes in rem. Since this is acting
 	   as an icon, it's okay to specify their sizes in pixels. */
diff --git a/src/librustdoc/html/static/css/themes/ayu.css b/src/librustdoc/html/static/css/themes/ayu.css
index ee74f81926a..33817c16808 100644
--- a/src/librustdoc/html/static/css/themes/ayu.css
+++ b/src/librustdoc/html/static/css/themes/ayu.css
@@ -248,7 +248,7 @@ kbd {
 	box-shadow: inset 0 -1px 0 #5c6773;
 }
 
-#settings-menu > a, #help-button > button {
+#settings-menu > a, #help-button > a {
 	color: #fff;
 }
 
@@ -257,7 +257,7 @@ kbd {
 }
 
 #settings-menu > a:hover, #settings-menu > a:focus,
-#help-button > button:hover, #help-button > button:focus {
+#help-button > a:hover, #help-button > a:focus {
 	border-color: #e0e0e0;
 }
 
diff --git a/src/librustdoc/html/static/css/themes/dark.css b/src/librustdoc/html/static/css/themes/dark.css
index 06baceca01d..d88710288b9 100644
--- a/src/librustdoc/html/static/css/themes/dark.css
+++ b/src/librustdoc/html/static/css/themes/dark.css
@@ -153,12 +153,12 @@ kbd {
 	box-shadow: inset 0 -1px 0 #c6cbd1;
 }
 
-#settings-menu > a, #help-button > button {
+#settings-menu > a, #help-button > a {
 	color: #000;
 }
 
 #settings-menu > a:hover, #settings-menu > a:focus,
-#help-button > button:hover, #help-button > button:focus {
+#help-button > a:hover, #help-button > a:focus {
 	border-color: #ffb900;
 }
 
diff --git a/src/librustdoc/html/static/css/themes/light.css b/src/librustdoc/html/static/css/themes/light.css
index 058974c078c..cadc71dab95 100644
--- a/src/librustdoc/html/static/css/themes/light.css
+++ b/src/librustdoc/html/static/css/themes/light.css
@@ -147,8 +147,12 @@ kbd {
 	box-shadow: inset 0 -1px 0 #c6cbd1;
 }
 
+#settings-menu > a, #help-button > a {
+	color: #000;
+}
+
 #settings-menu > a:hover, #settings-menu > a:focus,
-#help-button > button:hover, #help-button > button:focus {
+#help-button > a:hover, #help-button > a:focus {
 	border-color: #717171;
 }
 
diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js
index dc5b8acdf53..db1cc8e4516 100644
--- a/src/librustdoc/html/static/js/main.js
+++ b/src/librustdoc/html/static/js/main.js
@@ -192,6 +192,8 @@ function loadCss(cssFileName) {
 }
 
 (function() {
+    const isHelpPage = window.location.pathname.endsWith("/help.html");
+
     function loadScript(url) {
         const script = document.createElement("script");
         script.src = url;
@@ -199,6 +201,9 @@ function loadCss(cssFileName) {
     }
 
     getSettingsButton().onclick = event => {
+        if (event.ctrlKey || event.altKey || event.metaKey) {
+            return;
+        }
         addClass(getSettingsButton(), "rotate");
         event.preventDefault();
         // Sending request for the CSS and the JS files at the same time so it will
@@ -873,7 +878,10 @@ function loadCss(cssFileName) {
         rustdoc_version.appendChild(rustdoc_version_code);
 
         const container = document.createElement("div");
-        container.className = "popover";
+        if (!isHelpPage) {
+            container.className = "popover";
+        }
+        container.id = "help";
         container.style.display = "none";
 
         const side_by_side = document.createElement("div");
@@ -885,15 +893,22 @@ function loadCss(cssFileName) {
         container.appendChild(side_by_side);
         container.appendChild(rustdoc_version);
 
-        const help_button = getHelpButton();
-        help_button.appendChild(container);
+        if (isHelpPage) {
+            const help_section = document.createElement("section");
+            help_section.appendChild(container);
+            document.getElementById("main-content").appendChild(help_section);
+            container.style.display = "block";
+        } else {
+            const help_button = getHelpButton();
+            help_button.appendChild(container);
 
-        container.onblur = helpBlurHandler;
-        container.onclick = event => {
-            event.preventDefault();
-        };
-        help_button.onblur = helpBlurHandler;
-        help_button.children[0].onblur = helpBlurHandler;
+            container.onblur = helpBlurHandler;
+            container.onclick = event => {
+                event.preventDefault();
+            };
+            help_button.onblur = helpBlurHandler;
+            help_button.children[0].onblur = helpBlurHandler;
+        }
 
         return container;
     }
@@ -934,19 +949,43 @@ function loadCss(cssFileName) {
         }
     }
 
-    document.querySelector(`#${HELP_BUTTON_ID} > button`).addEventListener("click", event => {
-        const target = event.target;
-        if (target.tagName !== "BUTTON" || target.parentElement.id !== HELP_BUTTON_ID) {
-            return;
-        }
-        const menu = getHelpMenu(true);
-        const shouldShowHelp = menu.style.display === "none";
-        if (shouldShowHelp) {
-            showHelp();
-        } else {
-            window.hidePopoverMenus();
-        }
-    });
+    if (isHelpPage) {
+        showHelp();
+        document.querySelector(`#${HELP_BUTTON_ID} > a`).addEventListener("click", event => {
+            // Already on the help page, make help button a no-op.
+            const target = event.target;
+            if (target.tagName !== "A" ||
+                target.parentElement.id !== HELP_BUTTON_ID ||
+                event.ctrlKey ||
+                event.altKey ||
+                event.metaKey) {
+                return;
+            }
+            event.preventDefault();
+        });
+    } else {
+        document.querySelector(`#${HELP_BUTTON_ID} > a`).addEventListener("click", event => {
+            // By default, have help button open docs in a popover.
+            // If user clicks with a moderator, though, use default browser behavior,
+            // probably opening in a new window or tab.
+            const target = event.target;
+            if (target.tagName !== "A" ||
+                target.parentElement.id !== HELP_BUTTON_ID ||
+                event.ctrlKey ||
+                event.altKey ||
+                event.metaKey) {
+                return;
+            }
+            event.preventDefault();
+            const menu = getHelpMenu(true);
+            const shouldShowHelp = menu.style.display === "none";
+            if (shouldShowHelp) {
+                showHelp();
+            } else {
+                window.hidePopoverMenus();
+            }
+        });
+    }
 
     setMobileTopbar();
     addSidebarItems();
diff --git a/src/librustdoc/html/templates/page.html b/src/librustdoc/html/templates/page.html
index 01a2ea6c2ec..6e6bb70707d 100644
--- a/src/librustdoc/html/templates/page.html
+++ b/src/librustdoc/html/templates/page.html
@@ -122,7 +122,7 @@
                                 placeholder="Click or press ‘S’ to search, ‘?’ for more options…" {# -#}
                                 type="search"> {#- -#}
                             <div id="help-button" title="help" tabindex="-1"> {#- -#}
-                                <button type="button">?</button> {#- -#}
+                                <a href="{{page.root_path|safe}}help.html">?</a> {#- -#}
                             </div> {#- -#}
                             <div id="settings-menu" tabindex="-1"> {#- -#}
                                 <a href="{{page.root_path|safe}}settings.html" title="settings"> {#- -#}
diff --git a/src/test/rustdoc-gui/anchors.goml b/src/test/rustdoc-gui/anchors.goml
index 6673e9c20fe..e526fa47d15 100644
--- a/src/test/rustdoc-gui/anchors.goml
+++ b/src/test/rustdoc-gui/anchors.goml
@@ -1,154 +1,107 @@
 // This test is to ensure that the anchors (`§`) have the expected color and position.
-goto: "file://" + |DOC_PATH| + "/staged_api/struct.Foo.html"
 
-// This is needed to ensure that the text color is computed.
-show-text: true
+define-function: (
+    "check-colors",
+    (theme, main_color, title_color, fqn_color, fqn_type_color, src_link_color, sidebar_link_color),
+    [
+        ("goto", "file://" + |DOC_PATH| + "/staged_api/struct.Foo.html"),
+        // This is needed to ensure that the text color is computed.
+        ("show-text", true),
 
-// Set the theme to light.
-local-storage: {"rustdoc-theme": "light", "rustdoc-use-system-theme": "false"}
-// We reload the page so the local storage settings are being used.
-reload:
+        // Setting the theme.
+        ("local-storage", {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}),
+        // We reload the page so the local storage settings are being used.
+        ("reload"),
 
-assert-css: ("#toggle-all-docs", {"color": "rgb(0, 0, 0)"})
-assert-css: (".fqn a:nth-of-type(1)", {"color": "rgb(0, 0, 0)"})
-assert-css: (".fqn a:nth-of-type(2)", {"color": "rgb(173, 55, 138)"})
-assert-css: (
-    ".rightside .srclink",
-    {"color": "rgb(56, 115, 173)", "text-decoration": "none solid rgb(56, 115, 173)"},
-    ALL,
-)
-compare-elements-css: (".rightside .srclink", ".rightside.srclink", ["color", "text-decoration"])
-compare-elements-css: (".main-heading .srclink", ".rightside.srclink", ["color", "text-decoration"])
+        ("assert-css", ("#toggle-all-docs", {"color": |main_color|})),
+        ("assert-css", (".fqn a:nth-of-type(1)", {"color": |fqn_color|})),
+        ("assert-css", (".fqn a:nth-of-type(2)", {"color": |fqn_type_color|})),
+        ("assert-css", (
+             ".rightside .srclink",
+             {"color": |src_link_color|, "text-decoration": "none solid " + |src_link_color|},
+             ALL,
+        )),
+        (
+            "compare-elements-css",
+            (".rightside .srclink", ".rightside.srclink", ["color", "text-decoration"]),
+        ),
+        (
+            "compare-elements-css",
+            (".main-heading .srclink", ".rightside.srclink", ["color", "text-decoration"]),
+        ),
 
-move-cursor-to: ".main-heading .srclink"
-assert-css: (
-    ".main-heading .srclink",
-    {"color": "rgb(56, 115, 173)", "text-decoration": "underline solid rgb(56, 115, 173)"},
-)
-move-cursor-to: ".impl-items .rightside .srclink"
-assert-css: (
-    ".impl-items .rightside .srclink",
-    {"color": "rgb(56, 115, 173)", "text-decoration": "none solid rgb(56, 115, 173)"},
-)
-move-cursor-to: ".impl-items .rightside.srclink"
-assert-css: (
-    ".impl-items .rightside.srclink",
-    {"color": "rgb(56, 115, 173)", "text-decoration": "none solid rgb(56, 115, 173)"},
+        ("move-cursor-to", ".main-heading .srclink"),
+        ("assert-css", (
+             ".main-heading .srclink",
+             {"color": |src_link_color|, "text-decoration": "underline solid " + |src_link_color|},
+        )),
+        ("move-cursor-to", ".impl-items .rightside .srclink"),
+        ("assert-css", (
+             ".impl-items .rightside .srclink",
+             {"color": |src_link_color|, "text-decoration": "none solid " + |src_link_color|},
+        )),
+        ("move-cursor-to", ".impl-items .rightside.srclink"),
+        ("assert-css", (
+             ".impl-items .rightside.srclink",
+             {"color": |src_link_color|, "text-decoration": "none solid " + |src_link_color|},
+        )),
+
+        ("goto", "file://" + |DOC_PATH| + "/test_docs/struct.HeavilyDocumentedStruct.html"),
+        // Since we changed page, we need to set the theme again.
+        ("local-storage", {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}),
+        // We reload the page so the local storage settings are being used.
+        ("reload"),
+
+        ("assert-css", ("#top-doc-prose-title", {"color": |title_color|})),
+
+        ("assert-css", (".sidebar a", {"color": |sidebar_link_color|})),
+        ("assert-css", ("h1.fqn a", {"color": |title_color|})),
+
+        // We move the cursor over the "Implementations" title so the anchor is displayed.
+        ("move-cursor-to", "h2#implementations"),
+        ("assert-css", ("h2#implementations a.anchor", {"color": |main_color|})),
+
+        // Same thing with the impl block title.
+        ("move-cursor-to", "#impl-HeavilyDocumentedStruct"),
+        ("assert-css", ("#impl-HeavilyDocumentedStruct a.anchor", {"color": |main_color|})),
+
+        ("assert-css", ("#title-for-struct-impl-item-doc", {"margin-left": "0px"})),
+    ],
 )
 
-goto: "file://" + |DOC_PATH| + "/test_docs/struct.HeavilyDocumentedStruct.html"
-
-assert-css: ("#top-doc-prose-title", {"color": "rgb(0, 0, 0)"})
-
-assert-css: (".sidebar a", {"color": "rgb(53, 109, 164)"})
-assert-css: ("h1.fqn a", {"color": "rgb(0, 0, 0)"})
-
-// We move the cursor over the "Implementations" title so the anchor is displayed.
-move-cursor-to: "h2#implementations"
-assert-css: ("h2#implementations a.anchor", {"color": "rgb(0, 0, 0)"})
-
-// Same thing with the impl block title.
-move-cursor-to: "#impl-HeavilyDocumentedStruct"
-assert-css: ("#impl-HeavilyDocumentedStruct a.anchor", {"color": "rgb(0, 0, 0)"})
-
-assert-css: ("#title-for-struct-impl-item-doc", {"margin-left": "0px"})
-
-//
-// We do the same checks with the dark theme now.
-//
-local-storage: {"rustdoc-theme": "dark", "rustdoc-use-system-theme": "false"}
-goto: "file://" + |DOC_PATH| + "/staged_api/struct.Foo.html"
-
-assert-css: ("#toggle-all-docs", {"color": "rgb(221, 221, 221)"})
-assert-css: (".fqn a:nth-of-type(1)", {"color": "rgb(221, 221, 221)"})
-assert-css: (".fqn a:nth-of-type(2)", {"color": "rgb(45, 191, 184)"})
-assert-css: (
-    ".rightside .srclink",
-    {"color": "rgb(210, 153, 29)", "text-decoration": "none solid rgb(210, 153, 29)"},
-    ALL,
+call-function: (
+    "check-colors",
+    (
+        "ayu", // theme
+        "rgb(197, 197, 197)", // main color
+        "rgb(255, 255, 255)", // title color
+        "rgb(255, 255, 255)", // fqn color
+        "rgb(255, 160, 165)", // fqn type color
+        "rgb(57, 175, 215)", // src link
+        "rgb(83, 177, 219)", // sidebar link
+    ),
 )
-compare-elements-css: (".rightside .srclink", ".rightside.srclink", ["color", "text-decoration"])
-compare-elements-css: (".main-heading .srclink", ".rightside.srclink", ["color", "text-decoration"])
-
-move-cursor-to: ".main-heading .srclink"
-assert-css: (
-    ".main-heading .srclink",
-    {"color": "rgb(210, 153, 29)", "text-decoration": "underline solid rgb(210, 153, 29)"},
+call-function: (
+    "check-colors",
+    (
+        "dark", // theme
+        "rgb(221, 221, 221)", // main color
+        "rgb(221, 221, 221)", // title color
+        "rgb(221, 221, 221)", // fqn color
+        "rgb(45, 191, 184)", // fqn type color
+        "rgb(210, 153, 29)", // src link
+        "rgb(253, 191, 53)", // sidebar link
+    ),
 )
-move-cursor-to: ".impl-items .rightside .srclink"
-assert-css: (
-    ".impl-items .rightside .srclink",
-    {"color": "rgb(210, 153, 29)", "text-decoration": "none solid rgb(210, 153, 29)"},
+call-function: (
+    "check-colors",
+    (
+        "light", // theme
+        "rgb(0, 0, 0)", // main color
+        "rgb(0, 0, 0)", // title color
+        "rgb(0, 0, 0)", // fqn color
+        "rgb(173, 55, 138)", // fqn type color
+        "rgb(56, 115, 173)", // src link
+        "rgb(53, 109, 164)", // sidebar link
+    ),
 )
-move-cursor-to: ".impl-items .rightside.srclink"
-assert-css: (
-    ".impl-items .rightside.srclink",
-    {"color": "rgb(210, 153, 29)", "text-decoration": "none solid rgb(210, 153, 29)"},
-)
-
-goto: "file://" + |DOC_PATH| + "/test_docs/struct.HeavilyDocumentedStruct.html"
-
-assert-css: ("#top-doc-prose-title", {"color": "rgb(221, 221, 221)"})
-
-assert-css: (".sidebar a", {"color": "rgb(253, 191, 53)"})
-assert-css: ("h1.fqn a", {"color": "rgb(221, 221, 221)"})
-
-// We move the cursor over the "Implementations" title so the anchor is displayed.
-move-cursor-to: "h2#implementations"
-assert-css: ("h2#implementations a.anchor", {"color": "rgb(221, 221, 221)"})
-
-// Same thing with the impl block title.
-move-cursor-to: "#impl-HeavilyDocumentedStruct"
-assert-css: ("#impl-HeavilyDocumentedStruct a.anchor", {"color": "rgb(221, 221, 221)"})
-
-assert-css: ("#title-for-struct-impl-item-doc", {"margin-left": "0px"})
-
-//
-// We do the same checks with the ayu theme now.
-//
-local-storage: {"rustdoc-theme": "ayu", "rustdoc-use-system-theme": "false"}
-goto: "file://" + |DOC_PATH| + "/staged_api/struct.Foo.html"
-
-assert-css: ("#toggle-all-docs", {"color": "rgb(197, 197, 197)"})
-assert-css: (".fqn a:nth-of-type(1)", {"color": "rgb(255, 255, 255)"})
-assert-css: (".fqn a:nth-of-type(2)", {"color": "rgb(255, 160, 165)"})
-assert-css: (
-    ".rightside .srclink",
-    {"color": "rgb(57, 175, 215)", "text-decoration": "none solid rgb(57, 175, 215)"},
-    ALL,
-)
-compare-elements-css: (".rightside .srclink", ".rightside.srclink", ["color", "text-decoration"])
-compare-elements-css: (".main-heading .srclink", ".rightside.srclink", ["color", "text-decoration"])
-
-move-cursor-to: ".main-heading .srclink"
-assert-css: (
-    ".main-heading .srclink",
-    {"color": "rgb(57, 175, 215)", "text-decoration": "underline solid rgb(57, 175, 215)"},
-)
-move-cursor-to: ".impl-items .rightside .srclink"
-assert-css: (
-    ".impl-items .rightside .srclink",
-    {"color": "rgb(57, 175, 215)", "text-decoration": "none solid rgb(57, 175, 215)"},
-)
-move-cursor-to: ".impl-items .rightside.srclink"
-assert-css: (
-    ".impl-items .rightside.srclink",
-    {"color": "rgb(57, 175, 215)", "text-decoration": "none solid rgb(57, 175, 215)"},
-)
-
-goto: "file://" + |DOC_PATH| + "/test_docs/struct.HeavilyDocumentedStruct.html"
-
-assert-css: ("#top-doc-prose-title", {"color": "rgb(255, 255, 255)"})
-
-assert-css: (".sidebar a", {"color": "rgb(83, 177, 219)"})
-assert-css: ("h1.fqn a", {"color": "rgb(255, 255, 255)"})
-
-// We move the cursor over the "Implementations" title so the anchor is displayed.
-move-cursor-to: "h2#implementations"
-assert-css: ("h2#implementations a.anchor", {"color": "rgb(197, 197, 197)"})
-
-// Same thing with the impl block title.
-move-cursor-to: "#impl-HeavilyDocumentedStruct"
-assert-css: ("#impl-HeavilyDocumentedStruct a.anchor", {"color": "rgb(197, 197, 197)"})
-
-assert-css: ("#title-for-struct-impl-item-doc", {"margin-left": "0px"})
diff --git a/src/test/rustdoc-gui/help-page.goml b/src/test/rustdoc-gui/help-page.goml
new file mode 100644
index 00000000000..51f089cce74
--- /dev/null
+++ b/src/test/rustdoc-gui/help-page.goml
@@ -0,0 +1,24 @@
+// This test ensures that opening the help page in its own tab works.
+goto: "file://" + |DOC_PATH| + "/help.html"
+size: (1000, 1000) // Try desktop size first.
+wait-for: "#help"
+assert-css: ("#help", {"display": "block"})
+click: "#help-button > a"
+assert-css: ("#help", {"display": "block"})
+compare-elements-property: (".sub-container", "#help", ["offsetWidth"])
+compare-elements-position: (".sub-container", "#help", ("x"))
+size: (500, 1000) // Try mobile next.
+assert-css: ("#help", {"display": "block"})
+compare-elements-property: (".sub-container", "#help", ["offsetWidth"])
+compare-elements-position: (".sub-container", "#help", ("x"))
+
+// This test ensures that opening the help popover without switching pages works.
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+size: (1000, 1000) // Only supported on desktop.
+assert-false: "#help"
+click: "#help-button > a"
+assert-css: ("#help", {"display": "block"})
+click: "#help-button > a"
+assert-css: ("#help", {"display": "none"})
+compare-elements-property-false: (".sub-container", "#help", ["offsetWidth"])
+compare-elements-position-false: (".sub-container", "#help", ("x"))
diff --git a/src/test/rustdoc-gui/search-form-elements.goml b/src/test/rustdoc-gui/search-form-elements.goml
index fba9cc8777f..542db348c3b 100644
--- a/src/test/rustdoc-gui/search-form-elements.goml
+++ b/src/test/rustdoc-gui/search-form-elements.goml
@@ -33,7 +33,7 @@ assert-css: (
     {"border-color": "rgb(197, 197, 197)"},
 )
 assert-css: (
-    "#help-button > button",
+    "#help-button > a",
     {
         "color": "rgb(255, 255, 255)",
         "border-color": "rgb(92, 103, 115)",
@@ -47,13 +47,21 @@ assert-css: (
 )
 // Only "border-color" should change.
 assert-css: (
-    "#help-button:hover > button",
+    "#help-button:hover > a",
     {
         "color": "rgb(255, 255, 255)",
         "border-color": "rgb(224, 224, 224)",
         "background-color": "rgb(20, 25, 32)",
     },
 )
+// Link color inside
+click: "#help-button"
+assert-css: (
+    "#help a",
+    {
+        "color": "rgb(57, 175, 215)",
+    },
+)
 
 assert-css: (
     "#settings-menu",
@@ -62,7 +70,6 @@ assert-css: (
 assert-css: (
     "#settings-menu > a",
     {
-        "color": "rgb(255, 255, 255)",
         "border-color": "rgb(92, 103, 115)",
         "background-color": "rgb(20, 25, 32)",
     },
@@ -76,7 +83,6 @@ assert-css: (
 assert-css: (
     "#settings-menu:hover > a",
     {
-        "color": "rgb(255, 255, 255)",
         "border-color": "rgb(224, 224, 224)",
         "background-color": "rgb(20, 25, 32)",
     },
@@ -113,7 +119,7 @@ assert-css: (
     {"border-color": "rgb(221, 221, 221)"},
 )
 assert-css: (
-    "#help-button > button",
+    "#help-button > a",
     {
         "color": "rgb(0, 0, 0)",
         "border-color": "rgb(224, 224, 224)",
@@ -127,13 +133,21 @@ assert-css: (
 )
 // Only "border-color" should change.
 assert-css: (
-    "#help-button:hover > button",
+    "#help-button:hover > a",
     {
         "color": "rgb(0, 0, 0)",
         "border-color": "rgb(255, 185, 0)",
         "background-color": "rgb(240, 240, 240)",
     },
 )
+// Link color inside
+click: "#help-button"
+assert-css: (
+    "#help a",
+    {
+        "color": "rgb(210, 153, 29)",
+    },
+)
 
 assert-css: (
     "#settings-menu",
@@ -142,7 +156,6 @@ assert-css: (
 assert-css: (
     "#settings-menu > a",
     {
-        "color": "rgb(0, 0, 0)",
         "border-color": "rgb(224, 224, 224)",
         "background-color": "rgb(240, 240, 240)",
     },
@@ -193,7 +206,7 @@ assert-css: (
     {"border-color": "rgb(0, 0, 0)"},
 )
 assert-css: (
-    "#help-button > button",
+    "#help-button > a",
     {
         "color": "rgb(0, 0, 0)",
         "border-color": "rgb(224, 224, 224)",
@@ -207,13 +220,21 @@ assert-css: (
 )
 // Only "border-color" should change.
 assert-css: (
-    "#help-button:hover > button",
+    "#help-button:hover > a",
     {
         "color": "rgb(0, 0, 0)",
         "border-color": "rgb(113, 113, 113)",
         "background-color": "rgb(255, 255, 255)",
     },
 )
+// Link color inside
+click: "#help-button"
+assert-css: (
+    "#help a",
+    {
+        "color": "rgb(56, 115, 173)",
+    },
+)
 
 assert-css: (
     "#settings-menu",
@@ -222,7 +243,6 @@ assert-css: (
 assert-css: (
     "#settings-menu > a",
     {
-        "color": "rgb(56, 115, 173)",
         "border-color": "rgb(224, 224, 224)",
         "background-color": "rgb(255, 255, 255)",
     },
@@ -236,7 +256,7 @@ assert-css: (
 assert-css: (
     "#settings-menu:hover > a",
     {
-        "color": "rgb(56, 115, 173)",
+        "color": "rgb(0, 0, 0)",
         "border-color": "rgb(113, 113, 113)",
         "background-color": "rgb(255, 255, 255)",
     },
diff --git a/src/test/ui/deriving/deriving-hash.rs b/src/test/ui/deriving/deriving-hash.rs
index 8b51370bca5..16738ec4ae4 100644
--- a/src/test/ui/deriving/deriving-hash.rs
+++ b/src/test/ui/deriving/deriving-hash.rs
@@ -44,6 +44,17 @@ fn fake_hash<A: Hash>(v: &mut Vec<u8>, a: A) {
     a.hash(&mut FakeHasher(v));
 }
 
+struct OnlyOneByteHasher;
+impl Hasher for OnlyOneByteHasher {
+    fn finish(&self) -> u64 {
+        unreachable!()
+    }
+
+    fn write(&mut self, bytes: &[u8]) {
+        assert_eq!(bytes.len(), 1);
+    }
+}
+
 fn main() {
     let person1 = Person {
         id: 5,
@@ -73,4 +84,13 @@ fn main() {
     let mut v = vec![];
     fake_hash(&mut v, SingleVariantEnum::A(17));
     assert_eq!(vec![17], v);
+
+    // issue #39137
+    #[repr(u8)]
+    #[derive(Hash)]
+    enum E {
+        A,
+        B,
+    }
+    E::A.hash(&mut OnlyOneByteHasher);
 }