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} ;
34use rustc_errors:: Applicability ;
5+ use rustc_hir:: intravisit:: { walk_expr, NestedVisitorMap , Visitor } ;
46use rustc_hir:: { Block , BlockCheckMode , Expr , ExprKind , HirId , Local , UnsafeSource } ;
57use rustc_lexer:: TokenKind ;
6- use rustc_middle:: hir:: map:: Map ;
78use rustc_lint:: { LateContext , LateLintPass } ;
9+ use rustc_middle:: hir:: map:: Map ;
810use rustc_middle:: lint:: in_external_macro;
911use rustc_middle:: ty:: TyCtxt ;
10- use rustc_session:: { impl_lint_pass , declare_tool_lint } ;
12+ use rustc_session:: { declare_tool_lint , impl_lint_pass } ;
1113use rustc_span:: { BytePos , Span } ;
1214use std:: borrow:: Cow ;
13- use clippy_utils:: { is_lint_allowed, in_macro} ;
14- use rustc_hir:: intravisit:: { NestedVisitorMap , Visitor , walk_expr} ;
1515
1616declare_clippy_lint ! {
1717 /// ### What it does
@@ -53,6 +53,10 @@ pub struct UndocumentedUnsafeBlocks {
5353 // The local was already checked for an overall safety comment
5454 // There is no need to continue checking the blocks in the local
5555 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 ,
5660}
5761
5862impl LateLintPass < ' _ > for UndocumentedUnsafeBlocks {
@@ -63,22 +67,22 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlocks {
6367 if !in_external_macro( cx. tcx. sess, block. span) ;
6468 if let BlockCheckMode :: UnsafeBlock ( UnsafeSource :: UserProvided ) = block. rules;
6569 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 ) ;
6771 then {
6872 let mut span = block. span;
6973
7074 if let Some ( local_span) = self . local_span {
7175 span = local_span;
7276
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) ;
7478
7579 if result. unwrap_or( true ) {
7680 self . local_checked = true ;
7781 return ;
7882 }
7983 }
8084
81- lint( cx, span) ;
85+ self . lint( cx, span) ;
8286 }
8387 }
8488 }
@@ -118,91 +122,104 @@ impl<'hir> Visitor<'hir> for UndocumentedUnsafeBlocks {
118122 fn visit_expr ( & mut self , ex : & ' v Expr < ' v > ) {
119123 match ex. kind {
120124 ExprKind :: Block ( _, _) => self . local_level = self . local_level . saturating_add ( 1 ) ,
121- _ => walk_expr ( self , ex)
125+ _ => walk_expr ( self , ex) ,
122126 }
123127 }
124128}
125129
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 ;
197165 }
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+ } ,
201186 }
187+
188+ pos += token. len ;
202189 }
203190
204- pos += token . len ;
191+ Some ( false )
205192 }
206193
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+ }
208225}
0 commit comments