Skip to content

Commit 1c50948

Browse files
committed
Auto merge of #12342 - lucarlig:empty-docs, r=llogiq
Empty docs Fixes #9931 changelog: [`empty_doc`]: Detects documentation that is empty. changelog: Doc comment lints now trigger for struct field and enum variant documentation
2 parents d718995 + fca77c0 commit 1c50948

12 files changed

+223
-20
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -5158,6 +5158,7 @@ Released 2018-09-13
51585158
[`duration_subsec`]: https://rust-lang.github.io/rust-clippy/master/index.html#duration_subsec
51595159
[`eager_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#eager_transmute
51605160
[`else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else
5161+
[`empty_docs`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_docs
51615162
[`empty_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_drop
51625163
[`empty_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum
51635164
[`empty_enum_variants_with_brackets`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum_variants_with_brackets

clippy_lints/src/declared_lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
139139
crate::disallowed_types::DISALLOWED_TYPES_INFO,
140140
crate::doc::DOC_LINK_WITH_QUOTES_INFO,
141141
crate::doc::DOC_MARKDOWN_INFO,
142+
crate::doc::EMPTY_DOCS_INFO,
142143
crate::doc::MISSING_ERRORS_DOC_INFO,
143144
crate::doc::MISSING_PANICS_DOC_INFO,
144145
crate::doc::MISSING_SAFETY_DOC_INFO,

clippy_lints/src/doc/mod.rs

+54-7
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ use rustc_middle::hir::nested_filter;
1919
use rustc_middle::lint::in_external_macro;
2020
use rustc_middle::ty;
2121
use rustc_resolve::rustdoc::{
22-
add_doc_fragment, attrs_to_doc_fragments, main_body_opts, source_span_for_markdown_range, DocFragment,
22+
add_doc_fragment, attrs_to_doc_fragments, main_body_opts, source_span_for_markdown_range, span_of_fragments,
23+
DocFragment,
2324
};
2425
use rustc_session::impl_lint_pass;
2526
use rustc_span::edition::Edition;
@@ -338,6 +339,30 @@ declare_clippy_lint! {
338339
"suspicious usage of (outer) doc comments"
339340
}
340341

342+
declare_clippy_lint! {
343+
/// ### What it does
344+
/// Detects documentation that is empty.
345+
/// ### Why is this bad?
346+
/// Empty docs clutter code without adding value, reducing readability and maintainability.
347+
/// ### Example
348+
/// ```no_run
349+
/// ///
350+
/// fn returns_true() -> bool {
351+
/// true
352+
/// }
353+
/// ```
354+
/// Use instead:
355+
/// ```no_run
356+
/// fn returns_true() -> bool {
357+
/// true
358+
/// }
359+
/// ```
360+
#[clippy::version = "1.78.0"]
361+
pub EMPTY_DOCS,
362+
suspicious,
363+
"docstrings exist but documentation is empty"
364+
}
365+
341366
#[derive(Clone)]
342367
pub struct Documentation {
343368
valid_idents: FxHashSet<String>,
@@ -364,7 +389,8 @@ impl_lint_pass!(Documentation => [
364389
NEEDLESS_DOCTEST_MAIN,
365390
TEST_ATTR_IN_DOCTEST,
366391
UNNECESSARY_SAFETY_DOC,
367-
SUSPICIOUS_DOC_COMMENTS
392+
SUSPICIOUS_DOC_COMMENTS,
393+
EMPTY_DOCS,
368394
]);
369395

370396
impl<'tcx> LateLintPass<'tcx> for Documentation {
@@ -373,11 +399,22 @@ impl<'tcx> LateLintPass<'tcx> for Documentation {
373399
check_attrs(cx, &self.valid_idents, attrs);
374400
}
375401

402+
fn check_variant(&mut self, cx: &LateContext<'tcx>, variant: &'tcx hir::Variant<'tcx>) {
403+
let attrs = cx.tcx.hir().attrs(variant.hir_id);
404+
check_attrs(cx, &self.valid_idents, attrs);
405+
}
406+
407+
fn check_field_def(&mut self, cx: &LateContext<'tcx>, variant: &'tcx hir::FieldDef<'tcx>) {
408+
let attrs = cx.tcx.hir().attrs(variant.hir_id);
409+
check_attrs(cx, &self.valid_idents, attrs);
410+
}
411+
376412
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
377413
let attrs = cx.tcx.hir().attrs(item.hir_id());
378414
let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else {
379415
return;
380416
};
417+
381418
match item.kind {
382419
hir::ItemKind::Fn(ref sig, _, body_id) => {
383420
if !(is_entrypoint_fn(cx, item.owner_id.to_def_id()) || in_external_macro(cx.tcx.sess, item.span)) {
@@ -502,13 +539,23 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[
502539
suspicious_doc_comments::check(cx, attrs);
503540

504541
let (fragments, _) = attrs_to_doc_fragments(attrs.iter().map(|attr| (attr, None)), true);
505-
let mut doc = String::new();
506-
for fragment in &fragments {
507-
add_doc_fragment(&mut doc, fragment);
508-
}
542+
let mut doc = fragments.iter().fold(String::new(), |mut acc, fragment| {
543+
add_doc_fragment(&mut acc, fragment);
544+
acc
545+
});
509546
doc.pop();
510547

511-
if doc.is_empty() {
548+
if doc.trim().is_empty() {
549+
if let Some(span) = span_of_fragments(&fragments) {
550+
span_lint_and_help(
551+
cx,
552+
EMPTY_DOCS,
553+
span,
554+
"empty doc comment",
555+
None,
556+
"consider removing or filling it",
557+
);
558+
}
512559
return Some(DocHeaders::default());
513560
}
514561

clippy_lints/src/manual_is_ascii_check.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ enum CharRange {
7474
LowerChar,
7575
/// 'A'..='Z' | b'A'..=b'Z'
7676
UpperChar,
77-
/// AsciiLower | AsciiUpper
77+
/// `AsciiLower` | `AsciiUpper`
7878
FullChar,
7979
/// '0..=9'
8080
Digit,

clippy_lints/src/methods/filter_map.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ enum OffendingFilterExpr<'tcx> {
7575
},
7676
/// `.filter(|enum| matches!(enum, Enum::A(_)))`
7777
Matches {
78-
/// The DefId of the variant being matched
78+
/// The `DefId` of the variant being matched
7979
variant_def_id: hir::def_id::DefId,
8080
},
8181
}

clippy_lints/src/question_mark.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,12 @@ impl QuestionMark {
7474
enum IfBlockType<'hir> {
7575
/// An `if x.is_xxx() { a } else { b } ` expression.
7676
///
77-
/// Contains: caller (x), caller_type, call_sym (is_xxx), if_then (a), if_else (b)
77+
/// Contains: `caller (x), caller_type, call_sym (is_xxx), if_then (a), if_else (b)`
7878
IfIs(&'hir Expr<'hir>, Ty<'hir>, Symbol, &'hir Expr<'hir>),
7979
/// An `if let Xxx(a) = b { c } else { d }` expression.
8080
///
81-
/// Contains: let_pat_qpath (Xxx), let_pat_type, let_pat_sym (a), let_expr (b), if_then (c),
82-
/// if_else (d)
81+
/// Contains: `let_pat_qpath (Xxx), let_pat_type, let_pat_sym (a), let_expr (b), if_then (c),
82+
/// if_else (d)`
8383
IfLet(
8484
Res,
8585
Ty<'hir>,

clippy_lints/src/slow_vector_initialization.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ declare_lint_pass!(SlowVectorInit => [SLOW_VECTOR_INITIALIZATION]);
6262
/// assigned to a variable. For example, `let mut vec = Vec::with_capacity(0)` or
6363
/// `vec = Vec::with_capacity(0)`
6464
struct VecAllocation<'tcx> {
65-
/// HirId of the variable
65+
/// `HirId` of the variable
6666
local_id: HirId,
6767

6868
/// Reference to the expression which allocates the vector

tests/ui/empty_docs.rs

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#![allow(unused)]
2+
#![warn(clippy::empty_docs)]
3+
mod outer {
4+
//!
5+
6+
/// this is a struct
7+
struct Bananas {
8+
/// count
9+
count: usize,
10+
}
11+
12+
///
13+
enum Warn {
14+
///
15+
A,
16+
B,
17+
}
18+
19+
enum DontWarn {
20+
/// i
21+
A,
22+
B,
23+
}
24+
25+
#[doc = ""]
26+
fn warn_about_this() {}
27+
28+
#[doc = ""]
29+
#[doc = ""]
30+
fn this_doesn_warn() {}
31+
32+
#[doc = "a fine function"]
33+
fn this_is_fine() {}
34+
35+
///
36+
mod inner {
37+
///
38+
fn dont_warn_inner_outer() {
39+
//!w
40+
}
41+
42+
fn this_is_ok() {
43+
//!
44+
//! inside the function
45+
}
46+
47+
fn warn() {
48+
/*! */
49+
}
50+
51+
fn dont_warn() {
52+
/*! dont warn me */
53+
}
54+
55+
trait NoDoc {
56+
///
57+
fn some() {}
58+
}
59+
}
60+
61+
union Unite {
62+
/// lint y
63+
x: i32,
64+
///
65+
y: i32,
66+
}
67+
}

tests/ui/empty_docs.stderr

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
error: empty doc comment
2+
--> tests/ui/empty_docs.rs:4:5
3+
|
4+
LL | //!
5+
| ^^^
6+
|
7+
= help: consider removing or filling it
8+
= note: `-D clippy::empty-docs` implied by `-D warnings`
9+
= help: to override `-D warnings` add `#[allow(clippy::empty_docs)]`
10+
11+
error: empty doc comment
12+
--> tests/ui/empty_docs.rs:12:5
13+
|
14+
LL | ///
15+
| ^^^
16+
|
17+
= help: consider removing or filling it
18+
19+
error: empty doc comment
20+
--> tests/ui/empty_docs.rs:14:9
21+
|
22+
LL | ///
23+
| ^^^
24+
|
25+
= help: consider removing or filling it
26+
27+
error: empty doc comment
28+
--> tests/ui/empty_docs.rs:25:5
29+
|
30+
LL | #[doc = ""]
31+
| ^^^^^^^^^^^
32+
|
33+
= help: consider removing or filling it
34+
35+
error: empty doc comment
36+
--> tests/ui/empty_docs.rs:28:5
37+
|
38+
LL | / #[doc = ""]
39+
LL | | #[doc = ""]
40+
| |_______________^
41+
|
42+
= help: consider removing or filling it
43+
44+
error: empty doc comment
45+
--> tests/ui/empty_docs.rs:35:5
46+
|
47+
LL | ///
48+
| ^^^
49+
|
50+
= help: consider removing or filling it
51+
52+
error: empty doc comment
53+
--> tests/ui/empty_docs.rs:48:13
54+
|
55+
LL | /*! */
56+
| ^^^^^^
57+
|
58+
= help: consider removing or filling it
59+
60+
error: empty doc comment
61+
--> tests/ui/empty_docs.rs:56:13
62+
|
63+
LL | ///
64+
| ^^^
65+
|
66+
= help: consider removing or filling it
67+
68+
error: empty doc comment
69+
--> tests/ui/empty_docs.rs:64:9
70+
|
71+
LL | ///
72+
| ^^^
73+
|
74+
= help: consider removing or filling it
75+
76+
error: aborting due to 9 previous errors
77+

tests/ui/semicolon_if_nothing_returned.fixed

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
//@aux-build:proc_macro_attr.rs
22

33
#![warn(clippy::semicolon_if_nothing_returned)]
4-
#![allow(clippy::redundant_closure, clippy::uninlined_format_args, clippy::needless_late_init)]
4+
#![allow(
5+
clippy::redundant_closure,
6+
clippy::uninlined_format_args,
7+
clippy::needless_late_init,
8+
clippy::empty_docs
9+
)]
510

611
#[macro_use]
712
extern crate proc_macro_attr;

tests/ui/semicolon_if_nothing_returned.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
//@aux-build:proc_macro_attr.rs
22

33
#![warn(clippy::semicolon_if_nothing_returned)]
4-
#![allow(clippy::redundant_closure, clippy::uninlined_format_args, clippy::needless_late_init)]
4+
#![allow(
5+
clippy::redundant_closure,
6+
clippy::uninlined_format_args,
7+
clippy::needless_late_init,
8+
clippy::empty_docs
9+
)]
510

611
#[macro_use]
712
extern crate proc_macro_attr;

tests/ui/semicolon_if_nothing_returned.stderr

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: consider adding a `;` to the last statement for consistent formatting
2-
--> tests/ui/semicolon_if_nothing_returned.rs:13:5
2+
--> tests/ui/semicolon_if_nothing_returned.rs:18:5
33
|
44
LL | println!("Hello")
55
| ^^^^^^^^^^^^^^^^^ help: add a `;` here: `println!("Hello");`
@@ -8,25 +8,25 @@ LL | println!("Hello")
88
= help: to override `-D warnings` add `#[allow(clippy::semicolon_if_nothing_returned)]`
99

1010
error: consider adding a `;` to the last statement for consistent formatting
11-
--> tests/ui/semicolon_if_nothing_returned.rs:17:5
11+
--> tests/ui/semicolon_if_nothing_returned.rs:22:5
1212
|
1313
LL | get_unit()
1414
| ^^^^^^^^^^ help: add a `;` here: `get_unit();`
1515

1616
error: consider adding a `;` to the last statement for consistent formatting
17-
--> tests/ui/semicolon_if_nothing_returned.rs:22:5
17+
--> tests/ui/semicolon_if_nothing_returned.rs:27:5
1818
|
1919
LL | y = x + 1
2020
| ^^^^^^^^^ help: add a `;` here: `y = x + 1;`
2121

2222
error: consider adding a `;` to the last statement for consistent formatting
23-
--> tests/ui/semicolon_if_nothing_returned.rs:28:9
23+
--> tests/ui/semicolon_if_nothing_returned.rs:33:9
2424
|
2525
LL | hello()
2626
| ^^^^^^^ help: add a `;` here: `hello();`
2727

2828
error: consider adding a `;` to the last statement for consistent formatting
29-
--> tests/ui/semicolon_if_nothing_returned.rs:39:9
29+
--> tests/ui/semicolon_if_nothing_returned.rs:44:9
3030
|
3131
LL | ptr::drop_in_place(s.as_mut_ptr())
3232
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add a `;` here: `ptr::drop_in_place(s.as_mut_ptr());`

0 commit comments

Comments
 (0)