diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index 7a6549a7c54a..24ab45b6dbea 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -4,10 +4,10 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::walk_span_to_context; use clippy_utils::visitors::{for_each_expr_with_closures, Descend}; use clippy_utils::{get_parent_node, is_lint_allowed}; -use hir::HirId; +use hir::OwnerId; use rustc_data_structures::sync::Lrc; use rustc_hir as hir; -use rustc_hir::{Block, BlockCheckMode, ItemKind, Node, UnsafeSource}; +use rustc_hir::{Block, BlockCheckMode, HirId, ItemKind, Node, UnsafeSource}; use rustc_lexer::{tokenize, TokenKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -185,19 +185,7 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { return; } - let mk_spans = |pos: BytePos| { - let source_map = cx.tcx.sess.source_map(); - let span = Span::new(pos, pos, SyntaxContext::root(), None); - let help_span = source_map.span_extend_to_next_char(span, '\n', true); - let span = if source_map.is_multiline(item.span) { - source_map.span_until_char(item.span, '\n') - } else { - item.span - }; - (span, help_span) - }; - - let item_has_safety_comment = item_has_safety_comment(cx, item); + let item_has_safety_comment = item_has_safety_comment(cx, item.hir_id(), item.into()); match (&item.kind, item_has_safety_comment) { // lint unsafe impl without safety comment (hir::ItemKind::Impl(impl_), HasSafetyComment::No) if impl_.unsafety == hir::Unsafety::Unsafe => { @@ -224,7 +212,7 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { // lint safe impl with unnecessary safety comment (hir::ItemKind::Impl(impl_), HasSafetyComment::Yes(pos)) if impl_.unsafety == hir::Unsafety::Normal => { if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, item.hir_id()) { - let (span, help_span) = mk_spans(pos); + let (span, help_span) = mk_spans(cx, item.span, pos); span_lint_and_help( cx, @@ -239,30 +227,24 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { (hir::ItemKind::Impl(_), _) => {}, // const and static items only need a safety comment if their body is an unsafe block, lint otherwise (&hir::ItemKind::Const(.., body) | &hir::ItemKind::Static(.., body), HasSafetyComment::Yes(pos)) => { - if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, body.hir_id) { - let body = cx.tcx.hir().body(body); - if !matches!( - body.value.kind, hir::ExprKind::Block(block, _) - if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) - ) { - let (span, help_span) = mk_spans(pos); - - span_lint_and_help( - cx, - UNNECESSARY_SAFETY_COMMENT, - span, - &format!("{} has unnecessary safety comment", item.kind.descr()), - Some(help_span), - "consider removing the safety comment", - ); - } + if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, body.hir_id) && !body_is_unsafe_block(cx, body) { + let (span, help_span) = mk_spans(cx, item.span, pos); + + span_lint_and_help( + cx, + UNNECESSARY_SAFETY_COMMENT, + span, + &format!("{} has unnecessary safety comment", item.kind.descr()), + Some(help_span), + "consider removing the safety comment", + ); } }, // Aside from unsafe impls and consts/statics with an unsafe block, items in general // do not have safety invariants that need to be documented, so lint those. (_, HasSafetyComment::Yes(pos)) => { if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, item.hir_id()) { - let (span, help_span) = mk_spans(pos); + let (span, help_span) = mk_spans(cx, item.span, pos); span_lint_and_help( cx, @@ -277,6 +259,72 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { _ => (), } } + + // Check unnecessary unsafe comment above trait items. + // NB: This does not check undocumented unsafe block as they are handled + // by `check_block` and `block_parents_have_safety_comment` + fn check_trait_item(&mut self, cx: &LateContext<'_>, trait_item: &hir::TraitItem<'_>) { + let hir::TraitItemKind::Const(..) = trait_item.kind else { + return; + }; + lint_usc_on_associated_consts(cx, trait_item, trait_item.hir_id()); + } + + // Check unnecessary unsafe comment above impl items. + // NB: Same as [`check_trait_item`] above, this does not check undocumented unsafe block. + fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) { + let hir::ImplItemKind::Const(..) = impl_item.kind else { + return; + }; + lint_usc_on_associated_consts(cx, impl_item, impl_item.hir_id()); + } +} + +fn lint_usc_on_associated_consts<'hir, T: Into>>(cx: &LateContext<'_>, item: T, hir_id: HirId) { + let owner_node = item.into(); + let span = owner_node.span(); + let Some(body_id) = owner_node.body_id() else { return }; + if in_external_macro(cx.tcx.sess, span) + || is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, hir_id) + || body_is_unsafe_block(cx, body_id) + { + return; + } + + if let HasSafetyComment::Yes(pos) = item_has_safety_comment(cx, hir_id, owner_node) { + let (span, help_span) = mk_spans(cx, span, pos); + span_lint_and_help( + cx, + UNNECESSARY_SAFETY_COMMENT, + span, + "associated constant has unnecessary safety comment", + Some(help_span), + "consider removing the safety comment", + ); + } +} + +/// Get the lint span and help span from a detected item +/// (including [`TraitItem`](hir::TraitItem) and [`ImplItem`](hir::ImplItem)), +/// and the [`BytePos`] of its safety comment's starting pos +fn mk_spans(cx: &LateContext<'_>, item_span: Span, pos: BytePos) -> (Span, Span) { + let source_map = cx.tcx.sess.source_map(); + let span = Span::new(pos, pos, SyntaxContext::root(), None); + let help_span = source_map.span_extend_to_next_char(span, '\n', true); + let span = if source_map.is_multiline(item_span) { + source_map.span_until_char(item_span, '\n') + } else { + item_span + }; + (span, help_span) +} + +fn body_is_unsafe_block(cx: &LateContext<'_>, body_id: hir::BodyId) -> bool { + let body = cx.tcx.hir().body(body_id); + matches!( + body.value.kind, hir::ExprKind::Block(block, _) + if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) + ) } fn expr_has_unnecessary_safety_comment<'tcx>( @@ -340,47 +388,61 @@ fn block_parents_have_safety_comment( cx: &LateContext<'_>, id: hir::HirId, ) -> bool { - if let Some(node) = get_parent_node(cx.tcx, id) { - let (span, hir_id) = match node { - Node::Expr(expr) => match get_parent_node(cx.tcx, expr.hir_id) { - Some(Node::Local(hir::Local { span, hir_id, .. })) => (*span, *hir_id), - Some(Node::Item(hir::Item { - kind: hir::ItemKind::Const(..) | ItemKind::Static(..), + let Some(node) = get_parent_node(cx.tcx, id) else { + return false; + }; + + let (span, hir_id) = match node { + Node::Expr(expr) => match get_parent_node(cx.tcx, expr.hir_id) { + Some(Node::Local(hir::Local { span, hir_id, .. })) => (*span, *hir_id), + Some( + Node::Item(hir::Item { + kind: ItemKind::Const(..) | ItemKind::Static(..), span, owner_id, .. - })) => (*span, cx.tcx.local_def_id_to_hir_id(owner_id.def_id)), - _ => { - if is_branchy(expr) { - return false; - } - (expr.span, expr.hir_id) - }, + }) + | Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::Const(..), + span, + owner_id, + .. + }) + | Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Const(..), + span, + owner_id, + .. + }), + ) => (*span, cx.tcx.local_def_id_to_hir_id(owner_id.def_id)), + _ => { + if is_branchy(expr) { + return false; + } + (expr.span, expr.hir_id) }, - Node::Stmt(hir::Stmt { - kind: - hir::StmtKind::Local(hir::Local { span, hir_id, .. }) - | hir::StmtKind::Expr(hir::Expr { span, hir_id, .. }) - | hir::StmtKind::Semi(hir::Expr { span, hir_id, .. }), - .. - }) - | Node::Local(hir::Local { span, hir_id, .. }) => (*span, *hir_id), - Node::Item(hir::Item { - kind: hir::ItemKind::Const(..) | ItemKind::Static(..), - span, - owner_id, - .. - }) => (*span, cx.tcx.local_def_id_to_hir_id(owner_id.def_id)), - _ => return false, - }; - // if unsafe block is part of a let/const/static statement, - // and accept_comment_above_statement is set to true - // we accept the safety comment in the line the precedes this statement. - accept_comment_above_statement - && span_with_attrs_has_safety_comment(cx, span, hir_id, accept_comment_above_attributes) - } else { - false - } + }, + Node::Stmt(hir::Stmt { + kind: + hir::StmtKind::Local(hir::Local { span, hir_id, .. }) + | hir::StmtKind::Expr(hir::Expr { span, hir_id, .. }) + | hir::StmtKind::Semi(hir::Expr { span, hir_id, .. }), + .. + }) + | Node::Local(hir::Local { span, hir_id, .. }) => (*span, *hir_id), + Node::Item(hir::Item { + kind: hir::ItemKind::Const(..) | ItemKind::Static(..), + span, + owner_id, + .. + }) => (*span, cx.tcx.local_def_id_to_hir_id(owner_id.def_id)), + _ => return false, + }; + // if unsafe block is part of a let/const/static statement, + // and accept_comment_above_statement is set to true + // we accept the safety comment in the line the precedes this statement. + accept_comment_above_statement + && span_with_attrs_has_safety_comment(cx, span, hir_id, accept_comment_above_attributes) } /// Extends `span` to also include its attributes, then checks if that span has a safety comment. @@ -432,6 +494,7 @@ fn include_attrs_in_span(cx: &LateContext<'_>, hir_id: HirId, span: Span) -> Spa .fold(span, |acc, attr| acc.to(attr.span))) } +#[derive(Debug)] enum HasSafetyComment { Yes(BytePos), No, @@ -440,26 +503,42 @@ enum HasSafetyComment { /// Checks if the lines immediately preceding the item contain a safety comment. #[allow(clippy::collapsible_match)] -fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> HasSafetyComment { - match span_from_macro_expansion_has_safety_comment(cx, item.span) { +fn item_has_safety_comment(cx: &LateContext<'_>, hir_id: HirId, owner: hir::OwnerNode<'_>) -> HasSafetyComment { + match span_from_macro_expansion_has_safety_comment(cx, owner.span()) { HasSafetyComment::Maybe => (), has_safety_comment => return has_safety_comment, } - if item.span.ctxt() != SyntaxContext::root() { + if owner.span().from_expansion() { return HasSafetyComment::No; } - if let Some(parent_node) = get_parent_node(cx.tcx, item.hir_id()) { + if let Some(parent_node) = get_parent_node(cx.tcx, hir_id) { let comment_start = match parent_node { - Node::Crate(parent_mod) => { - comment_start_before_item_in_mod(cx, parent_mod, parent_mod.spans.inner_span, item) - }, + Node::Crate(parent_mod) => comment_start_pos( + cx, + parent_mod.item_ids.iter().map(|id| id.owner_id), + parent_mod.spans.inner_span, + owner.def_id(), + ), Node::Item(parent_item) => { - if let ItemKind::Mod(parent_mod) = &parent_item.kind { - comment_start_before_item_in_mod(cx, parent_mod, parent_item.span, item) - } else { + match parent_item.kind { + ItemKind::Mod(parent_mod) => comment_start_pos( + cx, + parent_mod.item_ids.iter().map(|id| id.owner_id), + parent_item.span, + owner.def_id(), + ), + ItemKind::Trait(_, _, _, _, refs) => { + comment_start_pos(cx, refs.iter().map(|r| r.id.owner_id), parent_item.span, owner.def_id()) + }, + ItemKind::Impl(hir::Impl { items, .. }) => comment_start_pos( + cx, + items.iter().map(|r| r.id.owner_id), + parent_item.span, + owner.def_id(), + ), // Doesn't support impls in this position. Pretend a comment was found. - return HasSafetyComment::Maybe; + _ => return HasSafetyComment::Maybe, } }, Node::Stmt(stmt) => { @@ -478,7 +557,7 @@ fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> HasSaf let source_map = cx.sess().source_map(); if let Some(comment_start) = comment_start - && let Ok(unsafe_line) = source_map.lookup_line(item.span.lo()) + && let Ok(unsafe_line) = source_map.lookup_line(owner.span().lo()) && let Ok(comment_start_line) = source_map.lookup_line(comment_start) && Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf) && let Some(src) = unsafe_line.sf.src.as_deref() @@ -542,33 +621,20 @@ fn stmt_has_safety_comment(cx: &LateContext<'_>, span: Span, hir_id: HirId) -> H HasSafetyComment::Maybe } -fn comment_start_before_item_in_mod( +/// Search and return the starting [`BytePos`] of the comment above an 'item' in its context. +fn comment_start_pos + DoubleEndedIterator>( cx: &LateContext<'_>, - parent_mod: &hir::Mod<'_>, - parent_mod_span: Span, - item: &hir::Item<'_>, + mut siblings: I, + search_span: Span, + owner_id: OwnerId, ) -> Option { - parent_mod.item_ids.iter().enumerate().find_map(|(idx, item_id)| { - if *item_id == item.item_id() { - if idx == 0 { - // mod A { /* comment */ unsafe impl T {} ... } - // ^------------------------------------------^ returns the start of this span - // ^---------------------^ finally checks comments in this range - if let Some(sp) = walk_span_to_context(parent_mod_span, SyntaxContext::root()) { - return Some(sp.lo()); - } - } else { - // some_item /* comment */ unsafe impl T {} - // ^-------^ returns the end of this span - // ^---------------^ finally checks comments in this range - let prev_item = cx.tcx.hir().item(parent_mod.item_ids[idx - 1]); - if let Some(sp) = walk_span_to_context(prev_item.span, SyntaxContext::root()) { - return Some(sp.hi()); - } - } - } - None - }) + let _ = siblings.rfind(|id| *id == owner_id); + if let Some(prev_sibling_id) = siblings.next_back() { + let prev_sibling_span = cx.tcx.hir().span(prev_sibling_id.into()); + walk_span_to_context(prev_sibling_span, SyntaxContext::root()).map(Span::lo) + } else { + walk_span_to_context(search_span, SyntaxContext::root()).map(Span::lo) + } } fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span) -> HasSafetyComment { @@ -607,32 +673,26 @@ fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span fn get_body_search_span(cx: &LateContext<'_>) -> Option { let body = cx.enclosing_body?; - let map = cx.tcx.hir(); - let mut span = map.body(body).value.span; - let mut maybe_global_var = false; - for (_, node) in map.parent_iter(body.hir_id) { - match node { - Node::Expr(e) => span = e.span, - Node::Block(_) | Node::Arm(_) | Node::Stmt(_) | Node::Local(_) => (), + for (_, parent_node) in cx.tcx.hir().parent_iter(body.hir_id) { + match parent_node { Node::Item(hir::Item { kind: hir::ItemKind::Const(..) | ItemKind::Static(..), .. - }) => maybe_global_var = true, - Node::Item(hir::Item { - kind: hir::ItemKind::Mod(_), - span: item_span, + }) + | Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::Const(..), .. - }) => { - span = *item_span; - break; - }, - Node::Crate(mod_) if maybe_global_var => { - span = mod_.spans.inner_span; - }, - _ => break, + }) + | Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Const(..), + .. + }) => {}, + Node::Item(item) => return Some(item.span), + Node::Crate(mod_) => return Some(mod_.spans.inner_span), + _ => {}, } } - Some(span) + None } fn span_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { diff --git a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.default.stderr b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.default.stderr index 15edf2a7dae4..4e41f1be56c5 100644 --- a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.default.stderr +++ b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.default.stderr @@ -312,5 +312,73 @@ LL | let bar = unsafe {}; | = help: consider adding a safety comment on the preceding line -error: aborting due to 35 previous errors +error: associated constant has unnecessary safety comment + --> $DIR/undocumented_unsafe_blocks.rs:590:9 + | +LL | const SAFE_CONST_IN_TRAIT: u8 = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> $DIR/undocumented_unsafe_blocks.rs:589:9 + | +LL | // SAFETY: unnecessary, lint + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:592:37 + | +LL | const MAYBE_NO_SAFETY: u8 = unsafe { 0 }; + | ^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:596:41 + | +LL | const NO_SAFETY_IN_TRAIT: i32 = unsafe { 1 }; + | ^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: associated constant has unnecessary safety comment + --> $DIR/undocumented_unsafe_blocks.rs:604:9 + | +LL | const SAFE_CONST: u8 = 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> $DIR/undocumented_unsafe_blocks.rs:603:9 + | +LL | // SAFETY: unnecessary, lint + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:608:42 + | +LL | const HAS_SAFETY_IN_TRAIT: i32 = unsafe { 3 }; + | ^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:615:31 + | +LL | const NO_SAFETY: u8 = unsafe { 5 }; + | ^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: associated constant has unnecessary safety comment + --> $DIR/undocumented_unsafe_blocks.rs:618:9 + | +LL | const SAFE: u8 = 1; + | ^^^^^^^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> $DIR/undocumented_unsafe_blocks.rs:617:9 + | +LL | // SAFETY: unnecessary, lint + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 42 previous errors diff --git a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr index cc9530f79b67..22988fcb952d 100644 --- a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr +++ b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr @@ -392,5 +392,73 @@ LL | unsafe {} | = help: consider adding a safety comment on the preceding line -error: aborting due to 45 previous errors +error: associated constant has unnecessary safety comment + --> $DIR/undocumented_unsafe_blocks.rs:590:9 + | +LL | const SAFE_CONST_IN_TRAIT: u8 = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> $DIR/undocumented_unsafe_blocks.rs:589:9 + | +LL | // SAFETY: unnecessary, lint + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:592:37 + | +LL | const MAYBE_NO_SAFETY: u8 = unsafe { 0 }; + | ^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:596:41 + | +LL | const NO_SAFETY_IN_TRAIT: i32 = unsafe { 1 }; + | ^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: associated constant has unnecessary safety comment + --> $DIR/undocumented_unsafe_blocks.rs:604:9 + | +LL | const SAFE_CONST: u8 = 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> $DIR/undocumented_unsafe_blocks.rs:603:9 + | +LL | // SAFETY: unnecessary, lint + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:608:42 + | +LL | const HAS_SAFETY_IN_TRAIT: i32 = unsafe { 3 }; + | ^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:615:31 + | +LL | const NO_SAFETY: u8 = unsafe { 5 }; + | ^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: associated constant has unnecessary safety comment + --> $DIR/undocumented_unsafe_blocks.rs:618:9 + | +LL | const SAFE: u8 = 1; + | ^^^^^^^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> $DIR/undocumented_unsafe_blocks.rs:617:9 + | +LL | // SAFETY: unnecessary, lint + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 52 previous errors diff --git a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs index a27813987606..b095dca5a024 100644 --- a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs +++ b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs @@ -582,4 +582,42 @@ mod issue_11246 { // Safety: Another safety comment const FOO: () = unsafe {}; +// trait items and impl items +mod issue_11709 { + trait MyTrait { + const SAFE_CONST: u8 = 0; + // SAFETY: unnecessary, lint + const SAFE_CONST_IN_TRAIT: u8 = 1; + //~^ ERROR: associated constant has unnecessary safety comment + const MAYBE_NO_SAFETY: u8 = unsafe { 0 }; + //~^ ERROR: unsafe block missing a safety comment + // SAFETY: safe, trust me + const HAS_SAFETY_IN_TRAIT: i32 = unsafe { 1 }; + const NO_SAFETY_IN_TRAIT: i32 = unsafe { 1 }; + //~^ ERROR: unsafe block missing a safety comment + } + + struct UnsafeStruct; + + impl MyTrait for UnsafeStruct { + // SAFETY: unnecessary, lint + const SAFE_CONST: u8 = 0; + //~^ ERROR: associated constant has unnecessary safety comment + // SAFETY: safe in this impl + const MAYBE_NO_SAFETY: u8 = unsafe { 2 }; + const HAS_SAFETY_IN_TRAIT: i32 = unsafe { 3 }; + //~^ ERROR: unsafe block missing a safety comment + } + + impl UnsafeStruct { + // SAFETY: safe + const HAS_SAFETY: u8 = unsafe { 4 }; + const NO_SAFETY: u8 = unsafe { 5 }; + //~^ ERROR: unsafe block missing a safety comment + // SAFETY: unnecessary, lint + const SAFE: u8 = 1; + //~^ ERROR: associated constant has unnecessary safety comment + } +} + fn main() {} diff --git a/tests/ui/doc_unsafe.rs b/tests/ui/doc_unsafe.rs index 0c8eac5ccffc..ebc051268011 100644 --- a/tests/ui/doc_unsafe.rs +++ b/tests/ui/doc_unsafe.rs @@ -7,6 +7,7 @@ use proc_macros::external; /// This is not sufficiently documented pub unsafe fn destroy_the_planet() { + //~^ ERROR: unsafe function's docs miss `# Safety` section unimplemented!(); } @@ -30,6 +31,7 @@ mod private_mod { } pub unsafe fn republished() { + //~^ ERROR: unsafe function's docs miss `# Safety` section unimplemented!(); } } @@ -38,12 +40,14 @@ pub use private_mod::republished; pub trait SafeTraitUnsafeMethods { unsafe fn woefully_underdocumented(self); + //~^ ERROR: unsafe function's docs miss `# Safety` section /// # Safety unsafe fn at_least_somewhat_documented(self); } pub unsafe trait UnsafeTrait { + //~^ ERROR: docs for unsafe trait missing `# Safety` section fn method(); } @@ -74,6 +78,7 @@ unsafe impl DocumentedUnsafeTrait for Struct { impl Struct { pub unsafe fn more_undocumented_unsafe() -> Self { + //~^ ERROR: unsafe function's docs miss `# Safety` section unimplemented!(); } @@ -103,6 +108,7 @@ macro_rules! very_unsafe { } very_unsafe!(); +//~^ ERROR: unsafe function's docs miss `# Safety` section // we don't lint code from external macros external! { diff --git a/tests/ui/doc_unsafe.stderr b/tests/ui/doc_unsafe.stderr index ab3fb3c029dd..325f51956cc8 100644 --- a/tests/ui/doc_unsafe.stderr +++ b/tests/ui/doc_unsafe.stderr @@ -8,31 +8,31 @@ LL | pub unsafe fn destroy_the_planet() { = help: to override `-D warnings` add `#[allow(clippy::missing_safety_doc)]` error: unsafe function's docs miss `# Safety` section - --> $DIR/doc_unsafe.rs:32:5 + --> $DIR/doc_unsafe.rs:33:5 | LL | pub unsafe fn republished() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unsafe function's docs miss `# Safety` section - --> $DIR/doc_unsafe.rs:40:5 + --> $DIR/doc_unsafe.rs:42:5 | LL | unsafe fn woefully_underdocumented(self); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: docs for unsafe trait missing `# Safety` section - --> $DIR/doc_unsafe.rs:46:1 + --> $DIR/doc_unsafe.rs:49:1 | LL | pub unsafe trait UnsafeTrait { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unsafe function's docs miss `# Safety` section - --> $DIR/doc_unsafe.rs:76:5 + --> $DIR/doc_unsafe.rs:80:5 | LL | pub unsafe fn more_undocumented_unsafe() -> Self { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unsafe function's docs miss `# Safety` section - --> $DIR/doc_unsafe.rs:92:9 + --> $DIR/doc_unsafe.rs:97:9 | LL | pub unsafe fn whee() { | ^^^^^^^^^^^^^^^^^^^^