Skip to content

Commit 6ff53c7

Browse files
committed
Add lint for broken doc links
1 parent 5f05ce4 commit 6ff53c7

File tree

6 files changed

+101
-0
lines changed

6 files changed

+101
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5441,6 +5441,7 @@ Released 2018-09-13
54415441
[`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type
54425442
[`disallowed_types`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types
54435443
[`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression
5444+
[`doc_broken_link`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_broken_link
54445445
[`doc_lazy_continuation`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_lazy_continuation
54455446
[`doc_link_with_quotes`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_link_with_quotes
54465447
[`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
135135
crate::disallowed_names::DISALLOWED_NAMES_INFO,
136136
crate::disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS_INFO,
137137
crate::disallowed_types::DISALLOWED_TYPES_INFO,
138+
crate::doc::DOC_BROKEN_LINK_INFO,
138139
crate::doc::DOC_LAZY_CONTINUATION_INFO,
139140
crate::doc::DOC_LINK_WITH_QUOTES_INFO,
140141
crate::doc::DOC_MARKDOWN_INFO,

clippy_lints/src/doc/broken_link.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
use super::{DOC_BROKEN_LINK, Fragments};
2+
use clippy_utils::diagnostics::span_lint;
3+
use rustc_lint::LateContext;
4+
use std::ops::Range;
5+
6+
// Check broken links in code docs.
7+
pub fn check(cx: &LateContext<'_>, _trimmed_text: &str, range: Range<usize>, fragments: Fragments<'_>, link: &str) {
8+
if let Some(span) = fragments.span(cx, range) {
9+
// Broken links are replaced with "fake" value by `fake_broken_link_callback` at `doc/mod.rs`.
10+
if link == "fake" {
11+
span_lint(cx, DOC_BROKEN_LINK, span, "possible broken doc link");
12+
}
13+
}
14+
}

clippy_lints/src/doc/mod.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ use rustc_span::{Span, sym};
3232
use std::ops::Range;
3333
use url::Url;
3434

35+
mod broken_link;
3536
mod empty_line_after;
3637
mod link_with_quotes;
3738
mod markdown;
@@ -261,6 +262,33 @@ declare_clippy_lint! {
261262
"possible typo for an intra-doc link"
262263
}
263264

265+
declare_clippy_lint! {
266+
/// ### What it does
267+
/// Checks the doc comments have unbroken links, mostly caused
268+
/// by bad formatted links such as broken across multiple lines.
269+
///
270+
/// ### Why is this bad?
271+
/// Because documentation generated by rustdoc will be broken
272+
/// since expected links won't be links and just text.
273+
///
274+
/// ### Examples
275+
/// This link is broken:
276+
/// ```no_run
277+
/// /// [example of a bad link](https://
278+
/// /// github.com/rust-lang/rust-clippy/)
279+
/// pub fn do_something() {}
280+
///
281+
/// It shouldn't be broken across multiple lines to work:
282+
/// ```no_run
283+
/// /// [example of a good link](https://github.com/rust-lang/rust-clippy/)
284+
/// pub fn do_something() {}
285+
/// ```
286+
#[clippy::version = "1.82.0"]
287+
pub DOC_BROKEN_LINK,
288+
pedantic,
289+
"broken document link"
290+
}
291+
264292
declare_clippy_lint! {
265293
/// ### What it does
266294
/// Checks for the doc comments of publicly visible
@@ -548,6 +576,7 @@ impl Documentation {
548576

549577
impl_lint_pass!(Documentation => [
550578
DOC_LINK_WITH_QUOTES,
579+
DOC_BROKEN_LINK,
551580
DOC_MARKDOWN,
552581
MISSING_SAFETY_DOC,
553582
MISSING_ERRORS_DOC,
@@ -929,6 +958,9 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
929958
} else {
930959
if in_link.is_some() {
931960
link_with_quotes::check(cx, trimmed_text, range.clone(), fragments);
961+
if let Some(link) = in_link.as_ref() {
962+
broken_link::check(cx, trimmed_text, range.clone(), fragments, link);
963+
}
932964
}
933965
if let Some(link) = in_link.as_ref()
934966
&& let Ok(url) = Url::parse(link)

tests/ui/doc_broken_link.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#![warn(clippy::doc_broken_link)]
2+
3+
fn main() {
4+
doc_valid_link();
5+
doc_valid_link_broken_title();
6+
doc_valid_link_broken_url_tag();
7+
doc_invalid_link_broken_url_scheme_part();
8+
doc_invalid_link_broken_url_host_part();
9+
}
10+
11+
/// Test valid link, whole link single line.
12+
/// [doc valid link](https://test.fake/doc_valid_link)
13+
pub fn doc_valid_link() {}
14+
15+
/// Test valid link, title tag broken across multiple lines.
16+
/// [doc invalid link broken
17+
/// title](https://test.fake/doc_valid_link_broken_title)
18+
pub fn doc_valid_link_broken_title() {}
19+
20+
/// Test valid link, url tag broken across multiple lines, but
21+
/// the whole url part in a single line.
22+
/// [doc valid link broken url tag](
23+
/// https://test.fake/doc_valid_link_broken_url_tag)
24+
pub fn doc_valid_link_broken_url_tag() {}
25+
26+
/// Test invalid link, url part broken across multiple lines.
27+
/// [doc invalid link broken url scheme part part](https://
28+
/// test.fake/doc_invalid_link_broken_url_scheme_part)
29+
//~^^ ERROR: possible broken doc link
30+
pub fn doc_invalid_link_broken_url_scheme_part() {}
31+
32+
/// Test invalid link, url part broken across multiple lines.
33+
/// [doc invalid link broken url host part](https://test
34+
/// .fake/doc_invalid_link_broken_url_host_part)
35+
//~^^ ERROR: possible broken doc link
36+
pub fn doc_invalid_link_broken_url_host_part() {}

tests/ui/doc_broken_link.stderr

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error: possible broken doc link
2+
--> tests/ui/doc_broken_link.rs:27:6
3+
|
4+
LL | /// [doc invalid link broken url scheme part part](https://
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: `-D clippy::doc-broken-link` implied by `-D warnings`
8+
= help: to override `-D warnings` add `#[allow(clippy::doc_broken_link)]`
9+
10+
error: possible broken doc link
11+
--> tests/ui/doc_broken_link.rs:33:6
12+
|
13+
LL | /// [doc invalid link broken url host part](https://test
14+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
15+
16+
error: aborting due to 2 previous errors
17+

0 commit comments

Comments
 (0)