@@ -8,64 +8,56 @@ 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
10
use rustc_span:: Span ;
11
- use core:: str:: Lines ;
12
- use core:: iter:: Rev ;
13
11
use std:: borrow:: Cow ;
14
12
use clippy_utils:: is_lint_allowed;
15
13
16
14
declare_clippy_lint ! {
17
15
/// ### What it does
18
16
/// Checks for `unsafe` blocks without a `// Safety: ` comment
19
17
/// explaining why the unsafe operations performed inside
20
- /// the block are (or are not) safe.
18
+ /// the block are safe.
21
19
///
22
20
/// ### Why is this bad?
23
- /// Unsafe blocks without a safety comment can be difficult to
24
- /// understand for those that are unfamiliar with the block's code
25
- /// and/or unsafe in general .
21
+ /// Undocumented unsafe blocks can make it difficult to
22
+ /// read and maintain code, as well as uncover unsoundness
23
+ /// and bugs .
26
24
///
27
25
/// ### Example
28
26
/// ```rust
29
- /// unsafe {};
27
+ /// use std::ptr::NonNull;
28
+ /// let a = &mut 42;
30
29
///
31
- /// let _ = unsafe {};
30
+ /// let ptr = unsafe { NonNull::new_unchecked(a) };
32
31
/// ```
33
32
/// Use instead:
34
33
/// ```rust
35
- /// // Safety: ...
36
- /// unsafe {}
34
+ /// use std::ptr::NonNull;
35
+ /// let a = &mut 42;
37
36
///
38
- /// // Safety: .. .
39
- /// let _ = unsafe {};
37
+ /// // Safety: references are guaranteed to be non-null .
38
+ /// let ptr = unsafe { NonNull::new_unchecked(a) };
40
39
/// ```
41
- pub UNDOCUMENTED_UNSAFE_BLOCK_SAFETY ,
40
+ pub UNDOCUMENTED_UNSAFE_BLOCKS ,
42
41
restriction,
43
- "creating an unsafe block without explaining why it is or is not safe"
42
+ "creating an unsafe block without explaining why it is safe"
44
43
}
45
44
46
- impl_lint_pass ! ( UndocumentedUnsafeBlockSafety => [ UNDOCUMENTED_UNSAFE_BLOCK_SAFETY ] ) ;
45
+ impl_lint_pass ! ( UndocumentedUnsafeBlocks => [ UNDOCUMENTED_UNSAFE_BLOCKS ] ) ;
47
46
48
47
#[ derive( Default ) ]
49
- pub struct UndocumentedUnsafeBlockSafety {
48
+ pub struct UndocumentedUnsafeBlocks {
50
49
pub local : bool
51
50
}
52
51
53
- #[ derive( PartialEq ) ]
54
- enum CommentType {
55
- Safety ,
56
- Unrelated ,
57
- None ,
58
- }
59
-
60
- impl LateLintPass < ' _ > for UndocumentedUnsafeBlockSafety {
52
+ impl LateLintPass < ' _ > for UndocumentedUnsafeBlocks {
61
53
fn check_block ( & mut self , cx : & LateContext < ' _ > , block : & ' _ Block < ' _ > ) {
62
54
if self . local {
63
55
self . local = false ;
64
56
return
65
57
}
66
58
67
59
if_chain ! {
68
- if !is_lint_allowed( cx, UNDOCUMENTED_UNSAFE_BLOCK_SAFETY , block. hir_id) ;
60
+ if !is_lint_allowed( cx, UNDOCUMENTED_UNSAFE_BLOCKS , block. hir_id) ;
69
61
if !in_external_macro( cx. tcx. sess, block. span) ;
70
62
if let BlockCheckMode :: UnsafeBlock ( UnsafeSource :: UserProvided ) = block. rules;
71
63
if let Some ( enclosing_scope_hir_id) = cx. tcx. hir( ) . get_enclosing_scope( block. hir_id) ;
@@ -77,7 +69,7 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlockSafety {
77
69
78
70
fn check_local ( & mut self , cx : & LateContext < ' _ > , local : & ' _ Local < ' _ > ) {
79
71
if_chain ! {
80
- if !is_lint_allowed( cx, UNDOCUMENTED_UNSAFE_BLOCK_SAFETY , local. hir_id) ;
72
+ if !is_lint_allowed( cx, UNDOCUMENTED_UNSAFE_BLOCKS , local. hir_id) ;
81
73
if !in_external_macro( cx. tcx. sess, local. span) ;
82
74
if let Some ( init) = local. init;
83
75
if let ExprKind :: Block ( block, _) = init. kind;
@@ -101,7 +93,7 @@ fn find_candidate(cx: &LateContext<'_>, span: Span, enclosing_hir_id: HirId) {
101
93
102
94
span_lint_and_sugg (
103
95
cx,
104
- UNDOCUMENTED_UNSAFE_BLOCK_SAFETY ,
96
+ UNDOCUMENTED_UNSAFE_BLOCKS ,
105
97
span,
106
98
"unsafe block missing a safety comment" ,
107
99
"consider adding a safety comment" ,
@@ -123,76 +115,28 @@ fn block_has_safety_comment(tcx: TyCtxt<'_>, enclosing_hir_id: HirId, block_span
123
115
let lex_start = ( between_span. lo ( ) . 0 + 1 ) as usize ;
124
116
let mut src_str = source_file. src . as_ref ( ) ?[ lex_start..between_span. hi ( ) . 0 as usize ] . to_string ( ) ;
125
117
126
- // Remove all whitespace, but retain newlines to verify the block immediately follows the comment
127
- src_str. retain ( |c| c == '\n' || !c. is_whitespace ( ) ) ;
118
+ src_str. retain ( |c| !c. is_whitespace ( ) ) ;
128
119
129
- let mut src_str_split = src_str. lines ( ) . rev ( ) ;
120
+ let src_str_split = src_str. lines ( ) . rev ( ) . collect :: < Vec < _ > > ( ) ;
121
+ let src_str = src_str_split. join ( "" ) ;
130
122
123
+ let mut pos = 0 ;
131
124
let mut found_safety_comment = false ;
132
125
133
- while let Some ( line) = src_str_split. next ( ) {
134
- match line {
135
- "*/" => return Some ( check_multiline_block ( & mut src_str_split, None ) ) ,
136
- line if ( line. starts_with ( '*' ) && line. ends_with ( "*/" ) ) => return Some ( check_multiline_block ( & mut src_str_split, Some ( & line[ 1 ..] ) ) ) ,
137
- // Covers both line comments and single line block comments
138
- line => match contains_safety_comment ( line, false ) {
139
- CommentType :: Safety => { found_safety_comment = true ; break } ,
140
- CommentType :: Unrelated => continue ,
141
- CommentType :: None => break ,
142
- }
143
- }
144
- }
145
-
146
- Some ( found_safety_comment)
147
- }
148
-
149
- fn check_multiline_block ( lines : & mut Rev < Lines < ' _ > > , same_line_terminator : Option < & str > ) -> bool {
150
- let mut found_safety = false ;
151
-
152
- if let Some ( line) = same_line_terminator {
153
- found_safety = contains_safety_comment ( line, true ) == CommentType :: Safety ;
154
- }
155
-
156
- for next_line in lines {
157
- if found_safety {
158
- break
159
- }
160
-
161
- let text_start = if next_line. starts_with ( '*' ) {
162
- 1
163
- } else if next_line. starts_with ( "/*" ) {
164
- 2
165
- } else {
166
- return false
167
- } ;
168
-
169
- found_safety = contains_safety_comment ( & next_line[ text_start..] , true ) == CommentType :: Safety ;
170
- }
171
-
172
- found_safety
173
- }
174
-
175
- fn contains_safety_comment ( comment : & str , block : bool ) -> CommentType {
176
- if block || is_comment ( comment) {
177
- let comment_upper = comment. to_uppercase ( ) ;
178
- if comment_upper. trim_start ( ) . starts_with ( "SAFETY:" ) || comment_upper. contains ( "SAFETY:" ) {
179
- return CommentType :: Safety
180
- }
181
-
182
- return CommentType :: Unrelated
183
- }
184
-
185
- CommentType :: None
186
- }
187
-
188
- fn is_comment ( comment : & str ) -> bool {
189
- if let Some ( token) = rustc_lexer:: tokenize ( comment) . next ( ) {
126
+ for token in rustc_lexer:: tokenize ( & src_str) {
190
127
match token. kind {
191
128
TokenKind :: LineComment { doc_style : None }
192
- | TokenKind :: BlockComment { doc_style : None , terminated : true } => return true ,
193
- _ => { } ,
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
133
+ }
134
+ } ,
135
+ _ => break
194
136
}
137
+
138
+ pos += token. len ;
195
139
}
196
140
197
- false
141
+ Some ( found_safety_comment )
198
142
}
0 commit comments