Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New lint [four_forward_slashes] #11140

Merged
merged 2 commits into from
Jul 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4867,6 +4867,7 @@ Released 2018-09-13
[`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref
[`format_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_in_format_args
[`format_push_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_push_string
[`four_forward_slashes`]: https://rust-lang.github.io/rust-clippy/master/index.html#four_forward_slashes
[`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect
[`from_over_into`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into
[`from_raw_with_void_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_raw_with_void_ptr
Expand Down
1 change: 1 addition & 0 deletions clippy_lints/src/declared_lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING_INFO,
crate::formatting::SUSPICIOUS_ELSE_FORMATTING_INFO,
crate::formatting::SUSPICIOUS_UNARY_OP_FORMATTING_INFO,
crate::four_forward_slashes::FOUR_FORWARD_SLASHES_INFO,
crate::from_over_into::FROM_OVER_INTO_INFO,
crate::from_raw_with_void_ptr::FROM_RAW_WITH_VOID_PTR_INFO,
crate::from_str_radix_10::FROM_STR_RADIX_10_INFO,
Expand Down
99 changes: 99 additions & 0 deletions clippy_lints/src/four_forward_slashes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
use clippy_utils::diagnostics::span_lint_and_then;
use rustc_errors::Applicability;
use rustc_hir::Item;
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::Span;

declare_clippy_lint! {
/// ### What it does
/// Checks for outer doc comments written with 4 forward slashes (`////`).
///
/// ### Why is this bad?
/// This is (probably) a typo, and results in it not being a doc comment; just a regular
/// comment.
///
/// ### Example
/// ```rust
/// //// My amazing data structure
/// pub struct Foo {
/// // ...
/// }
/// ```
///
/// Use instead:
/// ```rust
/// /// My amazing data structure
/// pub struct Foo {
/// // ...
/// }
/// ```
#[clippy::version = "1.72.0"]
pub FOUR_FORWARD_SLASHES,
suspicious,
"comments with 4 forward slashes (`////`) likely intended to be doc comments (`///`)"
}
declare_lint_pass!(FourForwardSlashes => [FOUR_FORWARD_SLASHES]);

impl<'tcx> LateLintPass<'tcx> for FourForwardSlashes {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
if item.span.from_expansion() {
return;
}
let sm = cx.sess().source_map();
let mut span = cx
.tcx
.hir()
.attrs(item.hir_id())
.iter()
.fold(item.span.shrink_to_lo(), |span, attr| span.to(attr.span));
let (Some(file), _, _, end_line, _) = sm.span_to_location_info(span) else {
return;
};
let mut bad_comments = vec![];
for line in (0..end_line.saturating_sub(1)).rev() {
let Some(contents) = file.get_line(line).map(|c| c.trim().to_owned()) else {
return;
};
// Keep searching until we find the next item
if !contents.is_empty() && !contents.starts_with("//") && !contents.starts_with("#[") {
break;
}

if contents.starts_with("////") && !matches!(contents.chars().nth(4), Some('/' | '!')) {
let bounds = file.line_bounds(line);
let line_span = Span::with_root_ctxt(bounds.start, bounds.end);
span = line_span.to(span);
bad_comments.push((line_span, contents));
}
}

if !bad_comments.is_empty() {
span_lint_and_then(
cx,
FOUR_FORWARD_SLASHES,
span,
"this item has comments with 4 forward slashes (`////`). These look like doc comments, but they aren't",
|diag| {
let msg = if bad_comments.len() == 1 {
"make this a doc comment by removing one `/`"
} else {
"turn these into doc comments by removing one `/`"
};

diag.multipart_suggestion(
msg,
bad_comments
.into_iter()
// It's a little unfortunate but the span includes the `\n` yet the contents
// do not, so we must add it back. If some codebase uses `\r\n` instead they
// will need normalization but it should be fine
.map(|(span, c)| (span, c.replacen("////", "///", 1) + "\n"))
.collect(),
Applicability::MachineApplicable,
);
},
);
}
}
}
2 changes: 2 additions & 0 deletions clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ mod format_args;
mod format_impl;
mod format_push_string;
mod formatting;
mod four_forward_slashes;
mod from_over_into;
mod from_raw_with_void_ptr;
mod from_str_radix_10;
Expand Down Expand Up @@ -1081,6 +1082,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_early_pass(|| Box::new(visibility::Visibility));
store.register_late_pass(move |_| Box::new(tuple_array_conversions::TupleArrayConversions { msrv: msrv() }));
store.register_late_pass(|_| Box::new(manual_float_methods::ManualFloatMethods));
store.register_late_pass(|_| Box::new(four_forward_slashes::FourForwardSlashes));
// add lints here, do not remove this comment, it's used in `new_lint`
}

Expand Down
48 changes: 48 additions & 0 deletions tests/ui/four_forward_slashes.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//@run-rustfix
//@aux-build:proc_macros.rs:proc-macro
#![feature(custom_inner_attributes)]
#![allow(unused)]
#![warn(clippy::four_forward_slashes)]
#![no_main]
#![rustfmt::skip]

#[macro_use]
extern crate proc_macros;

/// whoops
fn a() {}

/// whoops
#[allow(dead_code)]
fn b() {}

/// whoops
/// two borked comments!
#[track_caller]
fn c() {}

fn d() {}

#[test]
/// between attributes
#[allow(dead_code)]
fn g() {}

/// not very start of contents
fn h() {}

fn i() {
//// don't lint me bozo
todo!()
}

external! {
//// don't lint me bozo
fn e() {}
}

with_span! {
span
//// don't lint me bozo
fn f() {}
}
48 changes: 48 additions & 0 deletions tests/ui/four_forward_slashes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//@run-rustfix
//@aux-build:proc_macros.rs:proc-macro
#![feature(custom_inner_attributes)]
#![allow(unused)]
#![warn(clippy::four_forward_slashes)]
#![no_main]
#![rustfmt::skip]

#[macro_use]
extern crate proc_macros;

//// whoops
fn a() {}

//// whoops
#[allow(dead_code)]
fn b() {}

//// whoops
//// two borked comments!
#[track_caller]
fn c() {}

fn d() {}

#[test]
//// between attributes
#[allow(dead_code)]
fn g() {}

//// not very start of contents
fn h() {}

fn i() {
//// don't lint me bozo
todo!()
}

external! {
//// don't lint me bozo
fn e() {}
}

with_span! {
span
//// don't lint me bozo
fn f() {}
}
68 changes: 68 additions & 0 deletions tests/ui/four_forward_slashes.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
error: this item has comments with 4 forward slashes (`////`). These look like doc comments, but they aren't
--> $DIR/four_forward_slashes.rs:12:1
|
LL | / //// whoops
LL | | fn a() {}
| |_
|
= note: `-D clippy::four-forward-slashes` implied by `-D warnings`
help: make this a doc comment by removing one `/`
|
LL + /// whoops
|

error: this item has comments with 4 forward slashes (`////`). These look like doc comments, but they aren't
--> $DIR/four_forward_slashes.rs:15:1
|
LL | / //// whoops
LL | | #[allow(dead_code)]
LL | | fn b() {}
| |_
|
help: make this a doc comment by removing one `/`
|
LL + /// whoops
|

error: this item has comments with 4 forward slashes (`////`). These look like doc comments, but they aren't
--> $DIR/four_forward_slashes.rs:19:1
|
LL | / //// whoops
LL | | //// two borked comments!
LL | | #[track_caller]
LL | | fn c() {}
| |_
|
help: turn these into doc comments by removing one `/`
|
LL + /// whoops
LL ~ /// two borked comments!
|

error: this item has comments with 4 forward slashes (`////`). These look like doc comments, but they aren't
--> $DIR/four_forward_slashes.rs:27:1
|
LL | / //// between attributes
LL | | #[allow(dead_code)]
LL | | fn g() {}
| |_
|
help: make this a doc comment by removing one `/`
|
LL + /// between attributes
|

error: this item has comments with 4 forward slashes (`////`). These look like doc comments, but they aren't
--> $DIR/four_forward_slashes.rs:31:1
|
LL | / //// not very start of contents
LL | | fn h() {}
| |_
|
help: make this a doc comment by removing one `/`
|
LL + /// not very start of contents
|

error: aborting due to 5 previous errors

7 changes: 7 additions & 0 deletions tests/ui/four_forward_slashes_first_line.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/// borked doc comment on the first line. doesn't combust!
fn a() {}

//@run-rustfix
// This test's entire purpose is to make sure we don't panic if the comment with four slashes
// extends to the first line of the file. This is likely pretty rare in production, but an ICE is an
// ICE.
7 changes: 7 additions & 0 deletions tests/ui/four_forward_slashes_first_line.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
//// borked doc comment on the first line. doesn't combust!
fn a() {}

//@run-rustfix
// This test's entire purpose is to make sure we don't panic if the comment with four slashes
// extends to the first line of the file. This is likely pretty rare in production, but an ICE is an
// ICE.
15 changes: 15 additions & 0 deletions tests/ui/four_forward_slashes_first_line.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
error: this item has comments with 4 forward slashes (`////`). These look like doc comments, but they aren't
--> $DIR/four_forward_slashes_first_line.rs:1:1
|
LL | / //// borked doc comment on the first line. doesn't combust!
LL | | fn a() {}
| |_
|
= note: `-D clippy::four-forward-slashes` implied by `-D warnings`
help: make this a doc comment by removing one `/`
|
LL + /// borked doc comment on the first line. doesn't combust!
|

error: aborting due to previous error