Skip to content

Commit 5d01e6e

Browse files
committed
new lint: suspicious_doc_comments
1 parent b8cbce8 commit 5d01e6e

9 files changed

+427
-0
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -4925,6 +4925,7 @@ Released 2018-09-13
49254925
[`suspicious_arithmetic_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_arithmetic_impl
49264926
[`suspicious_assignment_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_assignment_formatting
49274927
[`suspicious_command_arg_space`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_command_arg_space
4928+
[`suspicious_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_doc_comments
49284929
[`suspicious_else_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_else_formatting
49294930
[`suspicious_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_map
49304931
[`suspicious_op_assign_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_op_assign_impl

clippy_lints/src/declared_lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
569569
crate::strings::STR_TO_STRING_INFO,
570570
crate::strings::TRIM_SPLIT_WHITESPACE_INFO,
571571
crate::strlen_on_c_strings::STRLEN_ON_C_STRINGS_INFO,
572+
crate::suspicious_doc_comments::SUSPICIOUS_DOC_COMMENTS_INFO,
572573
crate::suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS_INFO,
573574
crate::suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL_INFO,
574575
crate::suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL_INFO,

clippy_lints/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,7 @@ mod slow_vector_initialization;
284284
mod std_instead_of_core;
285285
mod strings;
286286
mod strlen_on_c_strings;
287+
mod suspicious_doc_comments;
287288
mod suspicious_operation_groupings;
288289
mod suspicious_trait_impl;
289290
mod suspicious_xor_used_as_pow;
@@ -958,6 +959,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
958959
store.register_late_pass(|_| Box::new(lines_filter_map_ok::LinesFilterMapOk));
959960
store.register_late_pass(|_| Box::new(tests_outside_test_module::TestsOutsideTestModule));
960961
store.register_late_pass(|_| Box::new(manual_slice_size_calculation::ManualSliceSizeCalculation));
962+
store.register_early_pass(|| Box::new(suspicious_doc_comments::SuspiciousDocComments));
961963
// add lints here, do not remove this comment, it's used in `new_lint`
962964
}
963965

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_then};
2+
use if_chain::if_chain;
3+
use rustc_ast::{token::CommentKind, AttrKind, AttrStyle, Attribute, Item};
4+
use rustc_errors::Applicability;
5+
use rustc_lint::{EarlyContext, EarlyLintPass};
6+
use rustc_session::{declare_lint_pass, declare_tool_lint};
7+
use rustc_span::Span;
8+
9+
declare_clippy_lint! {
10+
/// ### What it does
11+
/// Detects the use of outer doc comments (`///`, `/**`) followed by a bang (`!`): `///!`
12+
///
13+
/// ### Why is this bad?
14+
/// Triple-slash comments (known as "outer doc comments") apply to items that follow it.
15+
/// An outer doc comment followed by a bang (i.e. `///!`) has no specific meaning.
16+
///
17+
/// The user most likely meant to write an inner doc comment (`//!`, `/*!`), which
18+
/// applies to the parent item (i.e. the item that the comment is contained in,
19+
/// usually a module or crate).
20+
///
21+
/// ### Known problems
22+
/// Inner doc comments can only appear before items, so there are certain cases where the suggestion
23+
/// made by this lint is not valid code. For example:
24+
/// ```rs
25+
/// fn foo() {}
26+
/// ///!
27+
/// fn bar() {}
28+
/// ```
29+
/// This lint detects the doc comment and suggests changing it to `//!`, but an inner doc comment
30+
/// is not valid at that position.
31+
///
32+
/// ### Example
33+
/// In this example, the doc comment is attached to the *function*, rather than the *module*.
34+
/// ```rust
35+
/// pub mod util {
36+
/// ///! This module contains utility functions.
37+
///
38+
/// pub fn dummy() {}
39+
/// }
40+
/// ```
41+
///
42+
/// Use instead:
43+
/// ```rust
44+
/// pub mod util {
45+
/// //! This module contains utility functions.
46+
///
47+
/// pub fn dummy() {}
48+
/// }
49+
/// ```
50+
#[clippy::version = "1.70.0"]
51+
pub SUSPICIOUS_DOC_COMMENTS,
52+
suspicious,
53+
"suspicious usage of (outer) doc comments"
54+
}
55+
declare_lint_pass!(SuspiciousDocComments => [SUSPICIOUS_DOC_COMMENTS]);
56+
57+
const WARNING: &str = "this is an outer doc comment and does not apply to the parent module or crate";
58+
const HELP: &str = "use an inner doc comment to document the parent module or crate";
59+
60+
impl EarlyLintPass for SuspiciousDocComments {
61+
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
62+
let replacements = collect_doc_comment_replacements(&item.attrs);
63+
64+
if let Some(((lo_span, _), (hi_span, _))) = replacements.first().zip(replacements.last()) {
65+
let span = lo_span.to(*hi_span);
66+
67+
span_lint_and_then(cx, SUSPICIOUS_DOC_COMMENTS, span, WARNING, |diag| {
68+
multispan_sugg_with_applicability(diag, HELP, Applicability::MaybeIncorrect, replacements);
69+
});
70+
}
71+
}
72+
}
73+
74+
fn collect_doc_comment_replacements(attrs: &[Attribute]) -> Vec<(Span, String)> {
75+
attrs
76+
.iter()
77+
.filter_map(|attr| {
78+
if_chain! {
79+
if let AttrKind::DocComment(com_kind, sym) = attr.kind;
80+
if let AttrStyle::Outer = attr.style;
81+
if let Some(com) = sym.as_str().strip_prefix('!');
82+
then {
83+
let sugg = match com_kind {
84+
CommentKind::Line => format!("//!{com}"),
85+
CommentKind::Block => format!("/*!{com}*/")
86+
};
87+
Some((attr.span, sugg))
88+
} else {
89+
None
90+
}
91+
}
92+
})
93+
.collect()
94+
}
+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// run-rustfix
2+
#![allow(unused)]
3+
#![warn(clippy::suspicious_doc_comments)]
4+
5+
//! Real module documentation.
6+
//! Fake module documentation.
7+
fn baz() {}
8+
9+
pub mod singleline_outer_doc {
10+
//! This module contains useful functions.
11+
12+
pub fn bar() {}
13+
}
14+
15+
pub mod singleline_inner_doc {
16+
//! This module contains useful functions.
17+
18+
pub fn bar() {}
19+
}
20+
21+
pub mod multiline_outer_doc {
22+
/*! This module contains useful functions.
23+
*/
24+
25+
pub fn bar() {}
26+
}
27+
28+
pub mod multiline_inner_doc {
29+
/*! This module contains useful functions.
30+
*/
31+
32+
pub fn bar() {}
33+
}
34+
35+
pub mod multiline_outer_doc2 {
36+
//! This module
37+
//! contains
38+
//! useful functions.
39+
40+
pub fn bar() {}
41+
}
42+
43+
pub mod multiline_outer_doc3 {
44+
//! a
45+
//! b
46+
47+
/// c
48+
pub fn bar() {}
49+
}
50+
51+
pub mod multiline_outer_doc4 {
52+
//! a
53+
/// b
54+
pub fn bar() {}
55+
}
56+
57+
pub mod multiline_outer_doc_gap {
58+
//! a
59+
60+
//! b
61+
pub fn bar() {}
62+
}
63+
64+
pub mod multiline_outer_doc_commented {
65+
/////! This outer doc comment was commented out.
66+
pub fn bar() {}
67+
}
68+
69+
pub mod outer_doc_macro {
70+
//! Very cool macro
71+
macro_rules! x {
72+
() => {};
73+
}
74+
}
75+
76+
pub mod useless_outer_doc {
77+
//! Huh.
78+
use std::mem;
79+
}
80+
81+
fn main() {}

tests/ui/suspicious_doc_comments.rs

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// run-rustfix
2+
#![allow(unused)]
3+
#![warn(clippy::suspicious_doc_comments)]
4+
5+
//! Real module documentation.
6+
///! Fake module documentation.
7+
fn baz() {}
8+
9+
pub mod singleline_outer_doc {
10+
///! This module contains useful functions.
11+
12+
pub fn bar() {}
13+
}
14+
15+
pub mod singleline_inner_doc {
16+
//! This module contains useful functions.
17+
18+
pub fn bar() {}
19+
}
20+
21+
pub mod multiline_outer_doc {
22+
/**! This module contains useful functions.
23+
*/
24+
25+
pub fn bar() {}
26+
}
27+
28+
pub mod multiline_inner_doc {
29+
/*! This module contains useful functions.
30+
*/
31+
32+
pub fn bar() {}
33+
}
34+
35+
pub mod multiline_outer_doc2 {
36+
///! This module
37+
///! contains
38+
///! useful functions.
39+
40+
pub fn bar() {}
41+
}
42+
43+
pub mod multiline_outer_doc3 {
44+
///! a
45+
///! b
46+
47+
/// c
48+
pub fn bar() {}
49+
}
50+
51+
pub mod multiline_outer_doc4 {
52+
///! a
53+
/// b
54+
pub fn bar() {}
55+
}
56+
57+
pub mod multiline_outer_doc_gap {
58+
///! a
59+
60+
///! b
61+
pub fn bar() {}
62+
}
63+
64+
pub mod multiline_outer_doc_commented {
65+
/////! This outer doc comment was commented out.
66+
pub fn bar() {}
67+
}
68+
69+
pub mod outer_doc_macro {
70+
///! Very cool macro
71+
macro_rules! x {
72+
() => {};
73+
}
74+
}
75+
76+
pub mod useless_outer_doc {
77+
///! Huh.
78+
use std::mem;
79+
}
80+
81+
fn main() {}

0 commit comments

Comments
 (0)