1
- use clippy_utils:: diagnostics:: { span_lint_and_help , span_lint_and_sugg} ;
1
+ use clippy_utils:: diagnostics:: span_lint_and_sugg;
2
2
use clippy_utils:: source:: { snippet, indent_of, reindent_multiline} ;
3
3
use rustc_errors:: Applicability ;
4
4
use rustc_hir:: { Block , BlockCheckMode , Expr , ExprKind , HirId , Local , MatchSource , UnsafeSource , YieldSource } ;
@@ -47,23 +47,37 @@ impl_lint_pass!(UndocumentedUnsafeBlocks => [UNDOCUMENTED_UNSAFE_BLOCKS]);
47
47
#[ derive( Default ) ]
48
48
pub struct UndocumentedUnsafeBlocks {
49
49
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 ,
55
54
}
56
55
57
56
impl LateLintPass < ' _ > for UndocumentedUnsafeBlocks {
58
57
fn check_block ( & mut self , cx : & LateContext < ' _ > , block : & ' _ Block < ' _ > ) {
59
58
if_chain ! {
60
- if self . local_level == 0 ;
59
+ if ! self . local_checked ;
61
60
if !is_lint_allowed( cx, UNDOCUMENTED_UNSAFE_BLOCKS , block. hir_id) ;
62
61
if !in_external_macro( cx. tcx. sess, block. span) ;
63
62
if let BlockCheckMode :: UnsafeBlock ( UnsafeSource :: UserProvided ) = block. rules;
64
63
if let Some ( enclosing_scope_hir_id) = cx. tcx. hir( ) . get_enclosing_scope( block. hir_id) ;
65
64
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
+ }
67
81
}
68
82
}
69
83
}
@@ -75,103 +89,131 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlocks {
75
89
if let Some ( init) = local. init;
76
90
if let Some ( ( block, element_count) ) = find_block_in_expr( & init. kind) ;
77
91
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 ( ) ;
79
93
then {
80
94
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) ;
83
96
}
84
97
}
85
98
}
86
99
87
100
fn check_block_post ( & mut self , _: & LateContext < ' _ > , _: & ' _ Block < ' _ > ) {
88
101
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
+ }
89
107
}
90
108
}
91
109
92
110
fn find_block_in_expr ( expr_kind : & ExprKind < ' hir > ) -> Option < ( & ' tcx Block < ' hir > , u32 ) > {
93
111
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
+ ) ,
102
127
..
103
128
} => Some ( block) ,
104
- _ => None
105
- } ) . collect :: < Vec < _ > > ( ) ;
129
+ _ => None ,
130
+ } )
131
+ . collect :: < Vec < _ > > ( ) ;
106
132
107
133
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 ( ) ) ) ;
109
135
}
110
136
111
137
None
112
- } ,
138
+ }
113
139
ExprKind :: Box ( Expr {
114
140
kind : ExprKind :: Block ( block, _) ,
115
141
..
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 ,
140
194
}
141
195
}
142
196
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
+ ) ;
172
210
}
173
211
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 > {
175
217
let map = tcx. hir ( ) ;
176
218
let source_map = tcx. sess . source_map ( ) ;
177
219
@@ -190,32 +232,43 @@ fn block_has_safety_comment(tcx: TyCtxt<'_>, enclosing_hir_id: HirId, block_span
190
232
for token in rustc_lexer:: tokenize ( & src_str) {
191
233
match token. kind {
192
234
TokenKind :: LineComment { doc_style : None }
193
- | TokenKind :: BlockComment { doc_style : None , terminated : true } => {
235
+ | TokenKind :: BlockComment {
236
+ doc_style : None ,
237
+ terminated : true ,
238
+ } => {
194
239
let comment_str = src_str[ pos + 2 ..pos + token. len ] . to_ascii_uppercase ( ) ;
195
240
196
- if comment_str. contains ( "SAFETY: ..." ) {
197
- return Some ( ErrorKind :: Placeholder ) ;
198
- } else if comment_str. contains ( "SAFETY:" ) {
241
+ if comment_str. contains ( "SAFETY:" ) {
199
242
comment = true ;
200
243
}
201
- } ,
202
- TokenKind :: Whitespace => { } ,
244
+ }
245
+ // We need to add all whitespace to `pos` before checking the comment's line number
246
+ TokenKind :: Whitespace => { }
203
247
_ => {
204
248
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 ;
207
258
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 ) ;
210
263
}
211
264
212
265
comment = false ;
213
266
}
214
- } ,
267
+ }
215
268
}
216
269
217
270
pos += token. len ;
218
271
}
219
272
220
- Some ( ErrorKind :: NoComment )
273
+ Some ( false )
221
274
}
0 commit comments