Skip to content

Commit a2c037f

Browse files
committed
ignore generics and allow arbitrary threshold
1 parent 2061e0f commit a2c037f

14 files changed

+269
-179
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4957,6 +4957,7 @@ Released 2018-09-13
49574957
[`mem_replace_option_with_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_option_with_none
49584958
[`mem_replace_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default
49594959
[`mem_replace_with_uninit`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_uninit
4960+
[`min_ident_chars`]: https://rust-lang.github.io/rust-clippy/master/index.html#min_ident_chars
49604961
[`min_max`]: https://rust-lang.github.io/rust-clippy/master/index.html#min_max
49614962
[`misaligned_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#misaligned_transmute
49624963
[`mismatched_target_os`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatched_target_os
@@ -5153,7 +5154,6 @@ Released 2018-09-13
51535154
[`significant_drop_tightening`]: https://rust-lang.github.io/rust-clippy/master/index.html#significant_drop_tightening
51545155
[`similar_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#similar_names
51555156
[`single_char_add_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_add_str
5156-
[`single_char_idents`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_idents
51575157
[`single_char_lifetime_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_lifetime_names
51585158
[`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern
51595159
[`single_char_push_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_push_str

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
415415
crate::methods::VERBOSE_FILE_READS_INFO,
416416
crate::methods::WRONG_SELF_CONVENTION_INFO,
417417
crate::methods::ZST_OFFSET_INFO,
418+
crate::min_ident_chars::MIN_IDENT_CHARS_INFO,
418419
crate::minmax::MIN_MAX_INFO,
419420
crate::misc::SHORT_CIRCUIT_STATEMENT_INFO,
420421
crate::misc::TOPLEVEL_REF_ARG_INFO,
@@ -563,7 +564,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
563564
crate::shadow::SHADOW_SAME_INFO,
564565
crate::shadow::SHADOW_UNRELATED_INFO,
565566
crate::significant_drop_tightening::SIGNIFICANT_DROP_TIGHTENING_INFO,
566-
crate::single_char_idents::SINGLE_CHAR_IDENTS_INFO,
567567
crate::single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES_INFO,
568568
crate::single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS_INFO,
569569
crate::size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT_INFO,

clippy_lints/src/lib.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ mod matches;
196196
mod mem_forget;
197197
mod mem_replace;
198198
mod methods;
199+
mod min_ident_chars;
199200
mod minmax;
200201
mod misc;
201202
mod misc_early;
@@ -282,7 +283,6 @@ mod semicolon_if_nothing_returned;
282283
mod serde_api;
283284
mod shadow;
284285
mod significant_drop_tightening;
285-
mod single_char_idents;
286286
mod single_char_lifetime_names;
287287
mod single_component_path_imports;
288288
mod size_of_in_element_count;
@@ -1022,10 +1022,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10221022
store.register_late_pass(|_| Box::new(endian_bytes::EndianBytes));
10231023
store.register_late_pass(|_| Box::new(redundant_type_annotations::RedundantTypeAnnotations));
10241024
store.register_late_pass(|_| Box::new(arc_with_non_send_sync::ArcWithNonSendSync));
1025-
let allowed_idents = conf.allowed_idents.clone();
1026-
store.register_early_pass(move || {
1027-
Box::new(single_char_idents::SingleCharIdents {
1028-
allowed_idents: allowed_idents.clone(),
1025+
let allowed_idents_below_min_chars = conf.allowed_idents_below_min_chars.clone();
1026+
let min_ident_chars_threshold = conf.min_ident_chars_threshold;
1027+
store.register_late_pass(move |_| {
1028+
Box::new(min_ident_chars::MinIdentChars {
1029+
allowed_idents_below_min_chars: allowed_idents_below_min_chars.clone(),
1030+
min_ident_chars_threshold,
10291031
})
10301032
});
10311033
// add lints here, do not remove this comment, it's used in `new_lint`

clippy_lints/src/min_ident_chars.rs

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
use clippy_utils::{diagnostics::span_lint, is_from_proc_macro};
2+
use rustc_data_structures::fx::FxHashSet;
3+
use rustc_hir::{
4+
def::{DefKind, Res},
5+
intravisit::{walk_item, Visitor},
6+
GenericParamKind, HirId, Item, ItemKind, ItemLocalId, Node,
7+
};
8+
use rustc_lint::{LateContext, LateLintPass, LintContext};
9+
use rustc_middle::lint::in_external_macro;
10+
use rustc_session::{declare_tool_lint, impl_lint_pass};
11+
use std::borrow::Cow;
12+
13+
declare_clippy_lint! {
14+
/// ### What it does
15+
/// Checks for idents which comprise of a single letter.
16+
///
17+
/// Note: This lint can be very noisy when enabled; it may be desirable to only enable it
18+
/// temporarily.
19+
///
20+
/// ### Why is this bad?
21+
/// In many cases it's not, but at times it can severely hinder readability. Some codebases may
22+
/// wish to disallow this to improve readability.
23+
///
24+
/// ### Example
25+
/// ```rust,ignore
26+
/// for m in movies {
27+
/// let title = m.t;
28+
/// }
29+
/// ```
30+
/// Use instead:
31+
/// ```rust,ignore
32+
/// for movie in movies {
33+
/// let title = movie.title;
34+
/// }
35+
/// ```
36+
/// ```
37+
#[clippy::version = "1.72.0"]
38+
pub MIN_IDENT_CHARS,
39+
restriction,
40+
"disallows idents that are too short"
41+
}
42+
impl_lint_pass!(MinIdentChars => [MIN_IDENT_CHARS]);
43+
44+
#[derive(Clone)]
45+
pub struct MinIdentChars {
46+
pub allowed_idents_below_min_chars: FxHashSet<String>,
47+
pub min_ident_chars_threshold: u64,
48+
}
49+
50+
impl LateLintPass<'_> for MinIdentChars {
51+
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
52+
if self.min_ident_chars_threshold == 0 {
53+
return;
54+
}
55+
56+
walk_item(&mut IdentVisitor { conf: self, cx }, item);
57+
}
58+
}
59+
60+
struct IdentVisitor<'cx, 'tcx> {
61+
conf: &'cx MinIdentChars,
62+
cx: &'cx LateContext<'tcx>,
63+
}
64+
65+
#[expect(clippy::cast_possible_truncation)]
66+
impl Visitor<'_> for IdentVisitor<'_, '_> {
67+
fn visit_id(&mut self, hir_id: HirId) {
68+
let Self { conf, cx } = *self;
69+
// Reimplementation of `find`, as it uses indexing, which can (and will in async functions) panic.
70+
// This should probably be fixed on the rustc side, this is just a temporary workaround.
71+
// FIXME: Remove me if/when this is fixed in rustc
72+
let node = if hir_id.local_id == ItemLocalId::from_u32(0) {
73+
// In this case, we can just use `find`, `Owner`'s `node` field is private anyway so we can't
74+
// reimplement it even if we wanted to
75+
cx.tcx.hir().find(hir_id)
76+
} else {
77+
let Some(owner) = cx.tcx.hir_owner_nodes(hir_id.owner).as_owner() else {
78+
return;
79+
};
80+
owner.nodes.get(hir_id.local_id).copied().flatten().map(|p| p.node)
81+
};
82+
let Some(node) = node else {
83+
return;
84+
};
85+
let Some(ident) = node.ident() else {
86+
return;
87+
};
88+
89+
let str = ident.as_str();
90+
if !in_external_macro(cx.sess(), ident.span)
91+
&& str.len() <= conf.min_ident_chars_threshold as usize
92+
&& str.is_empty()
93+
&& conf.allowed_idents_below_min_chars.get(&str.to_owned()).is_none()
94+
{
95+
if let Node::Item(item) = node && let ItemKind::Use(..) = item.kind {
96+
return;
97+
}
98+
// `struct Awa<T>(T)`
99+
// ^
100+
if let Node::PathSegment(path) = node {
101+
if let Res::Def(def_kind, ..) = path.res && let DefKind::TyParam = def_kind {
102+
return;
103+
}
104+
if matches!(path.res, Res::PrimTy(..)) || path.res.opt_def_id().is_some_and(|def_id| !def_id.is_local())
105+
{
106+
return;
107+
}
108+
}
109+
// `struct Awa<T>(T)`
110+
// ^
111+
if let Node::GenericParam(generic_param) = node
112+
&& let GenericParamKind::Type { .. } = generic_param.kind
113+
{
114+
return;
115+
}
116+
117+
if is_from_proc_macro(cx, &ident) {
118+
return;
119+
}
120+
121+
let help = if conf.min_ident_chars_threshold == 1 {
122+
Cow::Borrowed("this ident consists of a single char")
123+
} else {
124+
Cow::Owned(format!(
125+
"this ident is too short ({} <= {}) ",
126+
str.len(),
127+
conf.min_ident_chars_threshold
128+
))
129+
};
130+
span_lint(cx, MIN_IDENT_CHARS, ident.span, &help);
131+
}
132+
}
133+
}

clippy_lints/src/single_char_idents.rs

Lines changed: 0 additions & 59 deletions
This file was deleted.

clippy_lints/src/utils/conf.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[
3434
"CamelCase",
3535
];
3636
const DEFAULT_DISALLOWED_NAMES: &[&str] = &["foo", "baz", "quux"];
37-
const DEFAULT_ALLOWED_IDENTS: &[char] = &['i', 'j', 'x', 'y', 'z', 'n'];
37+
const DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS: &[&str] = &["i", "j", "x", "y", "z", "n"];
3838

3939
/// Holds information used by `MISSING_ENFORCED_IMPORT_RENAMES` lint.
4040
#[derive(Clone, Debug, Deserialize)]
@@ -515,11 +515,15 @@ define_Conf! {
515515
///
516516
/// The byte size a `T` in `Box<T>` can have, below which it triggers the `clippy::unnecessary_box` lint
517517
(unnecessary_box_size: u64 = 128),
518-
/// Lint: SINGLE_LETTER_IDENTS.
518+
/// Lint: MIN_IDENT_CHARS.
519519
///
520-
/// Allowed single letter idents.
521-
(allowed_idents: rustc_data_structures::fx::FxHashSet<char>
522-
= super::DEFAULT_ALLOWED_IDENTS.iter().copied().collect()),
520+
/// Allowed names below the minimum allowed characters.
521+
(allowed_idents_below_min_chars: rustc_data_structures::fx::FxHashSet<String> =
522+
super::DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS.iter().map(ToString::to_string).collect()),
523+
/// Lint: MIN_IDENT_CHARS.
524+
///
525+
/// Minimum chars an ident can have, anything below or equal to this will be linted.
526+
(min_ident_chars_threshold: u64 = 1),
523527
}
524528

525529
/// Search for the configuration file.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#![allow(nonstandard_style, unused)]
2+
3+
pub struct Aaa;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
allowed-idents-below-min-chars = ["Owo", "Uwu", "wha", "t_e", "lse", "_do", "_i_", "put", "her", "_e"]
2+
min-ident-chars-threshold = 3
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//@aux-build:extern_types.rs
2+
#![allow(nonstandard_style, unused)]
3+
#![warn(clippy::min_ident_chars)]
4+
5+
extern crate extern_types;
6+
use extern_types::Aaa;
7+
8+
struct Owo {
9+
Uwu: u128,
10+
aaa: Aaa,
11+
}
12+
13+
fn main() {
14+
let wha = 1;
15+
let vvv = 1;
16+
let uuu = 1;
17+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error: this ident is too short (3 <= 3)
2+
--> $DIR/min_ident_chars.rs:6:19
3+
|
4+
LL | use extern_types::Aaa;
5+
| ^^^
6+
|
7+
= note: `-D clippy::min-ident-chars` implied by `-D warnings`
8+
9+
error: this ident is too short (3 <= 3)
10+
--> $DIR/min_ident_chars.rs:10:5
11+
|
12+
LL | aaa: Aaa,
13+
| ^^^
14+
15+
error: aborting due to 2 previous errors
16+

tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect
44
allow-mixed-uninlined-format-args
55
allow-print-in-tests
66
allow-unwrap-in-tests
7-
allowed-idents
7+
allowed-idents-below-min-chars
88
allowed-scripts
99
arithmetic-side-effects-allowed
1010
arithmetic-side-effects-allowed-binary
@@ -35,6 +35,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect
3535
max-struct-bools
3636
max-suggested-slice-pattern-length
3737
max-trait-bounds
38+
min-ident-chars-threshold
3839
missing-docs-in-crate-items
3940
msrv
4041
pass-by-value-size-limit
@@ -66,7 +67,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect
6667
allow-mixed-uninlined-format-args
6768
allow-print-in-tests
6869
allow-unwrap-in-tests
69-
allowed-idents
70+
allowed-idents-below-min-chars
7071
allowed-scripts
7172
arithmetic-side-effects-allowed
7273
arithmetic-side-effects-allowed-binary
@@ -97,6 +98,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect
9798
max-struct-bools
9899
max-suggested-slice-pattern-length
99100
max-trait-bounds
101+
min-ident-chars-threshold
100102
missing-docs-in-crate-items
101103
msrv
102104
pass-by-value-size-limit

tests/ui/single_char_idents.rs renamed to tests/ui/min_ident_chars.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//@aux-build:proc_macros.rs
22
#![allow(nonstandard_style, unused)]
3-
#![warn(clippy::single_char_idents)]
3+
#![warn(clippy::min_ident_chars)]
44

55
extern crate proc_macros;
66
use proc_macros::external;
@@ -38,11 +38,11 @@ fn main() {
3838
let w = 1;
3939
// Ok, not this one
4040
// let i = 1;
41-
let j = 1;
42-
let n = 1;
43-
let x = 1;
44-
let y = 1;
45-
let z = 1;
41+
let jz = 1;
42+
let nz = 1;
43+
let zx = 1;
44+
let yz = 1;
45+
let zz = 1;
4646

4747
for j in 0..1000 {}
4848

0 commit comments

Comments
 (0)