doc_suspicious_footnotes: lint text that looks like a footnote (#14708)
changelog: [`doc_suspicious_footnotes`]: lint for text that looks like a footnote reference but has no definition This is an alternative to https://github.com/rust-lang/rust/pull/137803, meant to address the concerns about false positives. This lint only fires when the apparent footnote reference has a name that's made from pure ASCII digits. This choice is justified by running lintcheck on the top 200 crates, plus the clippy default set: 1. [I ran lintcheck](https://gist.github.com/notriddle/59072476c9c1fd569fee421270dad665) with a modded version of this lint that didn't check for digits only. It produced a false positive warning on a line in mdbook that had a regex, and no true positives at all. 2. [I also ran lintcheck](https://gist.github.com/notriddle/74eb8c9e1939b9f5c5549bf1d4fa238a) with a custom lint that fired on any valid footnote reference with a non-ascii-digit name. `cargo` uses one in its job_queue module, and that's all it found. cc @GuillaumeGomez
This commit is contained in:
commit
c90c7c494d
10 changed files with 714 additions and 2 deletions
|
|
@ -5736,6 +5736,7 @@ Released 2018-09-13
|
|||
[`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
|
||||
[`doc_nested_refdefs`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_nested_refdefs
|
||||
[`doc_overindented_list_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_overindented_list_items
|
||||
[`doc_suspicious_footnotes`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_suspicious_footnotes
|
||||
[`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons
|
||||
[`double_ended_iterator_last`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_ended_iterator_last
|
||||
[`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use
|
||||
|
|
|
|||
|
|
@ -119,6 +119,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::doc::DOC_MARKDOWN_INFO,
|
||||
crate::doc::DOC_NESTED_REFDEFS_INFO,
|
||||
crate::doc::DOC_OVERINDENTED_LIST_ITEMS_INFO,
|
||||
crate::doc::DOC_SUSPICIOUS_FOOTNOTES_INFO,
|
||||
crate::doc::EMPTY_DOCS_INFO,
|
||||
crate::doc::MISSING_ERRORS_DOC_INFO,
|
||||
crate::doc::MISSING_PANICS_DOC_INFO,
|
||||
|
|
|
|||
113
clippy_lints/src/doc/doc_suspicious_footnotes.rs
Normal file
113
clippy_lints/src/doc/doc_suspicious_footnotes.rs
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use rustc_ast::token::CommentKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{AttrStyle, Attribute};
|
||||
use rustc_lint::{LateContext, LintContext};
|
||||
use rustc_resolve::rustdoc::DocFragmentKind;
|
||||
|
||||
use std::ops::Range;
|
||||
|
||||
use super::{DOC_SUSPICIOUS_FOOTNOTES, Fragments};
|
||||
|
||||
pub fn check(cx: &LateContext<'_>, doc: &str, range: Range<usize>, fragments: &Fragments<'_>, attrs: &[Attribute]) {
|
||||
for i in doc[range.clone()]
|
||||
.bytes()
|
||||
.enumerate()
|
||||
.filter_map(|(i, c)| if c == b'[' { Some(i) } else { None })
|
||||
{
|
||||
let start = i + range.start;
|
||||
if doc.as_bytes().get(start + 1) == Some(&b'^')
|
||||
&& let Some(end) = all_numbers_upto_brace(doc, start + 2)
|
||||
&& doc.as_bytes().get(end) != Some(&b':')
|
||||
&& doc.as_bytes().get(start - 1) != Some(&b'\\')
|
||||
&& let Some(this_fragment) = {
|
||||
// the `doc` string contains all fragments concatenated together
|
||||
// figure out which one this suspicious footnote comes from
|
||||
let mut starting_position = 0;
|
||||
let mut found_fragment = fragments.fragments.last();
|
||||
for fragment in fragments.fragments {
|
||||
if start >= starting_position && start < starting_position + fragment.doc.as_str().len() {
|
||||
found_fragment = Some(fragment);
|
||||
break;
|
||||
}
|
||||
starting_position += fragment.doc.as_str().len();
|
||||
}
|
||||
found_fragment
|
||||
}
|
||||
{
|
||||
let span = fragments.span(cx, start..end).unwrap_or(this_fragment.span);
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
DOC_SUSPICIOUS_FOOTNOTES,
|
||||
span,
|
||||
"looks like a footnote ref, but has no matching footnote",
|
||||
|diag| {
|
||||
if this_fragment.kind == DocFragmentKind::SugaredDoc {
|
||||
let (doc_attr, (_, doc_attr_comment_kind)) = attrs
|
||||
.iter()
|
||||
.filter(|attr| attr.span().overlaps(this_fragment.span))
|
||||
.rev()
|
||||
.find_map(|attr| Some((attr, attr.doc_str_and_comment_kind()?)))
|
||||
.unwrap();
|
||||
let (to_add, terminator) = match (doc_attr_comment_kind, doc_attr.style()) {
|
||||
(CommentKind::Line, AttrStyle::Outer) => ("\n///\n/// ", ""),
|
||||
(CommentKind::Line, AttrStyle::Inner) => ("\n//!\n//! ", ""),
|
||||
(CommentKind::Block, AttrStyle::Outer) => ("\n/** ", " */"),
|
||||
(CommentKind::Block, AttrStyle::Inner) => ("\n/*! ", " */"),
|
||||
};
|
||||
diag.span_suggestion_verbose(
|
||||
doc_attr.span().shrink_to_hi(),
|
||||
"add footnote definition",
|
||||
format!(
|
||||
"{to_add}{label}: <!-- description -->{terminator}",
|
||||
label = &doc[start..end]
|
||||
),
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
} else {
|
||||
let is_file_include = cx
|
||||
.sess()
|
||||
.source_map()
|
||||
.span_to_snippet(this_fragment.span)
|
||||
.as_ref()
|
||||
.map(|vdoc| vdoc.trim())
|
||||
== Ok(doc);
|
||||
if is_file_include {
|
||||
// if this is a file include, then there's no quote marks
|
||||
diag.span_suggestion_verbose(
|
||||
this_fragment.span.shrink_to_hi(),
|
||||
"add footnote definition",
|
||||
format!("\n\n{label}: <!-- description -->", label = &doc[start..end],),
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
} else {
|
||||
// otherwise, we wrap in a string
|
||||
diag.span_suggestion_verbose(
|
||||
this_fragment.span,
|
||||
"add footnote definition",
|
||||
format!(
|
||||
"r#\"{doc}\n\n{label}: <!-- description -->\"#",
|
||||
doc = this_fragment.doc,
|
||||
label = &doc[start..end],
|
||||
),
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn all_numbers_upto_brace(text: &str, i: usize) -> Option<usize> {
|
||||
for (j, c) in text.as_bytes()[i..].iter().copied().enumerate().take(64) {
|
||||
if c == b']' && j != 0 {
|
||||
return Some(i + j + 1);
|
||||
}
|
||||
if !c.is_ascii_digit() || j >= 64 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
|
@ -25,6 +25,7 @@ use std::ops::Range;
|
|||
use url::Url;
|
||||
|
||||
mod doc_comment_double_space_linebreaks;
|
||||
mod doc_suspicious_footnotes;
|
||||
mod include_in_doc_without_cfg;
|
||||
mod lazy_continuation;
|
||||
mod link_with_quotes;
|
||||
|
|
@ -607,6 +608,37 @@ declare_clippy_lint! {
|
|||
"double-space used for doc comment linebreak instead of `\\`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects syntax that looks like a footnote reference.
|
||||
///
|
||||
/// Rustdoc footnotes are compatible with GitHub-Flavored Markdown (GFM).
|
||||
/// GFM does not parse a footnote reference unless its definition also
|
||||
/// exists. This lint checks for footnote references with missing
|
||||
/// definitions, unless it thinks you're writing a regex.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This probably means that a footnote was meant to exist,
|
||||
/// but was not written.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// /// This is not a footnote[^1], because no definition exists.
|
||||
/// fn my_fn() {}
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// /// This is a footnote[^1].
|
||||
/// ///
|
||||
/// /// [^1]: defined here
|
||||
/// fn my_fn() {}
|
||||
/// ```
|
||||
#[clippy::version = "1.88.0"]
|
||||
pub DOC_SUSPICIOUS_FOOTNOTES,
|
||||
suspicious,
|
||||
"looks like a link or footnote ref, but with no definition"
|
||||
}
|
||||
|
||||
pub struct Documentation {
|
||||
valid_idents: FxHashSet<String>,
|
||||
check_private_items: bool,
|
||||
|
|
@ -638,7 +670,8 @@ impl_lint_pass!(Documentation => [
|
|||
DOC_OVERINDENTED_LIST_ITEMS,
|
||||
TOO_LONG_FIRST_DOC_PARAGRAPH,
|
||||
DOC_INCLUDE_WITHOUT_CFG,
|
||||
DOC_COMMENT_DOUBLE_SPACE_LINEBREAKS
|
||||
DOC_COMMENT_DOUBLE_SPACE_LINEBREAKS,
|
||||
DOC_SUSPICIOUS_FOOTNOTES,
|
||||
]);
|
||||
|
||||
impl EarlyLintPass for Documentation {
|
||||
|
|
@ -825,6 +858,7 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[
|
|||
doc: &doc,
|
||||
fragments: &fragments,
|
||||
},
|
||||
attrs,
|
||||
))
|
||||
}
|
||||
|
||||
|
|
@ -905,6 +939,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
|||
events: Events,
|
||||
doc: &str,
|
||||
fragments: Fragments<'_>,
|
||||
attrs: &[Attribute],
|
||||
) -> DocHeaders {
|
||||
// true if a safety header was found
|
||||
let mut headers = DocHeaders::default();
|
||||
|
|
@ -1148,7 +1183,8 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
|||
// Don't check the text associated with external URLs
|
||||
continue;
|
||||
}
|
||||
text_to_check.push((text, range, code_level));
|
||||
text_to_check.push((text, range.clone(), code_level));
|
||||
doc_suspicious_footnotes::check(cx, doc, range, &fragments, attrs);
|
||||
}
|
||||
}
|
||||
FootnoteReference(_) => {}
|
||||
|
|
|
|||
186
tests/ui/doc_suspicious_footnotes.fixed
Normal file
186
tests/ui/doc_suspicious_footnotes.fixed
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
#![warn(clippy::doc_suspicious_footnotes)]
|
||||
#![allow(clippy::needless_raw_string_hashes)]
|
||||
//! This is not a footnote[^1].
|
||||
//!
|
||||
//! [^1]: <!-- description -->
|
||||
//~^ doc_suspicious_footnotes
|
||||
//!
|
||||
//! This is not a footnote[^either], but it doesn't warn.
|
||||
//!
|
||||
//! This is not a footnote\[^1], but it also doesn't warn.
|
||||
//!
|
||||
//! This is not a footnote[^1\], but it also doesn't warn.
|
||||
//!
|
||||
//! This is not a `footnote[^1]`, but it also doesn't warn.
|
||||
//!
|
||||
//! This is a footnote[^2].
|
||||
//!
|
||||
//! [^2]: hello world
|
||||
|
||||
/// This is not a footnote[^1].
|
||||
///
|
||||
/// [^1]: <!-- description -->
|
||||
//~^ doc_suspicious_footnotes
|
||||
///
|
||||
/// This is not a footnote[^either], but it doesn't warn.
|
||||
///
|
||||
/// This is not a footnote\[^1], but it also doesn't warn.
|
||||
///
|
||||
/// This is not a footnote[^1\], but it also doesn't warn.
|
||||
///
|
||||
/// This is not a `footnote[^1]`, but it also doesn't warn.
|
||||
///
|
||||
/// This is a footnote[^2].
|
||||
///
|
||||
/// [^2]: hello world
|
||||
pub fn footnotes() {
|
||||
// test code goes here
|
||||
}
|
||||
|
||||
pub struct Foo;
|
||||
#[rustfmt::skip]
|
||||
impl Foo {
|
||||
#[doc = r#"This is not a footnote[^1].
|
||||
|
||||
[^1]: <!-- description -->"#]
|
||||
//~^ doc_suspicious_footnotes
|
||||
#[doc = r#""#]
|
||||
#[doc = r#"This is not a footnote[^either], but it doesn't warn."#]
|
||||
#[doc = r#""#]
|
||||
#[doc = r#"This is not a footnote\[^1], but it also doesn't warn."#]
|
||||
#[doc = r#""#]
|
||||
#[doc = r#"This is not a footnote[^1\], but it also doesn't warn."#]
|
||||
#[doc = r#""#]
|
||||
#[doc = r#"This is not a `footnote[^1]`, but it also doesn't warn."#]
|
||||
#[doc = r#""#]
|
||||
#[doc = r#"This is a footnote[^2]."#]
|
||||
#[doc = r#""#]
|
||||
#[doc = r#"[^2]: hello world"#]
|
||||
pub fn footnotes() {
|
||||
// test code goes here
|
||||
}
|
||||
#[doc = r#"This is not a footnote[^1].
|
||||
|
||||
This is not a footnote[^either], but it doesn't warn.
|
||||
|
||||
This is not a footnote\[^1], but it also doesn't warn.
|
||||
|
||||
This is not a footnote[^1\], but it also doesn't warn.
|
||||
|
||||
This is not a `footnote[^1]`, but it also doesn't warn.
|
||||
|
||||
This is a footnote[^2].
|
||||
|
||||
[^2]: hello world
|
||||
|
||||
|
||||
[^1]: <!-- description -->"#]
|
||||
//~^^^^^^^^^^^^^^ doc_suspicious_footnotes
|
||||
pub fn footnotes2() {
|
||||
// test code goes here
|
||||
}
|
||||
#[cfg_attr(
|
||||
not(FALSE),
|
||||
doc = r#"This is not a footnote[^1].
|
||||
|
||||
This is not a footnote[^either], but it doesn't warn.
|
||||
|
||||
[^1]: <!-- description -->"#
|
||||
//~^ doc_suspicious_footnotes
|
||||
)]
|
||||
pub fn footnotes3() {
|
||||
// test code goes here
|
||||
}
|
||||
#[doc = "My footnote [^foot\note]"]
|
||||
pub fn footnote4() {
|
||||
// test code goes here
|
||||
}
|
||||
#[doc = "Hihi"]pub fn footnote5() {
|
||||
// test code goes here
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = r#"This is not a footnote[^1].
|
||||
|
||||
[^1]: <!-- description -->"#]
|
||||
//~^ doc_suspicious_footnotes
|
||||
#[doc = r""]
|
||||
#[doc = r"This is not a footnote[^either], but it doesn't warn."]
|
||||
#[doc = r""]
|
||||
#[doc = r"This is not a footnote\[^1], but it also doesn't warn."]
|
||||
#[doc = r""]
|
||||
#[doc = r"This is not a footnote[^1\], but it also doesn't warn."]
|
||||
#[doc = r""]
|
||||
#[doc = r"This is not a `footnote[^1]`, but it also doesn't warn."]
|
||||
#[doc = r""]
|
||||
#[doc = r"This is a footnote[^2]."]
|
||||
#[doc = r""]
|
||||
#[doc = r"[^2]: hello world"]
|
||||
pub fn footnotes_attrs() {
|
||||
// test code goes here
|
||||
}
|
||||
|
||||
pub mod multiline {
|
||||
/*!
|
||||
* This is not a footnote[^1]. //~ doc_suspicious_footnotes
|
||||
*
|
||||
* This is not a footnote\[^1], but it doesn't warn.
|
||||
*
|
||||
* This is a footnote[^2].
|
||||
*
|
||||
* These give weird results, but correct ones, so it works.
|
||||
*
|
||||
* [^2]: hello world
|
||||
*/
|
||||
/*! [^1]: <!-- description --> */
|
||||
/**
|
||||
* This is not a footnote[^1]. //~ doc_suspicious_footnotes
|
||||
*
|
||||
* This is not a footnote\[^1], but it doesn't warn.
|
||||
*
|
||||
* This is a footnote[^2].
|
||||
*
|
||||
* These give weird results, but correct ones, so it works.
|
||||
*
|
||||
* [^2]: hello world
|
||||
*/
|
||||
/** [^1]: <!-- description --> */
|
||||
pub fn foo() {}
|
||||
}
|
||||
|
||||
/// This is not a footnote [^1]
|
||||
///
|
||||
/// [^1]: <!-- description -->
|
||||
//~^ doc_suspicious_footnotes
|
||||
///
|
||||
/// This one is [^2]
|
||||
///
|
||||
/// [^2]: contents
|
||||
#[doc = r#"This is not a footnote [^3]
|
||||
|
||||
[^3]: <!-- description -->"#]
|
||||
//~^ doc_suspicious_footnotes
|
||||
#[doc = ""]
|
||||
#[doc = "This one is [^4]"]
|
||||
#[doc = ""]
|
||||
#[doc = "[^4]: contents"]
|
||||
pub struct MultiFragmentFootnote;
|
||||
|
||||
#[doc(inline)]
|
||||
/// This is not a footnote [^5]
|
||||
///
|
||||
/// [^5]: <!-- description -->
|
||||
//~^ doc_suspicious_footnotes
|
||||
///
|
||||
/// This one is [^6]
|
||||
///
|
||||
/// [^6]: contents
|
||||
#[doc = r#"This is not a footnote [^7]
|
||||
|
||||
[^7]: <!-- description -->"#]
|
||||
//~^ doc_suspicious_footnotes
|
||||
#[doc = ""]
|
||||
#[doc = "This one is [^8]"]
|
||||
#[doc = ""]
|
||||
#[doc = "[^8]: contents"]
|
||||
pub use MultiFragmentFootnote as OtherInlinedFootnote;
|
||||
162
tests/ui/doc_suspicious_footnotes.rs
Normal file
162
tests/ui/doc_suspicious_footnotes.rs
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
#![warn(clippy::doc_suspicious_footnotes)]
|
||||
#![allow(clippy::needless_raw_string_hashes)]
|
||||
//! This is not a footnote[^1].
|
||||
//~^ doc_suspicious_footnotes
|
||||
//!
|
||||
//! This is not a footnote[^either], but it doesn't warn.
|
||||
//!
|
||||
//! This is not a footnote\[^1], but it also doesn't warn.
|
||||
//!
|
||||
//! This is not a footnote[^1\], but it also doesn't warn.
|
||||
//!
|
||||
//! This is not a `footnote[^1]`, but it also doesn't warn.
|
||||
//!
|
||||
//! This is a footnote[^2].
|
||||
//!
|
||||
//! [^2]: hello world
|
||||
|
||||
/// This is not a footnote[^1].
|
||||
//~^ doc_suspicious_footnotes
|
||||
///
|
||||
/// This is not a footnote[^either], but it doesn't warn.
|
||||
///
|
||||
/// This is not a footnote\[^1], but it also doesn't warn.
|
||||
///
|
||||
/// This is not a footnote[^1\], but it also doesn't warn.
|
||||
///
|
||||
/// This is not a `footnote[^1]`, but it also doesn't warn.
|
||||
///
|
||||
/// This is a footnote[^2].
|
||||
///
|
||||
/// [^2]: hello world
|
||||
pub fn footnotes() {
|
||||
// test code goes here
|
||||
}
|
||||
|
||||
pub struct Foo;
|
||||
#[rustfmt::skip]
|
||||
impl Foo {
|
||||
#[doc = r#"This is not a footnote[^1]."#]
|
||||
//~^ doc_suspicious_footnotes
|
||||
#[doc = r#""#]
|
||||
#[doc = r#"This is not a footnote[^either], but it doesn't warn."#]
|
||||
#[doc = r#""#]
|
||||
#[doc = r#"This is not a footnote\[^1], but it also doesn't warn."#]
|
||||
#[doc = r#""#]
|
||||
#[doc = r#"This is not a footnote[^1\], but it also doesn't warn."#]
|
||||
#[doc = r#""#]
|
||||
#[doc = r#"This is not a `footnote[^1]`, but it also doesn't warn."#]
|
||||
#[doc = r#""#]
|
||||
#[doc = r#"This is a footnote[^2]."#]
|
||||
#[doc = r#""#]
|
||||
#[doc = r#"[^2]: hello world"#]
|
||||
pub fn footnotes() {
|
||||
// test code goes here
|
||||
}
|
||||
#[doc = "This is not a footnote[^1].
|
||||
|
||||
This is not a footnote[^either], but it doesn't warn.
|
||||
|
||||
This is not a footnote\\[^1], but it also doesn't warn.
|
||||
|
||||
This is not a footnote[^1\\], but it also doesn't warn.
|
||||
|
||||
This is not a `footnote[^1]`, but it also doesn't warn.
|
||||
|
||||
This is a footnote[^2].
|
||||
|
||||
[^2]: hello world
|
||||
"]
|
||||
//~^^^^^^^^^^^^^^ doc_suspicious_footnotes
|
||||
pub fn footnotes2() {
|
||||
// test code goes here
|
||||
}
|
||||
#[cfg_attr(
|
||||
not(FALSE),
|
||||
doc = "This is not a footnote[^1].\n\nThis is not a footnote[^either], but it doesn't warn."
|
||||
//~^ doc_suspicious_footnotes
|
||||
)]
|
||||
pub fn footnotes3() {
|
||||
// test code goes here
|
||||
}
|
||||
#[doc = "My footnote [^foot\note]"]
|
||||
pub fn footnote4() {
|
||||
// test code goes here
|
||||
}
|
||||
#[doc = "Hihi"]pub fn footnote5() {
|
||||
// test code goes here
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = r"This is not a footnote[^1]."]
|
||||
//~^ doc_suspicious_footnotes
|
||||
#[doc = r""]
|
||||
#[doc = r"This is not a footnote[^either], but it doesn't warn."]
|
||||
#[doc = r""]
|
||||
#[doc = r"This is not a footnote\[^1], but it also doesn't warn."]
|
||||
#[doc = r""]
|
||||
#[doc = r"This is not a footnote[^1\], but it also doesn't warn."]
|
||||
#[doc = r""]
|
||||
#[doc = r"This is not a `footnote[^1]`, but it also doesn't warn."]
|
||||
#[doc = r""]
|
||||
#[doc = r"This is a footnote[^2]."]
|
||||
#[doc = r""]
|
||||
#[doc = r"[^2]: hello world"]
|
||||
pub fn footnotes_attrs() {
|
||||
// test code goes here
|
||||
}
|
||||
|
||||
pub mod multiline {
|
||||
/*!
|
||||
* This is not a footnote[^1]. //~ doc_suspicious_footnotes
|
||||
*
|
||||
* This is not a footnote\[^1], but it doesn't warn.
|
||||
*
|
||||
* This is a footnote[^2].
|
||||
*
|
||||
* These give weird results, but correct ones, so it works.
|
||||
*
|
||||
* [^2]: hello world
|
||||
*/
|
||||
/**
|
||||
* This is not a footnote[^1]. //~ doc_suspicious_footnotes
|
||||
*
|
||||
* This is not a footnote\[^1], but it doesn't warn.
|
||||
*
|
||||
* This is a footnote[^2].
|
||||
*
|
||||
* These give weird results, but correct ones, so it works.
|
||||
*
|
||||
* [^2]: hello world
|
||||
*/
|
||||
pub fn foo() {}
|
||||
}
|
||||
|
||||
/// This is not a footnote [^1]
|
||||
//~^ doc_suspicious_footnotes
|
||||
///
|
||||
/// This one is [^2]
|
||||
///
|
||||
/// [^2]: contents
|
||||
#[doc = "This is not a footnote [^3]"]
|
||||
//~^ doc_suspicious_footnotes
|
||||
#[doc = ""]
|
||||
#[doc = "This one is [^4]"]
|
||||
#[doc = ""]
|
||||
#[doc = "[^4]: contents"]
|
||||
pub struct MultiFragmentFootnote;
|
||||
|
||||
#[doc(inline)]
|
||||
/// This is not a footnote [^5]
|
||||
//~^ doc_suspicious_footnotes
|
||||
///
|
||||
/// This one is [^6]
|
||||
///
|
||||
/// [^6]: contents
|
||||
#[doc = "This is not a footnote [^7]"]
|
||||
//~^ doc_suspicious_footnotes
|
||||
#[doc = ""]
|
||||
#[doc = "This one is [^8]"]
|
||||
#[doc = ""]
|
||||
#[doc = "[^8]: contents"]
|
||||
pub use MultiFragmentFootnote as OtherInlinedFootnote;
|
||||
179
tests/ui/doc_suspicious_footnotes.stderr
Normal file
179
tests/ui/doc_suspicious_footnotes.stderr
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
error: looks like a footnote ref, but has no matching footnote
|
||||
--> tests/ui/doc_suspicious_footnotes.rs:3:27
|
||||
|
|
||||
LL | //! This is not a footnote[^1].
|
||||
| ^^^^
|
||||
|
|
||||
= note: `-D clippy::doc-suspicious-footnotes` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::doc_suspicious_footnotes)]`
|
||||
help: add footnote definition
|
||||
|
|
||||
LL ~ //! This is not a footnote[^1].
|
||||
LL + //!
|
||||
LL + //! [^1]: <!-- description -->
|
||||
|
|
||||
|
||||
error: looks like a footnote ref, but has no matching footnote
|
||||
--> tests/ui/doc_suspicious_footnotes.rs:18:27
|
||||
|
|
||||
LL | /// This is not a footnote[^1].
|
||||
| ^^^^
|
||||
|
|
||||
help: add footnote definition
|
||||
|
|
||||
LL ~ /// This is not a footnote[^1].
|
||||
LL + ///
|
||||
LL + /// [^1]: <!-- description -->
|
||||
|
|
||||
|
||||
error: looks like a footnote ref, but has no matching footnote
|
||||
--> tests/ui/doc_suspicious_footnotes.rs:39:13
|
||||
|
|
||||
LL | #[doc = r#"This is not a footnote[^1]."#]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: add footnote definition
|
||||
|
|
||||
LL ~ #[doc = r#"This is not a footnote[^1].
|
||||
LL +
|
||||
LL ~ [^1]: <!-- description -->"#]
|
||||
|
|
||||
|
||||
error: looks like a footnote ref, but has no matching footnote
|
||||
--> tests/ui/doc_suspicious_footnotes.rs:56:13
|
||||
|
|
||||
LL | #[doc = "This is not a footnote[^1].
|
||||
| _____________^
|
||||
LL | |
|
||||
LL | | This is not a footnote[^either], but it doesn't warn.
|
||||
... |
|
||||
LL | | [^2]: hello world
|
||||
LL | | "]
|
||||
| |_____^
|
||||
|
|
||||
help: add footnote definition
|
||||
|
|
||||
LL ~ #[doc = r#"This is not a footnote[^1].
|
||||
LL +
|
||||
LL + This is not a footnote[^either], but it doesn't warn.
|
||||
LL +
|
||||
LL + This is not a footnote\[^1], but it also doesn't warn.
|
||||
LL +
|
||||
LL + This is not a footnote[^1\], but it also doesn't warn.
|
||||
LL +
|
||||
LL + This is not a `footnote[^1]`, but it also doesn't warn.
|
||||
LL +
|
||||
LL + This is a footnote[^2].
|
||||
LL +
|
||||
LL + [^2]: hello world
|
||||
LL +
|
||||
LL +
|
||||
LL ~ [^1]: <!-- description -->"#]
|
||||
|
|
||||
|
||||
error: looks like a footnote ref, but has no matching footnote
|
||||
--> tests/ui/doc_suspicious_footnotes.rs:76:38
|
||||
|
|
||||
LL | doc = "This is not a footnote[^1].\n\nThis is not a footnote[^either], but it doesn't warn."
|
||||
| ^^^^
|
||||
|
|
||||
help: add footnote definition
|
||||
|
|
||||
LL ~ doc = r#"This is not a footnote[^1].
|
||||
LL +
|
||||
LL + This is not a footnote[^either], but it doesn't warn.
|
||||
LL +
|
||||
LL + [^1]: <!-- description -->"#
|
||||
|
|
||||
|
||||
error: looks like a footnote ref, but has no matching footnote
|
||||
--> tests/ui/doc_suspicious_footnotes.rs:91:9
|
||||
|
|
||||
LL | #[doc = r"This is not a footnote[^1]."]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: add footnote definition
|
||||
|
|
||||
LL ~ #[doc = r#"This is not a footnote[^1].
|
||||
LL +
|
||||
LL ~ [^1]: <!-- description -->"#]
|
||||
|
|
||||
|
||||
error: looks like a footnote ref, but has no matching footnote
|
||||
--> tests/ui/doc_suspicious_footnotes.rs:111:30
|
||||
|
|
||||
LL | * This is not a footnote[^1].
|
||||
| ^^^^
|
||||
|
|
||||
help: add footnote definition
|
||||
|
|
||||
LL ~ */
|
||||
LL + /*! [^1]: <!-- description --> */
|
||||
|
|
||||
|
||||
error: looks like a footnote ref, but has no matching footnote
|
||||
--> tests/ui/doc_suspicious_footnotes.rs:122:30
|
||||
|
|
||||
LL | * This is not a footnote[^1].
|
||||
| ^^^^
|
||||
|
|
||||
help: add footnote definition
|
||||
|
|
||||
LL ~ */
|
||||
LL + /** [^1]: <!-- description --> */
|
||||
|
|
||||
|
||||
error: looks like a footnote ref, but has no matching footnote
|
||||
--> tests/ui/doc_suspicious_footnotes.rs:135:28
|
||||
|
|
||||
LL | /// This is not a footnote [^1]
|
||||
| ^^^^
|
||||
|
|
||||
help: add footnote definition
|
||||
|
|
||||
LL ~ /// This is not a footnote [^1]
|
||||
LL + ///
|
||||
LL + /// [^1]: <!-- description -->
|
||||
|
|
||||
|
||||
error: looks like a footnote ref, but has no matching footnote
|
||||
--> tests/ui/doc_suspicious_footnotes.rs:141:33
|
||||
|
|
||||
LL | #[doc = "This is not a footnote [^3]"]
|
||||
| ^^^^
|
||||
|
|
||||
help: add footnote definition
|
||||
|
|
||||
LL ~ #[doc = r#"This is not a footnote [^3]
|
||||
LL +
|
||||
LL ~ [^3]: <!-- description -->"#]
|
||||
|
|
||||
|
||||
error: looks like a footnote ref, but has no matching footnote
|
||||
--> tests/ui/doc_suspicious_footnotes.rs:150:28
|
||||
|
|
||||
LL | /// This is not a footnote [^5]
|
||||
| ^^^^
|
||||
|
|
||||
help: add footnote definition
|
||||
|
|
||||
LL ~ /// This is not a footnote [^5]
|
||||
LL + ///
|
||||
LL + /// [^5]: <!-- description -->
|
||||
|
|
||||
|
||||
error: looks like a footnote ref, but has no matching footnote
|
||||
--> tests/ui/doc_suspicious_footnotes.rs:156:33
|
||||
|
|
||||
LL | #[doc = "This is not a footnote [^7]"]
|
||||
| ^^^^
|
||||
|
|
||||
help: add footnote definition
|
||||
|
|
||||
LL ~ #[doc = r#"This is not a footnote [^7]
|
||||
LL +
|
||||
LL ~ [^7]: <!-- description -->"#]
|
||||
|
|
||||
|
||||
error: aborting due to 12 previous errors
|
||||
|
||||
4
tests/ui/doc_suspicious_footnotes_include.rs
Normal file
4
tests/ui/doc_suspicious_footnotes_include.rs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
//@ error-in-other-file: footnote
|
||||
//@ no-rustfix
|
||||
#![warn(clippy::doc_suspicious_footnotes)]
|
||||
#![doc=include_str!("doc_suspicious_footnotes_include.txt")]
|
||||
17
tests/ui/doc_suspicious_footnotes_include.stderr
Normal file
17
tests/ui/doc_suspicious_footnotes_include.stderr
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
error: looks like a footnote ref, but has no matching footnote
|
||||
--> tests/ui/doc_suspicious_footnotes_include.txt:1:23
|
||||
|
|
||||
LL | This is not a footnote[^1].
|
||||
| ^^^^
|
||||
|
|
||||
= note: `-D clippy::doc-suspicious-footnotes` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::doc_suspicious_footnotes)]`
|
||||
help: add footnote definition
|
||||
|
|
||||
LL ~ [^2]: hello world
|
||||
LL +
|
||||
LL + [^1]: <!-- description -->
|
||||
|
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
13
tests/ui/doc_suspicious_footnotes_include.txt
Normal file
13
tests/ui/doc_suspicious_footnotes_include.txt
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
This is not a footnote[^1]. //~ doc_suspicious_footnotes
|
||||
|
||||
This is not a footnote[^either], but it doesn't warn.
|
||||
|
||||
This is not a footnote\[^1], but it also doesn't warn.
|
||||
|
||||
This is not a footnote[^1\], but it also doesn't warn.
|
||||
|
||||
This is not a `footnote[^1]`, but it also doesn't warn.
|
||||
|
||||
This is a footnote[^2].
|
||||
|
||||
[^2]: hello world
|
||||
Loading…
Add table
Add a link
Reference in a new issue