Deny broken intra-doc links in linkchecker
Since rustdoc isn't warning about these links, check for them manually.
This commit is contained in:
parent
dd7fc54ebd
commit
65835d1059
@ -1744,6 +1744,10 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "linkchecker"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
|
@ -9,4 +9,4 @@ llvm_asm!("nop" "nop");
|
||||
Considering that this would be a long explanation, we instead recommend you
|
||||
take a look at the [`llvm_asm`] chapter of the Unstable book:
|
||||
|
||||
[llvm_asm]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html
|
||||
[`llvm_asm`]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html
|
||||
|
@ -10,4 +10,4 @@ llvm_asm!("nop" : "r"(a));
|
||||
Considering that this would be a long explanation, we instead recommend you
|
||||
take a look at the [`llvm_asm`] chapter of the Unstable book:
|
||||
|
||||
[llvm_asm]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html
|
||||
[`llvm_asm`]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html
|
||||
|
@ -13,4 +13,4 @@ llvm_asm!("xor %eax, %eax"
|
||||
Considering that this would be a long explanation, we instead recommend you
|
||||
take a look at the [`llvm_asm`] chapter of the Unstable book:
|
||||
|
||||
[llvm_asm]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html
|
||||
[`llvm_asm`]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html
|
||||
|
@ -13,4 +13,4 @@ llvm_asm!("xor %eax, %eax"
|
||||
Considering that this would be a long explanation, we instead recommend you
|
||||
take a look at the [`llvm_asm`] chapter of the Unstable book:
|
||||
|
||||
[llvm_asm]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html
|
||||
[`llvm_asm`]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html
|
||||
|
@ -13,4 +13,4 @@ llvm_asm!("mov $$0x200, %eax"
|
||||
Considering that this would be a long explanation, we instead recommend you
|
||||
take a look at the [`llvm_asm`] chapter of the Unstable book:
|
||||
|
||||
[llvm_asm]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html
|
||||
[`llvm_asm`]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html
|
||||
|
@ -89,13 +89,11 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
pub unsafe trait AllocRef {
|
||||
/// Attempts to allocate a block of memory.
|
||||
///
|
||||
/// On success, returns a [`NonNull<[u8]>`] meeting the size and alignment guarantees of `layout`.
|
||||
/// On success, returns a [`NonNull<[u8]>`][NonNull] meeting the size and alignment guarantees of `layout`.
|
||||
///
|
||||
/// The returned block may have a larger size than specified by `layout.size()`, and may or may
|
||||
/// not have its contents initialized.
|
||||
///
|
||||
/// [`NonNull<[u8]>`]: NonNull
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returning `Err` indicates that either memory is exhausted or `layout` does not meet
|
||||
@ -146,7 +144,7 @@ fn alloc_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
|
||||
|
||||
/// Attempts to extend the memory block.
|
||||
///
|
||||
/// Returns a new [`NonNull<[u8]>`] containing a pointer and the actual size of the allocated
|
||||
/// Returns a new [`NonNull<[u8]>`][NonNull] containing a pointer and the actual size of the allocated
|
||||
/// memory. The pointer is suitable for holding data described by `new_layout`. To accomplish
|
||||
/// this, the allocator may extend the allocation referenced by `ptr` to fit the new layout.
|
||||
///
|
||||
@ -158,8 +156,6 @@ fn alloc_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
|
||||
/// If this method returns `Err`, then ownership of the memory block has not been transferred to
|
||||
/// this allocator, and the contents of the memory block are unaltered.
|
||||
///
|
||||
/// [`NonNull<[u8]>`]: NonNull
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// * `ptr` must denote a block of memory [*currently allocated*] via this allocator.
|
||||
@ -271,7 +267,7 @@ unsafe fn grow_zeroed(
|
||||
|
||||
/// Attempts to shrink the memory block.
|
||||
///
|
||||
/// Returns a new [`NonNull<[u8]>`] containing a pointer and the actual size of the allocated
|
||||
/// Returns a new [`NonNull<[u8]>`][NonNull] containing a pointer and the actual size of the allocated
|
||||
/// memory. The pointer is suitable for holding data described by `new_layout`. To accomplish
|
||||
/// this, the allocator may shrink the allocation referenced by `ptr` to fit the new layout.
|
||||
///
|
||||
@ -283,8 +279,6 @@ unsafe fn grow_zeroed(
|
||||
/// If this method returns `Err`, then ownership of the memory block has not been transferred to
|
||||
/// this allocator, and the contents of the memory block are unaltered.
|
||||
///
|
||||
/// [`NonNull<[u8]>`]: NonNull
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// * `ptr` must denote a block of memory [*currently allocated*] via this allocator.
|
||||
|
@ -134,6 +134,7 @@ pub const fn identity<T>(x: T) -> T {
|
||||
/// want to accept all references that can be converted to [`&str`] as an argument.
|
||||
/// Since both [`String`] and [`&str`] implement `AsRef<str>` we can accept both as input argument.
|
||||
///
|
||||
/// [`&str`]: primitive@str
|
||||
/// [`Option<T>`]: Option
|
||||
/// [`Result<T, E>`]: Result
|
||||
/// [`Borrow`]: crate::borrow::Borrow
|
||||
|
@ -122,6 +122,9 @@ pub trait DoubleEndedIterator: Iterator {
|
||||
/// assert_eq!(iter.advance_back_by(0), Ok(()));
|
||||
/// assert_eq!(iter.advance_back_by(100), Err(1)); // only `&3` was skipped
|
||||
/// ```
|
||||
///
|
||||
/// [`Ok(())`]: Ok
|
||||
/// [`Err(k)`]: Err
|
||||
#[inline]
|
||||
#[unstable(feature = "iter_advance_by", reason = "recently added", issue = "77404")]
|
||||
fn advance_back_by(&mut self, n: usize) -> Result<(), usize> {
|
||||
|
@ -289,12 +289,12 @@ fn some<T>(_: Option<T>, x: T) -> Option<T> {
|
||||
/// This method will eagerly skip `n` elements by calling [`next`] up to `n`
|
||||
/// times until [`None`] is encountered.
|
||||
///
|
||||
/// `advance_by(n)` will return [`Ok(())`] if the iterator successfully advances by
|
||||
/// `n` elements, or [`Err(k)`] if [`None`] is encountered, where `k` is the number
|
||||
/// `advance_by(n)` will return [`Ok(())`][Ok] if the iterator successfully advances by
|
||||
/// `n` elements, or [`Err(k)`][Err] if [`None`] is encountered, where `k` is the number
|
||||
/// of elements the iterator is advanced by before running out of elements (i.e. the
|
||||
/// length of the iterator). Note that `k` is always less than `n`.
|
||||
///
|
||||
/// Calling `advance_by(0)` does not consume any elements and always returns [`Ok(())`].
|
||||
/// Calling `advance_by(0)` does not consume any elements and always returns [`Ok(())`][Ok].
|
||||
///
|
||||
/// [`next`]: Iterator::next
|
||||
///
|
||||
|
@ -687,6 +687,7 @@ pub fn and_then<U, F: FnOnce(T) -> Option<U>>(self, f: F) -> Option<U> {
|
||||
/// assert_eq!(Some(4).filter(is_even), Some(4));
|
||||
/// ```
|
||||
///
|
||||
/// [`Some(t)`]: Some
|
||||
#[inline]
|
||||
#[stable(feature = "option_filter", since = "1.27.0")]
|
||||
pub fn filter<P: FnOnce(&T) -> bool>(self, predicate: P) -> Self {
|
||||
|
@ -1383,7 +1383,8 @@ pub fn to_str(&self) -> Result<&str, str::Utf8Error> {
|
||||
/// [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD] and return a
|
||||
/// [`Cow`]`::`[`Owned`]`(`[`String`]`)` with the result.
|
||||
///
|
||||
/// [`str`]: prim@str
|
||||
/// [`str`]: primitive@str
|
||||
/// [`&str`]: primitive@str
|
||||
/// [`Borrowed`]: Cow::Borrowed
|
||||
/// [`Owned`]: Cow::Owned
|
||||
/// [U+FFFD]: crate::char::REPLACEMENT_CHARACTER
|
||||
|
@ -10,6 +10,8 @@ Adds a free `default()` function to the `std::default` module. This function
|
||||
just forwards to [`Default::default()`], but may remove repetition of the word
|
||||
"default" from the call site.
|
||||
|
||||
[`Default::default()`]: https://doc.rust-lang.org/nightly/std/default/trait.Default.html#tymethod.default
|
||||
|
||||
Here is an example:
|
||||
|
||||
```rust
|
||||
|
@ -7,3 +7,7 @@ edition = "2018"
|
||||
[[bin]]
|
||||
name = "linkchecker"
|
||||
path = "main.rs"
|
||||
|
||||
[dependencies]
|
||||
regex = "1"
|
||||
once_cell = "1"
|
||||
|
@ -21,6 +21,9 @@
|
||||
use std::path::{Component, Path, PathBuf};
|
||||
use std::rc::Rc;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
|
||||
use crate::Redirect::*;
|
||||
|
||||
// Add linkcheck exceptions here
|
||||
@ -50,6 +53,44 @@
|
||||
("alloc/collections/btree_set/struct.BTreeSet.html", &["#insert-and-complex-keys"]),
|
||||
];
|
||||
|
||||
#[rustfmt::skip]
|
||||
const INTRA_DOC_LINK_EXCEPTIONS: &[(&str, &[&str])] = &[
|
||||
// This will never have links that are not in other pages.
|
||||
// To avoid repeating the exceptions twice, an empty list means all broken links are allowed.
|
||||
("reference/print.html", &[]),
|
||||
// All the reference 'links' are actually ENBF highlighted as code
|
||||
("reference/comments.html", &[
|
||||
"/</code> <code>!",
|
||||
"*</code> <code>!",
|
||||
]),
|
||||
("reference/identifiers.html", &[
|
||||
"a</code>-<code>z</code> <code>A</code>-<code>Z",
|
||||
"a</code>-<code>z</code> <code>A</code>-<code>Z</code> <code>0</code>-<code>9</code> <code>_",
|
||||
"a</code>-<code>z</code> <code>A</code>-<code>Z</code>] [<code>a</code>-<code>z</code> <code>A</code>-<code>Z</code> <code>0</code>-<code>9</code> <code>_",
|
||||
]),
|
||||
("reference/tokens.html", &[
|
||||
"0</code>-<code>1",
|
||||
"0</code>-<code>7",
|
||||
"0</code>-<code>9",
|
||||
"0</code>-<code>9",
|
||||
"0</code>-<code>9</code> <code>a</code>-<code>f</code> <code>A</code>-<code>F",
|
||||
]),
|
||||
("reference/notation.html", &[
|
||||
"b</code> <code>B",
|
||||
"a</code>-<code>z",
|
||||
]),
|
||||
// This is being used in the sense of 'inclusive range', not a markdown link
|
||||
("core/ops/struct.RangeInclusive.html", &["begin</code>, <code>end"]),
|
||||
("std/ops/struct.RangeInclusive.html", &["begin</code>, <code>end"]),
|
||||
("core/slice/trait.SliceIndex.html", &["begin</code>, <code>end"]),
|
||||
("alloc/slice/trait.SliceIndex.html", &["begin</code>, <code>end"]),
|
||||
("std/slice/trait.SliceIndex.html", &["begin</code>, <code>end"]),
|
||||
|
||||
];
|
||||
|
||||
static BROKEN_INTRA_DOC_LINK: Lazy<Regex> =
|
||||
Lazy::new(|| Regex::new(r#"\[<code>(.*)</code>\]"#).unwrap());
|
||||
|
||||
macro_rules! t {
|
||||
($e:expr) => {
|
||||
match $e {
|
||||
@ -138,6 +179,14 @@ fn walk(cache: &mut Cache, root: &Path, dir: &Path, errors: &mut bool) {
|
||||
}
|
||||
}
|
||||
|
||||
fn is_intra_doc_exception(file: &Path, link: &str) -> bool {
|
||||
if let Some(entry) = INTRA_DOC_LINK_EXCEPTIONS.iter().find(|&(f, _)| file.ends_with(f)) {
|
||||
entry.1.is_empty() || entry.1.contains(&link)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn is_exception(file: &Path, link: &str) -> bool {
|
||||
if let Some(entry) = LINKCHECK_EXCEPTIONS.iter().find(|&(f, _)| file.ends_with(f)) {
|
||||
entry.1.contains(&link)
|
||||
@ -292,6 +341,19 @@ fn check(cache: &mut Cache, root: &Path, file: &Path, errors: &mut bool) -> Opti
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Search for intra-doc links that rustdoc didn't warn about
|
||||
// FIXME(#77199, 77200) Rustdoc should just warn about these directly.
|
||||
// NOTE: only looks at one line at a time; in practice this should find most links
|
||||
for (i, line) in contents.lines().enumerate() {
|
||||
for broken_link in BROKEN_INTRA_DOC_LINK.captures_iter(line) {
|
||||
if !is_intra_doc_exception(file, &broken_link[1]) {
|
||||
*errors = true;
|
||||
print!("{}:{}: broken intra-doc link - ", pretty_file.display(), i + 1);
|
||||
println!("{}", &broken_link[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(pretty_file)
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user