#!/bin/sh # Two checks over docs/ that the doc build itself cannot make. # # `cargo docs` (with rustdocflags = -D warnings, set in .cargo/config.toml) # already catches broken intra-doc links from prose into code, which is the # main way these pages rot. It cannot catch either of the following, because # to rustdoc both are just text. # # Run by prek; also runnable by hand from anywhere in the checkout. set -eu cd "$(dirname "$0")/.." status=0 # 1. Untested Rust code fences. # # atgc is a binary-only crate, and rustdoc does not run doctests for binary # targets. A ```rust block in docs/ would therefore be checked by nothing # while looking exactly like the tested examples readers expect in Rust # documentation. A `rust,ignore` fence is allowed: rustdoc renders those with # a marker saying the example is not tested, which is the honest # presentation. See docs/writing-documentation.md. fences=$(grep -rnE '^ *```(rust|rs)([,{ ].*)?$' docs | grep -v ignore || true) if [ -n "$fences" ]; then echo "doc-lint: untested rust fence in docs/ — tag it rust,ignore or use a shell transcript" echo "$fences" status=1 fi # 1b. Fences with no language at all, which rustdoc treats as Rust and so # would fail the doc build the moment their contents happened to parse. # Every opening fence gets a tag: text, json, and so on. Counting fences per # file makes the odd-numbered ones the opening ones. bare=$(awk '/^ *```/ { n[FILENAME]++ } /^ *```$/ && n[FILENAME] % 2 == 1 { print FILENAME ":" FNR ": untagged fence" }' \ docs/*.md) if [ -n "$bare" ]; then echo "doc-lint: code fence with no language tag (rustdoc reads those as Rust)" echo "$bare" status=1 fi # 2. Relative links between pages whose target file does not exist. # # These are ordinary Markdown links, so a rename leaves a dead one behind # with nothing to notice. Covers inline [text](page.md) and reference-style # [label]: page.md, in both cases only for relative paths ending in .md. links=$(grep -rnoE '\]\([^):]+\.md(#[^)]*)?\)|^\[[^]]+\]: [^ :]+\.md' docs || true) missing=$(printf '%s\n' "$links" | while IFS= read -r hit; do [ -n "$hit" ] || continue file=${hit%%:*} target=${hit##*]} target=${target#\(} target=${target#: } target=${target%)} target=${target%%#*} [ -f "$(dirname "$file")/$target" ] || echo "doc-lint: $file links to a missing file: $target" done) if [ -n "$missing" ]; then echo "$missing" status=1 fi exit $status