Skip to content

Commit a76bb59

Browse files
committed
Allow commented blocks in locals, fix formatting
1 parent 59d93c5 commit a76bb59

File tree

3 files changed

+198
-136
lines changed

3 files changed

+198
-136
lines changed
Lines changed: 142 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
1+
use clippy_utils::diagnostics::span_lint_and_sugg;
22
use clippy_utils::source::{snippet, indent_of, reindent_multiline};
33
use rustc_errors::Applicability;
44
use rustc_hir::{Block, BlockCheckMode, Expr, ExprKind, HirId, Local, MatchSource, UnsafeSource, YieldSource};
@@ -47,23 +47,37 @@ impl_lint_pass!(UndocumentedUnsafeBlocks => [UNDOCUMENTED_UNSAFE_BLOCKS]);
4747
#[derive(Default)]
4848
pub struct UndocumentedUnsafeBlocks {
4949
pub local_level: u32,
50-
}
51-
52-
enum ErrorKind {
53-
NoComment,
54-
Placeholder
50+
pub local_span: Option<Span>,
51+
// The local was already checked for an overall safety comment
52+
// There is no need to continue checking the blocks in the local
53+
pub local_checked: bool,
5554
}
5655

5756
impl LateLintPass<'_> for UndocumentedUnsafeBlocks {
5857
fn check_block(&mut self, cx: &LateContext<'_>, block: &'_ Block<'_>) {
5958
if_chain! {
60-
if self.local_level == 0;
59+
if !self.local_checked;
6160
if !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, block.hir_id);
6261
if !in_external_macro(cx.tcx.sess, block.span);
6362
if let BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) = block.rules;
6463
if let Some(enclosing_scope_hir_id) = cx.tcx.hir().get_enclosing_scope(block.hir_id);
6564
then {
66-
find_candidate(cx, block.span, enclosing_scope_hir_id)
65+
if block_has_safety_comment(cx.tcx, enclosing_scope_hir_id, block.span) == Some(false) {
66+
let mut span = block.span;
67+
68+
if let Some(local_span) = self.local_span {
69+
span = local_span;
70+
71+
let result = block_has_safety_comment(cx.tcx, enclosing_scope_hir_id, span);
72+
73+
if result == Some(true) || result.is_none() {
74+
self.local_checked = true;
75+
return;
76+
}
77+
}
78+
79+
err(cx, span);
80+
}
6781
}
6882
}
6983
}
@@ -75,103 +89,131 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlocks {
7589
if let Some(init) = local.init;
7690
if let Some((block, element_count)) = find_block_in_expr(&init.kind);
7791
if let BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) = block.rules;
78-
if let Some(enclosing_scope_hir_id) = cx.tcx.hir().get_enclosing_scope(block.hir_id);
92+
if cx.tcx.hir().get_enclosing_scope(block.hir_id).is_some();
7993
then {
8094
self.local_level = self.local_level.saturating_add(element_count);
81-
82-
find_candidate(cx, local.span, enclosing_scope_hir_id)
95+
self.local_span = Some(local.span);
8396
}
8497
}
8598
}
8699

87100
fn check_block_post(&mut self, _: &LateContext<'_>, _: &'_ Block<'_>) {
88101
self.local_level = self.local_level.saturating_sub(1);
102+
103+
if self.local_level == 0 {
104+
self.local_checked = false;
105+
self.local_span = None;
106+
}
89107
}
90108
}
91109

