1
- use clippy_utils:: diagnostics:: span_lint_and_sugg;
1
+ use clippy_utils:: diagnostics:: { span_lint_and_help , span_lint_and_sugg} ;
2
2
use clippy_utils:: source:: { snippet, indent_of, reindent_multiline} ;
3
3
use rustc_errors:: Applicability ;
4
- use rustc_hir:: { Block , BlockCheckMode , ExprKind , HirId , Local , UnsafeSource } ;
4
+ use rustc_hir:: { Block , BlockCheckMode , Expr , ExprKind , HirId , Local , MatchSource , UnsafeSource , YieldSource } ;
5
5
use rustc_lexer:: TokenKind ;
6
6
use rustc_lint:: { LateContext , LateLintPass } ;
7
7
use rustc_middle:: lint:: in_external_macro;
8
8
use rustc_middle:: ty:: TyCtxt ;
9
9
use rustc_session:: { impl_lint_pass, declare_tool_lint} ;
10
- use rustc_span:: Span ;
10
+ use rustc_span:: { BytePos , Span } ;
11
11
use std:: borrow:: Cow ;
12
12
use clippy_utils:: is_lint_allowed;
13
13
@@ -46,23 +46,25 @@ impl_lint_pass!(UndocumentedUnsafeBlocks => [UNDOCUMENTED_UNSAFE_BLOCKS]);
46
46
47
47
#[ derive( Default ) ]
48
48
pub struct UndocumentedUnsafeBlocks {
49
- pub local : bool
49
+ pub local_level : u32 ,
50
+ }
51
+
52
+ enum ErrorKind {
53
+ NoComment ,
54
+ Placeholder
50
55
}
51
56
52
57
impl LateLintPass < ' _ > for UndocumentedUnsafeBlocks {
53
58
fn check_block ( & mut self , cx : & LateContext < ' _ > , block : & ' _ Block < ' _ > ) {
54
- if self . local {
55
- self . local = false ;
56
- return
57
- }
58
-
59
- if_chain ! {
60
- if !is_lint_allowed( cx, UNDOCUMENTED_UNSAFE_BLOCKS , block. hir_id) ;
61
- if !in_external_macro( cx. tcx. sess, block. span) ;
62
- if let BlockCheckMode :: UnsafeBlock ( UnsafeSource :: UserProvided ) = block. rules;
63
- if let Some ( enclosing_scope_hir_id) = cx. tcx. hir( ) . get_enclosing_scope( block. hir_id) ;
64
- then {
65
- find_candidate( cx, block. span, enclosing_scope_hir_id)
59
+ if self . local_level == 0 {
60
+ if_chain ! {
61
+ if !is_lint_allowed( cx, UNDOCUMENTED_UNSAFE_BLOCKS , block. hir_id) ;
62
+ if !in_external_macro( cx. tcx. sess, block. span) ;
63
+ if let BlockCheckMode :: UnsafeBlock ( UnsafeSource :: UserProvided ) = block. rules;
64
+ if let Some ( enclosing_scope_hir_id) = cx. tcx. hir( ) . get_enclosing_scope( block. hir_id) ;
65
+ then {
66
+ find_candidate( cx, block. span, enclosing_scope_hir_id)
67
+ }
66
68
}
67
69
}
68
70
}
@@ -72,71 +74,149 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlocks {
72
74
if !is_lint_allowed( cx, UNDOCUMENTED_UNSAFE_BLOCKS , local. hir_id) ;
73
75
if !in_external_macro( cx. tcx. sess, local. span) ;
74
76
if let Some ( init) = local. init;
75
- if let ExprKind :: Block ( block, _ ) = init. kind;
77
+ if let Some ( ( block, element_count ) ) = find_block_in_expr ( & init. kind) ;
76
78
if let BlockCheckMode :: UnsafeBlock ( UnsafeSource :: UserProvided ) = block. rules;
77
79
if let Some ( enclosing_scope_hir_id) = cx. tcx. hir( ) . get_enclosing_scope( block. hir_id) ;
78
80
then {
79
- self . local = true ;
81
+ self . local_level = self . local_level. saturating_add( element_count) ;
82
+
80
83
find_candidate( cx, local. span, enclosing_scope_hir_id)
81
84
}
82
85
}
83
86
}
87
+
88
+ fn check_block_post ( & mut self , _: & LateContext < ' _ > , _: & ' _ Block < ' _ > ) {
89
+ self . local_level = self . local_level . saturating_sub ( 1 ) ;
90
+ }
84
91
}
85
92
86
- fn find_candidate ( cx : & LateContext < ' _ > , span : Span , enclosing_hir_id : HirId ) {
87
- if let Some ( true ) = block_has_safety_comment ( cx. tcx , enclosing_hir_id, span) {
88
- return ;
93
+ fn find_block_in_expr ( expr_kind : & ExprKind < ' hir > ) -> Option < ( & ' tcx Block < ' hir > , u32 ) > {
94
+ match expr_kind {
95
+ ExprKind :: Array ( elements) | ExprKind :: Tup ( elements) => {
96
+ let unsafe_blocks = elements. iter ( ) . filter_map ( |e|
97
+ match e {
98
+ Expr {
99
+ kind : ExprKind :: Block ( block @ Block {
100
+ rules : BlockCheckMode :: UnsafeBlock ( UnsafeSource :: UserProvided ) ,
101
+ ..
102
+ } , _) ,
103
+ ..
104
+ } => Some ( block) ,
105
+ _ => None
106
+ } ) . collect :: < Vec < _ > > ( ) ;
107
+
108
+ if let Some ( block) = unsafe_blocks. get ( 0 ) {
109
+ return Some ( ( block, unsafe_blocks. len ( ) . try_into ( ) . unwrap ( ) ) )
110
+ }
111
+
112
+ None
113
+ } ,
114
+ ExprKind :: Box ( Expr {
115
+ kind : ExprKind :: Block ( block, _) ,
116
+ ..
117
+ } ) |
118
+ ExprKind :: Unary ( _, Expr {
119
+ kind : ExprKind :: Block ( block, _) ,
120
+ ..
121
+ } ) | ExprKind :: Match ( Expr {
122
+ kind : ExprKind :: Block ( block, _) ,
123
+ ..
124
+ } , _, MatchSource :: Normal ) | ExprKind :: Loop ( block, _, _, _) | ExprKind :: Block ( block, _) | ExprKind :: AddrOf ( _, _, Expr {
125
+ kind : ExprKind :: Block ( block, _) ,
126
+ ..
127
+ } ) | ExprKind :: Break ( _, Some ( Expr {
128
+ kind : ExprKind :: Block ( block, _) ,
129
+ ..
130
+ } ) ) | ExprKind :: Ret ( Some ( Expr {
131
+ kind : ExprKind :: Block ( block, _) ,
132
+ ..
133
+ } ) ) | ExprKind :: Repeat ( Expr {
134
+ kind : ExprKind :: Block ( block, _) ,
135
+ ..
136
+ } , _) | ExprKind :: Yield ( Expr {
137
+ kind : ExprKind :: Block ( block, _) ,
138
+ ..
139
+ } , YieldSource :: Yield ) => Some ( ( block, 1 ) ) ,
140
+ _ => None
89
141
}
142
+ }
90
143
91
- let block_indent = indent_of ( cx, span) ;
92
- let suggestion = format ! ( "// Safety: ...\n {}" , snippet( cx, span, ".." ) ) ;
93
-
94
- span_lint_and_sugg (
95
- cx,
96
- UNDOCUMENTED_UNSAFE_BLOCKS ,
97
- span,
98
- "unsafe block missing a safety comment" ,
99
- "consider adding a safety comment" ,
100
- reindent_multiline ( Cow :: Borrowed ( & suggestion) , true , block_indent) . to_string ( ) ,
101
- Applicability :: HasPlaceholders ,
102
- ) ;
144
+ fn find_candidate ( cx : & LateContext < ' _ > , span : Span , enclosing_hir_id : HirId ) {
145
+ if let Some ( error_kind) = block_has_safety_comment ( cx. tcx , enclosing_hir_id, span) {
146
+ match error_kind {
147
+ ErrorKind :: NoComment => {
148
+ let block_indent = indent_of ( cx, span) ;
149
+ let suggestion = format ! ( "// Safety: ...\n {}" , snippet( cx, span, ".." ) ) ;
150
+
151
+ span_lint_and_sugg (
152
+ cx,
153
+ UNDOCUMENTED_UNSAFE_BLOCKS ,
154
+ span,
155
+ "unsafe block missing a safety comment" ,
156
+ "consider adding a safety comment" ,
157
+ reindent_multiline ( Cow :: Borrowed ( & suggestion) , true , block_indent) . to_string ( ) ,
158
+ Applicability :: HasPlaceholders ,
159
+ ) ;
160
+ }
161
+ ErrorKind :: Placeholder => {
162
+ span_lint_and_help (
163
+ cx,
164
+ UNDOCUMENTED_UNSAFE_BLOCKS ,
165
+ span,
166
+ "unsafe block has safety comment placeholder" ,
167
+ None ,
168
+ "consider replacing the placeholder with a safety comment"
169
+ ) ;
170
+ }
171
+ }
172
+ }
103
173
}
104
174
105
- fn block_has_safety_comment ( tcx : TyCtxt < ' _ > , enclosing_hir_id : HirId , block_span : Span ) -> Option < bool > {
175
+ fn block_has_safety_comment ( tcx : TyCtxt < ' _ > , enclosing_hir_id : HirId , block_span : Span ) -> Option < ErrorKind > {
106
176
let map = tcx. hir ( ) ;
107
177
let source_map = tcx. sess . source_map ( ) ;
108
178
109
179
let enclosing_scope_span = map. opt_span ( enclosing_hir_id) ?;
110
- let between_span = enclosing_scope_span. until ( block_span) ;
180
+ let between_span = enclosing_scope_span. to ( block_span) ;
111
181
112
182
let file_name = source_map. span_to_filename ( between_span) ;
113
183
let source_file = source_map. get_source_file ( & file_name) ?;
114
184
115
185
let lex_start = ( between_span. lo ( ) . 0 + 1 ) as usize ;
116
- let mut src_str = source_file. src . as_ref ( ) ?[ lex_start..between_span. hi ( ) . 0 as usize ] . to_string ( ) ;
117
-
118
- src_str. retain ( |c| !c. is_whitespace ( ) ) ;
119
-
120
- let src_str_split = src_str. lines ( ) . rev ( ) . collect :: < Vec < _ > > ( ) ;
121
- let src_str = src_str_split. join ( "" ) ;
186
+ let src_str = source_file. src . as_ref ( ) ?[ lex_start..between_span. hi ( ) . 0 as usize ] . to_string ( ) ;
122
187
123
188
let mut pos = 0 ;
124
- let mut found_safety_comment = false ;
189
+ let mut comment = false ;
125
190
126
191
for token in rustc_lexer:: tokenize ( & src_str) {
127
192
match token. kind {
128
193
TokenKind :: LineComment { doc_style : None }
129
- | TokenKind :: BlockComment { doc_style : None , terminated : true } => {
130
- if src_str[ pos + 2 .. pos + token. len ] . to_ascii_uppercase ( ) . contains ( "SAFETY:" ) {
131
- found_safety_comment = true ;
132
- break
194
+ | TokenKind :: BlockComment { doc_style : None , terminated : true } => {
195
+ let comment_str = src_str[ pos + 2 ..pos + token. len ] . to_ascii_uppercase ( ) ;
196
+
197
+ if comment_str. contains ( "SAFETY: ..." ) {
198
+ return Some ( ErrorKind :: Placeholder ) ;
199
+ } else if comment_str. contains ( "SAFETY:" ) {
200
+ comment = true ;
201
+ }
202
+ } ,
203
+ TokenKind :: Whitespace => { } ,
204
+ _ => {
205
+ if comment {
206
+ let comment_line_num = source_file. lookup_file_pos_with_col_display ( BytePos ( ( lex_start + pos) . try_into ( ) . unwrap ( ) ) ) . 0 ;
207
+ let block_line_num = tcx. sess . source_map ( ) . lookup_char_pos ( block_span. lo ( ) ) . line ;
208
+
209
+ if block_line_num == comment_line_num + 1 || block_line_num == comment_line_num {
210
+ return None ;
211
+ }
212
+
213
+ comment = false ;
133
214
}
134
215
} ,
135
- _ => break
136
216
}
137
217
138
218
pos += token. len ;
139
219
}
140
220
141
- Some ( found_safety_comment )
221
+ Some ( ErrorKind :: NoComment )
142
222
}
0 commit comments