@@ -7,8 +7,8 @@ use clippy_utils::is_lint_allowed;
7
7
use clippy_utils:: source:: walk_span_to_context;
8
8
use clippy_utils:: visitors:: { Descend , for_each_expr} ;
9
9
use hir:: HirId ;
10
- use rustc_hir as hir ;
11
- use rustc_hir:: { Block , BlockCheckMode , Impl , ItemKind , Node , UnsafeSource } ;
10
+ use rustc_errors :: Applicability ;
11
+ use rustc_hir:: { self as hir , Block , BlockCheckMode , FnSig , Impl , ItemKind , Node , UnsafeSource } ;
12
12
use rustc_lexer:: { FrontmatterAllowed , TokenKind , tokenize} ;
13
13
use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
14
14
use rustc_session:: impl_lint_pass;
@@ -143,7 +143,7 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks {
143
143
if let Some ( tail) = block. expr
144
144
&& !is_lint_allowed ( cx, UNNECESSARY_SAFETY_COMMENT , tail. hir_id )
145
145
&& !tail. span . in_external_macro ( cx. tcx . sess . source_map ( ) )
146
- && let HasSafetyComment :: Yes ( pos) =
146
+ && let HasSafetyComment :: Yes ( pos, _ ) =
147
147
stmt_has_safety_comment ( cx, tail. span , tail. hir_id , self . accept_comment_above_attributes )
148
148
&& let Some ( help_span) = expr_has_unnecessary_safety_comment ( cx, tail, pos)
149
149
{
@@ -168,7 +168,7 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks {
168
168
} ;
169
169
if !is_lint_allowed ( cx, UNNECESSARY_SAFETY_COMMENT , stmt. hir_id )
170
170
&& !stmt. span . in_external_macro ( cx. tcx . sess . source_map ( ) )
171
- && let HasSafetyComment :: Yes ( pos) =
171
+ && let HasSafetyComment :: Yes ( pos, _ ) =
172
172
stmt_has_safety_comment ( cx, stmt. span , stmt. hir_id , self . accept_comment_above_attributes )
173
173
&& let Some ( help_span) = expr_has_unnecessary_safety_comment ( cx, expr, pos)
174
174
{
@@ -203,14 +203,14 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks {
203
203
204
204
let item_has_safety_comment = item_has_safety_comment ( cx, item, self . accept_comment_above_attributes ) ;
205
205
match item_has_safety_comment {
206
- HasSafetyComment :: Yes ( pos) => check_has_safety_comment ( cx, item, mk_spans ( pos) ) ,
206
+ HasSafetyComment :: Yes ( pos, is_doc ) => check_has_safety_comment ( cx, item, mk_spans ( pos) , is_doc ) ,
207
207
HasSafetyComment :: No => check_has_no_safety_comment ( cx, item) ,
208
208
HasSafetyComment :: Maybe => { } ,
209
209
}
210
210
}
211
211
}
212
212
213
- fn check_has_safety_comment ( cx : & LateContext < ' _ > , item : & hir:: Item < ' _ > , ( span, help_span) : ( Span , Span ) ) {
213
+ fn check_has_safety_comment ( cx : & LateContext < ' _ > , item : & hir:: Item < ' _ > , ( span, help_span) : ( Span , Span ) , is_doc : bool ) {
214
214
match & item. kind {
215
215
ItemKind :: Impl ( Impl {
216
216
of_trait : Some ( of_trait) ,
@@ -252,6 +252,50 @@ fn check_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>, (span, h
252
252
}
253
253
}
254
254
} ,
255
+ // Unsafe functions with a SAFETY comment are suggested to change it to a `# Safety` comment
256
+ ItemKind :: Fn {
257
+ sig : FnSig { header, .. } ,
258
+ ..
259
+ } if header. is_unsafe ( ) => {
260
+ if !is_lint_allowed ( cx, UNNECESSARY_SAFETY_COMMENT , item. hir_id ( ) ) {
261
+ span_lint_and_then (
262
+ cx,
263
+ UNNECESSARY_SAFETY_COMMENT ,
264
+ span,
265
+ format ! (
266
+ "{} has safety comment, but maybe a `# Safety` segment would be better" ,
267
+ cx. tcx. def_descr( item. owner_id. to_def_id( ) ) ,
268
+ ) ,
269
+ |diag| {
270
+ if is_doc {
271
+ // If it's already within a doc comment, we try to suggest the change
272
+
273
+ let source_map = cx. sess ( ) . source_map ( ) ;
274
+ if let Ok ( unsafe_line) = source_map. lookup_line ( item. span . lo ( ) )
275
+ && let Some ( src) = unsafe_line. sf . src . as_deref ( )
276
+ {
277
+ let help_pos_lo = source_map. lookup_byte_offset ( help_span. lo ( ) ) ;
278
+ let help_pos_hi = source_map. lookup_byte_offset ( help_span. hi ( ) ) ;
279
+
280
+ diag. span_suggestion (
281
+ help_span,
282
+ "consider changing it to a `# Safety` section" ,
283
+ src. get ( help_pos_lo. pos . to_usize ( ) ..help_pos_hi. pos . to_usize ( ) )
284
+ . unwrap ( )
285
+ . replace ( "SAFETY:" , "# Safety" ) ,
286
+ Applicability :: MachineApplicable ,
287
+ ) ;
288
+ }
289
+ } else {
290
+ diag. span_help (
291
+ help_span,
292
+ "consider changing the `SAFETY` comment for a `# Safety` doc comment" ,
293
+ ) ;
294
+ }
295
+ } ,
296
+ ) ;
297
+ }
298
+ } ,
255
299
// Aside from unsafe impls and consts/statics with an unsafe block, items in general
256
300
// do not have safety invariants that need to be documented, so lint those.
257
301
_ => {
@@ -453,16 +497,22 @@ fn block_has_safety_comment(cx: &LateContext<'_>, span: Span, accept_comment_abo
453
497
454
498
matches ! (
455
499
span_from_macro_expansion_has_safety_comment( cx, span, accept_comment_above_attributes) ,
456
- HasSafetyComment :: Yes ( _)
500
+ HasSafetyComment :: Yes ( _, _ )
457
501
) || span_has_safety_comment ( cx, span, accept_comment_above_attributes)
458
502
}
459
503
504
+ #[ derive( Debug ) ]
460
505
enum HasSafetyComment {
461
- Yes ( BytePos ) ,
506
+ Yes ( BytePos , bool ) ,
462
507
No ,
463
508
Maybe ,
464
509
}
465
510
511
+ enum SafetyComment {
512
+ Present { pos : BytePos , is_doc : bool } ,
513
+ Absent ,
514
+ }
515
+
466
516
/// Checks if the lines immediately preceding the item contain a safety comment.
467
517
#[ allow( clippy:: collapsible_match) ]
468
518
fn item_has_safety_comment (
@@ -522,8 +572,8 @@ fn item_has_safety_comment(
522
572
unsafe_line. sf . start_pos ,
523
573
accept_comment_above_attributes,
524
574
) {
525
- Some ( b ) => HasSafetyComment :: Yes ( b ) ,
526
- None => HasSafetyComment :: No ,
575
+ SafetyComment :: Present { pos , is_doc } => HasSafetyComment :: Yes ( pos , is_doc ) ,
576
+ SafetyComment :: Absent => HasSafetyComment :: No ,
527
577
}
528
578
} ;
529
579
}
@@ -568,8 +618,8 @@ fn stmt_has_safety_comment(
568
618
unsafe_line. sf . start_pos ,
569
619
accept_comment_above_attributes,
570
620
) {
571
- Some ( b ) => HasSafetyComment :: Yes ( b ) ,
572
- None => HasSafetyComment :: No ,
621
+ SafetyComment :: Present { pos , is_doc } => HasSafetyComment :: Yes ( pos , is_doc ) ,
622
+ SafetyComment :: Absent => HasSafetyComment :: No ,
573
623
}
574
624
} ;
575
625
}
@@ -649,8 +699,8 @@ fn span_from_macro_expansion_has_safety_comment(
649
699
unsafe_line. sf . start_pos ,
650
700
accept_comment_above_attributes,
651
701
) {
652
- Some ( b ) => HasSafetyComment :: Yes ( b ) ,
653
- None => HasSafetyComment :: No ,
702
+ SafetyComment :: Present { pos , is_doc } => HasSafetyComment :: Yes ( pos , is_doc ) ,
703
+ SafetyComment :: Absent => HasSafetyComment :: No ,
654
704
}
655
705
} else {
656
706
HasSafetyComment :: No
@@ -710,13 +760,15 @@ fn span_has_safety_comment(cx: &LateContext<'_>, span: Span, accept_comment_abov
710
760
// fn foo() { some_stuff; unsafe { stuff }; other_stuff; }
711
761
// ^-------------^
712
762
body_line. line < unsafe_line. line
713
- && text_has_safety_comment (
714
- src,
715
- & unsafe_line. sf . lines ( ) [ body_line. line + 1 ..=unsafe_line. line ] ,
716
- unsafe_line. sf . start_pos ,
717
- accept_comment_above_attributes,
763
+ && matches ! (
764
+ text_has_safety_comment(
765
+ src,
766
+ & unsafe_line. sf. lines( ) [ body_line. line + 1 ..=unsafe_line. line] ,
767
+ unsafe_line. sf. start_pos,
768
+ accept_comment_above_attributes,
769
+ ) ,
770
+ SafetyComment :: Present { .. }
718
771
)
719
- . is_some ( )
720
772
} else {
721
773
// Problem getting source text. Pretend a comment was found.
722
774
true
@@ -735,7 +787,7 @@ fn text_has_safety_comment(
735
787
line_starts : & [ RelativeBytePos ] ,
736
788
start_pos : BytePos ,
737
789
accept_comment_above_attributes : bool ,
738
- ) -> Option < BytePos > {
790
+ ) -> SafetyComment {
739
791
let mut lines = line_starts
740
792
. array_windows :: < 2 > ( )
741
793
. rev ( )
@@ -748,7 +800,10 @@ fn text_has_safety_comment(
748
800
} )
749
801
. filter ( |( _, text) | !( text. is_empty ( ) || ( accept_comment_above_attributes && is_attribute ( text) ) ) ) ;
750
802
751
- let ( line_start, line) = lines. next ( ) ?;
803
+ let Some ( ( line_start, line) ) = lines. next ( ) else {
804
+ return SafetyComment :: Absent ;
805
+ } ;
806
+
752
807
let mut in_codeblock = false ;
753
808
// Check for a sequence of line comments.
754
809
if line. starts_with ( "//" ) {
@@ -761,12 +816,15 @@ fn text_has_safety_comment(
761
816
in_codeblock = !in_codeblock;
762
817
}
763
818
764
- if line. to_ascii_uppercase ( ) . contains ( "SAFETY:" ) && !in_codeblock {
765
- return Some ( start_pos + BytePos ( u32:: try_from ( line_start) . unwrap ( ) ) ) ;
819
+ if !in_codeblock && line. to_ascii_uppercase ( ) . contains ( "SAFETY:" ) {
820
+ return SafetyComment :: Present {
821
+ pos : start_pos + BytePos ( u32:: try_from ( line_start) . unwrap ( ) ) ,
822
+ is_doc : line. starts_with ( "///" ) ,
823
+ } ;
766
824
}
767
825
match lines. next ( ) {
768
826
Some ( ( s, x) ) if x. starts_with ( "//" ) => ( line, line_start) = ( x, s) ,
769
- _ => return None ,
827
+ _ => return SafetyComment :: Absent ,
770
828
}
771
829
}
772
830
}
@@ -777,15 +835,22 @@ fn text_has_safety_comment(
777
835
if line. starts_with ( "/*" ) {
778
836
let src = & src[ line_start..line_starts. last ( ) . unwrap ( ) . to_usize ( ) ] ;
779
837
let mut tokens = tokenize ( src, FrontmatterAllowed :: No ) ;
780
- return ( src[ ..tokens. next ( ) . unwrap ( ) . len as usize ]
781
- . to_ascii_uppercase ( )
782
- . contains ( "SAFETY:" )
783
- && tokens. all ( |t| t. kind == TokenKind :: Whitespace ) )
784
- . then_some ( start_pos + BytePos ( u32:: try_from ( line_start) . unwrap ( ) ) ) ;
838
+ let a = tokens. next ( ) ;
839
+ if let Some ( safety_pos) = src[ ..a. unwrap ( ) . len as usize ] . to_ascii_uppercase ( ) . find ( "SAFETY:" )
840
+ && tokens. all ( |t| t. kind == TokenKind :: Whitespace )
841
+ {
842
+ return SafetyComment :: Present {
843
+ pos : start_pos
844
+ + BytePos ( u32:: try_from ( line_start) . unwrap ( ) )
845
+ + BytePos ( u32:: try_from ( safety_pos) . unwrap ( ) ) ,
846
+ is_doc : line. starts_with ( "/**" ) ,
847
+ } ;
848
+ }
849
+ return SafetyComment :: Absent ;
785
850
}
786
851
match lines. next ( ) {
787
852
Some ( x) => ( line_start, line) = x,
788
- None => return None ,
853
+ None => return SafetyComment :: Absent ,
789
854
}
790
855
}
791
856
}
0 commit comments