diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index 9afa9d65c261..ac840f1013c2 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -766,18 +766,11 @@ fn text_has_safety_comment( start_pos: BytePos, accept_comment_above_attributes: bool, ) -> HasSafetyComment { - let mut lines = line_starts - .array_windows::<2>() - .rev() - .map_while(|[start, end]| { - let start = start.to_usize(); - let end = end.to_usize(); - let text = src.get(start..end)?; - let trimmed = text.trim_start(); - Some((start + (text.len() - trimmed.len()), trimmed)) - }) - .filter(|(_, text)| !(text.is_empty() || (accept_comment_above_attributes && is_attribute(text)))); - + let mut lines: Box> = if accept_comment_above_attributes { + Box::new(reversed_lines_skipping_attributes(src, line_starts)) + } else { + Box::new(reversed_lines(src, line_starts)) + }; let Some((line_start, line)) = lines.next() else { return HasSafetyComment::No; }; @@ -835,8 +828,55 @@ fn text_has_safety_comment( } } -fn is_attribute(text: &str) -> bool { - (text.starts_with("#[") || text.starts_with("#![")) && text.trim_end().ends_with(']') +fn reversed_lines_skipping_attributes<'a>( + src: &'a str, + line_starts: &'a [RelativeBytePos], +) -> impl Iterator + 'a { + let lines = line_starts + .array_windows::<2>() + .filter_map(|[start, end]| process_line(src, *start, *end)); + + let mut buffer = vec![]; + let mut in_attribute = false; + for (start, text) in lines { + if in_attribute { + in_attribute = !is_attribute_end(text); + } else if is_attribute_start(text) { + if !is_attribute_end(text) { + in_attribute = true; + } + } else { + buffer.push((start, text)); + } + } + buffer.into_iter().rev() +} + +fn is_attribute_start(text: &str) -> bool { + text.starts_with("#[") || text.starts_with("#![") +} + +fn is_attribute_end(text: &str) -> bool { + text.trim_end().ends_with(']') +} + +fn reversed_lines<'a>(src: &'a str, line_starts: &'a [RelativeBytePos]) -> impl Iterator + 'a { + line_starts + .array_windows::<2>() + .rev() + .filter_map(|[start, end]| process_line(src, *start, *end)) +} + +fn process_line(src: &str, start: RelativeBytePos, end: RelativeBytePos) -> Option<(usize, &str)> { + let start_idx = start.to_usize(); + let end_idx = end.to_usize(); + let text = src.get(start_idx..end_idx)?; + let trimmed = text.trim_start(); + if trimmed.is_empty() { + None + } else { + Some((start_idx + (text.len() - trimmed.len()), trimmed)) + } } fn span_and_hid_of_item_alike_node(node: &Node<'_>) -> Option<(Span, HirId)> { diff --git a/tests/ui/undocumented_unsafe_blocks_multi_line.rs b/tests/ui/undocumented_unsafe_blocks_multi_line.rs new file mode 100644 index 000000000000..9d61ec2f254b --- /dev/null +++ b/tests/ui/undocumented_unsafe_blocks_multi_line.rs @@ -0,0 +1,14 @@ +//@ check-pass +#![warn(clippy::undocumented_unsafe_blocks)] + +unsafe fn f() {} + +fn main() { + // SAFETY: trust me + #[allow( + unused // can be any lint + )] + unsafe { + f(); + } +}