]> git.proxmox.com Git - rustc.git/blame - src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs
bump version to 1.80.1+dfsg1-1~bpo12+pve1
[rustc.git] / src / tools / clippy / clippy_lints / src / upper_case_acronyms.rs
CommitLineData
4b012472 1use clippy_utils::diagnostics::span_lint_hir_and_then;
f20569fa 2use itertools::Itertools;
f20569fa 3use rustc_errors::Applicability;
4b012472 4use rustc_hir::{HirId, Item, ItemKind};
17df50a5 5use rustc_lint::{LateContext, LateLintPass, LintContext};
f20569fa 6use rustc_middle::lint::in_external_macro;
4b012472 7use rustc_session::impl_lint_pass;
f20569fa
XL
8use rustc_span::symbol::Ident;
9
10declare_clippy_lint! {
94222f64
XL
11 /// ### What it does
12 /// Checks for fully capitalized names and optionally names containing a capitalized acronym.
f20569fa 13 ///
94222f64
XL
14 /// ### Why is this bad?
15 /// In CamelCase, acronyms count as one word.
f20569fa
XL
16 /// See [naming conventions](https://rust-lang.github.io/api-guidelines/naming.html#casing-conforms-to-rfc-430-c-case)
17 /// for more.
18 ///
19 /// By default, the lint only triggers on fully-capitalized names.
20 /// You can use the `upper-case-acronyms-aggressive: true` config option to enable linting
21 /// on all camel case names
22 ///
94222f64
XL
23 /// ### Known problems
24 /// When two acronyms are contiguous, the lint can't tell where
f20569fa
XL
25 /// the first acronym ends and the second starts, so it suggests to lowercase all of
26 /// the letters in the second acronym.
27 ///
94222f64 28 /// ### Example
ed00b5ec 29 /// ```no_run
f20569fa
XL
30 /// struct HTTPResponse;
31 /// ```
32 /// Use instead:
ed00b5ec 33 /// ```no_run
f20569fa
XL
34 /// struct HttpResponse;
35 /// ```
a2a8927a 36 #[clippy::version = "1.51.0"]
f20569fa
XL
37 pub UPPER_CASE_ACRONYMS,
38 style,
39 "capitalized acronyms are against the naming convention"
40}
41
42#[derive(Default)]
43pub struct UpperCaseAcronyms {
17df50a5 44 avoid_breaking_exported_api: bool,
f20569fa
XL
45 upper_case_acronyms_aggressive: bool,
46}
47
48impl UpperCaseAcronyms {
17df50a5 49 pub fn new(avoid_breaking_exported_api: bool, aggressive: bool) -> Self {
f20569fa 50 Self {
17df50a5 51 avoid_breaking_exported_api,
f20569fa
XL
52 upper_case_acronyms_aggressive: aggressive,
53 }
54 }
55}
56
57impl_lint_pass!(UpperCaseAcronyms => [UPPER_CASE_ACRONYMS]);
58
59fn correct_ident(ident: &str) -> String {
60 let ident = ident.chars().rev().collect::<String>();
61 let fragments = ident
62 .split_inclusive(|x: char| !x.is_ascii_lowercase())
63 .rev()
64 .map(|x| x.chars().rev().collect::<String>());
65
66 let mut ident = fragments.clone().next().unwrap();
67 for (ref prev, ref curr) in fragments.tuple_windows() {
fe692bf9 68 if <[&String; 2]>::from((prev, curr))
f20569fa
XL
69 .iter()
70 .all(|s| s.len() == 1 && s.chars().next().unwrap().is_ascii_uppercase())
71 {
72 ident.push_str(&curr.to_ascii_lowercase());
73 } else {
74 ident.push_str(curr);
75 }
76 }
77 ident
78}
79
4b012472 80fn check_ident(cx: &LateContext<'_>, ident: &Ident, hir_id: HirId, be_aggressive: bool) {
f20569fa 81 let span = ident.span;
a2a8927a 82 let ident = ident.as_str();
f20569fa
XL
83 let corrected = correct_ident(ident);
84 // warn if we have pure-uppercase idents
85 // assume that two-letter words are some kind of valid abbreviation like FP for false positive
86 // (and don't warn)
87 if (ident.chars().all(|c| c.is_ascii_uppercase()) && ident.len() > 2)
cdc7bbd5 88 // otherwise, warn if we have SOmeTHING lIKE THIs but only warn with the aggressive
f20569fa 89 // upper-case-acronyms-aggressive config option enabled
a2a8927a 90 || (be_aggressive && ident != corrected)
f20569fa 91 {
4b012472 92 span_lint_hir_and_then(
f20569fa
XL
93 cx,
94 UPPER_CASE_ACRONYMS,
4b012472 95 hir_id,
f20569fa 96 span,
e8be2606 97 format!("name `{ident}` contains a capitalized acronym"),
4b012472
FG
98 |diag| {
99 diag.span_suggestion(
100 span,
101 "consider making the acronym lowercase, except the initial letter",
102 corrected,
103 Applicability::MaybeIncorrect,
104 );
105 },
17df50a5 106 );
f20569fa
XL
107 }
108}
109
17df50a5
XL
110impl LateLintPass<'_> for UpperCaseAcronyms {
111 fn check_item(&mut self, cx: &LateContext<'_>, it: &Item<'_>) {
cdc7bbd5 112 // do not lint public items or in macros
17df50a5 113 if in_external_macro(cx.sess(), it.span)
2b03887a 114 || (self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(it.owner_id.def_id))
17df50a5
XL
115 {
116 return;
117 }
118 match it.kind {
119 ItemKind::TyAlias(..) | ItemKind::Struct(..) | ItemKind::Trait(..) => {
4b012472 120 check_ident(cx, &it.ident, it.hir_id(), self.upper_case_acronyms_aggressive);
17df50a5
XL
121 },
122 ItemKind::Enum(ref enumdef, _) => {
4b012472 123 check_ident(cx, &it.ident, it.hir_id(), self.upper_case_acronyms_aggressive);
5e7ed085 124 // check enum variants separately because again we only want to lint on private enums and
cdc7bbd5 125 // the fn check_variant does not know about the vis of the enum of its variants
4b012472
FG
126 enumdef.variants.iter().for_each(|variant| {
127 check_ident(cx, &variant.ident, variant.hir_id, self.upper_case_acronyms_aggressive);
128 });
17df50a5
XL
129 },
130 _ => {},
f20569fa
XL
131 }
132 }
f20569fa 133}