diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 87d88f707529..163d9f3a7102 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -454,6 +454,23 @@ fn lint_for_missing_headers( #[expect(clippy::cast_possible_truncation)] #[must_use] pub fn strip_doc_comment_decoration(doc: &str, comment_kind: CommentKind, span: Span) -> (String, Vec<(usize, Span)>) { + fn get_line_indentation(line: &Vec) -> u32 { + let mut indent = 0; + let mut smallest_indent = 0; + for c in line { + if *c == ' ' { + indent += 1; + } else { + if smallest_indent > indent { + smallest_indent = indent; + } + indent = 0; + continue; + } + } + indent + } + // one-line comments lose their prefix if comment_kind == CommentKind::Line { let mut doc = doc.to_owned(); @@ -465,15 +482,33 @@ pub fn strip_doc_comment_decoration(doc: &str, comment_kind: CommentKind, span: let mut sizes = vec![]; let mut contains_initial_stars = false; + let mut least_indented = 10; // (Hoping the document has less indentation than 10) for line in doc.lines() { let offset = line.as_ptr() as usize - doc.as_ptr() as usize; debug_assert_eq!(offset as u32 as usize, offset); + if !line.is_empty() { + // Empty lines don't have indentation + let line_indent = get_line_indentation(&line.chars().collect::>()); + if line_indent < least_indented { + least_indented = line_indent; + }; + }; contains_initial_stars |= line.trim_start().starts_with('*'); // +1 adds the newline, +3 skips the opening delimiter sizes.push((line.len() + 1, span.with_lo(span.lo() + BytePos(3 + offset as u32)))); } if !contains_initial_stars { - return (doc.to_string(), sizes); + let mut new_doc = Vec::new(); + for line in doc.lines() { + if line.len() >= least_indented as usize { + + new_doc.push(&line[least_indented as usize..]); // Sometimes users start the comment the same line the doc comment is defined. + // /** Example of this behaviour + // (Some description) + // */ + }; + } + return (new_doc.join("\n"), sizes); } // remove the initial '*'s if any let mut no_stars = String::with_capacity(doc.len()); diff --git a/tests/ui/doc_errors.rs b/tests/ui/doc_errors.rs index 30fdd3b08737..1d6cb454af72 100644 --- a/tests/ui/doc_errors.rs +++ b/tests/ui/doc_errors.rs @@ -1,4 +1,4 @@ -#![warn(clippy::missing_errors_doc)] +#![warn(clippy::missing_errors_doc, clippy::doc_markdown)] #![allow(clippy::result_unit_err)] #![allow(clippy::unnecessary_wraps)] @@ -63,6 +63,48 @@ impl Struct1 { unimplemented!(); } + /** + Before text + ``` + Indentation block + ``` + # Errors + Hi + */ + pub fn pub_method_with_block_errors_header() -> Result<(), ()> { + unimplemented!(); + } + + /** + This is not sufficiently documented. + */ + pub fn pub_method_missing_block_errors_header() -> Result<(), ()> { + unimplemented!(); + } + + /** + # Errors + This is sufficiently documented. + + This function is a test for this lint's capabilities with other + lints, such as `doc_markdown` + std::str; + */ + pub fn pub_method_block_errors_header_doc_markdown() -> Result<(), ()> { + unimplemented!(); + } + + /** + This is not sufficiently documented. + + This function is also a test for this lint's capabilities with other + lints, such as `doc_markdown` + std::str; + */ + pub fn pub_method_missing_block_errors_header_doc_markdown() -> Result<(), ()> { + unimplemented!(); + } + /// # Errors /// A description of the errors goes here. pub async fn async_pub_method_with_errors_header() -> Result<(), ()> { diff --git a/tests/ui/doc_errors.stderr b/tests/ui/doc_errors.stderr index d74f2dbfe1ba..c337e65d6c81 100644 --- a/tests/ui/doc_errors.stderr +++ b/tests/ui/doc_errors.stderr @@ -37,10 +37,22 @@ LL | pub async fn async_pub_method_missing_errors_header() -> Result<(), ()> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:85:5 + --> $DIR/doc_errors.rs:81:5 + | +LL | pub fn pub_method_missing_block_errors_header() -> Result<(), ()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: docs for function returning `Result` missing `# Errors` section + --> $DIR/doc_errors.rs:104:5 + | +LL | pub fn pub_method_missing_block_errors_header_doc_markdown() -> Result<(), ()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: docs for function returning `Result` missing `# Errors` section + --> $DIR/doc_errors.rs:127:5 | LL | fn trait_method_missing_errors_header() -> Result<(), ()>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 7 previous errors +error: aborting due to 9 previous errors diff --git a/tests/ui/missing_panics_doc.rs b/tests/ui/missing_panics_doc.rs index 0e1533fc1ab1..517aaefc2e17 100644 --- a/tests/ui/missing_panics_doc.rs +++ b/tests/ui/missing_panics_doc.rs @@ -112,6 +112,21 @@ pub fn todo() { todo!() } +/** +# Panics +A reason +*/ +pub fn panic_documented_in_block() { + panic!(); +} + +/** + +*/ +pub fn panic_undocumented_in_block() { + panic!(); +} + /// This is okay because it is private fn unwrap_private() { let result = Err("Hi"); diff --git a/tests/ui/missing_panics_doc.stderr b/tests/ui/missing_panics_doc.stderr index 3dbe2dfbd88f..e9d599cebf83 100644 --- a/tests/ui/missing_panics_doc.stderr +++ b/tests/ui/missing_panics_doc.stderr @@ -72,76 +72,88 @@ LL | assert_ne!(x, 0); | ^^^^^^^^^^^^^^^^ error: docs for function which may panic missing `# Panics` section - --> $DIR/missing_panics_doc.rs:151:5 + --> $DIR/missing_panics_doc.rs:126:1 + | +LL | pub fn panic_undocumented_in_block() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first possible panic found here + --> $DIR/missing_panics_doc.rs:127:5 + | +LL | panic!(); + | ^^^^^^^^ + +error: docs for function which may panic missing `# Panics` section + --> $DIR/missing_panics_doc.rs:166:5 | LL | pub fn option_unwrap(v: &[T]) -> &T { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: first possible panic found here - --> $DIR/missing_panics_doc.rs:153:9 + --> $DIR/missing_panics_doc.rs:168:9 | LL | o.unwrap() | ^^^^^^^^^^ error: docs for function which may panic missing `# Panics` section - --> $DIR/missing_panics_doc.rs:156:5 + --> $DIR/missing_panics_doc.rs:171:5 | LL | pub fn option_expect(v: &[T]) -> &T { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: first possible panic found here - --> $DIR/missing_panics_doc.rs:158:9 + --> $DIR/missing_panics_doc.rs:173:9 | LL | o.expect("passed an empty thing") | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: docs for function which may panic missing `# Panics` section - --> $DIR/missing_panics_doc.rs:161:5 + --> $DIR/missing_panics_doc.rs:176:5 | LL | pub fn result_unwrap(v: &[T]) -> &T { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: first possible panic found here - --> $DIR/missing_panics_doc.rs:163:9 + --> $DIR/missing_panics_doc.rs:178:9 | LL | res.unwrap() | ^^^^^^^^^^^^ error: docs for function which may panic missing `# Panics` section - --> $DIR/missing_panics_doc.rs:166:5 + --> $DIR/missing_panics_doc.rs:181:5 | LL | pub fn result_expect(v: &[T]) -> &T { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: first possible panic found here - --> $DIR/missing_panics_doc.rs:168:9 + --> $DIR/missing_panics_doc.rs:183:9 | LL | res.expect("passed an empty thing") | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: docs for function which may panic missing `# Panics` section - --> $DIR/missing_panics_doc.rs:171:5 + --> $DIR/missing_panics_doc.rs:186:5 | LL | pub fn last_unwrap(v: &[u32]) -> u32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: first possible panic found here - --> $DIR/missing_panics_doc.rs:172:10 + --> $DIR/missing_panics_doc.rs:187:10 | LL | *v.last().unwrap() | ^^^^^^^^^^^^^^^^^ error: docs for function which may panic missing `# Panics` section - --> $DIR/missing_panics_doc.rs:175:5 + --> $DIR/missing_panics_doc.rs:190:5 | LL | pub fn last_expect(v: &[u32]) -> u32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: first possible panic found here - --> $DIR/missing_panics_doc.rs:176:10 + --> $DIR/missing_panics_doc.rs:191:10 | LL | *v.last().expect("passed an empty thing") | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 12 previous errors +error: aborting due to 13 previous errors