1
- use clippy_utils:: diagnostics:: span_lint_and_sugg;
2
- use clippy_utils:: source:: { snippet, indent_of, reindent_multiline} ;
1
+ use clippy_utils:: diagnostics:: { span_lint_and_help, span_lint_and_sugg} ;
2
+ use clippy_utils:: source:: { indent_of, reindent_multiline, snippet} ;
3
+ use clippy_utils:: { in_macro, is_lint_allowed} ;
3
4
use rustc_errors:: Applicability ;
5
+ use rustc_hir:: intravisit:: { walk_expr, NestedVisitorMap , Visitor } ;
4
6
use rustc_hir:: { Block , BlockCheckMode , Expr , ExprKind , HirId , Local , UnsafeSource } ;
5
7
use rustc_lexer:: TokenKind ;
6
- use rustc_middle:: hir:: map:: Map ;
7
8
use rustc_lint:: { LateContext , LateLintPass } ;
9
+ use rustc_middle:: hir:: map:: Map ;
8
10
use rustc_middle:: lint:: in_external_macro;
9
11
use rustc_middle:: ty:: TyCtxt ;
10
- use rustc_session:: { impl_lint_pass , declare_tool_lint } ;
12
+ use rustc_session:: { declare_tool_lint , impl_lint_pass } ;
11
13
use rustc_span:: { BytePos , Span } ;
12
14
use std:: borrow:: Cow ;
13
- use clippy_utils:: { is_lint_allowed, in_macro} ;
14
- use rustc_hir:: intravisit:: { NestedVisitorMap , Visitor , walk_expr} ;
15
15
16
16
declare_clippy_lint ! {
17
17
/// ### What it does
@@ -53,6 +53,10 @@ pub struct UndocumentedUnsafeBlocks {
53
53
// The local was already checked for an overall safety comment
54
54
// There is no need to continue checking the blocks in the local
55
55
pub local_checked : bool ,
56
+ // Since we can only check the blocks from expanded macros
57
+ // We have to omit the suggestion due to the actual definition
58
+ // Not being available to us
59
+ pub macro_expansion : bool ,
56
60
}
57
61
58
62
impl LateLintPass < ' _ > for UndocumentedUnsafeBlocks {
@@ -63,22 +67,22 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlocks {
63
67
if !in_external_macro( cx. tcx. sess, block. span) ;
64
68
if let BlockCheckMode :: UnsafeBlock ( UnsafeSource :: UserProvided ) = block. rules;
65
69
if let Some ( enclosing_scope_hir_id) = cx. tcx. hir( ) . get_enclosing_scope( block. hir_id) ;
66
- if block_has_safety_comment( cx. tcx, enclosing_scope_hir_id, block. span) == Some ( false ) ;
70
+ if self . block_has_safety_comment( cx. tcx, enclosing_scope_hir_id, block. span) == Some ( false ) ;
67
71
then {
68
72
let mut span = block. span;
69
73
70
74
if let Some ( local_span) = self . local_span {
71
75
span = local_span;
72
76
73
- let result = block_has_safety_comment( cx. tcx, enclosing_scope_hir_id, span) ;
77
+ let result = self . block_has_safety_comment( cx. tcx, enclosing_scope_hir_id, span) ;
74
78
75
79
if result. unwrap_or( true ) {
76
80
self . local_checked = true ;
77
81
return ;
78
82
}
79
83
}
80
84
81
- lint( cx, span) ;
85
+ self . lint( cx, span) ;
82
86
}
83
87
}
84
88
}
@@ -118,91 +122,104 @@ impl<'hir> Visitor<'hir> for UndocumentedUnsafeBlocks {
118
122
fn visit_expr ( & mut self , ex : & ' v Expr < ' v > ) {
119
123
match ex. kind {
120
124
ExprKind :: Block ( _, _) => self . local_level = self . local_level . saturating_add ( 1 ) ,
121
- _ => walk_expr ( self , ex)
125
+ _ => walk_expr ( self , ex) ,
122
126
}
123
127
}
124
128
}
125
129
126
- fn lint ( cx : & LateContext < ' _ > , span : Span ) {
127
- let block_indent = indent_of ( cx, span) ;
128
- let suggestion = format ! ( "// Safety: ...\n {}" , snippet( cx, span, ".." ) ) ;
129
-
130
- span_lint_and_sugg (
131
- cx,
132
- UNDOCUMENTED_UNSAFE_BLOCKS ,
133
- span,
134
- "unsafe block missing a safety comment" ,
135
- "consider adding a safety comment" ,
136
- reindent_multiline ( Cow :: Borrowed ( & suggestion) , true , block_indent) . to_string ( ) ,
137
- Applicability :: HasPlaceholders ,
138
- ) ;
139
- }
140
-
141
- fn block_has_safety_comment (
142
- tcx : TyCtxt < ' _ > ,
143
- enclosing_hir_id : HirId ,
144
- block_span : Span ,
145
- ) -> Option < bool > {
146
- let map = tcx. hir ( ) ;
147
- let source_map = tcx. sess . source_map ( ) ;
148
-
149
- let enclosing_scope_span = map. opt_span ( enclosing_hir_id) ?;
150
-
151
- let between_span = if in_macro ( block_span) {
152
- enclosing_scope_span. with_hi ( block_span. hi ( ) )
153
- } else {
154
- enclosing_scope_span. to ( block_span)
155
- } ;
156
-
157
- let file_name = source_map. span_to_filename ( between_span) ;
158
- let source_file = source_map. get_source_file ( & file_name) ?;
159
-
160
- let lex_start = ( between_span. lo ( ) . 0 + 1 ) as usize ;
161
- let src_str = source_file. src . as_ref ( ) ?[ lex_start..between_span. hi ( ) . 0 as usize ] . to_string ( ) ;
162
-
163
- let mut pos = 0 ;
164
- let mut comment = false ;
165
-
166
- for token in rustc_lexer:: tokenize ( & src_str) {
167
- match token. kind {
168
- TokenKind :: LineComment { doc_style : None }
169
- | TokenKind :: BlockComment {
170
- doc_style : None ,
171
- terminated : true ,
172
- } => {
173
- let comment_str = src_str[ pos + 2 ..pos + token. len ] . to_ascii_uppercase ( ) ;
174
-
175
- if comment_str. contains ( "SAFETY:" ) {
176
- comment = true ;
177
- }
178
- }
179
- // We need to add all whitespace to `pos` before checking the comment's line number
180
- TokenKind :: Whitespace => { }
181
- _ => {
182
- if comment {
183
- // Get the line number of the "comment" (really wherever the trailing whitespace ended)
184
- let comment_line_num = source_file
185
- . lookup_file_pos_with_col_display ( BytePos (
186
- ( lex_start + pos) . try_into ( ) . unwrap ( ) ,
187
- ) )
188
- . 0 ;
189
- // Find the block/local's line number
190
- let block_line_num =
191
- tcx. sess . source_map ( ) . lookup_char_pos ( block_span. lo ( ) ) . line ;
192
-
193
- // Check the comment is immediately followed by the block/local
194
- if block_line_num == comment_line_num + 1 || block_line_num == comment_line_num
195
- {
196
- return Some ( true ) ;
130
+ impl UndocumentedUnsafeBlocks {
131
+ fn block_has_safety_comment ( & mut self , tcx : TyCtxt < ' _ > , enclosing_hir_id : HirId , block_span : Span ) -> Option < bool > {
132
+ let map = tcx. hir ( ) ;
133
+ let source_map = tcx. sess . source_map ( ) ;
134
+
135
+ let enclosing_scope_span = map. opt_span ( enclosing_hir_id) ?;
136
+
137
+ let between_span = if in_macro ( block_span) {
138
+ self . macro_expansion = true ;
139
+ enclosing_scope_span. with_hi ( block_span. hi ( ) )
140
+ } else {
141
+ self . macro_expansion = false ;
142
+ enclosing_scope_span. to ( block_span)
143
+ } ;
144
+
145
+ let file_name = source_map. span_to_filename ( between_span) ;
146
+ let source_file = source_map. get_source_file ( & file_name) ?;
147
+
148
+ let lex_start = ( between_span. lo ( ) . 0 + 1 ) as usize ;
149
+ let src_str = source_file. src . as_ref ( ) ?[ lex_start..between_span. hi ( ) . 0 as usize ] . to_string ( ) ;
150
+
151
+ let mut pos = 0 ;
152
+ let mut comment = false ;
153
+
154
+ for token in rustc_lexer:: tokenize ( & src_str) {
155
+ match token. kind {
156
+ TokenKind :: LineComment { doc_style : None }
157
+ | TokenKind :: BlockComment {
158
+ doc_style : None ,
159
+ terminated : true ,
160
+ } => {
161
+ let comment_str = src_str[ pos + 2 ..pos + token. len ] . to_ascii_uppercase ( ) ;
162
+
163
+ if comment_str. contains ( "SAFETY:" ) {
164
+ comment = true ;
197
165
}
198
-
199
- comment = false ;
200
- }
166
+ } ,
167
+ // We need to add all whitespace to `pos` before checking the comment's line number
168
+ TokenKind :: Whitespace => { } ,
169
+ _ => {
170
+ if comment {
171
+ // Get the line number of the "comment" (really wherever the trailing whitespace ended)
172
+ let comment_line_num = source_file
173
+ . lookup_file_pos_with_col_display ( BytePos ( ( lex_start + pos) . try_into ( ) . unwrap ( ) ) )
174
+ . 0 ;
175
+ // Find the block/local's line number
176
+ let block_line_num = tcx. sess . source_map ( ) . lookup_char_pos ( block_span. lo ( ) ) . line ;
177
+
178
+ // Check the comment is immediately followed by the block/local
179
+ if block_line_num == comment_line_num + 1 || block_line_num == comment_line_num {
180
+ return Some ( true ) ;
181
+ }
182
+
183
+ comment = false ;
184
+ }
185
+ } ,
201
186
}
187
+
188
+ pos += token. len ;
202
189
}
203
190
204
- pos += token . len ;
191
+ Some ( false )
205
192
}
206
193
207
- Some ( false )
194
+ fn lint ( & self , cx : & LateContext < ' _ > , mut span : Span ) {
195
+ let source_map = cx. tcx . sess . source_map ( ) ;
196
+
197
+ if source_map. is_multiline ( span) {
198
+ span = source_map. span_until_char ( span, '\n' ) ;
199
+ }
200
+
201
+ if self . macro_expansion {
202
+ span_lint_and_help (
203
+ cx,
204
+ UNDOCUMENTED_UNSAFE_BLOCKS ,
205
+ span,
206
+ "unsafe block in macro expansion missing a safety comment" ,
207
+ None ,
208
+ "consider adding a safety comment in the macro definition" ,
209
+ ) ;
210
+ } else {
211
+ let block_indent = indent_of ( cx, span) ;
212
+ let suggestion = format ! ( "// Safety: ...\n {}" , snippet( cx, span, ".." ) ) ;
213
+
214
+ span_lint_and_sugg (
215
+ cx,
216
+ UNDOCUMENTED_UNSAFE_BLOCKS ,
217
+ span,
218
+ "unsafe block missing a safety comment" ,
219
+ "consider adding a safety comment" ,
220
+ reindent_multiline ( Cow :: Borrowed ( & suggestion) , true , block_indent) . to_string ( ) ,
221
+ Applicability :: HasPlaceholders ,
222
+ ) ;
223
+ }
224
+ }
208
225
}
0 commit comments