11#![ allow( clippy:: lint_without_lint_pass) ]
22
3- mod lazy_continuation;
4- mod too_long_first_doc_paragraph;
5-
63use clippy_config:: Conf ;
74use clippy_utils:: attrs:: is_doc_hidden;
85use clippy_utils:: diagnostics:: { span_lint, span_lint_and_help, span_lint_and_then} ;
96use clippy_utils:: macros:: { is_panic, root_macro_call_first_node} ;
7+ use clippy_utils:: source:: snippet_opt;
108use clippy_utils:: ty:: is_type_diagnostic_item;
119use clippy_utils:: visitors:: Visitable ;
1210use clippy_utils:: { is_entrypoint_fn, is_trait_impl_item, method_chain_args} ;
@@ -33,12 +31,15 @@ use rustc_span::{Span, sym};
3331use std:: ops:: Range ;
3432use url:: Url ;
3533
34+ mod doc_comment_double_space_linebreaks;
3635mod include_in_doc_without_cfg;
36+ mod lazy_continuation;
3737mod link_with_quotes;
3838mod markdown;
3939mod missing_headers;
4040mod needless_doctest_main;
4141mod suspicious_doc_comments;
42+ mod too_long_first_doc_paragraph;
4243
4344declare_clippy_lint ! {
4445 /// ### What it does
@@ -567,6 +568,39 @@ declare_clippy_lint! {
567568 "link reference defined in list item or quote"
568569}
569570
571+ declare_clippy_lint ! {
572+ /// ### What it does
573+ /// Detects doc comment linebreaks that use double spaces to separate lines, instead of back-slash (`\`).
574+ ///
575+ /// ### Why is this bad?
576+ /// Double spaces, when used as doc comment linebreaks, can be difficult to see, and may
577+ /// accidentally be removed during automatic formatting or manual refactoring. The use of a back-slash (`\`)
578+ /// is clearer in this regard.
579+ ///
580+ /// ### Example
581+ /// The two replacement dots in this example represent a double space.
582+ /// ```no_run
583+ /// /// This command takes two numbers as inputs and··
584+ /// /// adds them together, and then returns the result.
585+ /// fn add(l: i32, r: i32) -> i32 {
586+ /// l + r
587+ /// }
588+ /// ```
589+ ///
590+ /// Use instead:
591+ /// ```no_run
592+ /// /// This command takes two numbers as inputs and\
593+ /// /// adds them together, and then returns the result.
594+ /// fn add(l: i32, r: i32) -> i32 {
595+ /// l + r
596+ /// }
597+ /// ```
598+ #[ clippy:: version = "1.87.0" ]
599+ pub DOC_COMMENT_DOUBLE_SPACE_LINEBREAKS ,
600+ pedantic,
601+ "double-space used for doc comment linebreak instead of `\\ `"
602+ }
603+
570604pub struct Documentation {
571605 valid_idents : FxHashSet < String > ,
572606 check_private_items : bool ,
@@ -598,6 +632,7 @@ impl_lint_pass!(Documentation => [
598632 DOC_OVERINDENTED_LIST_ITEMS ,
599633 TOO_LONG_FIRST_DOC_PARAGRAPH ,
600634 DOC_INCLUDE_WITHOUT_CFG ,
635+ DOC_COMMENT_DOUBLE_SPACE_LINEBREAKS
601636] ) ;
602637
603638impl EarlyLintPass for Documentation {
@@ -894,6 +929,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
894929 let mut paragraph_range = 0 ..0 ;
895930 let mut code_level = 0 ;
896931 let mut blockquote_level = 0 ;
932+ let mut collected_breaks: Vec < Span > = Vec :: new ( ) ;
897933 let mut is_first_paragraph = true ;
898934
899935 let mut containers = Vec :: new ( ) ;
@@ -1069,6 +1105,14 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
10691105 & containers[ ..] ,
10701106 ) ;
10711107 }
1108+
1109+ if let Some ( span) = fragments. span ( cx, range. clone ( ) )
1110+ && !span. from_expansion ( )
1111+ && let Some ( snippet) = snippet_opt ( cx, span)
1112+ && !snippet. trim ( ) . starts_with ( '\\' )
1113+ && event == HardBreak {
1114+ collected_breaks. push ( span) ;
1115+ }
10721116 } ,
10731117 Text ( text) => {
10741118 paragraph_range. end = range. end ;
@@ -1119,6 +1163,9 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
11191163 FootnoteReference ( _) => { }
11201164 }
11211165 }
1166+
1167+ doc_comment_double_space_linebreaks:: check ( cx, & collected_breaks) ;
1168+
11221169 headers
11231170}
11241171
0 commit comments