]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/upper_case_acronyms.rs
Auto merge of #81238 - RalfJung:copy-intrinsics, r=m-ou-se
[rust.git] / clippy_lints / src / upper_case_acronyms.rs
1 use crate::utils::span_lint_and_sugg;
2 use if_chain::if_chain;
3 use itertools::Itertools;
4 use rustc_ast::ast::{Item, ItemKind, Variant};
5 use rustc_errors::Applicability;
6 use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
7 use rustc_middle::lint::in_external_macro;
8 use rustc_session::{declare_lint_pass, declare_tool_lint};
9 use rustc_span::symbol::Ident;
10
11 declare_clippy_lint! {
12     /// **What it does:** Checks for camel case name containing a capitalized acronym.
13     ///
14     /// **Why is this bad?** In CamelCase, acronyms count as one word.
15     /// See [naming conventions](https://rust-lang.github.io/api-guidelines/naming.html#casing-conforms-to-rfc-430-c-case)
16     /// for more.
17     ///
18     /// **Known problems:** When two acronyms are contiguous, the lint can't tell where
19     /// the first acronym ends and the second starts, so it suggests to lowercase all of
20     /// the letters in the second acronym.
21     ///
22     /// **Example:**
23     ///
24     /// ```rust
25     /// struct HTTPResponse;
26     /// ```
27     /// Use instead:
28     /// ```rust
29     /// struct HttpResponse;
30     /// ```
31     pub UPPER_CASE_ACRONYMS,
32     style,
33     "capitalized acronyms are against the naming convention"
34 }
35
36 declare_lint_pass!(UpperCaseAcronyms => [UPPER_CASE_ACRONYMS]);
37
38 fn correct_ident(ident: &str) -> String {
39     let ident = ident.chars().rev().collect::<String>();
40     let fragments = ident
41         .split_inclusive(|x: char| !x.is_ascii_lowercase())
42         .rev()
43         .map(|x| x.chars().rev().collect::<String>());
44
45     let mut ident = fragments.clone().next().unwrap();
46     for (ref prev, ref curr) in fragments.tuple_windows() {
47         if [prev, curr]
48             .iter()
49             .all(|s| s.len() == 1 && s.chars().next().unwrap().is_ascii_uppercase())
50         {
51             ident.push_str(&curr.to_ascii_lowercase());
52         } else {
53             ident.push_str(curr);
54         }
55     }
56     ident
57 }
58
59 fn check_ident(cx: &EarlyContext<'_>, ident: &Ident) {
60     let span = ident.span;
61     let ident = &ident.as_str();
62     let corrected = correct_ident(ident);
63     if ident != &corrected {
64         span_lint_and_sugg(
65             cx,
66             UPPER_CASE_ACRONYMS,
67             span,
68             &format!("name `{}` contains a capitalized acronym", ident),
69             "consider making the acronym lowercase, except the initial letter",
70             corrected,
71             Applicability::MaybeIncorrect,
72         )
73     }
74 }
75
76 impl EarlyLintPass for UpperCaseAcronyms {
77     fn check_item(&mut self, cx: &EarlyContext<'_>, it: &Item) {
78         if_chain! {
79             if !in_external_macro(cx.sess(), it.span);
80             if matches!(
81                 it.kind,
82                 ItemKind::TyAlias(..) | ItemKind::Enum(..) | ItemKind::Struct(..) | ItemKind::Trait(..)
83             );
84             then {
85                 check_ident(cx, &it.ident);
86             }
87         }
88     }
89
90     fn check_variant(&mut self, cx: &EarlyContext<'_>, v: &Variant) {
91         check_ident(cx, &v.ident);
92     }
93 }