1
+ use std:: ops:: ControlFlow ;
2
+
1
3
use clippy_utils:: diagnostics:: span_lint_and_help;
2
4
use clippy_utils:: source:: walk_span_to_context;
5
+ use clippy_utils:: visitors:: { for_each_expr_with_closures, Descend } ;
3
6
use clippy_utils:: { get_parent_node, is_lint_allowed} ;
7
+ use hir:: HirId ;
4
8
use rustc_data_structures:: sync:: Lrc ;
5
9
use rustc_hir as hir;
6
10
use rustc_hir:: { Block , BlockCheckMode , ItemKind , Node , UnsafeSource } ;
@@ -90,8 +94,8 @@ declare_clippy_lint! {
90
94
91
95
declare_lint_pass ! ( UndocumentedUnsafeBlocks => [ UNDOCUMENTED_UNSAFE_BLOCKS , UNNECESSARY_SAFETY_COMMENT ] ) ;
92
96
93
- impl LateLintPass < ' _ > for UndocumentedUnsafeBlocks {
94
- fn check_block ( & mut self , cx : & LateContext < ' _ > , block : & ' _ Block < ' _ > ) {
97
+ impl < ' tcx > LateLintPass < ' tcx > for UndocumentedUnsafeBlocks {
98
+ fn check_block ( & mut self , cx : & LateContext < ' tcx > , block : & ' tcx Block < ' tcx > ) {
95
99
if block. rules == BlockCheckMode :: UnsafeBlock ( UnsafeSource :: UserProvided )
96
100
&& !in_external_macro ( cx. tcx . sess , block. span )
97
101
&& !is_lint_allowed ( cx, UNDOCUMENTED_UNSAFE_BLOCKS , block. hir_id )
@@ -115,6 +119,45 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlocks {
115
119
"consider adding a safety comment on the preceding line" ,
116
120
) ;
117
121
}
122
+
123
+ if let Some ( tail) = block. expr
124
+ && !is_lint_allowed ( cx, UNNECESSARY_SAFETY_COMMENT , tail. hir_id )
125
+ && !in_external_macro ( cx. tcx . sess , tail. span )
126
+ && let HasSafetyComment :: Yes ( pos) = stmt_has_safety_comment ( cx, tail. span , tail. hir_id )
127
+ && let Some ( help_span) = expr_has_unnecessary_safety_comment ( cx, tail, pos)
128
+ {
129
+ span_lint_and_help (
130
+ cx,
131
+ UNNECESSARY_SAFETY_COMMENT ,
132
+ tail. span ,
133
+ "expression has unnecessary safety comment" ,
134
+ Some ( help_span) ,
135
+ "consider removing the safety comment" ,
136
+ ) ;
137
+ }
138
+ }
139
+
140
+ fn check_stmt ( & mut self , cx : & LateContext < ' tcx > , stmt : & hir:: Stmt < ' tcx > ) {
141
+ let expr = match stmt. kind {
142
+ hir:: StmtKind :: Local ( & hir:: Local { init : Some ( expr) , .. } )
143
+ | hir:: StmtKind :: Expr ( expr)
144
+ | hir:: StmtKind :: Semi ( expr) => expr,
145
+ _ => return ,
146
+ } ;
147
+ if !is_lint_allowed ( cx, UNNECESSARY_SAFETY_COMMENT , stmt. hir_id )
148
+ && !in_external_macro ( cx. tcx . sess , stmt. span )
149
+ && let HasSafetyComment :: Yes ( pos) = stmt_has_safety_comment ( cx, stmt. span , stmt. hir_id )
150
+ && let Some ( help_span) = expr_has_unnecessary_safety_comment ( cx, expr, pos)
151
+ {
152
+ span_lint_and_help (
153
+ cx,
154
+ UNNECESSARY_SAFETY_COMMENT ,
155
+ stmt. span ,
156
+ "statement has unnecessary safety comment" ,
157
+ Some ( help_span) ,
158
+ "consider removing the safety comment" ,
159
+ ) ;
160
+ }
118
161
}
119
162
120
163
fn check_item ( & mut self , cx : & LateContext < ' _ > , item : & hir:: Item < ' _ > ) {
@@ -216,6 +259,36 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlocks {
216
259
}
217
260
}
218
261
262
+ fn expr_has_unnecessary_safety_comment < ' tcx > (
263
+ cx : & LateContext < ' tcx > ,
264
+ expr : & ' tcx hir:: Expr < ' tcx > ,
265
+ comment_pos : BytePos ,
266
+ ) -> Option < Span > {
267
+ // this should roughly be the reverse of `block_parents_have_safety_comment`
268
+ if for_each_expr_with_closures ( cx, expr, |expr| match expr. kind {
269
+ hir:: ExprKind :: Block (
270
+ Block {
271
+ rules : BlockCheckMode :: UnsafeBlock ( UnsafeSource :: UserProvided ) ,
272
+ ..
273
+ } ,
274
+ _,
275
+ ) => ControlFlow :: Break ( ( ) ) ,
276
+ // statements will be handled by check_stmt itself again
277
+ hir:: ExprKind :: Block ( ..) => ControlFlow :: Continue ( Descend :: No ) ,
278
+ _ => ControlFlow :: Continue ( Descend :: Yes ) ,
279
+ } )
280
+ . is_some ( )
281
+ {
282
+ return None ;
283
+ }
284
+
285
+ let source_map = cx. tcx . sess . source_map ( ) ;
286
+ let span = Span :: new ( comment_pos, comment_pos, SyntaxContext :: root ( ) , None ) ;
287
+ let help_span = source_map. span_extend_to_next_char ( span, '\n' , true ) ;
288
+
289
+ Some ( help_span)
290
+ }
291
+
219
292
fn is_unsafe_from_proc_macro ( cx : & LateContext < ' _ > , span : Span ) -> bool {
220
293
let source_map = cx. sess ( ) . source_map ( ) ;
221
294
let file_pos = source_map. lookup_byte_offset ( span. lo ( ) ) ;
@@ -358,6 +431,55 @@ fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> HasSaf
358
431
}
359
432
}
360
433
434
+ /// Checks if the lines immediately preceding the item contain a safety comment.
435
+ #[ allow( clippy:: collapsible_match) ]
436
+ fn stmt_has_safety_comment ( cx : & LateContext < ' _ > , span : Span , hir_id : HirId ) -> HasSafetyComment {
437
+ match span_from_macro_expansion_has_safety_comment ( cx, span) {
438
+ HasSafetyComment :: Maybe => ( ) ,
439
+ has_safety_comment => return has_safety_comment,
440
+ }
441
+
442
+ if span. ctxt ( ) == SyntaxContext :: root ( ) {
443
+ if let Some ( parent_node) = get_parent_node ( cx. tcx , hir_id) {
444
+ let comment_start = match parent_node {
445
+ Node :: Block ( block) => walk_span_to_context ( block. span , SyntaxContext :: root ( ) ) . map ( Span :: lo) ,
446
+ _ => return HasSafetyComment :: Maybe ,
447
+ } ;
448
+
449
+ let source_map = cx. sess ( ) . source_map ( ) ;
450
+ if let Some ( comment_start) = comment_start
451
+ && let Ok ( unsafe_line) = source_map. lookup_line ( span. lo ( ) )
452
+ && let Ok ( comment_start_line) = source_map. lookup_line ( comment_start)
453
+ && Lrc :: ptr_eq ( & unsafe_line. sf , & comment_start_line. sf )
454
+ && let Some ( src) = unsafe_line. sf . src . as_deref ( )
455
+ {
456
+ unsafe_line. sf . lines ( |lines| {
457
+ if comment_start_line. line >= unsafe_line. line {
458
+ HasSafetyComment :: No
459
+ } else {
460
+ match text_has_safety_comment (
461
+ src,
462
+ & lines[ comment_start_line. line + 1 ..=unsafe_line. line ] ,
463
+ unsafe_line. sf . start_pos . to_usize ( ) ,
464
+ ) {
465
+ Some ( b) => HasSafetyComment :: Yes ( b) ,
466
+ None => HasSafetyComment :: No ,
467
+ }
468
+ }
469
+ } )
470
+ } else {
471
+ // Problem getting source text. Pretend a comment was found.
472
+ HasSafetyComment :: Maybe
473
+ }
474
+ } else {
475
+ // No parent node. Pretend a comment was found.
476
+ HasSafetyComment :: Maybe
477
+ }
478
+ } else {
479
+ HasSafetyComment :: No
480
+ }
481
+ }
482
+
361
483
fn comment_start_before_item_in_mod (
362
484
cx : & LateContext < ' _ > ,
363
485
parent_mod : & hir:: Mod < ' _ > ,
0 commit comments