Skip to content

Commit 6b500aa

Browse files
Add new unused_footnote_definition rustdoc lint
1 parent e7b4bc8 commit 6b500aa

2 files changed

Lines changed: 42 additions & 3 deletions

File tree

src/librustdoc/lint.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,13 @@ declare_rustdoc_lint! {
203203
"footnote reference with no associated definition"
204204
}
205205

206+
declare_rustdoc_lint! {
207+
/// This lint checks if all footnote definitions are used.
208+
UNUSED_FOOTNOTE_DEFINITION,
209+
Warn,
210+
"unused footnote definition"
211+
}
212+
206213
pub(crate) static RUSTDOC_LINTS: Lazy<Vec<&'static Lint>> = Lazy::new(|| {
207214
vec![
208215
BROKEN_INTRA_DOC_LINKS,
@@ -217,6 +224,7 @@ pub(crate) static RUSTDOC_LINTS: Lazy<Vec<&'static Lint>> = Lazy::new(|| {
217224
UNESCAPED_BACKTICKS,
218225
REDUNDANT_EXPLICIT_LINKS,
219226
BROKEN_FOOTNOTE,
227+
UNUSED_FOOTNOTE_DEFINITION,
220228
]
221229
});
222230

src/librustdoc/passes/lint/footnotes.rs

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@
1212
1313
use std::ops::Range;
1414

15-
use rustc_data_structures::fx::FxHashSet;
15+
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
16+
use rustc_errors::DiagDecorator;
1617
use rustc_hir::HirId;
1718
use rustc_lint_defs::Applicability;
18-
use rustc_resolve::rustdoc::pulldown_cmark::{Event, Options, Parser};
19+
use rustc_resolve::rustdoc::pulldown_cmark::{Event, Options, Parser, Tag};
1920
use rustc_resolve::rustdoc::source_span_for_markdown_range;
2021

2122
use crate::clean::Item;
@@ -25,6 +26,8 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: &
2526
let tcx = cx.tcx;
2627

2728
let mut missing_footnote_references = FxHashSet::default();
29+
let mut footnote_references = FxHashSet::default();
30+
let mut footnote_definitions = FxHashMap::default();
2831

2932
let options = Options::ENABLE_FOOTNOTES;
3033
let mut parser = Parser::new_ext(dox, options).into_offset_iter().peekable();
@@ -40,10 +43,38 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: &
4043
{
4144
missing_footnote_references.insert(Range { start: span.start, end: end_span.end });
4245
}
46+
Event::FootnoteReference(label) => {
47+
footnote_references.insert(label);
48+
}
49+
Event::Start(Tag::FootnoteDefinition(label)) => {
50+
footnote_definitions.insert(label, span.start + 1);
51+
}
4352
_ => {}
4453
}
4554
}
4655

56+
#[allow(rustc::potential_query_instability)]
57+
for (footnote, span) in footnote_definitions {
58+
if !footnote_references.contains(&footnote) {
59+
let (span, _) = source_span_for_markdown_range(
60+
tcx,
61+
dox,
62+
&(span..span + 1),
63+
&item.attrs.doc_strings,
64+
)
65+
.unwrap_or_else(|| (item.attr_span(tcx), false));
66+
67+
tcx.emit_node_span_lint(
68+
crate::lint::UNUSED_FOOTNOTE_DEFINITION,
69+
hir_id,
70+
span,
71+
DiagDecorator(|lint| {
72+
lint.primary_message("unused footnote definition");
73+
}),
74+
);
75+
}
76+
}
77+
4778
#[allow(rustc::potential_query_instability)]
4879
for span in missing_footnote_references {
4980
let (ref_span, precise) =
@@ -56,7 +87,7 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: &
5687
crate::lint::BROKEN_FOOTNOTE,
5788
hir_id,
5889
ref_span,
59-
rustc_errors::DiagDecorator(|lint| {
90+
DiagDecorator(|lint| {
6091
lint.primary_message("no footnote definition matching this footnote");
6192
lint.span_suggestion(
6293
ref_span.shrink_to_lo(),

0 commit comments

Comments
 (0)