diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c9ea1140..76010d82c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5249,6 +5249,7 @@ Released 2018-09-13 [`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type [`disallowed_types`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types [`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression +[`doc_lazy_continuation`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_lazy_continuation [`doc_link_with_quotes`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_link_with_quotes [`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown [`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons diff --git a/clippy_lints/src/casts/cast_sign_loss.rs b/clippy_lints/src/casts/cast_sign_loss.rs index 2b6e17dc1..864489ee3 100644 --- a/clippy_lints/src/casts/cast_sign_loss.rs +++ b/clippy_lints/src/casts/cast_sign_loss.rs @@ -255,8 +255,10 @@ fn expr_add_sign(cx: &LateContext<'_>, expr: &Expr<'_>) -> Sign { /// Peels binary operators such as [`BinOpKind::Mul`], [`BinOpKind::Div`] or [`BinOpKind::Rem`], /// where the result depends on: +/// /// - the number of negative values in the entire expression, or /// - the number of negative values on the left hand side of the expression. +/// /// Ignores overflow. /// /// @@ -303,8 +305,10 @@ fn exprs_with_muldiv_binop_peeled<'e>(expr: &'e Expr<'_>) -> Vec<&'e Expr<'e>> { } /// Peels binary operators such as [`BinOpKind::Add`], where the result depends on: +/// /// - all the expressions being positive, or /// - all the expressions being negative. +/// /// Ignores overflow. /// /// Expressions using other operators are preserved, so we can try to evaluate them later. diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 5ff7d8e51..8a7e51fc7 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -140,6 +140,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::disallowed_names::DISALLOWED_NAMES_INFO, crate::disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS_INFO, crate::disallowed_types::DISALLOWED_TYPES_INFO, + crate::doc::DOC_LAZY_CONTINUATION_INFO, crate::doc::DOC_LINK_WITH_QUOTES_INFO, crate::doc::DOC_MARKDOWN_INFO, crate::doc::EMPTY_DOCS_INFO, diff --git a/clippy_lints/src/doc/lazy_continuation.rs b/clippy_lints/src/doc/lazy_continuation.rs new file mode 100644 index 000000000..38bc58a55 --- /dev/null +++ b/clippy_lints/src/doc/lazy_continuation.rs @@ -0,0 +1,95 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use itertools::Itertools; +use rustc_errors::{Applicability, SuggestionStyle}; +use rustc_lint::LateContext; +use rustc_span::{BytePos, Span}; +use std::ops::Range; + +use super::DOC_LAZY_CONTINUATION; + +fn map_container_to_text(c: &super::Container) -> &'static str { + match c { + super::Container::Blockquote => "> ", + // numbered list can have up to nine digits, plus the dot, plus four spaces on either side + super::Container::List(indent) => &" "[0..*indent], + } +} + +// TODO: Adjust the parameters as necessary +pub(super) fn check( + cx: &LateContext<'_>, + doc: &str, + range: Range, + mut span: Span, + containers: &[super::Container], +) { + if doc[range.clone()].contains('\t') { + // We don't do tab stops correctly. + return; + } + + let ccount = doc[range.clone()].chars().filter(|c| *c == '>').count(); + let blockquote_level = containers + .iter() + .filter(|c| matches!(c, super::Container::Blockquote)) + .count(); + let lcount = doc[range.clone()].chars().filter(|c| *c == ' ').count(); + let list_indentation = containers + .iter() + .map(|c| { + if let super::Container::List(indent) = c { + *indent + } else { + 0 + } + }) + .sum(); + if ccount < blockquote_level || lcount < list_indentation { + let msg = if ccount < blockquote_level { + "doc quote missing `>` marker" + } else { + "doc list item missing indentation" + }; + span_lint_and_then(cx, DOC_LAZY_CONTINUATION, span, msg, |diag| { + if ccount == 0 && blockquote_level == 0 { + // simpler suggestion style for indentation + let indent = list_indentation - lcount; + diag.span_suggestion_with_style( + span.shrink_to_hi(), + "indent this line", + std::iter::repeat(" ").take(indent).join(""), + Applicability::MaybeIncorrect, + SuggestionStyle::ShowAlways, + ); + diag.help("if this is supposed to be its own paragraph, add a blank line"); + return; + } + let mut doc_start_range = &doc[range]; + let mut suggested = String::new(); + for c in containers { + let text = map_container_to_text(c); + if doc_start_range.starts_with(text) { + doc_start_range = &doc_start_range[text.len()..]; + span = span + .with_lo(span.lo() + BytePos(u32::try_from(text.len()).expect("text is not 2**32 or bigger"))); + } else if matches!(c, super::Container::Blockquote) + && let Some(i) = doc_start_range.find('>') + { + doc_start_range = &doc_start_range[i + 1..]; + span = + span.with_lo(span.lo() + BytePos(u32::try_from(i).expect("text is not 2**32 or bigger") + 1)); + } else { + suggested.push_str(text); + } + } + diag.span_suggestion_with_style( + span, + "add markers to start of line", + suggested, + Applicability::MachineApplicable, + SuggestionStyle::ShowAlways, + ); + diag.help("if this not intended to be a quote at all, escape it with `\\>`"); + }); + } +} diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index 4bced104d..29ccf4fce 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -1,3 +1,4 @@ +mod lazy_continuation; use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; @@ -7,7 +8,7 @@ use clippy_utils::{is_entrypoint_fn, is_trait_impl_item, method_chain_args}; use pulldown_cmark::Event::{ Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text, }; -use pulldown_cmark::Tag::{BlockQuote, CodeBlock, Heading, Item, Link, Paragraph}; +use pulldown_cmark::Tag::{BlockQuote, CodeBlock, FootnoteDefinition, Heading, Item, Link, Paragraph}; use pulldown_cmark::{BrokenLink, CodeBlockKind, CowStr, Options}; use rustc_ast::ast::Attribute; use rustc_data_structures::fx::FxHashSet; @@ -362,6 +363,63 @@ declare_clippy_lint! { "docstrings exist but documentation is empty" } +declare_clippy_lint! { + /// ### What it does + /// + /// In CommonMark Markdown, the language used to write doc comments, a + /// paragraph nested within a list or block quote does not need any line + /// after the first one to be indented or marked. The specification calls + /// this a "lazy paragraph continuation." + /// + /// ### Why is this bad? + /// + /// This is easy to write but hard to read. Lazy continuations makes + /// unintended markers hard to see, and make it harder to deduce the + /// document's intended structure. + /// + /// ### Example + /// + /// This table is probably intended to have two rows, + /// but it does not. It has zero rows, and is followed by + /// a block quote. + /// ```no_run + /// /// Range | Description + /// /// ----- | ----------- + /// /// >= 1 | fully opaque + /// /// < 1 | partially see-through + /// fn set_opacity(opacity: f32) {} + /// ``` + /// + /// Fix it by escaping the marker: + /// ```no_run + /// /// Range | Description + /// /// ----- | ----------- + /// /// \>= 1 | fully opaque + /// /// < 1 | partially see-through + /// fn set_opacity(opacity: f32) {} + /// ``` + /// + /// This example is actually intended to be a list: + /// ```no_run + /// /// * Do nothing. + /// /// * Then do something. Whatever it is needs done, + /// /// it should be done right now. + /// # fn do_stuff() {} + /// ``` + /// + /// Fix it by indenting the list contents: + /// ```no_run + /// /// * Do nothing. + /// /// * Then do something. Whatever it is needs done, + /// /// it should be done right now. + /// # fn do_stuff() {} + /// ``` + #[clippy::version = "1.80.0"] + pub DOC_LAZY_CONTINUATION, + style, + "require every line of a paragraph to be indented and marked" +} + #[derive(Clone)] pub struct Documentation { valid_idents: FxHashSet, @@ -388,6 +446,7 @@ impl_lint_pass!(Documentation => [ UNNECESSARY_SAFETY_DOC, SUSPICIOUS_DOC_COMMENTS, EMPTY_DOCS, + DOC_LAZY_CONTINUATION, ]); impl<'tcx> LateLintPass<'tcx> for Documentation { @@ -551,6 +610,7 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[ cx, valid_idents, parser.into_offset_iter(), + &doc, Fragments { fragments: &fragments, doc: &doc, @@ -560,6 +620,11 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[ const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"]; +enum Container { + Blockquote, + List(usize), +} + /// Checks parsed documentation. /// This walks the "events" (think sections of markdown) produced by `pulldown_cmark`, /// so lints here will generally access that information. @@ -569,6 +634,7 @@ fn check_doc<'a, Events: Iterator, Range, valid_idents: &FxHashSet, events: Events, + doc: &str, fragments: Fragments<'_>, ) -> DocHeaders { // true if a safety header was found @@ -576,6 +642,7 @@ fn check_doc<'a, Events: Iterator, Range, Range { if tag.starts_with(", Range blockquote_level += 1, - End(BlockQuote) => blockquote_level -= 1, + Start(BlockQuote) => { + blockquote_level += 1; + containers.push(Container::Blockquote); + }, + End(BlockQuote) => { + blockquote_level -= 1; + containers.pop(); + }, Start(CodeBlock(ref kind)) => { in_code = true; if let CodeBlockKind::Fenced(lang) = kind { @@ -633,6 +710,13 @@ fn check_doc<'a, Events: Iterator, Range, Range, Range in_footnote_definition = true, + End(FootnoteDefinition(..)) => in_footnote_definition = false, Start(_tag) | End(_tag) => (), // We don't care about other tags - SoftBreak | HardBreak | TaskListMarker(_) | Code(_) | Rule => (), + SoftBreak | HardBreak => { + if !containers.is_empty() + && let Some((_next_event, next_range)) = events.peek() + && let Some(next_span) = fragments.span(cx, next_range.clone()) + && let Some(span) = fragments.span(cx, range.clone()) + && !in_footnote_definition + { + lazy_continuation::check( + cx, + doc, + range.end..next_range.start, + Span::new(span.hi(), next_span.lo(), span.ctxt(), span.parent()), + &containers[..], + ); + } + }, + TaskListMarker(_) | Code(_) | Rule => (), FootnoteReference(text) | Text(text) => { paragraph_range.end = range.end; ticks_unbalanced |= text.contains('`') && !in_code; diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs index 1eadc200b..e2ab44155 100644 --- a/clippy_lints/src/manual_clamp.rs +++ b/clippy_lints/src/manual_clamp.rs @@ -611,15 +611,22 @@ impl<'tcx> BinaryOp<'tcx> { /// The clamp meta pattern is a pattern shared between many (but not all) patterns. /// In summary, this pattern consists of two if statements that meet many criteria, +/// /// - binary operators that are one of [`>`, `<`, `>=`, `<=`]. +/// /// - Both binary statements must have a shared argument +/// /// - Which can appear on the left or right side of either statement +/// /// - The binary operators must define a finite range for the shared argument. To put this in /// the terms of Rust `std` library, the following ranges are acceptable +/// /// - `Range` /// - `RangeInclusive` +/// /// And all other range types are not accepted. For the purposes of `clamp` it's irrelevant /// whether the range is inclusive or not, the output is the same. +/// /// - The result of each if statement must be equal to the argument unique to that if statement. The /// result can not be the shared argument in either case. fn is_clamp_meta_pattern<'tcx>( diff --git a/clippy_lints/src/methods/iter_filter.rs b/clippy_lints/src/methods/iter_filter.rs index 12647ea1f..b93d51eac 100644 --- a/clippy_lints/src/methods/iter_filter.rs +++ b/clippy_lints/src/methods/iter_filter.rs @@ -126,15 +126,15 @@ enum FilterType { /// /// How this is done: /// 1. we know that this is invoked in a method call with `filter` as the method name via `mod.rs` -/// 2. we check that we are in a trait method. Therefore we are in an -/// `(x as Iterator).filter({filter_arg})` method call. +/// 2. we check that we are in a trait method. Therefore we are in an `(x as +/// Iterator).filter({filter_arg})` method call. /// 3. we check that the parent expression is not a map. This is because we don't want to lint /// twice, and we already have a specialized lint for that. /// 4. we check that the span of the filter does not contain a comment. /// 5. we get the type of the `Item` in the `Iterator`, and compare against the type of Option and -/// Result. +/// Result. /// 6. we finally check the contents of the filter argument to see if it is a call to `is_some` or -/// `is_ok`. +/// `is_ok`. /// 7. if all of the above are true, then we return the `FilterType` fn expression_type( cx: &LateContext<'_>, diff --git a/clippy_lints/src/methods/iter_kv_map.rs b/clippy_lints/src/methods/iter_kv_map.rs index 7c852a376..05e773861 100644 --- a/clippy_lints/src/methods/iter_kv_map.rs +++ b/clippy_lints/src/methods/iter_kv_map.rs @@ -12,8 +12,10 @@ use rustc_middle::ty; use rustc_span::{sym, Span}; /// lint use of: +/// /// - `hashmap.iter().map(|(_, v)| v)` /// - `hashmap.into_iter().map(|(_, v)| v)` +/// /// on `HashMaps` and `BTreeMaps` in std pub(super) fn check<'tcx>( diff --git a/clippy_lints/src/methods/utils.rs b/clippy_lints/src/methods/utils.rs index c50f24f82..34d7b9acb 100644 --- a/clippy_lints/src/methods/utils.rs +++ b/clippy_lints/src/methods/utils.rs @@ -120,6 +120,7 @@ fn pat_bindings(pat: &Pat<'_>) -> Vec { /// operations performed on `binding_hir_ids` are: /// * to take non-mutable references to them /// * to use them as non-mutable `&self` in method calls +/// /// If any of `binding_hir_ids` is used in any other way, then `clone_or_copy_needed` will be true /// when `CloneOrCopyVisitor` is done visiting. struct CloneOrCopyVisitor<'cx, 'tcx> { diff --git a/clippy_lints/src/needless_continue.rs b/clippy_lints/src/needless_continue.rs index 8b4a12bb7..b97cb4579 100644 --- a/clippy_lints/src/needless_continue.rs +++ b/clippy_lints/src/needless_continue.rs @@ -178,8 +178,7 @@ impl EarlyLintPass for NeedlessContinue { /// Given an expression, returns true if either of the following is true /// /// - The expression is a `continue` node. -/// - The expression node is a block with the first statement being a -/// `continue`. +/// - The expression node is a block with the first statement being a `continue`. fn needless_continue_in_else(else_expr: &ast::Expr, label: Option<&ast::Label>) -> bool { match else_expr.kind { ast::ExprKind::Block(ref else_block, _) => is_first_block_stmt_continue(else_block, label), diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 0ef732f92..1f7edabcf 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1513,15 +1513,18 @@ pub fn is_else_clause_in_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { } /// Checks whether the given `Expr` is a range equivalent to a `RangeFull`. +/// /// For the lower bound, this means that: /// - either there is none /// - or it is the smallest value that can be represented by the range's integer type +/// /// For the upper bound, this means that: /// - either there is none /// - or it is the largest value that can be represented by the range's integer type and is /// inclusive /// - or it is a call to some container's `len` method and is exclusive, and the range is passed to /// a method call on that same container (e.g. `v.drain(..v.len())`) +/// /// If the given `Expr` is not some kind of range, the function returns `false`. pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Option<&Path<'_>>) -> bool { let ty = cx.typeck_results().expr_ty(expr); diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index a4a6f8e15..69f593fe0 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -250,7 +250,7 @@ pub fn snippet<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow< /// - Applicability level `Unspecified` will never be changed. /// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`. /// - If the default value is used and the applicability level is `MachineApplicable`, change it to -/// `HasPlaceholders` +/// `HasPlaceholders` pub fn snippet_with_applicability<'a, T: LintContext>( cx: &T, span: Span, diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 8d6057272..1d49634af 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -68,8 +68,7 @@ impl<'a> Sugg<'a> { /// - Applicability level `Unspecified` will never be changed. /// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`. /// - If the default value is used and the applicability level is `MachineApplicable`, change it - /// to - /// `HasPlaceholders` + /// to `HasPlaceholders` pub fn hir_with_applicability( cx: &LateContext<'_>, expr: &hir::Expr<'_>, diff --git a/tests/ui/doc/doc_lazy_blockquote.fixed b/tests/ui/doc/doc_lazy_blockquote.fixed new file mode 100644 index 000000000..9877991f1 --- /dev/null +++ b/tests/ui/doc/doc_lazy_blockquote.fixed @@ -0,0 +1,47 @@ +#![warn(clippy::doc_lazy_continuation)] + +/// > blockquote with +/// > lazy continuation +//~^ ERROR: doc quote missing `>` marker +fn first() {} + +/// > blockquote with no +/// > lazy continuation +fn first_nowarn() {} + +/// > blockquote with no +/// +/// lazy continuation +fn two_nowarn() {} + +/// > nest here +/// > +/// > > nest here +/// > > lazy continuation +//~^ ERROR: doc quote missing `>` marker +fn two() {} + +/// > nest here +/// > +/// > > nest here +/// > > lazy continuation +//~^ ERROR: doc quote missing `>` marker +fn three() {} + +/// > * > nest here +/// > > lazy continuation +//~^ ERROR: doc quote missing `>` marker +fn four() {} + +/// > * > nest here +/// > > lazy continuation +//~^ ERROR: doc quote missing `>` marker +fn four_point_1() {} + +/// * > nest here lazy continuation +fn five() {} + +/// 1. > nest here +/// > lazy continuation (this results in strange indentation, but still works) +//~^ ERROR: doc quote missing `>` marker +fn six() {} diff --git a/tests/ui/doc/doc_lazy_blockquote.rs b/tests/ui/doc/doc_lazy_blockquote.rs new file mode 100644 index 000000000..587b2fdd5 --- /dev/null +++ b/tests/ui/doc/doc_lazy_blockquote.rs @@ -0,0 +1,47 @@ +#![warn(clippy::doc_lazy_continuation)] + +/// > blockquote with +/// lazy continuation +//~^ ERROR: doc quote missing `>` marker +fn first() {} + +/// > blockquote with no +/// > lazy continuation +fn first_nowarn() {} + +/// > blockquote with no +/// +/// lazy continuation +fn two_nowarn() {} + +/// > nest here +/// > +/// > > nest here +/// > lazy continuation +//~^ ERROR: doc quote missing `>` marker +fn two() {} + +/// > nest here +/// > +/// > > nest here +/// lazy continuation +//~^ ERROR: doc quote missing `>` marker +fn three() {} + +/// > * > nest here +/// lazy continuation +//~^ ERROR: doc quote missing `>` marker +fn four() {} + +/// > * > nest here +/// lazy continuation +//~^ ERROR: doc quote missing `>` marker +fn four_point_1() {} + +/// * > nest here lazy continuation +fn five() {} + +/// 1. > nest here +/// lazy continuation (this results in strange indentation, but still works) +//~^ ERROR: doc quote missing `>` marker +fn six() {} diff --git a/tests/ui/doc/doc_lazy_blockquote.stderr b/tests/ui/doc/doc_lazy_blockquote.stderr new file mode 100644 index 000000000..975184a01 --- /dev/null +++ b/tests/ui/doc/doc_lazy_blockquote.stderr @@ -0,0 +1,76 @@ +error: doc quote missing `>` marker + --> tests/ui/doc/doc_lazy_blockquote.rs:4:5 + | +LL | /// lazy continuation + | ^ + | + = help: if this not intended to be a quote at all, escape it with `\>` + = note: `-D clippy::doc-lazy-continuation` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::doc_lazy_continuation)]` +help: add markers to start of line + | +LL | /// > lazy continuation + | + + +error: doc quote missing `>` marker + --> tests/ui/doc/doc_lazy_blockquote.rs:20:5 + | +LL | /// > lazy continuation + | ^^ + | + = help: if this not intended to be a quote at all, escape it with `\>` +help: add markers to start of line + | +LL | /// > > lazy continuation + | + + +error: doc quote missing `>` marker + --> tests/ui/doc/doc_lazy_blockquote.rs:27:5 + | +LL | /// lazy continuation + | ^ + | + = help: if this not intended to be a quote at all, escape it with `\>` +help: add markers to start of line + | +LL | /// > > lazy continuation + | +++ + +error: doc quote missing `>` marker + --> tests/ui/doc/doc_lazy_blockquote.rs:32:5 + | +LL | /// lazy continuation + | ^ + | + = help: if this not intended to be a quote at all, escape it with `\>` +help: add markers to start of line + | +LL | /// > > lazy continuation + | +++++++ + +error: doc quote missing `>` marker + --> tests/ui/doc/doc_lazy_blockquote.rs:37:5 + | +LL | /// lazy continuation + | ^ + | + = help: if this not intended to be a quote at all, escape it with `\>` +help: add markers to start of line + | +LL | /// > > lazy continuation + | +++++ + +error: doc quote missing `>` marker + --> tests/ui/doc/doc_lazy_blockquote.rs:45:5 + | +LL | /// lazy continuation (this results in strange indentation, but still works) + | ^ + | + = help: if this not intended to be a quote at all, escape it with `\>` +help: add markers to start of line + | +LL | /// > lazy continuation (this results in strange indentation, but still works) + | + + +error: aborting due to 6 previous errors + diff --git a/tests/ui/doc/doc_lazy_list.fixed b/tests/ui/doc/doc_lazy_list.fixed new file mode 100644 index 000000000..e19371ea9 --- /dev/null +++ b/tests/ui/doc/doc_lazy_list.fixed @@ -0,0 +1,42 @@ +#![warn(clippy::doc_lazy_continuation)] + +/// 1. nest here +/// lazy continuation +//~^ ERROR: doc list item missing indentation +fn one() {} + +/// 1. first line +/// lazy list continuations don't make warnings with this lint +//~^ ERROR: doc list item missing indentation +/// because they don't have the +//~^ ERROR: doc list item missing indentation +fn two() {} + +/// - nest here +/// lazy continuation +//~^ ERROR: doc list item missing indentation +fn three() {} + +/// - first line +/// lazy list continuations don't make warnings with this lint +//~^ ERROR: doc list item missing indentation +/// because they don't have the +//~^ ERROR: doc list item missing indentation +fn four() {} + +/// - nest here +/// lazy continuation +//~^ ERROR: doc list item missing indentation +fn five() {} + +/// - - first line +/// this will warn on the lazy continuation +//~^ ERROR: doc list item missing indentation +/// and so should this +//~^ ERROR: doc list item missing indentation +fn six() {} + +/// - - first line +/// +/// this is not a lazy continuation +fn seven() {} diff --git a/tests/ui/doc/doc_lazy_list.rs b/tests/ui/doc/doc_lazy_list.rs new file mode 100644 index 000000000..fdfe9afaf --- /dev/null +++ b/tests/ui/doc/doc_lazy_list.rs @@ -0,0 +1,42 @@ +#![warn(clippy::doc_lazy_continuation)] + +/// 1. nest here +/// lazy continuation +//~^ ERROR: doc list item missing indentation +fn one() {} + +/// 1. first line +/// lazy list continuations don't make warnings with this lint +//~^ ERROR: doc list item missing indentation +/// because they don't have the +//~^ ERROR: doc list item missing indentation +fn two() {} + +/// - nest here +/// lazy continuation +//~^ ERROR: doc list item missing indentation +fn three() {} + +/// - first line +/// lazy list continuations don't make warnings with this lint +//~^ ERROR: doc list item missing indentation +/// because they don't have the +//~^ ERROR: doc list item missing indentation +fn four() {} + +/// - nest here +/// lazy continuation +//~^ ERROR: doc list item missing indentation +fn five() {} + +/// - - first line +/// this will warn on the lazy continuation +//~^ ERROR: doc list item missing indentation +/// and so should this +//~^ ERROR: doc list item missing indentation +fn six() {} + +/// - - first line +/// +/// this is not a lazy continuation +fn seven() {} diff --git a/tests/ui/doc/doc_lazy_list.stderr b/tests/ui/doc/doc_lazy_list.stderr new file mode 100644 index 000000000..d0f6279f6 --- /dev/null +++ b/tests/ui/doc/doc_lazy_list.stderr @@ -0,0 +1,112 @@ +error: doc list item missing indentation + --> tests/ui/doc/doc_lazy_list.rs:4:5 + | +LL | /// lazy continuation + | ^ + | + = help: if this is supposed to be its own paragraph, add a blank line + = note: `-D clippy::doc-lazy-continuation` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::doc_lazy_continuation)]` +help: indent this line + | +LL | /// lazy continuation + | +++ + +error: doc list item missing indentation + --> tests/ui/doc/doc_lazy_list.rs:9:5 + | +LL | /// lazy list continuations don't make warnings with this lint + | ^ + | + = help: if this is supposed to be its own paragraph, add a blank line +help: indent this line + | +LL | /// lazy list continuations don't make warnings with this lint + | +++ + +error: doc list item missing indentation + --> tests/ui/doc/doc_lazy_list.rs:11:5 + | +LL | /// because they don't have the + | ^ + | + = help: if this is supposed to be its own paragraph, add a blank line +help: indent this line + | +LL | /// because they don't have the + | +++ + +error: doc list item missing indentation + --> tests/ui/doc/doc_lazy_list.rs:16:5 + | +LL | /// lazy continuation + | ^ + | + = help: if this is supposed to be its own paragraph, add a blank line +help: indent this line + | +LL | /// lazy continuation + | ++++ + +error: doc list item missing indentation + --> tests/ui/doc/doc_lazy_list.rs:21:5 + | +LL | /// lazy list continuations don't make warnings with this lint + | ^ + | + = help: if this is supposed to be its own paragraph, add a blank line +help: indent this line + | +LL | /// lazy list continuations don't make warnings with this lint + | ++++ + +error: doc list item missing indentation + --> tests/ui/doc/doc_lazy_list.rs:23:5 + | +LL | /// because they don't have the + | ^ + | + = help: if this is supposed to be its own paragraph, add a blank line +help: indent this line + | +LL | /// because they don't have the + | ++++ + +error: doc list item missing indentation + --> tests/ui/doc/doc_lazy_list.rs:28:5 + | +LL | /// lazy continuation + | ^ + | + = help: if this is supposed to be its own paragraph, add a blank line +help: indent this line + | +LL | /// lazy continuation + | ++++ + +error: doc list item missing indentation + --> tests/ui/doc/doc_lazy_list.rs:33:5 + | +LL | /// this will warn on the lazy continuation + | ^ + | + = help: if this is supposed to be its own paragraph, add a blank line +help: indent this line + | +LL | /// this will warn on the lazy continuation + | ++++++ + +error: doc list item missing indentation + --> tests/ui/doc/doc_lazy_list.rs:35:5 + | +LL | /// and so should this + | ^^^^ + | + = help: if this is supposed to be its own paragraph, add a blank line +help: indent this line + | +LL | /// and so should this + | ++ + +error: aborting due to 9 previous errors +