-
-
Notifications
You must be signed in to change notification settings - Fork 14.5k
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: main
Are you sure you want to change the base?
Changes from all commits
1740c93
66dc579
e0d8136
3406533
2cdc54b
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,88 @@ | ||
| //! 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 rustc_data_structures::fx::{FxHashMap, FxHashSet}; | ||
| use rustc_hir::HirId; | ||
| use rustc_lint_defs::Applicability; | ||
| use rustc_resolve::rustdoc::pulldown_cmark::{Event, Options, Parser, Tag}; | ||
| 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(); | ||
|
Contributor
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
Contributor
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
Contributor
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. The docs are outdated. FootnoteReference is only emitted if the footnote definition exists.
Contributor
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. glad to see the docs getting fixed, but i still believe this code handles code blocks incorrectly. |
||
| 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), false)); | ||
|
|
||
| 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, _) = | ||
| source_span_for_markdown_range(tcx, dox, &span, &item.attrs.doc_strings) | ||
| .unwrap_or_else(|| (item.attr_span(tcx), false)); | ||
|
|
||
| tcx.node_span_lint(crate::lint::BROKEN_FOOTNOTE, hir_id, ref_span, |lint| { | ||
| lint.primary_message("no footnote definition matching this footnote"); | ||
|
Contributor
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. This lint is redundant with the doc_suspicious_footnotes clippy lint, but it doesn't implement the number checking the same way. Shouldn't that be added? |
||
| 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,7 @@ | ||
| #![deny(rustdoc::broken_footnote)] | ||
|
|
||
| //! 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:3: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:3: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` lint is working as expected. | ||
|
|
||
| #![deny(rustdoc::unused_footnote_definition)] | ||
|
|
||
| //! Footnote referenced. [^2] | ||
| //! | ||
| //! [^1]: footnote defined | ||
| //! [^2]: footnote defined | ||
| //~^^ ERROR: unused footnote definition |
| 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.
This doc comment is wrong.