1212
1313use std:: ops:: Range ;
1414
15- use rustc_data_structures:: fx:: FxHashSet ;
15+ use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
16+ use rustc_errors:: DiagDecorator ;
1617use rustc_hir:: HirId ;
1718use 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 } ;
1920use rustc_resolve:: rustdoc:: source_span_for_markdown_range;
2021
2122use 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