about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--book/src/lint_configuration.md2
-rw-r--r--clippy_config/src/conf.rs5
-rw-r--r--clippy_lints/src/doc/mod.rs8
-rw-r--r--clippy_lints/src/methods/is_empty.rs13
-rw-r--r--clippy_lints/src/unused_io_amount.rs24
-rw-r--r--tests/ui/const_is_empty.rs14
-rw-r--r--tests/ui/const_is_empty.stderr8
-rw-r--r--tests/ui/doc/doc-fixable.fixed1
-rw-r--r--tests/ui/doc/doc-fixable.rs1
-rw-r--r--tests/ui/doc/doc-fixable.stderr44
-rw-r--r--tests/ui/unused_io_amount.rs14
-rw-r--r--util/gh-pages/index_template.html2
-rw-r--r--util/gh-pages/script.js11
13 files changed, 111 insertions, 36 deletions
diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md
index 43b551ae216..9ea4c8418fd 100644
--- a/book/src/lint_configuration.md
+++ b/book/src/lint_configuration.md
@@ -456,7 +456,7 @@ default configuration of Clippy. By default, any configuration will replace the
 * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`.
 * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list.
 
-**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "AccessKit", "CoreFoundation", "CoreGraphics", "CoreText", "DevOps", "Direct2D", "Direct3D", "DirectWrite", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenAL", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "OpenType", "WebGL", "WebGL2", "WebGPU", "WebRTC", "WebSocket", "WebTransport", "WebP", "OpenExr", "YCbCr", "sRGB", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "NetBSD", "OpenBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]`
+**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "MHz", "GHz", "THz", "AccessKit", "CoreFoundation", "CoreGraphics", "CoreText", "DevOps", "Direct2D", "Direct3D", "DirectWrite", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenAL", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "OpenType", "WebGL", "WebGL2", "WebGPU", "WebRTC", "WebSocket", "WebTransport", "WebP", "OpenExr", "YCbCr", "sRGB", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "NetBSD", "OpenBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]`
 
 ---
 **Affected lints:**
diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs
index 4757c0b1339..b0faac6d2a8 100644
--- a/clippy_config/src/conf.rs
+++ b/clippy_config/src/conf.rs
@@ -17,6 +17,7 @@ use std::{cmp, env, fmt, fs, io};
 #[rustfmt::skip]
 const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[
     "KiB", "MiB", "GiB", "TiB", "PiB", "EiB",
+    "MHz", "GHz", "THz",
     "AccessKit",
     "CoreFoundation", "CoreGraphics", "CoreText",
     "DevOps",
@@ -102,7 +103,9 @@ pub fn sanitize_explanation(raw_docs: &str) -> String {
     // Remove tags and hidden code:
     let mut explanation = String::with_capacity(128);
     let mut in_code = false;
-    for line in raw_docs.lines().map(str::trim) {
+    for line in raw_docs.lines() {
+        let line = line.strip_prefix(' ').unwrap_or(line);
+
         if let Some(lang) = line.strip_prefix("```") {
             let tag = lang.split_once(',').map_or(lang, |(left, _)| left);
             if !in_code && matches!(tag, "" | "rust" | "ignore" | "should_panic" | "no_run" | "compile_fail") {
diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs
index 89c6a4e08dc..df7c37a192a 100644
--- a/clippy_lints/src/doc/mod.rs
+++ b/clippy_lints/src/doc/mod.rs
@@ -427,11 +427,11 @@ declare_clippy_lint! {
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks if the first line in the documentation of items listed in module page is too long.
+    /// Checks if the first paragraph in the documentation of items listed in the module page is too long.
     ///
     /// ### Why is this bad?
-    /// Documentation will show the first paragraph of the doscstring in the summary page of a
-    /// module, so having a nice, short summary in the first paragraph is part of writing good docs.
+    /// Documentation will show the first paragraph of the docstring in the summary page of a
+    /// module. Having a nice, short summary in the first paragraph is part of writing good docs.
     ///
     /// ### Example
     /// ```no_run
@@ -453,7 +453,7 @@ declare_clippy_lint! {
     #[clippy::version = "1.82.0"]
     pub TOO_LONG_FIRST_DOC_PARAGRAPH,
     nursery,
-    "ensure that the first line of a documentation paragraph isn't too long"
+    "ensure the first documentation paragraph is short"
 }
 
 declare_clippy_lint! {
diff --git a/clippy_lints/src/methods/is_empty.rs b/clippy_lints/src/methods/is_empty.rs
index cc82f6cfd63..a0c21faaa4c 100644
--- a/clippy_lints/src/methods/is_empty.rs
+++ b/clippy_lints/src/methods/is_empty.rs
@@ -1,6 +1,7 @@
 use clippy_utils::consts::ConstEvalCtxt;
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::{find_binding_init, path_to_local};
+use clippy_utils::macros::{is_assert_macro, root_macro_call};
+use clippy_utils::{find_binding_init, get_parent_expr, is_inside_always_const_context, path_to_local};
 use rustc_hir::{Expr, HirId};
 use rustc_lint::{LateContext, LintContext};
 use rustc_middle::lint::in_external_macro;
@@ -14,6 +15,16 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, receiver: &Expr<'_
     if in_external_macro(cx.sess(), expr.span) || !receiver.span.eq_ctxt(expr.span) {
         return;
     }
+    if let Some(parent) = get_parent_expr(cx, expr) {
+        if let Some(parent) = get_parent_expr(cx, parent) {
+            if is_inside_always_const_context(cx.tcx, expr.hir_id)
+                && let Some(macro_call) = root_macro_call(parent.span)
+                && is_assert_macro(cx, macro_call.def_id)
+            {
+                return;
+            }
+        }
+    }
     let init_expr = expr_or_init(cx, receiver);
     if !receiver.span.eq_ctxt(init_expr.span) {
         return;
diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs
index cf406b817da..a8cc14d269a 100644
--- a/clippy_lints/src/unused_io_amount.rs
+++ b/clippy_lints/src/unused_io_amount.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_hir_and_then;
 use clippy_utils::macros::{is_panic, root_macro_call_first_node};
-use clippy_utils::{is_res_lang_ctor, is_trait_method, match_trait_method, paths, peel_blocks};
+use clippy_utils::{is_res_lang_ctor, is_trait_method, match_def_path, match_trait_method, paths, peel_blocks};
 use hir::{ExprKind, HirId, PatKind};
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
@@ -83,6 +83,28 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount {
     /// to consider the arms, and we want to avoid breaking the logic for situations where things
     /// get desugared to match.
     fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'tcx>) {
+        let fn_def_id = block.hir_id.owner.to_def_id();
+        if let Some(impl_id) = cx.tcx.impl_of_method(fn_def_id)
+            && let Some(trait_id) = cx.tcx.trait_id_of_impl(impl_id)
+        {
+            // We don't want to lint inside io::Read or io::Write implementations, as the author has more
+            // information about their trait implementation than our lint, see https://github.com/rust-lang/rust-clippy/issues/4836
+            if cx.tcx.is_diagnostic_item(sym::IoRead, trait_id) || cx.tcx.is_diagnostic_item(sym::IoWrite, trait_id) {
+                return;
+            }
+
+            let async_paths: [&[&str]; 4] = [
+                &paths::TOKIO_IO_ASYNCREADEXT,
+                &paths::TOKIO_IO_ASYNCWRITEEXT,
+                &paths::FUTURES_IO_ASYNCREADEXT,
+                &paths::FUTURES_IO_ASYNCWRITEEXT,
+            ];
+
+            if async_paths.into_iter().any(|path| match_def_path(cx, trait_id, path)) {
+                return;
+            }
+        }
+
         for stmt in block.stmts {
             if let hir::StmtKind::Semi(exp) = stmt.kind {
                 check_expr(cx, exp);
diff --git a/tests/ui/const_is_empty.rs b/tests/ui/const_is_empty.rs
index 04e0de91ecf..b5e27b61548 100644
--- a/tests/ui/const_is_empty.rs
+++ b/tests/ui/const_is_empty.rs
@@ -171,3 +171,17 @@ fn constant_from_external_crate() {
     let _ = std::env::consts::EXE_EXTENSION.is_empty();
     // Do not lint, `exe_ext` comes from the `std` crate
 }
+
+fn issue_13106() {
+    const {
+        assert!(!NON_EMPTY_STR.is_empty());
+    }
+
+    const {
+        assert!(EMPTY_STR.is_empty());
+    }
+
+    const {
+        EMPTY_STR.is_empty();
+    }
+}
diff --git a/tests/ui/const_is_empty.stderr b/tests/ui/const_is_empty.stderr
index 7f80b520b1a..0afba940d8b 100644
--- a/tests/ui/const_is_empty.stderr
+++ b/tests/ui/const_is_empty.stderr
@@ -157,5 +157,11 @@ error: this expression always evaluates to true
 LL |     let _ = val.is_empty();
    |             ^^^^^^^^^^^^^^
 
-error: aborting due to 26 previous errors
+error: this expression always evaluates to true
+  --> tests/ui/const_is_empty.rs:185:9
+   |
+LL |         EMPTY_STR.is_empty();
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 27 previous errors
 
diff --git a/tests/ui/doc/doc-fixable.fixed b/tests/ui/doc/doc-fixable.fixed
index 355f2bc7736..8a379330c48 100644
--- a/tests/ui/doc/doc-fixable.fixed
+++ b/tests/ui/doc/doc-fixable.fixed
@@ -54,6 +54,7 @@ fn test_units() {
 
 /// This tests allowed identifiers.
 /// KiB MiB GiB TiB PiB EiB
+/// MHz GHz THz
 /// AccessKit
 /// CoreFoundation CoreGraphics CoreText
 /// Direct2D Direct3D DirectWrite DirectX
diff --git a/tests/ui/doc/doc-fixable.rs b/tests/ui/doc/doc-fixable.rs
index 9ced2677622..35f43d98e79 100644
--- a/tests/ui/doc/doc-fixable.rs
+++ b/tests/ui/doc/doc-fixable.rs
@@ -54,6 +54,7 @@ fn test_units() {
 
 /// This tests allowed identifiers.
 /// KiB MiB GiB TiB PiB EiB
+/// MHz GHz THz
 /// AccessKit
 /// CoreFoundation CoreGraphics CoreText
 /// Direct2D Direct3D DirectWrite DirectX
diff --git a/tests/ui/doc/doc-fixable.stderr b/tests/ui/doc/doc-fixable.stderr
index 67c0464149c..27a04e4b558 100644
--- a/tests/ui/doc/doc-fixable.stderr
+++ b/tests/ui/doc/doc-fixable.stderr
@@ -133,7 +133,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it`
    |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:78:5
+  --> tests/ui/doc/doc-fixable.rs:79:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -144,7 +144,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it`
    |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:95:5
+  --> tests/ui/doc/doc-fixable.rs:96:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -155,7 +155,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it`
    |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:103:8
+  --> tests/ui/doc/doc-fixable.rs:104:8
    |
 LL | /// ## CamelCaseThing
    |        ^^^^^^^^^^^^^^
@@ -166,7 +166,7 @@ LL | /// ## `CamelCaseThing`
    |        ~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:106:7
+  --> tests/ui/doc/doc-fixable.rs:107:7
    |
 LL | /// # CamelCaseThing
    |       ^^^^^^^^^^^^^^
@@ -177,7 +177,7 @@ LL | /// # `CamelCaseThing`
    |       ~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:108:22
+  --> tests/ui/doc/doc-fixable.rs:109:22
    |
 LL | /// Not a title #897 CamelCaseThing
    |                      ^^^^^^^^^^^^^^
@@ -188,7 +188,7 @@ LL | /// Not a title #897 `CamelCaseThing`
    |                      ~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:109:5
+  --> tests/ui/doc/doc-fixable.rs:110:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -199,7 +199,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it`
    |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:116:5
+  --> tests/ui/doc/doc-fixable.rs:117:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -210,7 +210,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it`
    |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:129:5
+  --> tests/ui/doc/doc-fixable.rs:130:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -221,7 +221,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it`
    |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:140:43
+  --> tests/ui/doc/doc-fixable.rs:141:43
    |
 LL | /** E.g., serialization of an empty list: FooBar
    |                                           ^^^^^^
@@ -232,7 +232,7 @@ LL | /** E.g., serialization of an empty list: `FooBar`
    |                                           ~~~~~~~~
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:145:5
+  --> tests/ui/doc/doc-fixable.rs:146:5
    |
 LL | And BarQuz too.
    |     ^^^^^^
@@ -243,7 +243,7 @@ LL | And `BarQuz` too.
    |     ~~~~~~~~
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:146:1
+  --> tests/ui/doc/doc-fixable.rs:147:1
    |
 LL | be_sure_we_got_to_the_end_of_it
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -254,7 +254,7 @@ LL | `be_sure_we_got_to_the_end_of_it`
    |
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:151:43
+  --> tests/ui/doc/doc-fixable.rs:152:43
    |
 LL | /** E.g., serialization of an empty list: FooBar
    |                                           ^^^^^^
@@ -265,7 +265,7 @@ LL | /** E.g., serialization of an empty list: `FooBar`
    |                                           ~~~~~~~~
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:156:5
+  --> tests/ui/doc/doc-fixable.rs:157:5
    |
 LL | And BarQuz too.
    |     ^^^^^^
@@ -276,7 +276,7 @@ LL | And `BarQuz` too.
    |     ~~~~~~~~
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:157:1
+  --> tests/ui/doc/doc-fixable.rs:158:1
    |
 LL | be_sure_we_got_to_the_end_of_it
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -287,7 +287,7 @@ LL | `be_sure_we_got_to_the_end_of_it`
    |
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:168:5
+  --> tests/ui/doc/doc-fixable.rs:169:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -298,7 +298,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it`
    |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:187:22
+  --> tests/ui/doc/doc-fixable.rs:188:22
    |
 LL | /// An iterator over mycrate::Collection's values.
    |                      ^^^^^^^^^^^^^^^^^^^
@@ -309,7 +309,7 @@ LL | /// An iterator over `mycrate::Collection`'s values.
    |                      ~~~~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:211:34
+  --> tests/ui/doc/doc-fixable.rs:212:34
    |
 LL | /// Foo \[bar\] \[baz\] \[qux\]. DocMarkdownLint
    |                                  ^^^^^^^^^^^^^^^
@@ -320,7 +320,7 @@ LL | /// Foo \[bar\] \[baz\] \[qux\]. `DocMarkdownLint`
    |                                  ~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:234:22
+  --> tests/ui/doc/doc-fixable.rs:235:22
    |
 LL | /// There is no try (do() or do_not()).
    |                      ^^^^
@@ -331,7 +331,7 @@ LL | /// There is no try (`do()` or do_not()).
    |                      ~~~~~~
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:234:30
+  --> tests/ui/doc/doc-fixable.rs:235:30
    |
 LL | /// There is no try (do() or do_not()).
    |                              ^^^^^^^^
@@ -342,7 +342,7 @@ LL | /// There is no try (do() or `do_not()`).
    |                              ~~~~~~~~~~
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:237:5
+  --> tests/ui/doc/doc-fixable.rs:238:5
    |
 LL | /// ABes
    |     ^^^^
@@ -353,7 +353,7 @@ LL | /// `ABes`
    |     ~~~~~~
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:243:9
+  --> tests/ui/doc/doc-fixable.rs:244:9
    |
 LL |     /// foo()
    |         ^^^^^
@@ -364,7 +364,7 @@ LL |     /// `foo()`
    |         ~~~~~~~
 
 error: you should put bare URLs between `<`/`>` or make a proper Markdown link
-  --> tests/ui/doc/doc-fixable.rs:247:5
+  --> tests/ui/doc/doc-fixable.rs:248:5
    |
 LL | /// https://github.com/rust-lang/rust-clippy/pull/12836
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `<https://github.com/rust-lang/rust-clippy/pull/12836>`
diff --git a/tests/ui/unused_io_amount.rs b/tests/ui/unused_io_amount.rs
index f5b200d5ffe..175c4ca7689 100644
--- a/tests/ui/unused_io_amount.rs
+++ b/tests/ui/unused_io_amount.rs
@@ -277,4 +277,18 @@ fn allow_works<F: std::io::Read>(mut f: F) {
     f.read(&mut data).unwrap();
 }
 
+struct Reader {}
+
+impl Read for Reader {
+    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+        todo!()
+    }
+
+    fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
+        // We shouldn't recommend using Read::read_exact inside Read::read_exact!
+        self.read(buf).unwrap();
+        Ok(())
+    }
+}
+
 fn main() {}
diff --git a/util/gh-pages/index_template.html b/util/gh-pages/index_template.html
index 012d4806c6c..f95fe76d996 100644
--- a/util/gh-pages/index_template.html
+++ b/util/gh-pages/index_template.html
@@ -150,7 +150,7 @@ Otherwise, have a great day =^.^=
                             <h2 class="panel-title"> {# #}
                                 <div class="panel-title-name" id="lint-{{lint.id}}"> {# #}
                                     <span>{{lint.id}}</span> {#+ #}
-                                    <a href="#{{lint.id}}" class="anchor label label-default" onclick="openLint(event)">&para;</a> {#+ #}
+                                    <a href="#{{lint.id}}" onclick="lintAnchor(event)" class="anchor label label-default">&para;</a> {#+ #}
                                     <a href="" class="anchor label label-default" onclick="copyToClipboard(event)"> {# #}
                                         &#128203; {# #}
                                     </a> {# #}
diff --git a/util/gh-pages/script.js b/util/gh-pages/script.js
index ac928621362..9a5365b2158 100644
--- a/util/gh-pages/script.js
+++ b/util/gh-pages/script.js
@@ -151,11 +151,14 @@ function expandLint(lintId) {
     highlightIfNeeded(lintId);
 }
 
-// Show details for one lint
-function openLint(event) {
+function lintAnchor(event) {
     event.preventDefault();
     event.stopPropagation();
-    expandLint(event.target.getAttribute("href").slice(1));
+
+    const id = event.target.getAttribute("href").replace("#", "");
+    window.location.hash = id;
+
+    expandLint(id);
 }
 
 function copyToClipboard(event) {
@@ -519,7 +522,7 @@ function scrollToLint(lintId) {
 
 // If the page we arrive on has link to a given lint, we scroll to it.
 function scrollToLintByURL() {
-    const lintId = window.location.hash.substring(2);
+    const lintId = window.location.hash.substring(1);
     if (lintId.length > 0) {
         scrollToLint(lintId);
     }