From 8f56f86df5635cca21d24e3c03fb605ed9a956e1 Mon Sep 17 00:00:00 2001 From: atlas Date: Wed, 2 Sep 2026 11:42:04 +0200 Subject: [PATCH] ci: refuse relative markdown links in rust doc comments check-doc-refs.sh's relative arm resolved every link against the linking file's directory. That is right for markdown and wrong for a .rs doc comment: rustdoc emits the href verbatim onto a page under target/doc///, so the link resolves against THAT directory instead. The consequence was a false pass, not a miss -- a link resolving from the source dir got certified while rendering broken in the published docs. Reproduced against main's script: a `](../../docs/README.md)` planted in hive-forge/src/client.rs resolves from hive-forge/src/ and the old rule exits 0. Refused rather than resolved rustdoc-aware. The repo has zero relative links in .rs doc comments (measured on 54daf9ac), so nothing needs the second reader, and the backticked repo-root form the root arm already gates renders correctly under both. Inferring module nesting would be machinery in service of a form nothing uses. Closes #3928 --- scripts/check-doc-refs.sh | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/scripts/check-doc-refs.sh b/scripts/check-doc-refs.sh index b4ead89e..24894438 100755 --- a/scripts/check-doc-refs.sh +++ b/scripts/check-doc-refs.sh @@ -60,6 +60,26 @@ while IFS=: read -r file lineno match; do [ -n "${match:-}" ] || continue exempt "$file" "$lineno" && continue link="${match#](}" + # A `.rs` doc comment has a second renderer, and it does not count `../` + # the way the rest of this arm assumes. rustdoc emits the href verbatim + # onto a page at `target/doc///`, so the link resolves + # against *that* directory, not the source file's. Resolving from the + # source therefore certifies links that render broken — a false pass, not + # a miss, and worse than no check. + # + # Refused rather than resolved rustdoc-aware: the repo has zero relative + # links in `.rs` doc comments (measured on this commit), so nothing needs + # the second reader, and the backticked repo-root form the root arm above + # already gates works under both. Inferring module nesting would be + # machinery in service of a form nothing uses. + case "$file" in + *.rs) + dead=$((dead + 1)) + printf '::error file=%s,line=%s::relative markdown link in a rust doc comment renders against the rustdoc output directory, not this file; use a backticked repo-root pointer instead: %s\n' \ + "$file" "$lineno" "$link" + continue + ;; + esac # Resolved from the linking file's directory, so `../` counts the same way # the renderer counts it. `-m` keeps a link that escapes the repo root # resolvable enough to report rather than erroring out here.