summaryrefslogtreecommitdiffstats
path: root/src/tools/clippy/clippy_lints/src/doc.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/tools/clippy/clippy_lints/src/doc.rs128
1 files changed, 78 insertions, 50 deletions
diff --git a/src/tools/clippy/clippy_lints/src/doc.rs b/src/tools/clippy/clippy_lints/src/doc.rs
index 24d6a6951..cdc23a4d2 100644
--- a/src/tools/clippy/clippy_lints/src/doc.rs
+++ b/src/tools/clippy/clippy_lints/src/doc.rs
@@ -11,7 +11,7 @@ use rustc_ast::token::CommentKind;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::sync::Lrc;
use rustc_errors::emitter::EmitterWriter;
-use rustc_errors::{Applicability, Handler, MultiSpan, SuggestionStyle};
+use rustc_errors::{Applicability, Handler, SuggestionStyle};
use rustc_hir as hir;
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{AnonConst, Expr};
@@ -221,6 +221,42 @@ declare_clippy_lint! {
"possible typo for an intra-doc link"
}
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for the doc comments of publicly visible
+ /// safe functions and traits and warns if there is a `# Safety` section.
+ ///
+ /// ### Why is this bad?
+ /// Safe functions and traits are safe to implement and therefore do not
+ /// need to describe safety preconditions that users are required to uphold.
+ ///
+ /// ### Examples
+ /// ```rust
+ ///# type Universe = ();
+ /// /// # Safety
+ /// ///
+ /// /// This function should not be called before the horsemen are ready.
+ /// pub fn start_apocalypse_but_safely(u: &mut Universe) {
+ /// unimplemented!();
+ /// }
+ /// ```
+ ///
+ /// The function is safe, so there shouldn't be any preconditions
+ /// that have to be explained for safety reasons.
+ ///
+ /// ```rust
+ ///# type Universe = ();
+ /// /// This function should really be documented
+ /// pub fn start_apocalypse(u: &mut Universe) {
+ /// unimplemented!();
+ /// }
+ /// ```
+ #[clippy::version = "1.66.0"]
+ pub UNNECESSARY_SAFETY_DOC,
+ restriction,
+ "`pub fn` or `pub trait` with `# Safety` docs"
+}
+
#[expect(clippy::module_name_repetitions)]
#[derive(Clone)]
pub struct DocMarkdown {
@@ -243,7 +279,8 @@ impl_lint_pass!(DocMarkdown => [
MISSING_SAFETY_DOC,
MISSING_ERRORS_DOC,
MISSING_PANICS_DOC,
- NEEDLESS_DOCTEST_MAIN
+ NEEDLESS_DOCTEST_MAIN,
+ UNNECESSARY_SAFETY_DOC,
]);
impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
@@ -254,7 +291,7 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
let attrs = cx.tcx.hir().attrs(item.hir_id());
- let headers = check_attrs(cx, &self.valid_idents, attrs);
+ let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else { return };
match item.kind {
hir::ItemKind::Fn(ref sig, _, body_id) => {
if !(is_entrypoint_fn(cx, item.owner_id.to_def_id()) || in_external_macro(cx.tcx.sess, item.span)) {
@@ -265,29 +302,26 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
panic_span: None,
};
fpu.visit_expr(body.value);
- lint_for_missing_headers(
- cx,
- item.owner_id.def_id,
- item.span,
- sig,
- headers,
- Some(body_id),
- fpu.panic_span,
- );
+ lint_for_missing_headers(cx, item.owner_id.def_id, sig, headers, Some(body_id), fpu.panic_span);
}
},
hir::ItemKind::Impl(impl_) => {
self.in_trait_impl = impl_.of_trait.is_some();
},
- hir::ItemKind::Trait(_, unsafety, ..) => {
- if !headers.safety && unsafety == hir::Unsafety::Unsafe {
- span_lint(
- cx,
- MISSING_SAFETY_DOC,
- item.span,
- "docs for unsafe trait missing `# Safety` section",
- );
- }
+ hir::ItemKind::Trait(_, unsafety, ..) => match (headers.safety, unsafety) {
+ (false, hir::Unsafety::Unsafe) => span_lint(
+ cx,
+ MISSING_SAFETY_DOC,
+ cx.tcx.def_span(item.owner_id),
+ "docs for unsafe trait missing `# Safety` section",
+ ),
+ (true, hir::Unsafety::Normal) => span_lint(
+ cx,
+ UNNECESSARY_SAFETY_DOC,
+ cx.tcx.def_span(item.owner_id),
+ "docs for safe trait have unnecessary `# Safety` section",
+ ),
+ _ => (),
},
_ => (),
}
@@ -301,17 +335,17 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
let attrs = cx.tcx.hir().attrs(item.hir_id());
- let headers = check_attrs(cx, &self.valid_idents, attrs);
+ let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else { return };
if let hir::TraitItemKind::Fn(ref sig, ..) = item.kind {
if !in_external_macro(cx.tcx.sess, item.span) {
- lint_for_missing_headers(cx, item.owner_id.def_id, item.span, sig, headers, None, None);
+ lint_for_missing_headers(cx, item.owner_id.def_id, sig, headers, None, None);
}
}
}
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
let attrs = cx.tcx.hir().attrs(item.hir_id());
- let headers = check_attrs(cx, &self.valid_idents, attrs);
+ let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else { return };
if self.in_trait_impl || in_external_macro(cx.tcx.sess, item.span) {
return;
}
@@ -323,23 +357,14 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
panic_span: None,
};
fpu.visit_expr(body.value);
- lint_for_missing_headers(
- cx,
- item.owner_id.def_id,
- item.span,
- sig,
- headers,
- Some(body_id),
- fpu.panic_span,
- );
+ lint_for_missing_headers(cx, item.owner_id.def_id, sig, headers, Some(body_id), fpu.panic_span);
}
}
}
-fn lint_for_missing_headers<'tcx>(
- cx: &LateContext<'tcx>,
+fn lint_for_missing_headers(
+ cx: &LateContext<'_>,
def_id: LocalDefId,
- span: impl Into<MultiSpan> + Copy,
sig: &hir::FnSig<'_>,
headers: DocHeaders,
body_id: Option<hir::BodyId>,
@@ -359,13 +384,21 @@ fn lint_for_missing_headers<'tcx>(
return;
}
- if !headers.safety && sig.header.unsafety == hir::Unsafety::Unsafe {
- span_lint(
+ let span = cx.tcx.def_span(def_id);
+ match (headers.safety, sig.header.unsafety) {
+ (false, hir::Unsafety::Unsafe) => span_lint(
cx,
MISSING_SAFETY_DOC,
span,
"unsafe function's docs miss `# Safety` section",
- );
+ ),
+ (true, hir::Unsafety::Normal) => span_lint(
+ cx,
+ UNNECESSARY_SAFETY_DOC,
+ span,
+ "safe function's docs have unnecessary `# Safety` section",
+ ),
+ _ => (),
}
if !headers.panics && panic_span.is_some() {
span_lint_and_note(
@@ -394,9 +427,7 @@ fn lint_for_missing_headers<'tcx>(
let body = cx.tcx.hir().body(body_id);
let ret_ty = typeck.expr_ty(body.value);
if implements_trait(cx, ret_ty, future, &[]);
- if let ty::Opaque(_, subs) = ret_ty.kind();
- if let Some(gen) = subs.types().next();
- if let ty::Generator(_, subs, _) = gen.kind();
+ if let ty::Generator(_, subs, _) = ret_ty.kind();
if is_type_diagnostic_item(cx, subs.as_generator().return_ty(), sym::Result);
then {
span_lint(
@@ -467,7 +498,7 @@ struct DocHeaders {
panics: bool,
}
-fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &'a [Attribute]) -> DocHeaders {
+fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[Attribute]) -> Option<DocHeaders> {
use pulldown_cmark::{BrokenLink, CowStr, Options};
/// We don't want the parser to choke on intra doc links. Since we don't
/// actually care about rendering them, just pretend that all broken links are
@@ -488,11 +519,7 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs
} else if attr.has_name(sym::doc) {
// ignore mix of sugared and non-sugared doc
// don't trigger the safety or errors check
- return DocHeaders {
- safety: true,
- errors: true,
- panics: true,
- };
+ return None;
}
}
@@ -504,7 +531,7 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs
}
if doc.is_empty() {
- return DocHeaders::default();
+ return Some(DocHeaders::default());
}
let mut cb = fake_broken_link_callback;
@@ -527,7 +554,7 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs
(previous, current) => Err(((previous, previous_range), (current, current_range))),
}
});
- check_doc(cx, valid_idents, events, &spans)
+ Some(check_doc(cx, valid_idents, events, &spans))
}
const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"];
@@ -691,6 +718,7 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) {
false,
None,
false,
+ false,
);
let handler = Handler::with_emitter(false, None, Box::new(emitter));
let sess = ParseSess::with_span_handler(handler, sm);