92110
fn find_block_in_expr(expr_kind: &ExprKind<'hir>) -> Option<(&'tcx Block<'hir>, u32)> {
93111
match expr_kind {
94-
ExprKind::Array(elements) | ExprKind::Tup(elements) => {
95-
let unsafe_blocks = elements.iter().filter_map(|e|
96-
match e {
97-
Expr{
98-
kind: ExprKind::Block(block @ Block {
99-
rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided),
100-
..
101-
}, _),
112+
ExprKind::Array(elements) | ExprKind::Tup(elements) => {
113+
let unsafe_blocks = elements
114+
.iter()
115+
.filter_map(|e| match e {
116+
Expr {
117+
kind:
118+
ExprKind::Block(
119+
block
120+
@
121+
Block {
122+
rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided),
123+
..
124+
},
125+
_,
126+
),
102127
..
103128
} => Some(block),
104-
_ => None
105-
}).collect::<Vec<_>>();
129+
_ => None,
130+
})
131+
.collect::<Vec<_>>();
106132

107133
if let Some(block) = unsafe_blocks.get(0) {
108-
return Some((block, unsafe_blocks.len().try_into().unwrap()))
134+
return Some((block, unsafe_blocks.len().try_into().unwrap()));
109135
}
110136

111137
None
112-
},
138+
}
113139
ExprKind::Box(Expr {
114140
kind: ExprKind::Block(block, _),
115141
..
116-
}) |
117-
ExprKind::Unary(_, Expr {
118-
kind: ExprKind::Block(block, _),
119-
..
120-
}) | ExprKind::Match(Expr {
121-
kind: ExprKind::Block(block, _),
122-
..
123-
}, _, MatchSource::Normal) | ExprKind::Loop(block, _, _, _) | ExprKind::Block(block, _) | ExprKind::AddrOf(_, _, Expr {
124-
kind: ExprKind::Block(block, _),
125-
..
126-
}) | ExprKind::Break(_, Some(Expr {
127-
kind: ExprKind::Block(block, _),
128-
..
129-
})) | ExprKind::Ret(Some(Expr {
130-
kind: ExprKind::Block(block, _),
131-
..
132-
})) | ExprKind::Repeat(Expr {
133-
kind: ExprKind::Block(block, _),
134-
..
135-
}, _) | ExprKind::Yield(Expr{
136-
kind: ExprKind::Block(block, _),
137-
..
138-
}, YieldSource::Yield) => Some((block, 1)),
139-
_ => None
142+
})
143+
| ExprKind::Unary(
144+
_,
145+
Expr {
146+
kind: ExprKind::Block(block, _),
147+
..
148+
},
149+
)
150+
| ExprKind::Match(
151+
Expr {
152+
kind: ExprKind::Block(block, _),
153+
..
154+
},
155+
_,
156+
MatchSource::Normal,
157+
)
158+
| ExprKind::Loop(block, _, _, _)
159+
| ExprKind::Block(block, _)
160+
| ExprKind::AddrOf(
161+
_,
162+
_,
163+
Expr {
164+
kind: ExprKind::Block(block, _),
165+
..
166+
},
167+
)
168+
| ExprKind::Break(
169+
_,
170+
Some(Expr {
171+
kind: ExprKind::Block(block, _),
172+
..
173+
}),
174+
)
175+
| ExprKind::Ret(Some(Expr {
176+
kind: ExprKind::Block(block, _),
177+
..
178+
}))
179+
| ExprKind::Repeat(
180+
Expr {
181+
kind: ExprKind::Block(block, _),
182+
..
183+
},
184+
_,
185+
)
186+
| ExprKind::Yield(
187+
Expr {
188+
kind: ExprKind::Block(block, _),
189+
..
190+
},
191+
YieldSource::Yield,
192+
) => Some((block, 1)),
193+
_ => None,
140194
}
141195
}
142196

