@@ -8,64 +8,56 @@ use rustc_middle::lint::in_external_macro;
88use rustc_middle:: ty:: TyCtxt ;
99use rustc_session:: { impl_lint_pass, declare_tool_lint} ;
1010use rustc_span:: Span ;
11- use core:: str:: Lines ;
12- use core:: iter:: Rev ;
1311use std:: borrow:: Cow ;
1412use clippy_utils:: is_lint_allowed;
1513
1614declare_clippy_lint ! {
1715 /// ### What it does
1816 /// Checks for `unsafe` blocks without a `// Safety: ` comment
1917 /// explaining why the unsafe operations performed inside
20- /// the block are (or are not) safe.
18+ /// the block are safe.
2119 ///
2220 /// ### 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 .
2624 ///
2725 /// ### Example
2826 /// ```rust
29- /// unsafe {};
27+ /// use std::ptr::NonNull;
28+ /// let a = &mut 42;
3029 ///
31- /// let _ = unsafe {};
30+ /// let ptr = unsafe { NonNull::new_unchecked(a) };
3231 /// ```
3332 /// Use instead:
3433 /// ```rust
35- /// // Safety: ...
36- /// unsafe {}
34+ /// use std::ptr::NonNull;
35+ /// let a = &mut 42;
3736 ///
38- /// // Safety: .. .
39- /// let _ = unsafe {};
37+ /// // Safety: references are guaranteed to be non-null .
38+ /// let ptr = unsafe { NonNull::new_unchecked(a) };
4039 /// ```
41- pub UNDOCUMENTED_UNSAFE_BLOCK_SAFETY ,
40+ pub UNDOCUMENTED_UNSAFE_BLOCKS ,
4241 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"
4443}
4544
46- impl_lint_pass ! ( UndocumentedUnsafeBlockSafety => [ UNDOCUMENTED_UNSAFE_BLOCK_SAFETY ] ) ;
45+ impl_lint_pass ! ( UndocumentedUnsafeBlocks => [ UNDOCUMENTED_UNSAFE_BLOCKS ] ) ;
4746
4847#[ derive( Default ) ]
49- pub struct UndocumentedUnsafeBlockSafety {
48+ pub struct UndocumentedUnsafeBlocks {
5049 pub local : bool
5150}
5251
53- #[ derive( PartialEq ) ]
54- enum CommentType {
55- Safety ,
56- Unrelated ,
57- None ,
58- }
59-
60- impl LateLintPass < ' _ > for UndocumentedUnsafeBlockSafety {
52+ impl LateLintPass < ' _ > for UndocumentedUnsafeBlocks {
6153 fn check_block ( & mut self , cx : & LateContext < ' _ > , block : & ' _ Block < ' _ > ) {
6254 if self . local {
6355 self . local = false ;
6456 return
6557 }
6658
6759 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) ;
6961 if !in_external_macro( cx. tcx. sess, block. span) ;
7062 if let BlockCheckMode :: UnsafeBlock ( UnsafeSource :: UserProvided ) = block. rules;
7163 if let Some ( enclosing_scope_hir_id) = cx. tcx. hir( ) . get_enclosing_scope( block. hir_id) ;
@@ -77,7 +69,7 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlockSafety {
7769
7870 fn check_local ( & mut self , cx : & LateContext < ' _ > , local : & ' _ Local < ' _ > ) {
7971 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) ;
8173 if !in_external_macro( cx. tcx. sess, local. span) ;
8274 if let Some ( init) = local. init;
8375 if let ExprKind :: Block ( block, _) = init. kind;
@@ -101,7 +93,7 @@ fn find_candidate(cx: &LateContext<'_>, span: Span, enclosing_hir_id: HirId) {
10193
10294 span_lint_and_sugg (
10395 cx,
104- UNDOCUMENTED_UNSAFE_BLOCK_SAFETY ,
96+ UNDOCUMENTED_UNSAFE_BLOCKS ,
10597 span,
10698 "unsafe block missing a safety comment" ,
10799 "consider adding a safety comment" ,
@@ -123,76 +115,28 @@ fn block_has_safety_comment(tcx: TyCtxt<'_>, enclosing_hir_id: HirId, block_span
123115 let lex_start = ( between_span. lo ( ) . 0 + 1 ) as usize ;
124116 let mut src_str = source_file. src . as_ref ( ) ?[ lex_start..between_span. hi ( ) . 0 as usize ] . to_string ( ) ;
125117
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 ( ) ) ;
128119
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 ( "" ) ;
130122
123+ let mut pos = 0 ;
131124 let mut found_safety_comment = false ;
132125
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) {
190127 match token. kind {
191128 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
194136 }
137+
138+ pos += token. len ;
195139 }
196140
197- false
141+ Some ( found_safety_comment )
198142}
0 commit comments