1- use clippy_utils:: diagnostics:: { span_lint_and_help , span_lint_and_sugg} ;
1+ use clippy_utils:: diagnostics:: span_lint_and_sugg;
22use clippy_utils:: source:: { snippet, indent_of, reindent_multiline} ;
33use rustc_errors:: Applicability ;
44use 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 ) ]
4848pub 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
5756impl 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
92110fn 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}
0 commit comments