Skip to content

Commit dfd6d0a

Browse files
undocumented_unsafe_blocks: Handle multi-line attributes between //SAFETY and unsafe
1 parent 8ff0cf8 commit dfd6d0a

File tree

2 files changed

+68
-14
lines changed

2 files changed

+68
-14
lines changed

clippy_lints/src/undocumented_unsafe_blocks.rs

Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -766,18 +766,11 @@ fn text_has_safety_comment(
766766
start_pos: BytePos,
767767
accept_comment_above_attributes: bool,
768768
) -> HasSafetyComment {
769-
let mut lines = line_starts
770-
.array_windows::<2>()
771-
.rev()
772-
.map_while(|[start, end]| {
773-
let start = start.to_usize();
774-
let end = end.to_usize();
775-
let text = src.get(start..end)?;
776-
let trimmed = text.trim_start();
777-
Some((start + (text.len() - trimmed.len()), trimmed))
778-
})
779-
.filter(|(_, text)| !(text.is_empty() || (accept_comment_above_attributes && is_attribute(text))));
780-
769+
let mut lines: Box<dyn Iterator<Item = (usize, &str)>> = if accept_comment_above_attributes {
770+
Box::new(reversed_lines_skipping_attributes(src, line_starts))
771+
} else {
772+
Box::new(reversed_lines(src, line_starts))
773+
};
781774
let Some((line_start, line)) = lines.next() else {
782775
return HasSafetyComment::No;
783776
};
@@ -835,8 +828,55 @@ fn text_has_safety_comment(
835828
}
836829
}
837830

838-
fn is_attribute(text: &str) -> bool {
839-
(text.starts_with("#[") || text.starts_with("#![")) && text.trim_end().ends_with(']')
831+
fn reversed_lines_skipping_attributes<'a>(
832+
src: &'a str,
833+
line_starts: &'a [RelativeBytePos],
834+
) -> impl Iterator<Item = (usize, &'a str)> + 'a {
835+
let lines = line_starts
836+
.array_windows::<2>()
837+
.filter_map(|[start, end]| process_line(src, *start, *end));
838+
839+
let mut buffer = vec![];
840+
let mut in_attribute = false;
841+
for (start, text) in lines {
842+
if in_attribute {
843+
in_attribute = !is_attribute_end(text);
844+
} else if is_attribute_start(text) {
845+
if !is_attribute_end(text) {
846+
in_attribute = true;
847+
}
848+
} else {
849+
buffer.push((start, text));
850+
}
851+
}
852+
buffer.into_iter().rev()
853+
}
854+
855+
fn is_attribute_start(text: &str) -> bool {
856+
text.starts_with("#[") || text.starts_with("#![")
857+
}
858+
859+
fn is_attribute_end(text: &str) -> bool {
860+
text.trim_end().ends_with(']')
861+
}
862+
863+
fn reversed_lines<'a>(src: &'a str, line_starts: &'a [RelativeBytePos]) -> impl Iterator<Item = (usize, &'a str)> + 'a {
864+
line_starts
865+
.array_windows::<2>()
866+
.rev()
867+
.filter_map(|[start, end]| process_line(src, *start, *end))
868+
}
869+
870+
fn process_line(src: &str, start: RelativeBytePos, end: RelativeBytePos) -> Option<(usize, &str)> {
871+
let start_idx = start.to_usize();
872+
let end_idx = end.to_usize();
873+
let text = src.get(start_idx..end_idx)?;
874+
let trimmed = text.trim_start();
875+
if trimmed.is_empty() {
876+
None
877+
} else {
878+
Some((start_idx + (text.len() - trimmed.len()), trimmed))
879+
}
840880
}
841881

842882
fn span_and_hid_of_item_alike_node(node: &Node<'_>) -> Option<(Span, HirId)> {
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//@ check-pass
2+
#![warn(clippy::undocumented_unsafe_blocks)]
3+
4+
unsafe fn f() {}
5+
6+
fn main() {
7+
// SAFETY: trust me
8+
#[allow(
9+
unused // can be any lint
10+
)]
11+
unsafe {
12+
f();
13+
}
14+
}

0 commit comments

Comments
 (0)