diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 4c888b63793b..21167111154e 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -643,3 +643,23 @@ The byte size a `T` in `Box` can have, below which it triggers the `clippy::u * [`unnecessary_box_returns`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_box_returns) +## `allowed-idents-below-min-chars` +Allowed names below the minimum allowed characters. + +**Default Value:** `{"j", "z", "i", "y", "n", "x", "w"}` (`rustc_data_structures::fx::FxHashSet`) + +--- +**Affected lints:** +* [`min_ident_chars`](https://rust-lang.github.io/rust-clippy/master/index.html#min_ident_chars) + + +## `min-ident-chars-threshold` +Minimum chars an ident can have, anything below or equal to this will be linted. + +**Default Value:** `1` (`u64`) + +--- +**Affected lints:** +* [`min_ident_chars`](https://rust-lang.github.io/rust-clippy/master/index.html#min_ident_chars) + + diff --git a/clippy_lints/src/min_ident_chars.rs b/clippy_lints/src/min_ident_chars.rs index bbe7fc540e92..cc027b6f4988 100644 --- a/clippy_lints/src/min_ident_chars.rs +++ b/clippy_lints/src/min_ident_chars.rs @@ -3,7 +3,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir::{ def::{DefKind, Res}, intravisit::{walk_item, Visitor}, - GenericParamKind, HirId, Item, ItemKind, ItemLocalId, Node, + GenericParamKind, HirId, Item, ItemKind, ItemLocalId, Node, Pat, PatKind, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -55,6 +55,29 @@ impl LateLintPass<'_> for MinIdentChars { walk_item(&mut IdentVisitor { conf: self, cx }, item); } + + // This is necessary as bindings are not visited in `visit_id`. :/ + #[expect(clippy::cast_possible_truncation)] + fn check_pat(&mut self, cx: &LateContext<'_>, pat: &Pat<'_>) { + if let PatKind::Binding(_, _, ident, ..) = pat.kind + && let str = ident.as_str() + && !in_external_macro(cx.sess(), ident.span) + && str.len() <= self.min_ident_chars_threshold as usize + && !str.is_empty() + && self.allowed_idents_below_min_chars.get(&str.to_owned()).is_none() + { + let help = if self.min_ident_chars_threshold == 1 { + Cow::Borrowed("this ident consists of a single char") + } else { + Cow::Owned(format!( + "this ident is too short ({} <= {})", + str.len(), + self.min_ident_chars_threshold, + )) + }; + span_lint(cx, MIN_IDENT_CHARS, ident.span, &help); + } + } } struct IdentVisitor<'cx, 'tcx> { @@ -62,8 +85,8 @@ struct IdentVisitor<'cx, 'tcx> { cx: &'cx LateContext<'tcx>, } -#[expect(clippy::cast_possible_truncation)] impl Visitor<'_> for IdentVisitor<'_, '_> { + #[expect(clippy::cast_possible_truncation)] fn visit_id(&mut self, hir_id: HirId) { let Self { conf, cx } = *self; // Reimplementation of `find`, as it uses indexing, which can (and will in async functions) panic. @@ -122,9 +145,9 @@ impl Visitor<'_> for IdentVisitor<'_, '_> { Cow::Borrowed("this ident consists of a single char") } else { Cow::Owned(format!( - "this ident is too short ({} <= {}) ", + "this ident is too short ({} <= {})", str.len(), - conf.min_ident_chars_threshold + conf.min_ident_chars_threshold, )) }; span_lint(cx, MIN_IDENT_CHARS, ident.span, &help); diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 8c1e4f07384b..89611750da42 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -34,7 +34,7 @@ const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[ "CamelCase", ]; const DEFAULT_DISALLOWED_NAMES: &[&str] = &["foo", "baz", "quux"]; -const DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS: &[&str] = &["i", "j", "x", "y", "z", "n"]; +const DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS: &[&str] = &["i", "j", "x", "y", "z", "w", "n"]; /// Holds information used by `MISSING_ENFORCED_IMPORT_RENAMES` lint. #[derive(Clone, Debug, Deserialize)] diff --git a/tests/ui-toml/min_ident_chars/min_ident_chars.rs b/tests/ui-toml/min_ident_chars/min_ident_chars.rs index 33100119ca5b..4326c7159c83 100644 --- a/tests/ui-toml/min_ident_chars/min_ident_chars.rs +++ b/tests/ui-toml/min_ident_chars/min_ident_chars.rs @@ -14,4 +14,6 @@ fn main() { let wha = 1; let vvv = 1; let uuu = 1; + let (mut a, mut b) = (1, 2); + for i in 0..1000 {} } diff --git a/tests/ui-toml/min_ident_chars/min_ident_chars.stderr b/tests/ui-toml/min_ident_chars/min_ident_chars.stderr index 541f5b10051a..d9a27628ddb2 100644 --- a/tests/ui-toml/min_ident_chars/min_ident_chars.stderr +++ b/tests/ui-toml/min_ident_chars/min_ident_chars.stderr @@ -1,4 +1,4 @@ -error: this ident is too short (3 <= 3) +error: this ident is too short (3 <= 3) --> $DIR/min_ident_chars.rs:6:19 | LL | use extern_types::Aaa; @@ -6,11 +6,41 @@ LL | use extern_types::Aaa; | = note: `-D clippy::min-ident-chars` implied by `-D warnings` -error: this ident is too short (3 <= 3) +error: this ident is too short (3 <= 3) --> $DIR/min_ident_chars.rs:10:5 | LL | aaa: Aaa, | ^^^ -error: aborting due to 2 previous errors +error: this ident is too short (3 <= 3) + --> $DIR/min_ident_chars.rs:15:9 + | +LL | let vvv = 1; + | ^^^ + +error: this ident is too short (3 <= 3) + --> $DIR/min_ident_chars.rs:16:9 + | +LL | let uuu = 1; + | ^^^ + +error: this ident is too short (1 <= 3) + --> $DIR/min_ident_chars.rs:17:14 + | +LL | let (mut a, mut b) = (1, 2); + | ^ + +error: this ident is too short (1 <= 3) + --> $DIR/min_ident_chars.rs:17:21 + | +LL | let (mut a, mut b) = (1, 2); + | ^ + +error: this ident is too short (1 <= 3) + --> $DIR/min_ident_chars.rs:18:9 + | +LL | for i in 0..1000 {} + | ^ + +error: aborting due to 7 previous errors diff --git a/tests/ui/min_ident_chars.rs b/tests/ui/min_ident_chars.rs index 134c17c553f3..85608d030eda 100644 --- a/tests/ui/min_ident_chars.rs +++ b/tests/ui/min_ident_chars.rs @@ -1,5 +1,5 @@ //@aux-build:proc_macros.rs -#![allow(nonstandard_style, unused)] +#![allow(irrefutable_let_patterns, nonstandard_style, unused)] #![warn(clippy::min_ident_chars)] extern crate proc_macros; @@ -15,6 +15,10 @@ struct A { struct B(u32); +struct O { + o: u32, +} + struct i; enum C { @@ -38,11 +42,29 @@ fn main() { let w = 1; // Ok, not this one // let i = 1; - let jz = 1; - let nz = 1; - let zx = 1; - let yz = 1; - let zz = 1; + let j = 1; + let n = 1; + let z = 1; + let y = 1; + let z = 1; + // Implicitly disallowed idents + let h = 1; + let e = 2; + let l = 3; + let l = 4; + let o = 6; + // 2 len does not lint + let hi = 0; + // Lint + let (h, o, w) = (1, 2, 3); + for (a, (r, e)) in (0..1000).enumerate().enumerate() {} + let you = Vec4 { x: 1, y: 2, z: 3, w: 4 }; + while let (d, o, _i, n, g) = (true, true, false, false, true) {} + let today = true; + // Ideally this wouldn't lint, but this would (likely) require global analysis, outta scope + // of this lint regardless + let o = 1; + let o = O { o }; for j in 0..1000 {} diff --git a/tests/ui/min_ident_chars.stderr b/tests/ui/min_ident_chars.stderr index 4b887617058b..cd8825634c31 100644 --- a/tests/ui/min_ident_chars.stderr +++ b/tests/ui/min_ident_chars.stderr @@ -31,40 +31,136 @@ LL | struct B(u32); | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:20:6 + --> $DIR/min_ident_chars.rs:18:8 + | +LL | struct O { + | ^ + +error: this ident consists of a single char + --> $DIR/min_ident_chars.rs:19:5 + | +LL | o: u32, + | ^ + +error: this ident consists of a single char + --> $DIR/min_ident_chars.rs:24:6 | LL | enum C { | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:21:5 + --> $DIR/min_ident_chars.rs:25:5 | LL | D, | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:22:5 + --> $DIR/min_ident_chars.rs:26:5 | LL | E, | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:23:5 + --> $DIR/min_ident_chars.rs:27:5 | LL | F, | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:31:5 + --> $DIR/min_ident_chars.rs:51:9 | -LL | w: u32, - | ^ +LL | let h = 1; + | ^ + +error: this ident consists of a single char + --> $DIR/min_ident_chars.rs:52:9 + | +LL | let e = 2; + | ^ + +error: this ident consists of a single char + --> $DIR/min_ident_chars.rs:53:9 + | +LL | let l = 3; + | ^ + +error: this ident consists of a single char + --> $DIR/min_ident_chars.rs:54:9 + | +LL | let l = 4; + | ^ + +error: this ident consists of a single char + --> $DIR/min_ident_chars.rs:55:9 + | +LL | let o = 6; + | ^ + +error: this ident consists of a single char + --> $DIR/min_ident_chars.rs:59:10 + | +LL | let (h, o, w) = (1, 2, 3); + | ^ + +error: this ident consists of a single char + --> $DIR/min_ident_chars.rs:59:13 + | +LL | let (h, o, w) = (1, 2, 3); + | ^ + +error: this ident consists of a single char + --> $DIR/min_ident_chars.rs:60:10 + | +LL | for (a, (r, e)) in (0..1000).enumerate().enumerate() {} + | ^ + +error: this ident consists of a single char + --> $DIR/min_ident_chars.rs:60:14 + | +LL | for (a, (r, e)) in (0..1000).enumerate().enumerate() {} + | ^ + +error: this ident consists of a single char + --> $DIR/min_ident_chars.rs:60:17 + | +LL | for (a, (r, e)) in (0..1000).enumerate().enumerate() {} + | ^ + +error: this ident consists of a single char + --> $DIR/min_ident_chars.rs:62:16 + | +LL | while let (d, o, _i, n, g) = (true, true, false, false, true) {} + | ^ + +error: this ident consists of a single char + --> $DIR/min_ident_chars.rs:62:19 + | +LL | while let (d, o, _i, n, g) = (true, true, false, false, true) {} + | ^ + +error: this ident consists of a single char + --> $DIR/min_ident_chars.rs:62:29 + | +LL | while let (d, o, _i, n, g) = (true, true, false, false, true) {} + | ^ + +error: this ident consists of a single char + --> $DIR/min_ident_chars.rs:66:9 + | +LL | let o = 1; + | ^ + +error: this ident consists of a single char + --> $DIR/min_ident_chars.rs:67:9 + | +LL | let o = O { o }; + | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:58:4 + --> $DIR/min_ident_chars.rs:80:4 | LL | fn b() {} | ^ -error: aborting due to 11 previous errors +error: aborting due to 27 previous errors