-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Add lint for broken doc links #13696
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
use clippy_utils::diagnostics::span_lint; | ||
use pulldown_cmark::BrokenLink as PullDownBrokenLink; | ||
use rustc_lint::LateContext; | ||
use rustc_resolve::rustdoc::{DocFragment, source_span_for_markdown_range}; | ||
use rustc_span::{BytePos, Pos, Span}; | ||
|
||
use super::DOC_BROKEN_LINK; | ||
|
||
/// Scan and report broken link on documents. | ||
/// It ignores false positives detected by `pulldown_cmark`, and only | ||
/// warns users when the broken link is consider a URL. | ||
// NOTE: We don't check these other cases because | ||
// rustdoc itself will check and warn about it: | ||
// - When a link url is broken across multiple lines in the URL path part | ||
// - When a link tag is missing the close parenthesis character at the end. | ||
// - When a link has whitespace within the url link. | ||
pub fn check(cx: &LateContext<'_>, bl: &PullDownBrokenLink<'_>, doc: &str, fragments: &[DocFragment]) { | ||
warn_if_broken_link(cx, bl, doc, fragments); | ||
} | ||
|
||
fn warn_if_broken_link(cx: &LateContext<'_>, bl: &PullDownBrokenLink<'_>, doc: &str, fragments: &[DocFragment]) { | ||
if let Some(span) = source_span_for_markdown_range(cx.tcx, doc, &bl.span, fragments) { | ||
let mut len = 0; | ||
|
||
// grab raw link data | ||
let (_, raw_link) = doc.split_at(bl.span.start); | ||
|
||
// strip off link text part | ||
let raw_link = match raw_link.split_once(']') { | ||
None => return, | ||
Some((prefix, suffix)) => { | ||
len += prefix.len() + 1; | ||
suffix | ||
}, | ||
}; | ||
|
||
let raw_link = match raw_link.split_once('(') { | ||
None => return, | ||
Some((prefix, suffix)) => { | ||
if !prefix.is_empty() { | ||
// there is text between ']' and '(' chars, so it is not a valid link | ||
return; | ||
} | ||
len += prefix.len() + 1; | ||
suffix | ||
}, | ||
}; | ||
|
||
if raw_link.starts_with("(http") { | ||
// reduce chances of false positive reports | ||
// by limiting this checking only to http/https links. | ||
return; | ||
} | ||
|
||
for c in raw_link.chars() { | ||
if c == ')' { | ||
// it is a valid link | ||
return; | ||
} | ||
|
||
if c == '\n' { | ||
report_broken_link(cx, span, len); | ||
break; | ||
} | ||
|
||
len += 1; | ||
} | ||
} | ||
} | ||
|
||
fn report_broken_link(cx: &LateContext<'_>, frag_span: Span, offset: usize) { | ||
let start = frag_span.lo(); | ||
let end = start + BytePos::from_usize(offset); | ||
|
||
let span = Span::new(start, end, frag_span.ctxt(), frag_span.parent()); | ||
|
||
span_lint( | ||
cx, | ||
DOC_BROKEN_LINK, | ||
span, | ||
"possible broken doc link: broken across multiple lines", | ||
); | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
#![warn(clippy::doc_broken_link)] | ||
|
||
fn main() {} | ||
|
||
pub struct FakeType {} | ||
|
||
/// This might be considered a link false positive | ||
/// and should be ignored by this lint rule: | ||
/// Example of referencing some code with brackets [FakeType]. | ||
pub fn doc_ignore_link_false_positive_1() {} | ||
|
||
/// This might be considered a link false positive | ||
/// and should be ignored by this lint rule: | ||
/// [`FakeType`]. Continue text after brackets, | ||
/// then (something in | ||
/// parenthesis). | ||
pub fn doc_ignore_link_false_positive_2() {} | ||
|
||
/// Test valid link, whole link single line. | ||
/// [doc valid link](https://test.fake/doc_valid_link) | ||
pub fn doc_valid_link() {} | ||
|
||
/// Test valid link, whole link single line but it has special chars such as brackets and | ||
/// parenthesis. [doc invalid link url invalid char](https://test.fake/doc_valid_link_url_invalid_char?foo[bar]=1&bar(foo)=2) | ||
pub fn doc_valid_link_url_invalid_char() {} | ||
|
||
/// Test valid link, text tag broken across multiple lines. | ||
/// [doc valid link broken | ||
/// text](https://test.fake/doc_valid_link_broken_text) | ||
pub fn doc_valid_link_broken_text() {} | ||
|
||
/// Test valid link, url tag broken across multiple lines, but | ||
/// the whole url part in a single line. | ||
/// [doc valid link broken url tag two lines first](https://test.fake/doc_valid_link_broken_url_tag_two_lines_first | ||
/// ) | ||
pub fn doc_valid_link_broken_url_tag_two_lines_first() {} | ||
|
||
/// Test valid link, url tag broken across multiple lines, but | ||
/// the whole url part in a single line. | ||
/// [doc valid link broken url tag two lines second]( | ||
/// https://test.fake/doc_valid_link_broken_url_tag_two_lines_second) | ||
pub fn doc_valid_link_broken_url_tag_two_lines_second() {} | ||
|
||
/// Test valid link, url tag broken across multiple lines, but | ||
/// the whole url part in a single line, but the closing pharentesis | ||
/// in a third line. | ||
/// [doc valid link broken url tag three lines]( | ||
/// https://test.fake/doc_valid_link_broken_url_tag_three_lines | ||
/// ) | ||
pub fn doc_valid_link_broken_url_tag_three_lines() {} | ||
|
||
/// Test invalid link, url part broken across multiple lines. | ||
/// [doc invalid link broken url scheme part](https:// | ||
/// test.fake/doc_invalid_link_broken_url_scheme_part) | ||
//~^^ ERROR: possible broken doc link: broken across multiple lines | ||
pub fn doc_invalid_link_broken_url_scheme_part() {} | ||
|
||
/// Test invalid link, url part broken across multiple lines. | ||
/// [doc invalid link broken url host part](https://test | ||
/// .fake/doc_invalid_link_broken_url_host_part) | ||
//~^^ ERROR: possible broken doc link: broken across multiple lines | ||
pub fn doc_invalid_link_broken_url_host_part() {} | ||
|
||
/// Test invalid link, for multiple urls in the same block of comment. | ||
/// There is a [fist link - invalid](https://test | ||
/// .fake) then it continues | ||
//~^^ ERROR: possible broken doc link: broken across multiple lines | ||
/// with a [second link - valid](https://test.fake/doc_valid_link) and another [third link - invalid](https://test | ||
/// .fake). It ends with another | ||
//~^^ ERROR: possible broken doc link: broken across multiple lines | ||
/// line of comment. | ||
pub fn doc_multiple_invalid_link_broken_url() {} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
error: possible broken doc link: broken across multiple lines | ||
--> tests/ui/doc_broken_link.rs:53:5 | ||
| | ||
LL | /// [doc invalid link broken url scheme part](https:// | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
| | ||
= note: `-D clippy::doc-broken-link` implied by `-D warnings` | ||
= help: to override `-D warnings` add `#[allow(clippy::doc_broken_link)]` | ||
|
||
error: possible broken doc link: broken across multiple lines | ||
--> tests/ui/doc_broken_link.rs:59:5 | ||
| | ||
LL | /// [doc invalid link broken url host part](https://test | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
|
||
error: possible broken doc link: broken across multiple lines | ||
--> tests/ui/doc_broken_link.rs:65:16 | ||
| | ||
LL | /// There is a [fist link - invalid](https://test | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
|
||
error: possible broken doc link: broken across multiple lines | ||
--> tests/ui/doc_broken_link.rs:68:80 | ||
| | ||
LL | /// with a [second link - valid](https://test.fake/doc_valid_link) and another [third link - invalid](https://test | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
|
||
error: aborting due to 4 previous errors | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.