about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJacher <jwc2002@outlook.com>2024-06-01 15:44:27 +0000
committerxFrednet <xFrednet@gmail.com>2025-03-01 12:01:01 +0100
commitf94f64f5e84e5b55c39be795e4e2a8515bbca517 (patch)
tree76173d44f541a262fd75e5cc42b584ba74799fe9
parent1419ac2982e8c33496f896cc98c4bb4b14c94813 (diff)
downloadrust-f94f64f5e84e5b55c39be795e4e2a8515bbca517.tar.gz
rust-f94f64f5e84e5b55c39be795e4e2a8515bbca517.zip
new lint: `doc_comment_double_space_linebreak`
fix typo

change replacement character in example, remove extraneous space from suggested change

add additional testcases; check doc comment not from expansion

do not lint on macros, add more testcases

fix wording, remove commented out code, add additonal testcase

uibless

fix doc comments, use optional snippets

Remove unneeded additional space
-rw-r--r--CHANGELOG.md1
-rw-r--r--clippy_lints/src/declared_lints.rs1
-rw-r--r--clippy_lints/src/doc/doc_comment_double_space_linebreak.rs41
-rw-r--r--clippy_lints/src/doc/mod.rs49
-rw-r--r--tests/ui/doc/doc_comment_double_space_linebreak.fixed94
-rw-r--r--tests/ui/doc/doc_comment_double_space_linebreak.rs94
-rw-r--r--tests/ui/doc/doc_comment_double_space_linebreak.stderr76
7 files changed, 356 insertions, 0 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 51441ab9fc0..91608fb8d1e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5571,6 +5571,7 @@ Released 2018-09-13
 [`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_include_without_cfg`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_include_without_cfg
+[`doc_comment_double_space_linebreak`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_comment_double_space_linebreak
 [`doc_lazy_continuation`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_lazy_continuation
 [`doc_link_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_link_code
 [`doc_link_with_quotes`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_link_with_quotes
diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs
index 0834618499c..586ee22d9ef 100644
--- a/clippy_lints/src/declared_lints.rs
+++ b/clippy_lints/src/declared_lints.rs
@@ -138,6 +138,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
     crate::disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS_INFO,
     crate::disallowed_types::DISALLOWED_TYPES_INFO,
     crate::doc::DOC_INCLUDE_WITHOUT_CFG_INFO,
+    crate::doc::DOC_COMMENT_DOUBLE_SPACE_LINEBREAK_INFO,
     crate::doc::DOC_LAZY_CONTINUATION_INFO,
     crate::doc::DOC_LINK_CODE_INFO,
     crate::doc::DOC_LINK_WITH_QUOTES_INFO,
diff --git a/clippy_lints/src/doc/doc_comment_double_space_linebreak.rs b/clippy_lints/src/doc/doc_comment_double_space_linebreak.rs
new file mode 100644
index 00000000000..4c2475ad8d8
--- /dev/null
+++ b/clippy_lints/src/doc/doc_comment_double_space_linebreak.rs
@@ -0,0 +1,41 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet_opt;
+use rustc_errors::Applicability;
+use rustc_lint::LateContext;
+use rustc_span::Span;
+
+use super::DOC_COMMENT_DOUBLE_SPACE_LINEBREAK;
+
+pub fn check(cx: &LateContext<'_>, collected_breaks: &[Span]) {
+    let replacements: Vec<_> = collect_doc_replacements(cx, collected_breaks);
+
+    if let Some((&(lo_span, _), &(hi_span, _))) = replacements.first().zip(replacements.last()) {
+        span_lint_and_then(
+            cx,
+            DOC_COMMENT_DOUBLE_SPACE_LINEBREAK,
+            lo_span.to(hi_span),
+            "doc comment uses two spaces for a hard line break",
+            |diag| {
+                diag.multipart_suggestion(
+                    "replace this double space with a backslash",
+                    replacements,
+                    Applicability::MachineApplicable,
+                );
+            },
+        );
+    }
+}
+
+fn collect_doc_replacements(cx: &LateContext<'_>, spans: &[Span]) -> Vec<(Span, String)> {
+    spans
+        .iter()
+        .map(|span| {
+            // we already made sure the snippet exists when collecting spans
+            let s = snippet_opt(cx, *span).expect("snippet was already validated to exist");
+            let after_newline = s.trim_start_matches(' ');
+
+            let new_comment = format!("\\{after_newline}");
+            (*span, new_comment)
+        })
+        .collect()
+}
diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs
index c742d6abb5c..d36ef10f4b9 100644
--- a/clippy_lints/src/doc/mod.rs
+++ b/clippy_lints/src/doc/mod.rs
@@ -7,6 +7,7 @@ use clippy_config::Conf;
 use clippy_utils::attrs::is_doc_hidden;
 use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_then};
 use clippy_utils::macros::{is_panic, root_macro_call_first_node};
+use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::visitors::Visitable;
 use clippy_utils::{is_entrypoint_fn, is_trait_impl_item, method_chain_args};
@@ -33,6 +34,7 @@ use rustc_span::{Span, sym};
 use std::ops::Range;
 use url::Url;
 
+mod doc_comment_double_space_linebreak;
 mod include_in_doc_without_cfg;
 mod link_with_quotes;
 mod markdown;
@@ -567,6 +569,38 @@ declare_clippy_lint! {
     "link reference defined in list item or quote"
 }
 
+declare_clippy_lint! {
+    /// Detects doc comment linebreaks that use double spaces to separate lines, instead of back-slash (`\`).
+    ///
+    /// ### Why is this bad?
+    /// Double spaces, when used as doc comment linebreaks, can be difficult to see, and may
+    /// accidentally be removed during automatic formatting or manual refactoring. The use of a back-slash (`\`)
+    /// is clearer in this regard.
+    ///
+    /// ### Example
+    /// The two replacement dots in this example represent a double space.
+    /// ```no_run
+    /// /// This command takes two numbers as inputs andΒ·Β·
+    /// /// adds them together, and then returns the result.
+    /// fn add(l: i32, r: i32) -> i32 {
+    ///     l + r
+    /// }
+    /// ```
+    ///
+    /// Use instead:
+    /// ```no_run
+    /// /// This command takes two numbers as inputs and\
+    /// /// adds them together, and then returns the result.
+    /// fn add(l: i32, r: i32) -> i32 {
+    ///     l + r
+    /// }
+    /// ```
+    #[clippy::version = "1.80.0"]
+    pub DOC_COMMENT_DOUBLE_SPACE_LINEBREAK,
+    pedantic,
+    "double-space used for doc comment linebreak instead of `\\`"
+}
+
 pub struct Documentation {
     valid_idents: FxHashSet<String>,
     check_private_items: bool,
@@ -598,6 +632,7 @@ impl_lint_pass!(Documentation => [
     DOC_OVERINDENTED_LIST_ITEMS,
     TOO_LONG_FIRST_DOC_PARAGRAPH,
     DOC_INCLUDE_WITHOUT_CFG,
+    DOC_COMMENT_DOUBLE_SPACE_LINEBREAK
 ]);
 
 impl EarlyLintPass for Documentation {
@@ -737,6 +772,8 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[
         return None;
     }
 
+    suspicious_doc_comments::check(cx, attrs);
+
     let (fragments, _) = attrs_to_doc_fragments(
         attrs.iter().filter_map(|attr| {
             if attr.doc_str_and_comment_kind().is_none() || attr.span().in_external_macro(cx.sess().source_map()) {
@@ -894,6 +931,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
     let mut paragraph_range = 0..0;
     let mut code_level = 0;
     let mut blockquote_level = 0;
+    let mut collected_breaks: Vec<Span> = Vec::new();
     let mut is_first_paragraph = true;
 
     let mut containers = Vec::new();
@@ -1069,6 +1107,14 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
                         &containers[..],
                     );
                 }
+
+                if let Some(span) = fragments.span(cx, range.clone())
+                    && !span.from_expansion()
+                    && let Some(snippet) = snippet_opt(cx, span)
+                    && !snippet.trim().starts_with('\\')
+                    && event == HardBreak {
+                    collected_breaks.push(span);
+                }
             },
             Text(text) => {
                 paragraph_range.end = range.end;
@@ -1119,6 +1165,9 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
             FootnoteReference(_) => {}
         }
     }
+
+    doc_comment_double_space_linebreak::check(cx, &collected_breaks);
+
     headers
 }
 
diff --git a/tests/ui/doc/doc_comment_double_space_linebreak.fixed b/tests/ui/doc/doc_comment_double_space_linebreak.fixed
new file mode 100644
index 00000000000..efcd56809b7
--- /dev/null
+++ b/tests/ui/doc/doc_comment_double_space_linebreak.fixed
@@ -0,0 +1,94 @@
+#![feature(custom_inner_attributes)]
+#![rustfmt::skip]
+
+#![warn(clippy::doc_comment_double_space_linebreak)]
+#![allow(unused, clippy::empty_docs)]
+
+//! Should warn on double space linebreaks\
+//! in file/module doc comment
+
+/// Should not warn on single-line doc comments
+fn single_line() {}
+
+/// Should not warn on single-line doc comments
+/// split across multiple lines
+fn single_line_split() {}
+
+// Should not warn on normal comments
+
+// note: cargo fmt can remove double spaces from normal and block comments
+// Should not warn on normal comments  
+// with double spaces at the end of a line  
+
+#[doc = "This is a doc attribute, which should not be linted"]
+fn normal_comment() {
+    /*
+       Should not warn on block comments
+    */
+
+    /*
+        Should not warn on block comments  
+        with double space at the end of a line
+     */
+}
+
+/// Should warn when doc comment uses double space\
+/// as a line-break, even when there are multiple\
+/// in a row
+fn double_space_doc_comment() {}
+
+/// Should not warn when back-slash is used \
+/// as a line-break
+fn back_slash_doc_comment() {}
+
+/// 🌹 are πŸŸ₯\
+/// 🌷 are 🟦\
+/// πŸ“Ž is 😎\
+/// and so are 🫡\
+/// (hopefully no formatting weirdness linting this)
+fn multi_byte_chars_tada() {}
+
+macro_rules! macro_that_makes_function {
+   () => {
+      /// Shouldn't lint on this!  
+      /// (hopefully)
+      fn my_macro_created_function() {}
+   }
+}
+
+macro_that_makes_function!();
+
+// dont lint when its alone on a line
+///  
+fn alone() {}
+
+/// | First column | Second column |  
+/// | ------------ | ------------- |  
+/// | Not a line   | break when    |  
+/// | after a line | in a table    |  
+fn table() {}
+
+/// ```text  
+/// It's also not a hard line break if  
+/// there's two spaces at the end of a  
+/// line in a block code.  
+/// ```  
+fn codeblock() {}
+
+/// It's also not a hard line break `if  
+/// there's` two spaces in the middle of inline code.  
+fn inline() {}
+
+/// It's also not a hard line break [when](  
+/// https://example.com) in a URL.  
+fn url() {}
+
+/// here we mix\
+/// double spaces\
+/// and also\
+/// adding backslash\
+/// to some of them\
+/// to see how that looks
+fn mixed() {}
+
+fn main() {}
diff --git a/tests/ui/doc/doc_comment_double_space_linebreak.rs b/tests/ui/doc/doc_comment_double_space_linebreak.rs
new file mode 100644
index 00000000000..c64eedc2428
--- /dev/null
+++ b/tests/ui/doc/doc_comment_double_space_linebreak.rs
@@ -0,0 +1,94 @@
+#![feature(custom_inner_attributes)]
+#![rustfmt::skip]
+
+#![warn(clippy::doc_comment_double_space_linebreak)]
+#![allow(unused, clippy::empty_docs)]
+
+//! Should warn on double space linebreaks  
+//! in file/module doc comment
+
+/// Should not warn on single-line doc comments
+fn single_line() {}
+
+/// Should not warn on single-line doc comments
+/// split across multiple lines
+fn single_line_split() {}
+
+// Should not warn on normal comments
+
+// note: cargo fmt can remove double spaces from normal and block comments
+// Should not warn on normal comments  
+// with double spaces at the end of a line  
+
+#[doc = "This is a doc attribute, which should not be linted"]
+fn normal_comment() {
+    /*
+       Should not warn on block comments
+    */
+
+    /*
+        Should not warn on block comments  
+        with double space at the end of a line
+     */
+}
+
+/// Should warn when doc comment uses double space  
+/// as a line-break, even when there are multiple  
+/// in a row
+fn double_space_doc_comment() {}
+
+/// Should not warn when back-slash is used \
+/// as a line-break
+fn back_slash_doc_comment() {}
+
+/// 🌹 are πŸŸ₯  
+/// 🌷 are 🟦  
+/// πŸ“Ž is 😎  
+/// and so are 🫡  
+/// (hopefully no formatting weirdness linting this)
+fn multi_byte_chars_tada() {}
+
+macro_rules! macro_that_makes_function {
+   () => {
+      /// Shouldn't lint on this!  
+      /// (hopefully)
+      fn my_macro_created_function() {}
+   }
+}
+
+macro_that_makes_function!();
+
+// dont lint when its alone on a line
+///  
+fn alone() {}
+
+/// | First column | Second column |  
+/// | ------------ | ------------- |  
+/// | Not a line   | break when    |  
+/// | after a line | in a table    |  
+fn table() {}
+
+/// ```text  
+/// It's also not a hard line break if  
+/// there's two spaces at the end of a  
+/// line in a block code.  
+/// ```  
+fn codeblock() {}
+
+/// It's also not a hard line break `if  
+/// there's` two spaces in the middle of inline code.  
+fn inline() {}
+
+/// It's also not a hard line break [when](  
+/// https://example.com) in a URL.  
+fn url() {}
+
+/// here we mix  
+/// double spaces\
+/// and also  
+/// adding backslash\
+/// to some of them  
+/// to see how that looks
+fn mixed() {}
+
+fn main() {}
diff --git a/tests/ui/doc/doc_comment_double_space_linebreak.stderr b/tests/ui/doc/doc_comment_double_space_linebreak.stderr
new file mode 100644
index 00000000000..4c77f8e1503
--- /dev/null
+++ b/tests/ui/doc/doc_comment_double_space_linebreak.stderr
@@ -0,0 +1,76 @@
+error: doc comment uses two spaces for a hard line break
+  --> tests/ui/doc/doc_comment_double_space_linebreak.rs:7:43
+   |
+LL |   //! Should warn on double space linebreaks  
+   |  ___________________________________________^
+LL | | //! in file/module doc comment
+   | |____^
+   |
+   = note: `-D clippy::doc-comment-double-space-linebreak` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::doc_comment_double_space_linebreak)]`
+help: replace this double space with a backslash
+   |
+LL ~ //! Should warn on double space linebreaks\
+LL ~ //! in file/module doc comment
+   |
+
+error: doc comment uses two spaces for a hard line break
+  --> tests/ui/doc/doc_comment_double_space_linebreak.rs:35:51
+   |
+LL |   /// Should warn when doc comment uses double space  
+   |  ___________________________________________________^
+LL | | /// as a line-break, even when there are multiple  
+LL | | /// in a row
+   | |____^
+   |
+help: replace this double space with a backslash
+   |
+LL ~ /// Should warn when doc comment uses double space\
+LL ~ /// as a line-break, even when there are multiple\
+LL ~ /// in a row
+   |
+
+error: doc comment uses two spaces for a hard line break
+  --> tests/ui/doc/doc_comment_double_space_linebreak.rs:44:12
+   |
+LL |   /// 🌹 are πŸŸ₯  
+   |  ______________^
+LL | | /// 🌷 are 🟦  
+LL | | /// πŸ“Ž is 😎  
+LL | | /// and so are 🫡  
+LL | | /// (hopefully no formatting weirdness linting this)
+   | |____^
+   |
+help: replace this double space with a backslash
+   |
+LL ~ /// 🌹 are πŸŸ₯\
+LL ~ /// 🌷 are 🟦\
+LL ~ /// πŸ“Ž is 😎\
+LL ~ /// and so are 🫡\
+LL ~ /// (hopefully no formatting weirdness linting this)
+   |
+
+error: doc comment uses two spaces for a hard line break
+  --> tests/ui/doc/doc_comment_double_space_linebreak.rs:86:16
+   |
+LL |   /// here we mix  
+   |  ________________^
+LL | | /// double spaces\
+LL | | /// and also  
+LL | | /// adding backslash\
+LL | | /// to some of them  
+LL | | /// to see how that looks
+   | |____^
+   |
+help: replace this double space with a backslash
+   |
+LL ~ /// here we mix\
+LL ~ /// double spaces\
+LL ~ /// and also\
+LL ~ /// adding backslash\
+LL ~ /// to some of them\
+LL ~ /// to see how that looks
+   |
+
+error: aborting due to 4 previous errors
+