Skip to content

Commit d70fb58

Browse files
committed
Fixed the ICE and safety comments between attributes
- No longer using attr.span() - ignoring attributes manually
1 parent f89ffbf commit d70fb58

File tree

5 files changed

+99
-59
lines changed

5 files changed

+99
-59
lines changed

clippy_lints/src/undocumented_unsafe_blocks.rs

Lines changed: 39 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks {
113113
&& !block.span.in_external_macro(cx.tcx.sess.source_map())
114114
&& !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, block.hir_id)
115115
&& !is_unsafe_from_proc_macro(cx, block.span)
116-
&& !block_has_safety_comment(cx, block.span)
116+
&& !block_has_safety_comment(cx, block.span, self.accept_comment_above_attributes)
117117
&& !block_parents_have_safety_comment(
118118
self.accept_comment_above_statement,
119119
self.accept_comment_above_attributes,
@@ -408,46 +408,29 @@ fn block_parents_have_safety_comment(
408408
cx: &LateContext<'_>,
409409
id: HirId,
410410
) -> bool {
411-
let (span, hir_id) = match cx.tcx.parent_hir_node(id) {
412-
Node::Expr(expr) if let Some(inner) = find_unsafe_block_parent_in_expr(cx, expr) => inner,
411+
let span = match cx.tcx.parent_hir_node(id) {
412+
Node::Expr(expr) if let Some((span, _)) = find_unsafe_block_parent_in_expr(cx, expr) => span,
413413
Node::Stmt(hir::Stmt {
414414
kind:
415-
hir::StmtKind::Let(hir::LetStmt { span, hir_id, .. })
416-
| hir::StmtKind::Expr(hir::Expr { span, hir_id, .. })
417-
| hir::StmtKind::Semi(hir::Expr { span, hir_id, .. }),
415+
hir::StmtKind::Let(hir::LetStmt { span, .. })
416+
| hir::StmtKind::Expr(hir::Expr { span, .. })
417+
| hir::StmtKind::Semi(hir::Expr { span, .. }),
418418
..
419419
})
420-
| Node::LetStmt(hir::LetStmt { span, hir_id, .. }) => (*span, *hir_id),
420+
| Node::LetStmt(hir::LetStmt { span, .. }) => *span,
421421

422-
node if let Some((span, hir_id)) = span_and_hid_of_item_alike_node(&node)
422+
node if let Some((span, _)) = span_and_hid_of_item_alike_node(&node)
423423
&& is_const_or_static(&node) =>
424424
{
425-
(span, hir_id)
425+
span
426426
},
427427

428428
_ => return false,
429429
};
430430
// if unsafe block is part of a let/const/static statement,
431431
// and accept_comment_above_statement is set to true
432432
// we accept the safety comment in the line the precedes this statement.
433-
accept_comment_above_statement
434-
&& span_with_attrs_has_safety_comment(cx, span, hir_id, accept_comment_above_attributes)
435-
}
436-
437-
/// Extends `span` to also include its attributes, then checks if that span has a safety comment.
438-
fn span_with_attrs_has_safety_comment(
439-
cx: &LateContext<'_>,
440-
span: Span,
441-
hir_id: HirId,
442-
accept_comment_above_attributes: bool,
443-
) -> bool {
444-
let span = if accept_comment_above_attributes {
445-
include_attrs_in_span(cx, hir_id, span)
446-
} else {
447-
span
448-
};
449-
450-
span_has_safety_comment(cx, span)
433+
accept_comment_above_statement && span_has_safety_comment(cx, span, accept_comment_above_attributes)
451434
}
452435

453436
/// Checks if an expression is "branchy", e.g. loop, match/if/etc.
@@ -459,7 +442,7 @@ fn is_branchy(expr: &hir::Expr<'_>) -> bool {
459442
}
460443

461444
/// Checks if the lines immediately preceding the block contain a safety comment.
462-
fn block_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool {
445+
fn block_has_safety_comment(cx: &LateContext<'_>, span: Span, accept_comment_above_attributes: bool) -> bool {
463446
// This intentionally ignores text before the start of a function so something like:
464447
// ```
465448
// // SAFETY: reason
@@ -469,18 +452,9 @@ fn block_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool {
469452
// attributes and doc comments.
470453

471454
matches!(
472-
span_from_macro_expansion_has_safety_comment(cx, span),
455+
span_from_macro_expansion_has_safety_comment(cx, span, accept_comment_above_attributes),
473456
HasSafetyComment::Yes(_)
474-
) || span_has_safety_comment(cx, span)
475-
}
476-
477-
fn include_attrs_in_span(cx: &LateContext<'_>, hir_id: HirId, span: Span) -> Span {
478-
span.to(cx.tcx.hir_attrs(hir_id).iter().fold(span, |acc, attr| {
479-
if attr.is_doc_comment() {
480-
return acc;
481-
}
482-
acc.to(attr.span())
483-
}))
457+
) || span_has_safety_comment(cx, span, accept_comment_above_attributes)
484458
}
485459

486460
#[derive(Debug)]
@@ -497,7 +471,7 @@ fn item_has_safety_comment(
497471
item: &hir::Item<'_>,
498472
accept_comment_above_attributes: bool,
499473
) -> HasSafetyComment {
500-
match span_from_macro_expansion_has_safety_comment(cx, item.span) {
474+
match span_from_macro_expansion_has_safety_comment(cx, item.span, accept_comment_above_attributes) {
501475
HasSafetyComment::Maybe => (),
502476
has_safety_comment => return has_safety_comment,
503477
}
@@ -530,15 +504,10 @@ fn item_has_safety_comment(
530504
},
531505
};
532506

533-
let mut span = item.span;
534-
if accept_comment_above_attributes {
535-
span = include_attrs_in_span(cx, item.hir_id(), span);
536-
}
537-
538507
let source_map = cx.sess().source_map();
539508
// If the comment is in the first line of the file, there is no preceding line
540509
if let Some(comment_start) = comment_start
541-
&& let Ok(unsafe_line) = source_map.lookup_line(span.lo())
510+
&& let Ok(unsafe_line) = source_map.lookup_line(item.span.lo())
542511
&& let Ok(comment_start_line) = source_map.lookup_line(comment_start.into())
543512
&& let include_first_line_of_file = matches!(comment_start, CommentStartBeforeItem::Start)
544513
&& (include_first_line_of_file || Arc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf))
@@ -552,6 +521,7 @@ fn item_has_safety_comment(
552521
&unsafe_line.sf.lines()
553522
[(comment_start_line.line + usize::from(!include_first_line_of_file))..=unsafe_line.line],
554523
unsafe_line.sf.start_pos,
524+
accept_comment_above_attributes,
555525
) {
556526
Some(b) => HasSafetyComment::Yes(b),
557527
None => HasSafetyComment::No,
@@ -569,7 +539,7 @@ fn stmt_has_safety_comment(
569539
hir_id: HirId,
570540
accept_comment_above_attributes: bool,
571541
) -> HasSafetyComment {
572-
match span_from_macro_expansion_has_safety_comment(cx, span) {
542+
match span_from_macro_expansion_has_safety_comment(cx, span, accept_comment_above_attributes) {
573543
HasSafetyComment::Maybe => (),
574544
has_safety_comment => return has_safety_comment,
575545
}
@@ -583,26 +553,21 @@ fn stmt_has_safety_comment(
583553
_ => return HasSafetyComment::Maybe,
584554
};
585555

586-
let mut span = span;
587-
if accept_comment_above_attributes {
588-
span = include_attrs_in_span(cx, hir_id, span);
589-
}
590-
591556
let source_map = cx.sess().source_map();
592557
if let Some(comment_start) = comment_start
593558
&& let Ok(unsafe_line) = source_map.lookup_line(span.lo())
594559
&& let Ok(comment_start_line) = source_map.lookup_line(comment_start)
595560
&& Arc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf)
596561
&& let Some(src) = unsafe_line.sf.src.as_deref()
597562
{
598-
// dbg!(src);
599563
return if comment_start_line.line >= unsafe_line.line {
600564
HasSafetyComment::No
601565
} else {
602566
match text_has_safety_comment(
603567
src,
604568
&unsafe_line.sf.lines()[comment_start_line.line + 1..=unsafe_line.line],
605569
unsafe_line.sf.start_pos,
570+
accept_comment_above_attributes,
606571
) {
607572
Some(b) => HasSafetyComment::Yes(b),
608573
None => HasSafetyComment::No,
@@ -659,7 +624,11 @@ fn comment_start_before_item_in_mod(
659624
})
660625
}
661626

662-
fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span) -> HasSafetyComment {
627+
fn span_from_macro_expansion_has_safety_comment(
628+
cx: &LateContext<'_>,
629+
span: Span,
630+
accept_comment_above_attributes: bool,
631+
) -> HasSafetyComment {
663632
let source_map = cx.sess().source_map();
664633
let ctxt = span.ctxt();
665634
if ctxt == SyntaxContext::root() {
@@ -679,6 +648,7 @@ fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span
679648
src,
680649
&unsafe_line.sf.lines()[macro_line.line + 1..=unsafe_line.line],
681650
unsafe_line.sf.start_pos,
651+
accept_comment_above_attributes,
682652
) {
683653
Some(b) => HasSafetyComment::Yes(b),
684654
None => HasSafetyComment::No,
@@ -725,7 +695,7 @@ fn get_body_search_span(cx: &LateContext<'_>) -> Option<Span> {
725695
None
726696
}
727697

728-
fn span_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool {
698+
fn span_has_safety_comment(cx: &LateContext<'_>, span: Span, accept_comment_above_attributes: bool) -> bool {
729699
let source_map = cx.sess().source_map();
730700
let ctxt = span.ctxt();
731701
if ctxt.is_root()
@@ -745,6 +715,7 @@ fn span_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool {
745715
src,
746716
&unsafe_line.sf.lines()[body_line.line + 1..=unsafe_line.line],
747717
unsafe_line.sf.start_pos,
718+
accept_comment_above_attributes,
748719
)
749720
.is_some()
750721
} else {
@@ -757,7 +728,14 @@ fn span_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool {
757728
}
758729

759730
/// Checks if the given text has a safety comment for the immediately proceeding line.
760-
fn text_has_safety_comment(src: &str, line_starts: &[RelativeBytePos], start_pos: BytePos) -> Option<BytePos> {
731+
///
732+
/// If `accept_comment_above_attributes` is true, it will ignore attributes inbetween blocks of comments
733+
fn text_has_safety_comment(
734+
src: &str,
735+
line_starts: &[RelativeBytePos],
736+
start_pos: BytePos,
737+
accept_comment_above_attributes: bool,
738+
) -> Option<BytePos> {
761739
let mut lines = line_starts
762740
.array_windows::<2>()
763741
.rev()
@@ -768,7 +746,7 @@ fn text_has_safety_comment(src: &str, line_starts: &[RelativeBytePos], start_pos
768746
let trimmed = text.trim_start();
769747
Some((start + (text.len() - trimmed.len()), trimmed))
770748
})
771-
.filter(|(_, text)| !text.is_empty());
749+
.filter(|(_, text)| !(text.is_empty() || (accept_comment_above_attributes && is_attribute(text))));
772750

773751
let (line_start, line) = lines.next()?;
774752
let mut in_codeblock = false;
@@ -812,6 +790,10 @@ fn text_has_safety_comment(src: &str, line_starts: &[RelativeBytePos], start_pos
812790
}
813791
}
814792

793+
fn is_attribute(text: &str) -> bool {
794+
(text.starts_with("#[") || text.starts_with("#![")) && text.trim_end().ends_with(']')
795+
}
796+
815797
fn span_and_hid_of_item_alike_node(node: &Node<'_>) -> Option<(Span, HirId)> {
816798
match node {
817799
Node::Item(item) => Some((item.span, item.owner_id.into())),

tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.default.stderr

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -390,5 +390,29 @@ help: consider removing the safety comment
390390
LL | // SAFETY: ...
391391
| ^^^^^^^^^^^^^^
392392

393-
error: aborting due to 43 previous errors
393+
error: module has unnecessary safety comment
394+
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:750:5
395+
|
396+
LL | mod z {}
397+
| ^^^^^^^^
398+
|
399+
help: consider removing the safety comment
400+
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:749:5
401+
|
402+
LL | // SAFETY: ...
403+
| ^^^^^^^^^^^^^^
404+
405+
error: module has unnecessary safety comment
406+
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:758:5
407+
|
408+
LL | mod y {}
409+
| ^^^^^^^^
410+
|
411+
help: consider removing the safety comment
412+
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:756:5
413+
|
414+
LL | // SAFETY: ...
415+
| ^^^^^^^^^^^^^^
416+
417+
error: aborting due to 45 previous errors
394418

tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -748,7 +748,15 @@ mod issue_14555 {
748748
#[doc(hidden)]
749749
// SAFETY: ...
750750
mod z {}
751-
//~[disabled]^ unnecessary_safety_comment
751+
//~^ unnecessary_safety_comment
752+
}
753+
754+
mod issue_todo {
755+
#[must_use]
756+
// SAFETY: ...
757+
#[doc(hidden)]
758+
mod y {}
759+
//~[default]^ unnecessary_safety_comment
752760
}
753761

754762
fn main() {}

tests/ui/crashes/ice-15684.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#![warn(clippy::unnecessary_safety_comment)]
2+
3+
fn foo() -> i32 {
4+
// SAFETY: fail ONLY if `accept-comment-above-attribute = false`
5+
#[must_use]
6+
return 33;
7+
//~^ unnecessary_safety_comment
8+
}
9+
10+
fn main() {}

tests/ui/crashes/ice-15684.stderr

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error: statement has unnecessary safety comment
2+
--> tests/ui/crashes/ice-15684.rs:6:5
3+
|
4+
LL | return 33;
5+
| ^^^^^^^^^^
6+
|
7+
help: consider removing the safety comment
8+
--> tests/ui/crashes/ice-15684.rs:4:5
9+
|
10+
LL | // SAFETY: fail ONLY if `accept-comment-above-attribute = false`
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
12+
= note: `-D clippy::unnecessary-safety-comment` implied by `-D warnings`
13+
= help: to override `-D warnings` add `#[allow(clippy::unnecessary_safety_comment)]`
14+
15+
error: aborting due to 1 previous error
16+

0 commit comments

Comments
 (0)