@@ -3,6 +3,7 @@ use clippy_config::Conf;
33use clippy_utils:: attrs:: is_doc_hidden;
44use clippy_utils:: diagnostics:: { span_lint, span_lint_and_help} ;
55use clippy_utils:: macros:: { is_panic, root_macro_call_first_node} ;
6+ use clippy_utils:: source:: snippet;
67use clippy_utils:: ty:: is_type_diagnostic_item;
78use clippy_utils:: visitors:: Visitable ;
89use clippy_utils:: { is_entrypoint_fn, is_trait_impl_item, method_chain_args} ;
@@ -27,9 +28,11 @@ use rustc_resolve::rustdoc::{
2728use rustc_session:: impl_lint_pass;
2829use rustc_span:: edition:: Edition ;
2930use rustc_span:: { sym, Span } ;
31+ use std:: borrow:: Cow ;
3032use std:: ops:: Range ;
3133use url:: Url ;
3234
35+ mod doc_comment_double_space_linebreak;
3336mod link_with_quotes;
3437mod markdown;
3538mod missing_headers;
@@ -422,6 +425,39 @@ declare_clippy_lint! {
422425 "require every line of a paragraph to be indented and marked"
423426}
424427
428+ declare_clippy_lint ! {
429+ /// ### What it does
430+ /// Detects doc comment linebreaks that use double spaces to separate lines, instead of back-slash (\).
431+ ///
432+ /// ### Why is this bad?
433+ /// Double spaces, when used as doc comment linebreaks, can be difficult to see, and may
434+ /// accidentally be removed during automatic fofmatting or manual refactoring. The use of a back-slash (\)
435+ /// is clearer in this regard.
436+ ///
437+ /// ### Example
438+ /// The two dots in this example represent a double space.
439+ /// ```no_run
440+ /// /// This command takes two numbers as inputs and..
441+ /// /// adds them together, and then returns the result.
442+ /// fn add(l: i32, r: i32) -> i32 {
443+ /// l + r
444+ /// }
445+ /// ``````
446+ ///
447+ /// Use instead:
448+ /// ```no_run
449+ /// /// This command takes two numbers as inputs and \
450+ /// /// adds them together, and then returns the result.
451+ /// fn add(l: i32, r: i32) -> i32 {
452+ /// l + r
453+ /// }
454+ /// ```
455+ #[ clippy:: version = "1.80.0" ]
456+ pub DOC_COMMENT_DOUBLE_SPACE_LINEBREAK ,
457+ restriction,
458+ "double-space used for doc comment linebreak instead of `\\ `"
459+ }
460+
425461pub struct Documentation {
426462 valid_idents : & ' static FxHashSet < String > ,
427463 check_private_items : bool ,
@@ -448,6 +484,7 @@ impl_lint_pass!(Documentation => [
448484 SUSPICIOUS_DOC_COMMENTS ,
449485 EMPTY_DOCS ,
450486 DOC_LAZY_CONTINUATION ,
487+ DOC_COMMENT_DOUBLE_SPACE_LINEBREAK
451488] ) ;
452489
453490impl < ' tcx > LateLintPass < ' tcx > for Documentation {
@@ -569,6 +606,7 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[
569606 return None ;
570607 }
571608
609+ //doc_comment_double_space_linebreak::check(cx, attrs);
572610 suspicious_doc_comments:: check ( cx, attrs) ;
573611
574612 let ( fragments, _) = attrs_to_doc_fragments (
@@ -653,6 +691,8 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
653691 let mut paragraph_range = 0 ..0 ;
654692 let mut code_level = 0 ;
655693 let mut blockquote_level = 0 ;
694+ let mut prev_text: Option < ( Span , CowStr < ' _ > ) > = None ;
695+ let mut collected_breaks: Vec < ( Span , ( Span , CowStr < ' _ > ) , Cow < ' _ , str > ) > = Vec :: new ( ) ;
656696
657697 let mut containers = Vec :: new ( ) ;
658698
@@ -767,8 +807,20 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
767807 span,
768808 ) ;
769809 }
810+
811+ if let Some ( span) = fragments. span ( cx, range. clone ( ) )
812+ && let Some ( ref p) = prev_text
813+ && let snippet = snippet ( cx, span, ".." )
814+ && !snippet. trim ( ) . starts_with ( "\\ " )
815+ && event == HardBreak {
816+ collected_breaks. push ( ( span, p. clone ( ) , snippet) ) ;
817+ prev_text = None ;
818+ }
770819 } ,
771820 Text ( text) => {
821+ if let Some ( span) = fragments. span ( cx, range. clone ( ) ) {
822+ prev_text = Some ( ( span, text. clone ( ) ) ) ;
823+ }
772824 paragraph_range. end = range. end ;
773825 let range_ = range. clone ( ) ;
774826 ticks_unbalanced |= text. contains ( '`' )
@@ -816,6 +868,9 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
816868 FootnoteReference ( _) => { }
817869 }
818870 }
871+
872+ doc_comment_double_space_linebreak:: check ( cx, collected_breaks) ;
873+
819874 headers
820875}
821876
0 commit comments