diff options
| -rw-r--r-- | clippy_lints/src/doc.rs | 22 | ||||
| -rw-r--r-- | tests/ui/needless_doc_main.rs | 36 | ||||
| -rw-r--r-- | tests/ui/needless_doc_main.stderr | 14 |
3 files changed, 65 insertions, 7 deletions
diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 35578ae68a4..754d38f4015 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -367,6 +367,8 @@ fn check_attrs<'a>(cx: &LateContext<'_, '_>, valid_idents: &FxHashSet<String>, a check_doc(cx, valid_idents, events, &spans) } +const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail", "edition2018"]; + fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize>)>>( cx: &LateContext<'_, '_>, valid_idents: &FxHashSet<String>, @@ -374,6 +376,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize spans: &[(usize, Span)], ) -> DocHeaders { // true if a safety header was found + use pulldown_cmark::CodeBlockKind; use pulldown_cmark::Event::{ Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text, }; @@ -386,11 +389,20 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize let mut in_code = false; let mut in_link = None; let mut in_heading = false; - + let mut is_rust = false; for (event, range) in events { match event { - Start(CodeBlock(_)) => in_code = true, - End(CodeBlock(_)) => in_code = false, + Start(CodeBlock(ref kind)) => { + in_code = true; + if let CodeBlockKind::Fenced(lang) = kind { + is_rust = + lang.is_empty() || !lang.contains("ignore") && lang.split(',').any(|i| RUST_CODE.contains(&i)); + } + }, + End(CodeBlock(_)) => { + in_code = false; + is_rust = false; + }, Start(Link(_, url, _)) => in_link = Some(url), End(Link(..)) => in_link = None, Start(Heading(_)) => in_heading = true, @@ -413,7 +425,9 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize }; let (begin, span) = spans[index]; if in_code { - check_code(cx, &text, span); + if is_rust { + check_code(cx, &text, span); + } } else { // Adjust for the beginning of the current `Event` let span = span.with_lo(span.lo() + BytePos::from_usize(range.start - begin)); diff --git a/tests/ui/needless_doc_main.rs b/tests/ui/needless_doc_main.rs index 813d2606153..682d7b3c4ce 100644 --- a/tests/ui/needless_doc_main.rs +++ b/tests/ui/needless_doc_main.rs @@ -8,7 +8,23 @@ /// unimplemented!(); /// } /// ``` -fn bad_doctest() {} +/// +/// This should, too. +/// +/// ```rust +/// fn main() { +/// unimplemented!(); +/// } +/// ``` +/// +/// This one too. +/// +/// ```no_run +/// fn main() { +/// unimplemented!(); +/// } +/// ``` +fn bad_doctests() {} /// # Examples /// @@ -34,9 +50,25 @@ fn bad_doctest() {} /// assert_eq(1u8, test::black_box(1)); /// } /// ``` +/// +/// We should not lint ignored examples: +/// +/// ```rust,ignore +/// fn main() { +/// unimplemented!(); +/// } +/// ``` +/// +/// Or even non-rust examples: +/// +/// ```text +/// fn main() { +/// is what starts the program +/// } +/// ``` fn no_false_positives() {} fn main() { - bad_doctest(); + bad_doctests(); no_false_positives(); } diff --git a/tests/ui/needless_doc_main.stderr b/tests/ui/needless_doc_main.stderr index 22d145aad58..65d40ee6832 100644 --- a/tests/ui/needless_doc_main.stderr +++ b/tests/ui/needless_doc_main.stderr @@ -6,5 +6,17 @@ LL | /// fn main() { | = note: `-D clippy::needless-doctest-main` implied by `-D warnings` -error: aborting due to previous error +error: needless `fn main` in doctest + --> $DIR/needless_doc_main.rs:15:4 + | +LL | /// fn main() { + | ^^^^^^^^^^^^ + +error: needless `fn main` in doctest + --> $DIR/needless_doc_main.rs:23:4 + | +LL | /// fn main() { + | ^^^^^^^^^^^^ + +error: aborting due to 3 previous errors |
