-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Add new unused_footnote_definition
rustdoc lint
#137858
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
base: master
Are you sure you want to change the base?
Changes from all commits
273dd4d
d8266c2
1bc04a2
27c7ec4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
//! Detects specific markdown syntax that's different between pulldown-cmark | ||
//! 0.9 and 0.11. | ||
//! | ||
//! This is a mitigation for old parser bugs that affected some | ||
//! real crates' docs. The old parser claimed to comply with CommonMark, | ||
//! but it did not. These warnings will eventually be removed, | ||
//! though some of them may become Clippy lints. | ||
//! | ||
//! <https://github.com/rust-lang/rust/pull/121659#issuecomment-1992752820> | ||
//! | ||
//! <https://rustc-dev-guide.rust-lang.org/bug-fix-procedure.html#add-the-lint-to-the-list-of-removed-lists> | ||
use std::ops::Range; | ||
|
||
use pulldown_cmark::{Event, Options, Parser, Tag}; | ||
use rustc_data_structures::fx::{FxHashMap, FxHashSet}; | ||
use rustc_hir::HirId; | ||
use rustc_lint_defs::Applicability; | ||
use rustc_resolve::rustdoc::source_span_for_markdown_range; | ||
|
||
use crate::clean::Item; | ||
use crate::core::DocContext; | ||
|
||
pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: &str) { | ||
let tcx = cx.tcx; | ||
|
||
let mut missing_footnote_references = FxHashSet::default(); | ||
let mut footnote_references = FxHashSet::default(); | ||
let mut footnote_definitions = FxHashMap::default(); | ||
|
||
let options = Options::ENABLE_FOOTNOTES; | ||
let mut parser = Parser::new_ext(dox, options).into_offset_iter().peekable(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we be making sure the lint is enabled before invoking the parser? I know the other lints don't do this, but maybe they should? |
||
while let Some((event, span)) = parser.next() { | ||
match event { | ||
Event::Text(text) | ||
if &*text == "[" | ||
&& let Some((Event::Text(text), _)) = parser.peek() | ||
&& text.trim_start().starts_with('^') | ||
&& parser.next().is_some() | ||
&& let Some((Event::Text(text), end_span)) = parser.peek() | ||
&& &**text == "]" => | ||
{ | ||
missing_footnote_references.insert(Range { start: span.start, end: end_span.end }); | ||
} | ||
Comment on lines
+35
to
+44
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is quite odd that pulldown_cmark isn't emmitting some form of FootnoteReference here despite the docs saying they might not map to an actual definition. In any case, I don't think this implementation is correct, since One way to handle this is to track the type of the last |
||
Event::FootnoteReference(label) => { | ||
footnote_references.insert(label); | ||
} | ||
Event::Start(Tag::FootnoteDefinition(label)) => { | ||
footnote_definitions.insert(label, span.start + 1); | ||
} | ||
_ => {} | ||
} | ||
} | ||
|
||
#[allow(rustc::potential_query_instability)] | ||
for (footnote, span) in footnote_definitions { | ||
if !footnote_references.contains(&footnote) { | ||
let span = source_span_for_markdown_range( | ||
tcx, | ||
dox, | ||
&(span..span + 1), | ||
&item.attrs.doc_strings, | ||
) | ||
.unwrap_or_else(|| item.attr_span(tcx)); | ||
|
||
tcx.node_span_lint(crate::lint::UNUSED_FOOTNOTE_DEFINITION, hir_id, span, |lint| { | ||
lint.primary_message("unused footnote definition"); | ||
}); | ||
} | ||
} | ||
|
||
#[allow(rustc::potential_query_instability)] | ||
for span in missing_footnote_references { | ||
let (ref_span, precise) = | ||
source_span_for_markdown_range(tcx, dox, &span, &item.attrs.doc_strings) | ||
.map(|span| (span, true)) | ||
.unwrap_or_else(|| (item.attr_span(tcx), false)); | ||
Comment on lines
+74
to
+77
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Couldn't this just be a It doesn't seem like we use |
||
|
||
if precise { | ||
tcx.node_span_lint(crate::lint::BROKEN_FOOTNOTE, hir_id, ref_span, |lint| { | ||
lint.primary_message("no footnote definition matching this footnote"); | ||
lint.span_suggestion( | ||
ref_span.shrink_to_lo(), | ||
"if it should not be a footnote, escape it", | ||
"\\", | ||
Applicability::MaybeIncorrect, | ||
); | ||
}); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
#![deny(rustdoc::broken_footnote)] | ||
#![allow(rustdoc::unportable_markdown)] | ||
|
||
//! Footnote referenced [^1]. And [^2]. And [^bla]. | ||
//! | ||
//! [^1]: footnote defined | ||
//~^^^ ERROR: no footnote definition matching this footnote | ||
//~| ERROR: no footnote definition matching this footnote |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
error: no footnote definition matching this footnote | ||
--> $DIR/broken-footnote.rs:4:45 | ||
| | ||
LL | //! Footnote referenced [^1]. And [^2]. And [^bla]. | ||
| -^^^^^ | ||
| | | ||
| help: if it should not be a footnote, escape it: `\` | ||
| | ||
note: the lint level is defined here | ||
--> $DIR/broken-footnote.rs:1:9 | ||
| | ||
LL | #![deny(rustdoc::broken_footnote)] | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^ | ||
|
||
error: no footnote definition matching this footnote | ||
--> $DIR/broken-footnote.rs:4:35 | ||
| | ||
LL | //! Footnote referenced [^1]. And [^2]. And [^bla]. | ||
| -^^^ | ||
| | | ||
| help: if it should not be a footnote, escape it: `\` | ||
|
||
error: aborting due to 2 previous errors | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,9 @@ | ||||||
// This test ensures that the rustdoc `unused_footnote` is working as expected. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
#![deny(rustdoc::unused_footnote_definition)] | ||||||
|
||||||
//! Footnote referenced. [^2] | ||||||
//! | ||||||
//! [^1]: footnote defined | ||||||
//! [^2]: footnote defined | ||||||
//~^^ unused_footnote_definition | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Nit: for consistency with other tests, look for the actual error and not the note telling you why it is enabled |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
error: unused footnote definition | ||
--> $DIR/unused-footnote.rs:7:6 | ||
| | ||
LL | //! [^1]: footnote defined | ||
| ^ | ||
| | ||
note: the lint level is defined here | ||
--> $DIR/unused-footnote.rs:3:9 | ||
| | ||
LL | #![deny(rustdoc::unused_footnote_definition)] | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
|
||
error: aborting due to 1 previous error | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for consistency with other lint descriptions