From 963746fa643cf0f8c29f7f33878daf715e7622d3 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 11 Mar 2025 14:54:18 +0100 Subject: [PATCH 1/8] Add new rustdoc `broken_footnote` lint --- src/librustdoc/lint.rs | 8 +++ src/librustdoc/passes/lint.rs | 2 + src/librustdoc/passes/lint/footnotes.rs | 71 +++++++++++++++++++++++++ 3 files changed, 81 insertions(+) create mode 100644 src/librustdoc/passes/lint/footnotes.rs diff --git a/src/librustdoc/lint.rs b/src/librustdoc/lint.rs index b09ea05688595..1e27bd03456e0 100644 --- a/src/librustdoc/lint.rs +++ b/src/librustdoc/lint.rs @@ -196,6 +196,13 @@ declare_rustdoc_lint! { "detects redundant explicit links in doc comments" } +declare_rustdoc_lint! { + /// This lint checks for uses of footnote references without definition. + BROKEN_FOOTNOTE, + Warn, + "footnote reference with no associated definition" +} + pub(crate) static RUSTDOC_LINTS: Lazy> = Lazy::new(|| { vec![ BROKEN_INTRA_DOC_LINKS, @@ -209,6 +216,7 @@ pub(crate) static RUSTDOC_LINTS: Lazy> = Lazy::new(|| { MISSING_CRATE_LEVEL_DOCS, UNESCAPED_BACKTICKS, REDUNDANT_EXPLICIT_LINKS, + BROKEN_FOOTNOTE, ] }); diff --git a/src/librustdoc/passes/lint.rs b/src/librustdoc/passes/lint.rs index 7740d14148bf0..bb952b32393cf 100644 --- a/src/librustdoc/passes/lint.rs +++ b/src/librustdoc/passes/lint.rs @@ -3,6 +3,7 @@ mod bare_urls; mod check_code_block_syntax; +mod footnotes; mod html_tags; mod redundant_explicit_links; mod unescaped_backticks; @@ -41,6 +42,7 @@ impl DocVisitor<'_> for Linter<'_, '_> { if may_have_link { bare_urls::visit_item(self.cx, item, hir_id, &dox); redundant_explicit_links::visit_item(self.cx, item, hir_id); + footnotes::visit_item(self.cx, item, hir_id, &dox); } if may_have_code { check_code_block_syntax::visit_item(self.cx, item, &dox); diff --git a/src/librustdoc/passes/lint/footnotes.rs b/src/librustdoc/passes/lint/footnotes.rs new file mode 100644 index 0000000000000..2c1b42170cba7 --- /dev/null +++ b/src/librustdoc/passes/lint/footnotes.rs @@ -0,0 +1,71 @@ +//! 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. +//! +//! +//! +//! + +use std::ops::Range; + +use rustc_data_structures::fx::FxHashSet; +use rustc_hir::HirId; +use rustc_lint_defs::Applicability; +use rustc_resolve::rustdoc::pulldown_cmark::{Event, Options, Parser}; +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 options = Options::ENABLE_FOOTNOTES; + let mut parser = Parser::new_ext(dox, options).into_offset_iter().peekable(); + 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 }); + } + _ => {} + } + } + + #[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)); + + if precise { + tcx.emit_node_span_lint( + crate::lint::BROKEN_FOOTNOTE, + hir_id, + ref_span, + rustc_errors::DiagDecorator(|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, + ); + }), + ); + } + } +} From e7b4bc85bed5678ac909d7a8aef27720d0d634ba Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 11 Mar 2025 14:58:06 +0100 Subject: [PATCH 2/8] Add ui test for rustdoc `broken_footnote` lint --- tests/rustdoc-ui/lints/broken-footnote.rs | 7 ++++++ tests/rustdoc-ui/lints/broken-footnote.stderr | 24 +++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 tests/rustdoc-ui/lints/broken-footnote.rs create mode 100644 tests/rustdoc-ui/lints/broken-footnote.stderr diff --git a/tests/rustdoc-ui/lints/broken-footnote.rs b/tests/rustdoc-ui/lints/broken-footnote.rs new file mode 100644 index 0000000000000..ef030d0e14999 --- /dev/null +++ b/tests/rustdoc-ui/lints/broken-footnote.rs @@ -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 diff --git a/tests/rustdoc-ui/lints/broken-footnote.stderr b/tests/rustdoc-ui/lints/broken-footnote.stderr new file mode 100644 index 0000000000000..0d63ab8f01513 --- /dev/null +++ b/tests/rustdoc-ui/lints/broken-footnote.stderr @@ -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 + From 6b500aa1b21d8639253d788aed7fe5cfafda72de Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 11 Mar 2025 15:10:40 +0100 Subject: [PATCH 3/8] Add new `unused_footnote_definition` rustdoc lint --- src/librustdoc/lint.rs | 8 ++++++ src/librustdoc/passes/lint/footnotes.rs | 37 +++++++++++++++++++++++-- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/src/librustdoc/lint.rs b/src/librustdoc/lint.rs index 1e27bd03456e0..f2b9ede415c84 100644 --- a/src/librustdoc/lint.rs +++ b/src/librustdoc/lint.rs @@ -203,6 +203,13 @@ declare_rustdoc_lint! { "footnote reference with no associated definition" } +declare_rustdoc_lint! { + /// This lint checks if all footnote definitions are used. + UNUSED_FOOTNOTE_DEFINITION, + Warn, + "unused footnote definition" +} + pub(crate) static RUSTDOC_LINTS: Lazy> = Lazy::new(|| { vec![ BROKEN_INTRA_DOC_LINKS, @@ -217,6 +224,7 @@ pub(crate) static RUSTDOC_LINTS: Lazy> = Lazy::new(|| { UNESCAPED_BACKTICKS, REDUNDANT_EXPLICIT_LINKS, BROKEN_FOOTNOTE, + UNUSED_FOOTNOTE_DEFINITION, ] }); diff --git a/src/librustdoc/passes/lint/footnotes.rs b/src/librustdoc/passes/lint/footnotes.rs index 2c1b42170cba7..42842574034fd 100644 --- a/src/librustdoc/passes/lint/footnotes.rs +++ b/src/librustdoc/passes/lint/footnotes.rs @@ -12,10 +12,11 @@ use std::ops::Range; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::DiagDecorator; use rustc_hir::HirId; use rustc_lint_defs::Applicability; -use rustc_resolve::rustdoc::pulldown_cmark::{Event, Options, Parser}; +use rustc_resolve::rustdoc::pulldown_cmark::{Event, Options, Parser, Tag}; use rustc_resolve::rustdoc::source_span_for_markdown_range; use crate::clean::Item; @@ -25,6 +26,8 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: & 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(); @@ -40,10 +43,38 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: & { missing_footnote_references.insert(Range { start: span.start, end: end_span.end }); } + 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.emit_node_span_lint( + crate::lint::UNUSED_FOOTNOTE_DEFINITION, + hir_id, + span, + DiagDecorator(|lint| { + lint.primary_message("unused footnote definition"); + }), + ); + } + } + #[allow(rustc::potential_query_instability)] for span in missing_footnote_references { let (ref_span, precise) = @@ -56,7 +87,7 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: & crate::lint::BROKEN_FOOTNOTE, hir_id, ref_span, - rustc_errors::DiagDecorator(|lint| { + DiagDecorator(|lint| { lint.primary_message("no footnote definition matching this footnote"); lint.span_suggestion( ref_span.shrink_to_lo(), From 621d98417e11d73ca8a7f4dfbd63b5547aec22be Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 11 Mar 2025 15:10:54 +0100 Subject: [PATCH 4/8] Add ui test for new `unused_footnote_definition` rustdoc lint --- tests/rustdoc-ui/lints/unused-footnote.rs | 9 +++++++++ tests/rustdoc-ui/lints/unused-footnote.stderr | 14 ++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 tests/rustdoc-ui/lints/unused-footnote.rs create mode 100644 tests/rustdoc-ui/lints/unused-footnote.stderr diff --git a/tests/rustdoc-ui/lints/unused-footnote.rs b/tests/rustdoc-ui/lints/unused-footnote.rs new file mode 100644 index 0000000000000..d144b42d30fb2 --- /dev/null +++ b/tests/rustdoc-ui/lints/unused-footnote.rs @@ -0,0 +1,9 @@ +// This test ensures that the rustdoc `unused_footnote` is working as expected. + +#![deny(rustdoc::unused_footnote_definition)] + +//! Footnote referenced. [^2] +//! +//! [^1]: footnote defined +//! [^2]: footnote defined +//~^^ ERROR: unused_footnote_definition diff --git a/tests/rustdoc-ui/lints/unused-footnote.stderr b/tests/rustdoc-ui/lints/unused-footnote.stderr new file mode 100644 index 0000000000000..d227cef181df3 --- /dev/null +++ b/tests/rustdoc-ui/lints/unused-footnote.stderr @@ -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 + From 4ddd48c74aa353fa20ac4e8fc0e80fb4b346e95c Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 26 Jan 2026 12:05:56 +0100 Subject: [PATCH 5/8] Improve description of new rustdoc lints --- src/librustdoc/lint.rs | 4 +-- src/librustdoc/passes/lint/footnotes.rs | 37 +++++++++++------------ tests/rustdoc-ui/lints/unused-footnote.rs | 2 +- 3 files changed, 20 insertions(+), 23 deletions(-) diff --git a/src/librustdoc/lint.rs b/src/librustdoc/lint.rs index f2b9ede415c84..c1e6d067b1977 100644 --- a/src/librustdoc/lint.rs +++ b/src/librustdoc/lint.rs @@ -200,14 +200,14 @@ declare_rustdoc_lint! { /// This lint checks for uses of footnote references without definition. BROKEN_FOOTNOTE, Warn, - "footnote reference with no associated definition" + "detects footnote references with no associated definition" } declare_rustdoc_lint! { /// This lint checks if all footnote definitions are used. UNUSED_FOOTNOTE_DEFINITION, Warn, - "unused footnote definition" + "detects unused footnote definitions" } pub(crate) static RUSTDOC_LINTS: Lazy> = Lazy::new(|| { diff --git a/src/librustdoc/passes/lint/footnotes.rs b/src/librustdoc/passes/lint/footnotes.rs index 42842574034fd..3b4ca28b24487 100644 --- a/src/librustdoc/passes/lint/footnotes.rs +++ b/src/librustdoc/passes/lint/footnotes.rs @@ -77,26 +77,23 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: & #[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)); + let ref_span = source_span_for_markdown_range(tcx, dox, &span, &item.attrs.doc_strings) + .map(|(span, _)| span) + .unwrap_or_else(|| item.attr_span(tcx)); - if precise { - tcx.emit_node_span_lint( - crate::lint::BROKEN_FOOTNOTE, - hir_id, - ref_span, - DiagDecorator(|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, - ); - }), - ); - } + tcx.emit_node_span_lint( + crate::lint::BROKEN_FOOTNOTE, + hir_id, + ref_span, + DiagDecorator(|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, + ); + }), + ); } } diff --git a/tests/rustdoc-ui/lints/unused-footnote.rs b/tests/rustdoc-ui/lints/unused-footnote.rs index d144b42d30fb2..a71e20ff6d500 100644 --- a/tests/rustdoc-ui/lints/unused-footnote.rs +++ b/tests/rustdoc-ui/lints/unused-footnote.rs @@ -1,4 +1,4 @@ -// This test ensures that the rustdoc `unused_footnote` is working as expected. +// This test ensures that the `rustdoc::unused_footnote` lint is working as expected. #![deny(rustdoc::unused_footnote_definition)] From 5deff4bdffa47964bcc9d9e509cee0ff4db253b3 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 25 Feb 2026 16:45:41 +0100 Subject: [PATCH 6/8] Remove outdated comment --- src/librustdoc/passes/lint/footnotes.rs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/librustdoc/passes/lint/footnotes.rs b/src/librustdoc/passes/lint/footnotes.rs index 3b4ca28b24487..7c975cf4fed08 100644 --- a/src/librustdoc/passes/lint/footnotes.rs +++ b/src/librustdoc/passes/lint/footnotes.rs @@ -1,15 +1,3 @@ -//! 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. -//! -//! -//! -//! - use std::ops::Range; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; From 7b8b405ba7b7951a0a9a5dddfe8f135974aa4d9e Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 12 Mar 2026 15:10:37 +0100 Subject: [PATCH 7/8] Correctly handle "escaped footnotes" --- src/librustdoc/passes/lint/footnotes.rs | 21 ++++++++++---- tests/rustdoc-ui/lints/broken-footnote.rs | 20 +++++++++++++ tests/rustdoc-ui/lints/broken-footnote.stderr | 28 +++++++++++++++---- 3 files changed, 57 insertions(+), 12 deletions(-) diff --git a/src/librustdoc/passes/lint/footnotes.rs b/src/librustdoc/passes/lint/footnotes.rs index 7c975cf4fed08..d55c8ef0da242 100644 --- a/src/librustdoc/passes/lint/footnotes.rs +++ b/src/librustdoc/passes/lint/footnotes.rs @@ -23,13 +23,22 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: & 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 == "]" => + && let Some((Event::Text(_), range)) = parser.next() + && dox[span.end..range.end].starts_with('^') => { - missing_footnote_references.insert(Range { start: span.start, end: end_span.end }); + loop { + let Some((Event::Text(text), new_span)) = parser.peek() else { break }; + if &**text != "]" { + parser.next(); + continue; + } + let text = &dox[span.end..new_span.end]; + if !text.ends_with("\\]") { + missing_footnote_references + .insert(Range { start: span.start, end: new_span.end }); + } + break; + } } Event::FootnoteReference(label) => { footnote_references.insert(label); diff --git a/tests/rustdoc-ui/lints/broken-footnote.rs b/tests/rustdoc-ui/lints/broken-footnote.rs index ef030d0e14999..bf5b2ba10ae6d 100644 --- a/tests/rustdoc-ui/lints/broken-footnote.rs +++ b/tests/rustdoc-ui/lints/broken-footnote.rs @@ -5,3 +5,23 @@ //! [^1]: footnote defined //~^^^ ERROR: no footnote definition matching this footnote //~| ERROR: no footnote definition matching this footnote + +//! [^*] special characters can appear within footnote references +//~^ ERROR: no footnote definition matching this footnote +//! +//! [^**] +//! +//! [^**]: not an error +//! +//! [^\_] so can escaped characters +//~^ ERROR: no footnote definition matching this footnote + +// Backslash escaped footnotes should not be recognized: +//! [\^4] +//! +//! [^5\] +//! +//! \[^yup] +//! +//! [^foo\ +//! bar] diff --git a/tests/rustdoc-ui/lints/broken-footnote.stderr b/tests/rustdoc-ui/lints/broken-footnote.stderr index 0d63ab8f01513..f8c048839f55b 100644 --- a/tests/rustdoc-ui/lints/broken-footnote.stderr +++ b/tests/rustdoc-ui/lints/broken-footnote.stderr @@ -1,10 +1,10 @@ error: no footnote definition matching this footnote - --> $DIR/broken-footnote.rs:3:45 + --> $DIR/broken-footnote.rs:9:5 | -LL | //! Footnote referenced [^1]. And [^2]. And [^bla]. - | -^^^^^ - | | - | help: if it should not be a footnote, escape it: `\` +LL | //! [^*] special characters can appear within footnote references + | -^^^ + | | + | help: if it should not be a footnote, escape it: `\` | note: the lint level is defined here --> $DIR/broken-footnote.rs:1:9 @@ -20,5 +20,21 @@ 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 +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: `\` + +error: no footnote definition matching this footnote + --> $DIR/broken-footnote.rs:16:5 + | +LL | //! [^\_] so can escaped characters + | -^^^^ + | | + | help: if it should not be a footnote, escape it: `\` + +error: aborting due to 4 previous errors From c470ca8c009c5db5efc235ea7d7567cfd1fc20bf Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 12 Mar 2026 17:56:14 +0100 Subject: [PATCH 8/8] Improve suggestion for `broken_footnote` lint --- src/librustdoc/passes/lint/footnotes.rs | 2 +- tests/rustdoc-ui/lints/broken-footnote.stderr | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/librustdoc/passes/lint/footnotes.rs b/src/librustdoc/passes/lint/footnotes.rs index d55c8ef0da242..938a2fbbff03a 100644 --- a/src/librustdoc/passes/lint/footnotes.rs +++ b/src/librustdoc/passes/lint/footnotes.rs @@ -87,7 +87,7 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: & lint.span_suggestion( ref_span.shrink_to_lo(), "if it should not be a footnote, escape it", - "\\", + format!("\\{}", &dox[span]), Applicability::MaybeIncorrect, ); }), diff --git a/tests/rustdoc-ui/lints/broken-footnote.stderr b/tests/rustdoc-ui/lints/broken-footnote.stderr index f8c048839f55b..1ecc9f54f2cbd 100644 --- a/tests/rustdoc-ui/lints/broken-footnote.stderr +++ b/tests/rustdoc-ui/lints/broken-footnote.stderr @@ -4,7 +4,7 @@ error: no footnote definition matching this footnote LL | //! [^*] special characters can appear within footnote references | -^^^ | | - | help: if it should not be a footnote, escape it: `\` + | help: if it should not be a footnote, escape it: `\[^*]` | note: the lint level is defined here --> $DIR/broken-footnote.rs:1:9 @@ -18,7 +18,7 @@ error: no footnote definition matching this footnote LL | //! Footnote referenced [^1]. And [^2]. And [^bla]. | -^^^ | | - | help: if it should not be a footnote, escape it: `\` + | help: if it should not be a footnote, escape it: `\[^2]` error: no footnote definition matching this footnote --> $DIR/broken-footnote.rs:3:45 @@ -26,7 +26,7 @@ error: no footnote definition matching this footnote LL | //! Footnote referenced [^1]. And [^2]. And [^bla]. | -^^^^^ | | - | help: if it should not be a footnote, escape it: `\` + | help: if it should not be a footnote, escape it: `\[^bla]` error: no footnote definition matching this footnote --> $DIR/broken-footnote.rs:16:5 @@ -34,7 +34,7 @@ error: no footnote definition matching this footnote LL | //! [^\_] so can escaped characters | -^^^^ | | - | help: if it should not be a footnote, escape it: `\` + | help: if it should not be a footnote, escape it: `\[^\_]` error: aborting due to 4 previous errors