Skip to content

Commit 62f34f2

Browse files
authored
fix: undocumented_unsafe_blocks FP on trait/impl items (#13888)
fixes #11709 Continuation of #12672. r? @Alexendoo if you don't mind? changelog: [`undocumented_unsafe_blocks`] fix FP on trait/impl items
2 parents 35746de + d027ca9 commit 62f34f2

File tree

4 files changed

+241
-47
lines changed

4 files changed

+241
-47
lines changed

clippy_lints/src/undocumented_unsafe_blocks.rs

Lines changed: 88 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,33 @@ fn is_unsafe_from_proc_macro(cx: &LateContext<'_>, span: Span) -> bool {
339339
.is_none_or(|src| !src.starts_with("unsafe"))
340340
}
341341

342+
fn find_unsafe_block_parent_in_expr<'tcx>(
343+
cx: &LateContext<'tcx>,
344+
expr: &'tcx hir::Expr<'tcx>,
345+
) -> Option<(Span, HirId)> {
346+
match cx.tcx.parent_hir_node(expr.hir_id) {
347+
Node::LetStmt(hir::LetStmt { span, hir_id, .. })
348+
| Node::Expr(hir::Expr {
349+
hir_id,
350+
kind: hir::ExprKind::Assign(_, _, span),
351+
..
352+
}) => Some((*span, *hir_id)),
353+
Node::Expr(expr) => find_unsafe_block_parent_in_expr(cx, expr),
354+
node if let Some((span, hir_id)) = span_and_hid_of_item_alike_node(&node)
355+
&& is_const_or_static(&node) =>
356+
{
357+
Some((span, hir_id))
358+
},
359+
360+
_ => {
361+
if is_branchy(expr) {
362+
return None;
363+
}
364+
Some((expr.span, expr.hir_id))
365+
},
366+
}
367+
}
368+
342369
// Checks if any parent {expression, statement, block, local, const, static}
343370
// has a safety comment
344371
fn block_parents_have_safety_comment(
@@ -348,21 +375,7 @@ fn block_parents_have_safety_comment(
348375
id: HirId,
349376
) -> bool {
350377
let (span, hir_id) = match cx.tcx.parent_hir_node(id) {
351-
Node::Expr(expr) => match cx.tcx.parent_hir_node(expr.hir_id) {
352-
Node::LetStmt(hir::LetStmt { span, hir_id, .. }) => (*span, *hir_id),
353-
Node::Item(hir::Item {
354-
kind: ItemKind::Const(..) | ItemKind::Static(..),
355-
span,
356-
owner_id,
357-
..
358-
}) => (*span, cx.tcx.local_def_id_to_hir_id(owner_id.def_id)),
359-
_ => {
360-
if is_branchy(expr) {
361-
return false;
362-
}
363-
(expr.span, expr.hir_id)
364-
},
365-
},
378+
Node::Expr(expr) if let Some(inner) = find_unsafe_block_parent_in_expr(cx, expr) => inner,
366379
Node::Stmt(hir::Stmt {
367380
kind:
368381
hir::StmtKind::Let(hir::LetStmt { span, hir_id, .. })
@@ -371,12 +384,13 @@ fn block_parents_have_safety_comment(
371384
..
372385
})
373386
| Node::LetStmt(hir::LetStmt { span, hir_id, .. }) => (*span, *hir_id),
374-
Node::Item(hir::Item {
375-
kind: ItemKind::Const(..) | ItemKind::Static(..),
376-
span,
377-
owner_id,
378-
..
379-
}) => (*span, cx.tcx.local_def_id_to_hir_id(owner_id.def_id)),
387+
388+
node if let Some((span, hir_id)) = span_and_hid_of_item_alike_node(&node)
389+
&& is_const_or_static(&node) =>
390+
{
391+
(span, hir_id)
392+
},
393+
380394
_ => return false,
381395
};
382396
// if unsafe block is part of a let/const/static statement,
@@ -427,12 +441,12 @@ fn block_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool {
427441
}
428442

429443
fn include_attrs_in_span(cx: &LateContext<'_>, hir_id: HirId, span: Span) -> Span {
430-
span.to(cx
431-
.tcx
432-
.hir()
433-
.attrs(hir_id)
434-
.iter()
435-
.fold(span, |acc, attr| acc.to(attr.span())))
444+
span.to(cx.tcx.hir().attrs(hir_id).iter().fold(span, |acc, attr| {
445+
if attr.is_doc_comment() {
446+
return acc;
447+
}
448+
acc.to(attr.span())
449+
}))
436450
}
437451

438452
enum HasSafetyComment {
@@ -604,31 +618,35 @@ fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span
604618

605619
fn get_body_search_span(cx: &LateContext<'_>) -> Option<Span> {
606620
let body = cx.enclosing_body?;
607-
let mut span = cx.tcx.hir_body(body).value.span;
608-
let mut maybe_global_var = false;
609-
for (_, node) in cx.tcx.hir_parent_iter(body.hir_id) {
610-
match node {
611-
Node::Expr(e) => span = e.span,
612-
Node::Block(_) | Node::Arm(_) | Node::Stmt(_) | Node::LetStmt(_) => (),
613-
Node::Item(hir::Item {
614-
kind: ItemKind::Const(..) | ItemKind::Static(..),
615-
..
616-
}) => maybe_global_var = true,
621+
let mut maybe_mod_item = None;
622+
623+
for (_, parent_node) in cx.tcx.hir_parent_iter(body.hir_id) {
624+
match parent_node {
625+
Node::Crate(mod_) => return Some(mod_.spans.inner_span),
617626
Node::Item(hir::Item {
618-
kind: ItemKind::Mod(_),
619-
span: item_span,
627+
kind: ItemKind::Mod(mod_),
628+
span,
620629
..
621630
}) => {
622-
span = *item_span;
623-
break;
631+
return maybe_mod_item
632+
.and_then(|item| comment_start_before_item_in_mod(cx, mod_, *span, &item))
633+
.map(|comment_start| mod_.spans.inner_span.with_lo(comment_start))
634+
.or(Some(*span));
635+
},
636+
node if let Some((span, _)) = span_and_hid_of_item_alike_node(&node)
637+
&& !is_const_or_static(&node) =>
638+
{
639+
return Some(span);
640+
},
641+
Node::Item(item) => {
642+
maybe_mod_item = Some(*item);
624643
},
625-
Node::Crate(mod_) if maybe_global_var => {
626-
span = mod_.spans.inner_span;
644+
_ => {
645+
maybe_mod_item = None;
627646
},
628-
_ => break,
629647
}
630648
}
631-
Some(span)
649+
None
632650
}
633651

634652
fn span_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool {
@@ -717,3 +735,28 @@ fn text_has_safety_comment(src: &str, line_starts: &[RelativeBytePos], start_pos
717735
}
718736
}
719737
}
738+
739+
fn span_and_hid_of_item_alike_node(node: &Node<'_>) -> Option<(Span, HirId)> {
740+
match node {
741+
Node::Item(item) => Some((item.span, item.owner_id.into())),
742+
Node::TraitItem(ti) => Some((ti.span, ti.owner_id.into())),
743+
Node::ImplItem(ii) => Some((ii.span, ii.owner_id.into())),
744+
_ => None,
745+
}
746+
}
747+
748+
fn is_const_or_static(node: &Node<'_>) -> bool {
749+
matches!(
750+
node,
751+
Node::Item(hir::Item {
752+
kind: ItemKind::Const(..) | ItemKind::Static(..),
753+
..
754+
}) | Node::ImplItem(hir::ImplItem {
755+
kind: hir::ImplItemKind::Const(..),
756+
..
757+
}) | Node::TraitItem(hir::TraitItem {
758+
kind: hir::TraitItemKind::Const(..),
759+
..
760+
})
761+
)
762+
}

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

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,5 +310,37 @@ LL | let bar = unsafe {};
310310
|
311311
= help: consider adding a safety comment on the preceding line
312312

313-
error: aborting due to 35 previous errors
313+
error: unsafe block missing a safety comment
314+
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:638:52
315+
|
316+
LL | const NO_SAFETY_IN_TRAIT_BUT_IN_IMPL: u8 = unsafe { 0 };
317+
| ^^^^^^^^^^^^
318+
|
319+
= help: consider adding a safety comment on the preceding line
320+
321+
error: unsafe block missing a safety comment
322+
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:647:41
323+
|
324+
LL | const NO_SAFETY_IN_TRAIT: i32 = unsafe { 1 };
325+
| ^^^^^^^^^^^^
326+
|
327+
= help: consider adding a safety comment on the preceding line
328+
329+
error: unsafe block missing a safety comment
330+
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:657:42
331+
|
332+
LL | const HAS_SAFETY_IN_TRAIT: i32 = unsafe { 3 };
333+
| ^^^^^^^^^^^^
334+
|
335+
= help: consider adding a safety comment on the preceding line
336+
337+
error: unsafe block missing a safety comment
338+
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:662:40
339+
|
340+
LL | const NO_SAFETY_IN_IMPL: i32 = unsafe { 1 };
341+
| ^^^^^^^^^^^^
342+
|
343+
= help: consider adding a safety comment on the preceding line
344+
345+
error: aborting due to 39 previous errors
314346

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

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -390,5 +390,53 @@ LL | unsafe {}
390390
|
391391
= help: consider adding a safety comment on the preceding line
392392

393-
error: aborting due to 45 previous errors
393+
error: unsafe block missing a safety comment
394+
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:638:52
395+
|
396+
LL | const NO_SAFETY_IN_TRAIT_BUT_IN_IMPL: u8 = unsafe { 0 };
397+
| ^^^^^^^^^^^^
398+
|
399+
= help: consider adding a safety comment on the preceding line
400+
401+
error: unsafe block missing a safety comment
402+
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:647:41
403+
|
404+
LL | const NO_SAFETY_IN_TRAIT: i32 = unsafe { 1 };
405+
| ^^^^^^^^^^^^
406+
|
407+
= help: consider adding a safety comment on the preceding line
408+
409+
error: unsafe block missing a safety comment
410+
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:657:42
411+
|
412+
LL | const HAS_SAFETY_IN_TRAIT: i32 = unsafe { 3 };
413+
| ^^^^^^^^^^^^
414+
|
415+
= help: consider adding a safety comment on the preceding line
416+
417+
error: unsafe block missing a safety comment
418+
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:662:40
419+
|
420+
LL | const NO_SAFETY_IN_IMPL: i32 = unsafe { 1 };
421+
| ^^^^^^^^^^^^
422+
|
423+
= help: consider adding a safety comment on the preceding line
424+
425+
error: unsafe block missing a safety comment
426+
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:673:9
427+
|
428+
LL | unsafe { here_is_another_variable_with_long_name };
429+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
430+
|
431+
= help: consider adding a safety comment on the preceding line
432+
433+
error: unsafe block missing a safety comment
434+
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:702:9
435+
|
436+
LL | unsafe { Date::__from_ordinal_date_unchecked(1970, 1) }.into_julian_day_just_make_this_line_longer();
437+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
438+
|
439+
= help: consider adding a safety comment on the preceding line
440+
441+
error: aborting due to 51 previous errors
394442

tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,4 +632,75 @@ mod issue_11246 {
632632
// Safety: Another safety comment
633633
const FOO: () = unsafe {};
634634

635+
// trait items and impl items
636+
mod issue_11709 {
637+
trait MyTrait {
638+
const NO_SAFETY_IN_TRAIT_BUT_IN_IMPL: u8 = unsafe { 0 };
639+
//~^ ERROR: unsafe block missing a safety comment
640+
641+
// SAFETY: safe
642+
const HAS_SAFETY_IN_TRAIT: i32 = unsafe { 1 };
643+
644+
// SAFETY: unrelated
645+
unsafe fn unsafe_fn() {}
646+
647+
const NO_SAFETY_IN_TRAIT: i32 = unsafe { 1 };
648+
//~^ ERROR: unsafe block missing a safety comment
649+
}
650+
651+
struct UnsafeStruct;
652+
653+
impl MyTrait for UnsafeStruct {
654+
// SAFETY: safe in this impl
655+
const NO_SAFETY_IN_TRAIT_BUT_IN_IMPL: u8 = unsafe { 2 };
656+
657+
const HAS_SAFETY_IN_TRAIT: i32 = unsafe { 3 };
658+
//~^ ERROR: unsafe block missing a safety comment
659+
}
660+
661+
impl UnsafeStruct {
662+
const NO_SAFETY_IN_IMPL: i32 = unsafe { 1 };
663+
//~^ ERROR: unsafe block missing a safety comment
664+
}
665+
}
666+
667+
fn issue_13024() {
668+
let mut just_a_simple_variable_with_a_very_long_name_that_has_seventy_eight_characters = 0;
669+
let here_is_another_variable_with_long_name = 100;
670+
671+
// SAFETY: fail ONLY if `accept-comment-above-statement = false`
672+
just_a_simple_variable_with_a_very_long_name_that_has_seventy_eight_characters =
673+
unsafe { here_is_another_variable_with_long_name };
674+
//~[disabled]^ undocumented_unsafe_blocks
675+
}
676+
677+
// https://docs.rs/time/0.3.36/src/time/offset_date_time.rs.html#35
678+
mod issue_11709_regression {
679+
use std::num::NonZeroI32;
680+
681+
struct Date {
682+
value: NonZeroI32,
683+
}
684+
685+
impl Date {
686+
const unsafe fn __from_ordinal_date_unchecked(year: i32, ordinal: u16) -> Self {
687+
Self {
688+
// Safety: The caller must guarantee that `ordinal` is not zero.
689+
value: unsafe { NonZeroI32::new_unchecked((year << 9) | ordinal as i32) },
690+
}
691+
}
692+
693+
const fn into_julian_day_just_make_this_line_longer(self) -> i32 {
694+
42
695+
}
696+
}
697+
698+
/// The Julian day of the Unix epoch.
699+
// SAFETY: fail ONLY if `accept-comment-above-attribute = false`
700+
#[allow(unsafe_code)]
701+
const UNIX_EPOCH_JULIAN_DAY: i32 =
702+
unsafe { Date::__from_ordinal_date_unchecked(1970, 1) }.into_julian_day_just_make_this_line_longer();
703+
//~[disabled]^ undocumented_unsafe_blocks
704+
}
705+
635706
fn main() {}

0 commit comments

Comments
 (0)