1
1
use clippy_utils:: diagnostics:: span_lint_and_help;
2
- use clippy_utils:: is_lint_allowed;
3
2
use clippy_utils:: source:: walk_span_to_context;
3
+ use clippy_utils:: { get_parent_node, is_lint_allowed} ;
4
4
use rustc_data_structures:: sync:: Lrc ;
5
5
use rustc_hir as hir;
6
- use rustc_hir:: { Block , BlockCheckMode , UnsafeSource } ;
6
+ use rustc_hir:: { Block , BlockCheckMode , ItemKind , Node , UnsafeSource } ;
7
7
use rustc_lexer:: { tokenize, TokenKind } ;
8
8
use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
9
9
use rustc_middle:: lint:: in_external_macro;
10
10
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
11
11
use rustc_span:: { BytePos , Pos , Span , SyntaxContext } ;
12
- use std:: rc:: Rc ;
13
12
14
13
declare_clippy_lint ! {
15
14
/// ### What it does
16
- /// Checks for `unsafe` blocks without a `// SAFETY: ` comment
15
+ /// Checks for `unsafe` blocks and impls without a `// SAFETY: ` comment
17
16
/// explaining why the unsafe operations performed inside
18
17
/// the block are safe.
19
18
///
@@ -36,7 +35,7 @@ declare_clippy_lint! {
36
35
/// ```
37
36
///
38
37
/// ### Why is this bad?
39
- /// Undocumented unsafe blocks can make it difficult to
38
+ /// Undocumented unsafe blocks and impls can make it difficult to
40
39
/// read and maintain code, as well as uncover unsoundness
41
40
/// and bugs.
42
41
///
@@ -68,7 +67,7 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlocks {
68
67
if block. rules == BlockCheckMode :: UnsafeBlock ( UnsafeSource :: UserProvided )
69
68
&& !in_external_macro ( cx. tcx . sess , block. span )
70
69
&& !is_lint_allowed ( cx, UNDOCUMENTED_UNSAFE_BLOCKS , block. hir_id )
71
- && !is_unsafe_from_proc_macro ( cx, block)
70
+ && !is_unsafe_from_proc_macro ( cx, block. span )
72
71
&& !block_has_safety_comment ( cx, block)
73
72
{
74
73
let source_map = cx. tcx . sess . source_map ( ) ;
@@ -89,70 +88,36 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlocks {
89
88
}
90
89
}
91
90
92
- fn check_mod ( & mut self , cx : & LateContext < ' _ > , module : & ' _ hir:: Mod < ' _ > , mod_span : Span , hir_id : hir:: HirId ) {
93
- let source_map = cx. sess ( ) . source_map ( ) ;
94
- let mut item_and_spans: Vec < ( & hir:: Item < ' _ > , Span ) > = Vec :: new ( ) ; // (start, end, item)
95
-
96
- // Collect all items and their spans
97
- for item_id in module. item_ids {
98
- let item = cx. tcx . hir ( ) . item ( * item_id) ;
99
- item_and_spans. push ( ( item, item. span ) ) ;
100
- }
101
- // Sort items by start position
102
- item_and_spans. sort_by_key ( |e| e. 1 . lo ( ) ) ;
103
-
104
- for ( idx, ( item, item_span) ) in item_and_spans. iter ( ) . enumerate ( ) {
105
- if let hir:: ItemKind :: Impl ( imple) = & item. kind
106
- && imple. unsafety == hir:: Unsafety :: Unsafe
107
- && !item_span. from_expansion ( )
108
- && !is_lint_allowed ( cx, UNDOCUMENTED_UNSAFE_BLOCKS , hir_id)
109
- {
110
- // Checks if the lines immediately preceding the impl contain a safety comment.
111
- let impl_has_safety_comment = {
112
- let span_before_impl = if idx == 0 {
113
- // mod A { /* comment */ unsafe impl T {} }
114
- // ^--------------------^
115
- todo ! ( ) ;
116
- //mod_span.until(module.spans)
117
- } else {
118
- // unsafe impl S {} /* comment */ unsafe impl T {}
119
- // ^-------------^
120
- item_and_spans[ idx - 1 ] . 1 . between ( * item_span)
121
- } ;
122
-
123
- if let Ok ( start) = source_map. lookup_line ( span_before_impl. lo ( ) )
124
- && let Ok ( end) = source_map. lookup_line ( span_before_impl. hi ( ) )
125
- && let Some ( src) = start. sf . src . as_deref ( )
126
- {
127
- start. line < end. line && text_has_safety_comment (
128
- src,
129
- & start. sf . lines [ start. line + 1 ..= end. line ] ,
130
- start. sf . start_pos . to_usize ( )
131
- )
132
- } else {
133
- // Problem getting source text. Pretend a comment was found.
134
- true
135
- }
136
- } ;
91
+ fn check_item ( & mut self , cx : & LateContext < ' _ > , item : & hir:: Item < ' _ > ) {
92
+ if let hir:: ItemKind :: Impl ( imple) = item. kind
93
+ && imple. unsafety == hir:: Unsafety :: Unsafe
94
+ && !in_external_macro ( cx. tcx . sess , item. span )
95
+ && !is_lint_allowed ( cx, UNDOCUMENTED_UNSAFE_BLOCKS , item. hir_id ( ) )
96
+ && !is_unsafe_from_proc_macro ( cx, item. span )
97
+ && !item_has_safety_comment ( cx, item)
98
+ {
99
+ let source_map = cx. tcx . sess . source_map ( ) ;
100
+ let span = if source_map. is_multiline ( item. span ) {
101
+ source_map. span_until_char ( item. span , '\n' )
102
+ } else {
103
+ item. span
104
+ } ;
137
105
138
- if !impl_has_safety_comment {
139
- span_lint_and_help (
140
- cx,
141
- UNDOCUMENTED_UNSAFE_BLOCKS ,
142
- * item_span,
143
- "unsafe impl missing a safety comment" ,
144
- None ,
145
- "consider adding a safety comment on the preceding line" ,
146
- ) ;
147
- }
148
- }
106
+ span_lint_and_help (
107
+ cx,
108
+ UNDOCUMENTED_UNSAFE_BLOCKS ,
109
+ span,
110
+ "unsafe impl missing a safety comment" ,
111
+ None ,
112
+ "consider adding a safety comment on the preceding line" ,
113
+ ) ;
149
114
}
150
115
}
151
116
}
152
117
153
- fn is_unsafe_from_proc_macro ( cx : & LateContext < ' _ > , block : & Block < ' _ > ) -> bool {
118
+ fn is_unsafe_from_proc_macro ( cx : & LateContext < ' _ > , span : Span ) -> bool {
154
119
let source_map = cx. sess ( ) . source_map ( ) ;
155
- let file_pos = source_map. lookup_byte_offset ( block . span . lo ( ) ) ;
120
+ let file_pos = source_map. lookup_byte_offset ( span. lo ( ) ) ;
156
121
file_pos
157
122
. sf
158
123
. src
@@ -162,7 +127,7 @@ fn is_unsafe_from_proc_macro(cx: &LateContext<'_>, block: &Block<'_>) -> bool {
162
127
}
163
128
164
129
/// Checks if the lines immediately preceding the block contain a safety comment.
165
- fn block_has_safety_comment ( cx : & LateContext < ' _ > , block : & Block < ' _ > ) -> bool {
130
+ fn block_has_safety_comment ( cx : & LateContext < ' _ > , block : & hir :: Block < ' _ > ) -> bool {
166
131
// This intentionally ignores text before the start of a function so something like:
167
132
// ```
168
133
// // SAFETY: reason
@@ -171,13 +136,85 @@ fn block_has_safety_comment(cx: &LateContext<'_>, block: &Block<'_>) -> bool {
171
136
// won't work. This is to avoid dealing with where such a comment should be place relative to
172
137
// attributes and doc comments.
173
138
139
+ span_from_macro_expansion_has_safety_comment ( cx, block. span ) || span_in_body_has_safety_comment ( cx, block. span )
140
+ }
141
+
142
+ /// Checks if the lines immediately preceding the item contain a safety comment.
143
+ fn item_has_safety_comment ( cx : & LateContext < ' _ > , item : & hir:: Item < ' _ > ) -> bool {
144
+ if span_from_macro_expansion_has_safety_comment ( cx, item. span ) || span_in_body_has_safety_comment ( cx, item. span ) {
145
+ return true ;
146
+ }
147
+
148
+ if item. span . ctxt ( ) == SyntaxContext :: root ( ) {
149
+ if let Some ( parent_node) = get_parent_node ( cx. tcx , item. hir_id ( ) ) {
150
+ let mut span_before_item = None ;
151
+ let mut hi = false ;
152
+ if let Node :: Item ( parent_item) = parent_node {
153
+ if let ItemKind :: Mod ( parent_mod) = & parent_item. kind {
154
+ for ( idx, item_id) in parent_mod. item_ids . iter ( ) . enumerate ( ) {
155
+ if * item_id == item. item_id ( ) {
156
+ if idx == 0 {
157
+ // mod A { /* comment */ unsafe impl T {} ... }
158
+ // ^------------------------------------------^ gets this span
159
+ // ^---------------------^ finally checks the text in this range
160
+ hi = false ;
161
+ span_before_item = Some ( parent_item. span ) ;
162
+ } else {
163
+ let prev_item = cx. tcx . hir ( ) . item ( parent_mod. item_ids [ idx - 1 ] ) ;
164
+ // some_item /* comment */ unsafe impl T {}
165
+ // ^-------^ gets this span
166
+ // ^---------------^ finally checks the text in this range
167
+ hi = true ;
168
+ span_before_item = Some ( prev_item. span ) ;
169
+ }
170
+ break ;
171
+ }
172
+ }
173
+ }
174
+ }
175
+ let span_before_item = span_before_item. unwrap ( ) ;
176
+
177
+ let source_map = cx. sess ( ) . source_map ( ) ;
178
+ if let Some ( item_span) = walk_span_to_context ( item. span , SyntaxContext :: root ( ) )
179
+ && let Some ( span_before_item) = walk_span_to_context ( span_before_item, SyntaxContext :: root ( ) )
180
+ && let Ok ( unsafe_line) = source_map. lookup_line ( item_span. lo ( ) )
181
+ && let Ok ( line_before_unsafe) = source_map. lookup_line ( if hi {
182
+ span_before_item. hi ( )
183
+ } else {
184
+ span_before_item. lo ( )
185
+ } )
186
+ && Lrc :: ptr_eq ( & unsafe_line. sf , & line_before_unsafe. sf )
187
+ && let Some ( src) = unsafe_line. sf . src . as_deref ( )
188
+ {
189
+ line_before_unsafe. line < unsafe_line. line && text_has_safety_comment (
190
+ src,
191
+ & unsafe_line. sf . lines [ line_before_unsafe. line + 1 ..=unsafe_line. line ] ,
192
+ unsafe_line. sf . start_pos . to_usize ( ) ,
193
+ )
194
+ } else {
195
+ // Problem getting source text. Pretend a comment was found.
196
+ true
197
+ }
198
+ } else {
199
+ // No parent node. Pretend a comment was found.
200
+ true
201
+ }
202
+ } else {
203
+ false
204
+ }
205
+ }
206
+
207
+ fn span_from_macro_expansion_has_safety_comment ( cx : & LateContext < ' _ > , span : Span ) -> bool {
174
208
let source_map = cx. sess ( ) . source_map ( ) ;
175
- let ctxt = block. span . ctxt ( ) ;
176
- if ctxt != SyntaxContext :: root ( ) {
177
- // From a macro expansion. Get the text from the start of the macro declaration to start of the unsafe block.
209
+ let ctxt = span. ctxt ( ) ;
210
+ if ctxt == SyntaxContext :: root ( ) {
211
+ false
212
+ } else {
213
+ // From a macro expansion. Get the text from the start of the macro declaration to start of the
214
+ // unsafe block.
178
215
// macro_rules! foo { () => { stuff }; (x) => { unsafe { stuff } }; }
179
216
// ^--------------------------------------------^
180
- if let Ok ( unsafe_line) = source_map. lookup_line ( block . span . lo ( ) )
217
+ if let Ok ( unsafe_line) = source_map. lookup_line ( span. lo ( ) )
181
218
&& let Ok ( macro_line) = source_map. lookup_line ( ctxt. outer_expn_data ( ) . def_site . lo ( ) )
182
219
&& Lrc :: ptr_eq ( & unsafe_line. sf , & macro_line. sf )
183
220
&& let Some ( src) = unsafe_line. sf . src . as_deref ( )
@@ -191,24 +228,35 @@ fn block_has_safety_comment(cx: &LateContext<'_>, block: &Block<'_>) -> bool {
191
228
// Problem getting source text. Pretend a comment was found.
192
229
true
193
230
}
194
- } else if let Ok ( unsafe_line) = source_map. lookup_line ( block. span . lo ( ) )
231
+ }
232
+ }
233
+
234
+ fn span_in_body_has_safety_comment ( cx : & LateContext < ' _ > , span : Span ) -> bool {
235
+ let source_map = cx. sess ( ) . source_map ( ) ;
236
+ let ctxt = span. ctxt ( ) ;
237
+ if ctxt == SyntaxContext :: root ( )
195
238
&& let Some ( body) = cx. enclosing_body
196
- && let Some ( body_span) = walk_span_to_context ( cx. tcx . hir ( ) . body ( body) . value . span , SyntaxContext :: root ( ) )
197
- && let Ok ( body_line) = source_map. lookup_line ( body_span. lo ( ) )
198
- && Lrc :: ptr_eq ( & unsafe_line. sf , & body_line. sf )
199
- && let Some ( src) = unsafe_line. sf . src . as_deref ( )
200
239
{
201
- // Get the text from the start of function body to the unsafe block.
202
- // fn foo() { some_stuff; unsafe { stuff }; other_stuff; }
203
- // ^-------------^
204
- body_line. line < unsafe_line. line && text_has_safety_comment (
205
- src,
206
- & unsafe_line. sf . lines [ body_line. line + 1 ..=unsafe_line. line ] ,
207
- unsafe_line. sf . start_pos . to_usize ( ) ,
208
- )
240
+ if let Ok ( unsafe_line) = source_map. lookup_line ( span. lo ( ) )
241
+ && let Some ( body_span) = walk_span_to_context ( cx. tcx . hir ( ) . body ( body) . value . span , SyntaxContext :: root ( ) )
242
+ && let Ok ( body_line) = source_map. lookup_line ( body_span. lo ( ) )
243
+ && Lrc :: ptr_eq ( & unsafe_line. sf , & body_line. sf )
244
+ && let Some ( src) = unsafe_line. sf . src . as_deref ( )
245
+ {
246
+ // Get the text from the start of function body to the unsafe block.
247
+ // fn foo() { some_stuff; unsafe { stuff }; other_stuff; }
248
+ // ^-------------^
249
+ body_line. line < unsafe_line. line && text_has_safety_comment (
250
+ src,
251
+ & unsafe_line. sf . lines [ body_line. line + 1 ..=unsafe_line. line ] ,
252
+ unsafe_line. sf . start_pos . to_usize ( ) ,
253
+ )
254
+ } else {
255
+ // Problem getting source text. Pretend a comment was found.
256
+ true
257
+ }
209
258
} else {
210
- // Problem getting source text. Pretend a comment was found.
211
- true
259
+ false
212
260
}
213
261
}
214
262
0 commit comments