Skip to content

Commit 9f0cbfd

Browse files
committed
Auto merge of rust-lang#11140 - Centri3:four_forward_slashes, r=blyxyas
New lint [`four_forward_slashes`] Closes rust-lang#9212 changelog: New lint [`four_forward_slashes`]
2 parents 747df85 + 2e43d0c commit 9f0cbfd

10 files changed

+296
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4868,6 +4868,7 @@ Released 2018-09-13
48684868
[`format_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_collect
48694869
[`format_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_in_format_args
48704870
[`format_push_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_push_string
4871+
[`four_forward_slashes`]: https://rust-lang.github.io/rust-clippy/master/index.html#four_forward_slashes
48714872
[`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect
48724873
[`from_over_into`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into
48734874
[`from_raw_with_void_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_raw_with_void_ptr

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
183183
crate::formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING_INFO,
184184
crate::formatting::SUSPICIOUS_ELSE_FORMATTING_INFO,
185185
crate::formatting::SUSPICIOUS_UNARY_OP_FORMATTING_INFO,
186+
crate::four_forward_slashes::FOUR_FORWARD_SLASHES_INFO,
186187
crate::from_over_into::FROM_OVER_INTO_INFO,
187188
crate::from_raw_with_void_ptr::FROM_RAW_WITH_VOID_PTR_INFO,
188189
crate::from_str_radix_10::FROM_STR_RADIX_10_INFO,
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
use clippy_utils::diagnostics::span_lint_and_then;
2+
use rustc_errors::Applicability;
3+
use rustc_hir::Item;
4+
use rustc_lint::{LateContext, LateLintPass, LintContext};
5+
use rustc_session::{declare_lint_pass, declare_tool_lint};
6+
use rustc_span::Span;
7+
8+
declare_clippy_lint! {
9+
/// ### What it does
10+
/// Checks for outer doc comments written with 4 forward slashes (`////`).
11+
///
12+
/// ### Why is this bad?
13+
/// This is (probably) a typo, and results in it not being a doc comment; just a regular
14+
/// comment.
15+
///
16+
/// ### Example
17+
/// ```rust
18+
/// //// My amazing data structure
19+
/// pub struct Foo {
20+
/// // ...
21+
/// }
22+
/// ```
23+
///
24+
/// Use instead:
25+
/// ```rust
26+
/// /// My amazing data structure
27+
/// pub struct Foo {
28+
/// // ...
29+
/// }
30+
/// ```
31+
#[clippy::version = "1.72.0"]
32+
pub FOUR_FORWARD_SLASHES,
33+
suspicious,
34+
"comments with 4 forward slashes (`////`) likely intended to be doc comments (`///`)"
35+
}
36+
declare_lint_pass!(FourForwardSlashes => [FOUR_FORWARD_SLASHES]);
37+
38+
impl<'tcx> LateLintPass<'tcx> for FourForwardSlashes {
39+
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
40+
if item.span.from_expansion() {
41+
return;
42+
}
43+
let sm = cx.sess().source_map();
44+
let mut span = cx
45+
.tcx
46+
.hir()
47+
.attrs(item.hir_id())
48+
.iter()
49+
.fold(item.span.shrink_to_lo(), |span, attr| span.to(attr.span));
50+
let (Some(file), _, _, end_line, _) = sm.span_to_location_info(span) else {
51+
return;
52+
};
53+
let mut bad_comments = vec![];
54+
for line in (0..end_line.saturating_sub(1)).rev() {
55+
let Some(contents) = file.get_line(line).map(|c| c.trim().to_owned()) else {
56+
return;
57+
};
58+
// Keep searching until we find the next item
59+
if !contents.is_empty() && !contents.starts_with("//") && !contents.starts_with("#[") {
60+
break;
61+
}
62+
63+
if contents.starts_with("////") && !matches!(contents.chars().nth(4), Some('/' | '!')) {
64+
let bounds = file.line_bounds(line);
65+
let line_span = Span::with_root_ctxt(bounds.start, bounds.end);
66+
span = line_span.to(span);
67+
bad_comments.push((line_span, contents));
68+
}
69+
}
70+
71+
if !bad_comments.is_empty() {
72+
span_lint_and_then(
73+
cx,
74+
FOUR_FORWARD_SLASHES,
75+
span,
76+
"this item has comments with 4 forward slashes (`////`). These look like doc comments, but they aren't",
77+
|diag| {
78+
let msg = if bad_comments.len() == 1 {
79+
"make this a doc comment by removing one `/`"
80+
} else {
81+
"turn these into doc comments by removing one `/`"
82+
};
83+
84+
diag.multipart_suggestion(
85+
msg,
86+
bad_comments
87+
.into_iter()
88+
// It's a little unfortunate but the span includes the `\n` yet the contents
89+
// do not, so we must add it back. If some codebase uses `\r\n` instead they
90+
// will need normalization but it should be fine
91+
.map(|(span, c)| (span, c.replacen("////", "///", 1) + "\n"))
92+
.collect(),
93+
Applicability::MachineApplicable,
94+
);
95+
},
96+
);
97+
}
98+
}
99+
}

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ mod format_args;
136136
mod format_impl;
137137
mod format_push_string;
138138
mod formatting;
139+
mod four_forward_slashes;
139140
mod from_over_into;
140141
mod from_raw_with_void_ptr;
141142
mod from_str_radix_10;
@@ -1078,6 +1079,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10781079
store.register_early_pass(|| Box::new(visibility::Visibility));
10791080
store.register_late_pass(move |_| Box::new(tuple_array_conversions::TupleArrayConversions { msrv: msrv() }));
10801081
store.register_late_pass(|_| Box::new(manual_float_methods::ManualFloatMethods));
1082+
store.register_late_pass(|_| Box::new(four_forward_slashes::FourForwardSlashes));
10811083
// add lints here, do not remove this comment, it's used in `new_lint`
10821084
}
10831085

tests/ui/four_forward_slashes.fixed

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//@run-rustfix
2+
//@aux-build:proc_macros.rs:proc-macro
3+
#![feature(custom_inner_attributes)]
4+
#![allow(unused)]
5+
#![warn(clippy::four_forward_slashes)]
6+
#![no_main]
7+
#![rustfmt::skip]
8+
9+
#[macro_use]
10+
extern crate proc_macros;
11+
12+
/// whoops
13+
fn a() {}
14+
15+
/// whoops
16+
#[allow(dead_code)]
17+
fn b() {}
18+
19+
/// whoops
20+
/// two borked comments!
21+
#[track_caller]
22+
fn c() {}
23+
24+
fn d() {}
25+
26+
#[test]
27+
/// between attributes
28+
#[allow(dead_code)]
29+
fn g() {}
30+
31+
/// not very start of contents
32+
fn h() {}
33+
34+
fn i() {
35+
//// don't lint me bozo
36+
todo!()
37+
}
38+
39+
external! {
40+
//// don't lint me bozo
41+
fn e() {}
42+
}
43+
44+
with_span! {
45+
span
46+
//// don't lint me bozo
47+
fn f() {}
48+
}

tests/ui/four_forward_slashes.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//@run-rustfix
2+
//@aux-build:proc_macros.rs:proc-macro
3+
#![feature(custom_inner_attributes)]
4+
#![allow(unused)]
5+
#![warn(clippy::four_forward_slashes)]
6+
#![no_main]
7+
#![rustfmt::skip]
8+
9+
#[macro_use]
10+
extern crate proc_macros;
11+
12+
//// whoops
13+
fn a() {}
14+
15+
//// whoops
16+
#[allow(dead_code)]
17+
fn b() {}
18+
19+
//// whoops
20+
//// two borked comments!
21+
#[track_caller]
22+
fn c() {}
23+
24+
fn d() {}
25+
26+
#[test]
27+
//// between attributes
28+
#[allow(dead_code)]
29+
fn g() {}
30+
31+
//// not very start of contents
32+
fn h() {}
33+
34+
fn i() {
35+
//// don't lint me bozo
36+
todo!()
37+
}
38+
39+
external! {
40+
//// don't lint me bozo
41+
fn e() {}
42+
}
43+
44+
with_span! {
45+
span
46+
//// don't lint me bozo
47+
fn f() {}
48+
}

tests/ui/four_forward_slashes.stderr

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
error: this item has comments with 4 forward slashes (`////`). These look like doc comments, but they aren't
2+
--> $DIR/four_forward_slashes.rs:12:1
3+
|
4+
LL | / //// whoops
5+
LL | | fn a() {}
6+
| |_
7+
|
8+
= note: `-D clippy::four-forward-slashes` implied by `-D warnings`
9+
help: make this a doc comment by removing one `/`
10+
|
11+
LL + /// whoops
12+
|
13+
14+
error: this item has comments with 4 forward slashes (`////`). These look like doc comments, but they aren't
15+
--> $DIR/four_forward_slashes.rs:15:1
16+
|
17+
LL | / //// whoops
18+
LL | | #[allow(dead_code)]
19+
LL | | fn b() {}
20+
| |_
21+
|
22+
help: make this a doc comment by removing one `/`
23+
|
24+
LL + /// whoops
25+
|
26+
27+
error: this item has comments with 4 forward slashes (`////`). These look like doc comments, but they aren't
28+
--> $DIR/four_forward_slashes.rs:19:1
29+
|
30+
LL | / //// whoops
31+
LL | | //// two borked comments!
32+
LL | | #[track_caller]
33+
LL | | fn c() {}
34+
| |_
35+
|
36+
help: turn these into doc comments by removing one `/`
37+
|
38+
LL + /// whoops
39+
LL ~ /// two borked comments!
40+
|
41+
42+
error: this item has comments with 4 forward slashes (`////`). These look like doc comments, but they aren't
43+
--> $DIR/four_forward_slashes.rs:27:1
44+
|
45+
LL | / //// between attributes
46+
LL | | #[allow(dead_code)]
47+
LL | | fn g() {}
48+
| |_
49+
|
50+
help: make this a doc comment by removing one `/`
51+
|
52+
LL + /// between attributes
53+
|
54+
55+
error: this item has comments with 4 forward slashes (`////`). These look like doc comments, but they aren't
56+
--> $DIR/four_forward_slashes.rs:31:1
57+
|
58+
LL | / //// not very start of contents
59+
LL | | fn h() {}
60+
| |_
61+
|
62+
help: make this a doc comment by removing one `/`
63+
|
64+
LL + /// not very start of contents
65+
|
66+
67+
error: aborting due to 5 previous errors
68+
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/// borked doc comment on the first line. doesn't combust!
2+
fn a() {}
3+
4+
//@run-rustfix
5+
// This test's entire purpose is to make sure we don't panic if the comment with four slashes
6+
// extends to the first line of the file. This is likely pretty rare in production, but an ICE is an
7+
// ICE.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
//// borked doc comment on the first line. doesn't combust!
2+
fn a() {}
3+
4+
//@run-rustfix
5+
// This test's entire purpose is to make sure we don't panic if the comment with four slashes
6+
// extends to the first line of the file. This is likely pretty rare in production, but an ICE is an
7+
// ICE.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error: this item has comments with 4 forward slashes (`////`). These look like doc comments, but they aren't
2+
--> $DIR/four_forward_slashes_first_line.rs:1:1
3+
|
4+
LL | / //// borked doc comment on the first line. doesn't combust!
5+
LL | | fn a() {}
6+
| |_
7+
|
8+
= note: `-D clippy::four-forward-slashes` implied by `-D warnings`
9+
help: make this a doc comment by removing one `/`
10+
|
11+
LL + /// borked doc comment on the first line. doesn't combust!
12+
|
13+
14+
error: aborting due to previous error
15+

0 commit comments

Comments
 (0)