rust/clippy_lints/src/four_forward_slashes.rs

83 lines
3.0 KiB
Rust
Raw Normal View History

use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet_opt};
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, SyntaxContext};
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 src = cx.sess().source_map();
let item_and_attrs_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, _) = src.span_to_location_info(item_and_attrs_span) else {
return;
};
for line in (0..end_line.saturating_sub(1)).rev() {
let Some(contents) = file.get_line(line) else {
continue;
};
let contents = contents.trim();
if contents.is_empty() {
break;
}
if contents.starts_with("////") {
let bounds = file.line_bounds(line);
let span = Span::new(bounds.start, bounds.end, SyntaxContext::root(), None);
if snippet_opt(cx, span).is_some_and(|s| s.trim().starts_with("////")) {
span_lint_and_sugg(
cx,
FOUR_FORWARD_SLASHES,
span,
"comment with 4 forward slashes (`////`). This looks like a doc comment, but it isn't",
"make this a doc comment by removing one `/`",
// 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
contents.replacen("////", "///", 1) + "\n",
Applicability::MachineApplicable,
);
}
}
}
}
}