]> git.proxmox.com Git - rustc.git/blob - src/tools/clippy/clippy_lints/src/four_forward_slashes.rs
bump version to 1.79.0+dfsg1-1~bpo12+pve2
[rustc.git] / src / tools / clippy / clippy_lints / src / four_forward_slashes.rs
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;
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 /// ```no_run
18 /// //// My amazing data structure
19 /// pub struct Foo {
20 /// // ...
21 /// }
22 /// ```
23 ///
24 /// Use instead:
25 /// ```no_run
26 /// /// My amazing data structure
27 /// pub struct Foo {
28 /// // ...
29 /// }
30 /// ```
31 #[clippy::version = "1.73.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 }