143-
fn find_candidate(cx: &LateContext<'_>, span: Span, enclosing_hir_id: HirId) {
144-
if let Some(error_kind) = block_has_safety_comment(cx.tcx, enclosing_hir_id, span) {
145-
match error_kind {
146-
ErrorKind::NoComment => {
147-
let block_indent = indent_of(cx, span);
148-
let suggestion = format!("// Safety: ...\n{}", snippet(cx, span, ".."));
149-
150-
span_lint_and_sugg(
151-
cx,
152-
UNDOCUMENTED_UNSAFE_BLOCKS,
153-
span,
154-
"unsafe block missing a safety comment",
155-
"consider adding a safety comment",
156-
reindent_multiline(Cow::Borrowed(&suggestion), true, block_indent).to_string(),
157-
Applicability::HasPlaceholders,
158-
);
159-
}
160-
ErrorKind::Placeholder => {
161-
span_lint_and_help(
162-
cx,
163-
UNDOCUMENTED_UNSAFE_BLOCKS,
164-
span,
165-
"unsafe block has safety comment placeholder",
166-
None,
167-
"consider replacing the placeholder with a safety comment"
168-
);
169-
}
170-
}
171-
}
197+
fn err(cx: &LateContext<'_>, span: Span) {
198+
let block_indent = indent_of(cx, span);
199+
let suggestion = format!("// Safety: ...\n{}", snippet(cx, span, ".."));
200+
201+
span_lint_and_sugg(
202+
cx,
203+
UNDOCUMENTED_UNSAFE_BLOCKS,
204+
span,
205+
"unsafe block missing a safety comment",
206+
"consider adding a safety comment",
207+
reindent_multiline(Cow::Borrowed(&suggestion), true, block_indent).to_string(),
208+
Applicability::HasPlaceholders,
209+
);
172210
}
173211

174-
fn block_has_safety_comment(tcx: TyCtxt<'_>, enclosing_hir_id: HirId, block_span: Span) -> Option<ErrorKind> {
212+
fn block_has_safety_comment(
213+
tcx: TyCtxt<'_>,
214+
enclosing_hir_id: HirId,
215+
block_span: Span,
216+
) -> Option<bool> {
175217
let map = tcx.hir();
176218
let source_map = tcx.sess.source_map();
177219

@@ -190,32 +232,43 @@ fn block_has_safety_comment(tcx: TyCtxt<'_>, enclosing_hir_id: HirId, block_span
190232
for token in rustc_lexer::tokenize(&src_str) {
191233
match token.kind {
192234
TokenKind::LineComment { doc_style: None }
193-
| TokenKind::BlockComment { doc_style: None, terminated: true } => {
235+
| TokenKind::BlockComment {
236+
doc_style: None,
237+
terminated: true,
238+
} => {
194239
let comment_str = src_str[pos + 2..pos + token.len].to_ascii_uppercase();
195240

196-
if comment_str.contains("SAFETY: ...") {
197-
return Some(ErrorKind::Placeholder);
198-
} else if comment_str.contains("SAFETY:") {
241+
if comment_str.contains("SAFETY:") {
199242
comment = true;
200243
}
201-
},
202-
TokenKind::Whitespace => {},
244+
}
245+
// We need to add all whitespace to `pos` before checking the comment's line number
246+
TokenKind::Whitespace => {}
203247
_ => {
204248
if comment {
205-
let comment_line_num = source_file.lookup_file_pos_with_col_display(BytePos((lex_start + pos).try_into().unwrap())).0;
206-
let block_line_num = tcx.sess.source_map().lookup_char_pos(block_span.lo()).line;
249+
// Get the line number of the "comment" (really wherever the trailing whitespace ended)
250+
let comment_line_num = source_file
251+
.lookup_file_pos_with_col_display(BytePos(
252+
(lex_start + pos).try_into().unwrap(),
253+
))
254+
.0;
255+
// Find the block/local's line number
256+
let block_line_num =
257+
tcx.sess.source_map().lookup_char_pos(block_span.lo()).line;
207258

208-
if block_line_num == comment_line_num + 1 || block_line_num == comment_line_num {
209-
return None;
259+
// Check the comment is immediately followed by the block/local
260+
if block_line_num == comment_line_num + 1 || block_line_num == comment_line_num
261+
{
262+
return Some(true);
210263
}
211264

212265
comment = false;
213266
}
214-
},
267+
}
215268
}
216269

217270
pos += token.len;
218271
}
219272

220-
Some(ErrorKind::NoComment)
273+
Some(false)
221274
}

tests/ui/undocumented_unsafe_blocks.rs

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -136,17 +136,56 @@ fn local_block_comment() {
136136
let _ = unsafe {};
137137
}
138138

139-
// Invalid comments
139+
fn comment_array() {
140+
// Safety:
141+
let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
142+
}
140143

141-
fn no_comment() {
142-
unsafe {}
144+
fn comment_tuple() {
145+
// Safety:
146+
let _ = (42, unsafe {}, "test", unsafe {});
147+
}
148+
149+
fn comment_unary() {
150+
// Safety:
151+
let _ = *unsafe { &42 };
152+
}
153+
154+
#[allow(clippy::match_single_binding)]
155+
fn comment_match() {
156+
// Safety:
157+
let _ = match unsafe {} {
158+
_ => {},
159+
};
160+
}
161+
162+
fn comment_addr_of() {
163+
// Safety:
164+
let _ = &unsafe {};
143165
}
144166

145-
fn contains_placeholder() {
146-
// Safety: ...
167+
fn comment_repeat() {
168+
// Safety:
169+
let _ = [unsafe {}; 5];
170+
}
171+
172+
fn non_ascii_comment() {
173+
// ॐ᧻໒ Safety: ௵∰
147174
unsafe {};
148175
}
149176

177+
fn local_commented_block() {
178+
let _ =
179+
// Safety:
180+
unsafe {};
181+
}
182+
183+
// Invalid comments
184+
185+
fn no_comment() {
186+
unsafe {}
187+
}
188+
150189
fn no_comment_array() {
151190
let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
152191
}
@@ -178,12 +217,6 @@ fn local_no_comment() {
178217
let _ = unsafe {};
179218
}
180219

181-
fn local_commented_block() {
182-
let _ =
183-
// Safety:
184-
unsafe {};
185-
}
186-
187220
fn trailing_comment() {
188221
unsafe {} // Safety:
189222
}

0 commit comments

Comments
 (0)