Skip to content

Commit f83651a

Browse files
authored
Rollup merge of rust-lang#77971 - jyn514:broken-intra-doc-links, r=mark-simulacrum
Deny broken intra-doc links in linkchecker Since rustdoc isn't warning about these links, check for them manually. This also fixes the broken links that popped up from the lint.
2 parents 617b87f + b221819 commit f83651a

File tree

17 files changed

+92
-20
lines changed

17 files changed

+92
-20
lines changed

Cargo.lock

+4
Original file line numberDiff line numberDiff line change
@@ -1744,6 +1744,10 @@ dependencies = [
17441744
[[package]]
17451745
name = "linkchecker"
17461746
version = "0.1.0"
1747+
dependencies = [
1748+
"once_cell",
1749+
"regex",
1750+
]
17471751

17481752
[[package]]
17491753
name = "linked-hash-map"

compiler/rustc_error_codes/src/error_codes/E0660.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@ llvm_asm!("nop" "nop");
99
Considering that this would be a long explanation, we instead recommend you
1010
take a look at the [`llvm_asm`] chapter of the Unstable book:
1111

12-
[llvm_asm]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html
12+
[`llvm_asm`]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html

compiler/rustc_error_codes/src/error_codes/E0661.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ llvm_asm!("nop" : "r"(a));
1010
Considering that this would be a long explanation, we instead recommend you
1111
take a look at the [`llvm_asm`] chapter of the Unstable book:
1212

13-
[llvm_asm]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html
13+
[`llvm_asm`]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html

compiler/rustc_error_codes/src/error_codes/E0662.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ llvm_asm!("xor %eax, %eax"
1313
Considering that this would be a long explanation, we instead recommend you
1414
take a look at the [`llvm_asm`] chapter of the Unstable book:
1515

16-
[llvm_asm]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html
16+
[`llvm_asm`]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html

compiler/rustc_error_codes/src/error_codes/E0663.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ llvm_asm!("xor %eax, %eax"
1313
Considering that this would be a long explanation, we instead recommend you
1414
take a look at the [`llvm_asm`] chapter of the Unstable book:
1515

16-
[llvm_asm]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html
16+
[`llvm_asm`]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html

compiler/rustc_error_codes/src/error_codes/E0664.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ llvm_asm!("mov $$0x200, %eax"
1313
Considering that this would be a long explanation, we instead recommend you
1414
take a look at the [`llvm_asm`] chapter of the Unstable book:
1515

16-
[llvm_asm]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html
16+
[`llvm_asm`]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html

library/core/src/alloc/mod.rs

+3-9
Original file line numberDiff line numberDiff line change
@@ -89,13 +89,11 @@ impl fmt::Display for AllocError {
8989
pub unsafe trait AllocRef {
9090
/// Attempts to allocate a block of memory.
9191
///
92-
/// On success, returns a [`NonNull<[u8]>`] meeting the size and alignment guarantees of `layout`.
92+
/// On success, returns a [`NonNull<[u8]>`][NonNull] meeting the size and alignment guarantees of `layout`.
9393
///
9494
/// The returned block may have a larger size than specified by `layout.size()`, and may or may
9595
/// not have its contents initialized.
9696
///
97-
/// [`NonNull<[u8]>`]: NonNull
98-
///
9997
/// # Errors
10098
///
10199
/// Returning `Err` indicates that either memory is exhausted or `layout` does not meet
@@ -146,7 +144,7 @@ pub unsafe trait AllocRef {
146144

147145
/// Attempts to extend the memory block.
148146
///
149-
/// Returns a new [`NonNull<[u8]>`] containing a pointer and the actual size of the allocated
147+
/// Returns a new [`NonNull<[u8]>`][NonNull] containing a pointer and the actual size of the allocated
150148
/// memory. The pointer is suitable for holding data described by `new_layout`. To accomplish
151149
/// this, the allocator may extend the allocation referenced by `ptr` to fit the new layout.
152150
///
@@ -158,8 +156,6 @@ pub unsafe trait AllocRef {
158156
/// If this method returns `Err`, then ownership of the memory block has not been transferred to
159157
/// this allocator, and the contents of the memory block are unaltered.
160158
///
161-
/// [`NonNull<[u8]>`]: NonNull
162-
///
163159
/// # Safety
164160
///
165161
/// * `ptr` must denote a block of memory [*currently allocated*] via this allocator.
@@ -271,7 +267,7 @@ pub unsafe trait AllocRef {
271267

272268
/// Attempts to shrink the memory block.
273269
///
274-
/// Returns a new [`NonNull<[u8]>`] containing a pointer and the actual size of the allocated
270+
/// Returns a new [`NonNull<[u8]>`][NonNull] containing a pointer and the actual size of the allocated
275271
/// memory. The pointer is suitable for holding data described by `new_layout`. To accomplish
276272
/// this, the allocator may shrink the allocation referenced by `ptr` to fit the new layout.
277273
///
@@ -283,8 +279,6 @@ pub unsafe trait AllocRef {
283279
/// If this method returns `Err`, then ownership of the memory block has not been transferred to
284280
/// this allocator, and the contents of the memory block are unaltered.
285281
///
286-
/// [`NonNull<[u8]>`]: NonNull
287-
///
288282
/// # Safety
289283
///
290284
/// * `ptr` must denote a block of memory [*currently allocated*] via this allocator.

library/core/src/convert/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ pub const fn identity<T>(x: T) -> T {
134134
/// want to accept all references that can be converted to [`&str`] as an argument.
135135
/// Since both [`String`] and [`&str`] implement `AsRef<str>` we can accept both as input argument.
136136
///
137+
/// [`&str`]: primitive@str
137138
/// [`Option<T>`]: Option
138139
/// [`Result<T, E>`]: Result
139140
/// [`Borrow`]: crate::borrow::Borrow

library/core/src/iter/traits/double_ended.rs

+3
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,9 @@ pub trait DoubleEndedIterator: Iterator {
122122
/// assert_eq!(iter.advance_back_by(0), Ok(()));
123123
/// assert_eq!(iter.advance_back_by(100), Err(1)); // only `&3` was skipped
124124
/// ```
125+
///
126+
/// [`Ok(())`]: Ok
127+
/// [`Err(k)`]: Err
125128
#[inline]
126129
#[unstable(feature = "iter_advance_by", reason = "recently added", issue = "77404")]
127130
fn advance_back_by(&mut self, n: usize) -> Result<(), usize> {

library/core/src/iter/traits/iterator.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -289,12 +289,12 @@ pub trait Iterator {
289289
/// This method will eagerly skip `n` elements by calling [`next`] up to `n`
290290
/// times until [`None`] is encountered.
291291
///
292-
/// `advance_by(n)` will return [`Ok(())`] if the iterator successfully advances by
293-
/// `n` elements, or [`Err(k)`] if [`None`] is encountered, where `k` is the number
292+
/// `advance_by(n)` will return [`Ok(())`][Ok] if the iterator successfully advances by
293+
/// `n` elements, or [`Err(k)`][Err] if [`None`] is encountered, where `k` is the number
294294
/// of elements the iterator is advanced by before running out of elements (i.e. the
295295
/// length of the iterator). Note that `k` is always less than `n`.
296296
///
297-
/// Calling `advance_by(0)` does not consume any elements and always returns [`Ok(())`].
297+
/// Calling `advance_by(0)` does not consume any elements and always returns [`Ok(())`][Ok].
298298
///
299299
/// [`next`]: Iterator::next
300300
///

library/core/src/option.rs

+1
Original file line numberDiff line numberDiff line change
@@ -687,6 +687,7 @@ impl<T> Option<T> {
687687
/// assert_eq!(Some(4).filter(is_even), Some(4));
688688
/// ```
689689
///
690+
/// [`Some(t)`]: Some
690691
#[inline]
691692
#[stable(feature = "option_filter", since = "1.27.0")]
692693
pub fn filter<P: FnOnce(&T) -> bool>(self, predicate: P) -> Self {

library/std/src/ffi/c_str.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1383,7 +1383,8 @@ impl CStr {
13831383
/// [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD] and return a
13841384
/// [`Cow`]`::`[`Owned`]`(`[`String`]`)` with the result.
13851385
///
1386-
/// [`str`]: prim@str
1386+
/// [`str`]: primitive@str
1387+
/// [`&str`]: primitive@str
13871388
/// [`Borrowed`]: Cow::Borrowed
13881389
/// [`Owned`]: Cow::Owned
13891390
/// [U+FFFD]: crate::char::REPLACEMENT_CHARACTER

src/doc/unstable-book/src/library-features/default-free-fn.md

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ Adds a free `default()` function to the `std::default` module. This function
1010
just forwards to [`Default::default()`], but may remove repetition of the word
1111
"default" from the call site.
1212

13+
[`Default::default()`]: https://doc.rust-lang.org/nightly/std/default/trait.Default.html#tymethod.default
14+
1315
Here is an example:
1416

1517
```rust

src/tools/linkchecker/Cargo.toml

+4
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,7 @@ edition = "2018"
77
[[bin]]
88
name = "linkchecker"
99
path = "main.rs"
10+
11+
[dependencies]
12+
regex = "1"
13+
once_cell = "1"

src/tools/linkchecker/main.rs

+62
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ use std::fs;
2121
use std::path::{Component, Path, PathBuf};
2222
use std::rc::Rc;
2323

24+
use once_cell::sync::Lazy;
25+
use regex::Regex;
26+
2427
use crate::Redirect::*;
2528

2629
// Add linkcheck exceptions here
@@ -50,6 +53,44 @@ const LINKCHECK_EXCEPTIONS: &[(&str, &[&str])] = &[
5053
("alloc/collections/btree_set/struct.BTreeSet.html", &["#insert-and-complex-keys"]),
5154
];
5255

56+
#[rustfmt::skip]
57+
const INTRA_DOC_LINK_EXCEPTIONS: &[(&str, &[&str])] = &[
58+
// This will never have links that are not in other pages.
59+
// To avoid repeating the exceptions twice, an empty list means all broken links are allowed.
60+
("reference/print.html", &[]),
61+
// All the reference 'links' are actually ENBF highlighted as code
62+
("reference/comments.html", &[
63+
"/</code> <code>!",
64+
"*</code> <code>!",
65+
]),
66+
("reference/identifiers.html", &[
67+
"a</code>-<code>z</code> <code>A</code>-<code>Z",
68+
"a</code>-<code>z</code> <code>A</code>-<code>Z</code> <code>0</code>-<code>9</code> <code>_",
69+
"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>_",
70+
]),
71+
("reference/tokens.html", &[
72+
"0</code>-<code>1",
73+
"0</code>-<code>7",
74+
"0</code>-<code>9",
75+
"0</code>-<code>9",
76+
"0</code>-<code>9</code> <code>a</code>-<code>f</code> <code>A</code>-<code>F",
77+
]),
78+
("reference/notation.html", &[
79+
"b</code> <code>B",
80+
"a</code>-<code>z",
81+
]),
82+
// This is being used in the sense of 'inclusive range', not a markdown link
83+
("core/ops/struct.RangeInclusive.html", &["begin</code>, <code>end"]),
84+
("std/ops/struct.RangeInclusive.html", &["begin</code>, <code>end"]),
85+
("core/slice/trait.SliceIndex.html", &["begin</code>, <code>end"]),
86+
("alloc/slice/trait.SliceIndex.html", &["begin</code>, <code>end"]),
87+
("std/slice/trait.SliceIndex.html", &["begin</code>, <code>end"]),
88+
89+
];
90+
91+
static BROKEN_INTRA_DOC_LINK: Lazy<Regex> =
92+
Lazy::new(|| Regex::new(r#"\[<code>(.*)</code>\]"#).unwrap());
93+
5394
macro_rules! t {
5495
($e:expr) => {
5596
match $e {
@@ -138,6 +179,14 @@ fn walk(cache: &mut Cache, root: &Path, dir: &Path, errors: &mut bool) {
138179
}
139180
}
140181

182+
fn is_intra_doc_exception(file: &Path, link: &str) -> bool {
183+
if let Some(entry) = INTRA_DOC_LINK_EXCEPTIONS.iter().find(|&(f, _)| file.ends_with(f)) {
184+
entry.1.is_empty() || entry.1.contains(&link)
185+
} else {
186+
false
187+
}
188+
}
189+
141190
fn is_exception(file: &Path, link: &str) -> bool {
142191
if let Some(entry) = LINKCHECK_EXCEPTIONS.iter().find(|&(f, _)| file.ends_with(f)) {
143192
entry.1.contains(&link)
@@ -292,6 +341,19 @@ fn check(cache: &mut Cache, root: &Path, file: &Path, errors: &mut bool) -> Opti
292341
}
293342
}
294343
});
344+
345+
// Search for intra-doc links that rustdoc didn't warn about
346+
// FIXME(#77199, 77200) Rustdoc should just warn about these directly.
347+
// NOTE: only looks at one line at a time; in practice this should find most links
348+
for (i, line) in contents.lines().enumerate() {
349+
for broken_link in BROKEN_INTRA_DOC_LINK.captures_iter(line) {
350+
if !is_intra_doc_exception(file, &broken_link[1]) {
351+
*errors = true;
352+
print!("{}:{}: broken intra-doc link - ", pretty_file.display(), i + 1);
353+
println!("{}", &broken_link[0]);
354+
}
355+
}
356+
}
295357
Some(pretty_file)
296358
}
297359

0 commit comments

Comments
 (0)