diff --git a/CHANGELOG.md b/CHANGELOG.md index 28147dfbea3e..86fa8c36043d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5938,6 +5938,7 @@ Released 2018-09-13 [`literal_string_with_formatting_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#literal_string_with_formatting_args [`little_endian_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#little_endian_bytes [`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug +[`long_variable_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#long_variable_names [`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal [`macro_metavars_in_unsafe`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_metavars_in_unsafe [`macro_use_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_use_imports @@ -6535,6 +6536,7 @@ Released 2018-09-13 [`max-struct-bools`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-struct-bools [`max-suggested-slice-pattern-length`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-suggested-slice-pattern-length [`max-trait-bounds`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-trait-bounds +[`max-variable-name-length`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-variable-name-length [`min-ident-chars-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#min-ident-chars-threshold [`missing-docs-allow-unused`]: https://doc.rust-lang.org/clippy/lint_configuration.html#missing-docs-allow-unused [`missing-docs-in-crate-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#missing-docs-in-crate-items diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 0db4182dbcdb..e567ec2f6918 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -734,6 +734,16 @@ The maximum number of bounds a trait can have to be linted * [`type_repetition_in_bounds`](https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds) +## `max-variable-name-length` +The maximum length of a variable + +**Default Value:** `100` + +--- +**Affected lints:** +* [`long_variable_names`](https://rust-lang.github.io/rust-clippy/master/index.html#long_variable_names) + + ## `min-ident-chars-threshold` Minimum chars an ident can have, anything below or equal to this will be linted. diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index ad0aea39d41a..62fe8c0c8a51 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -675,6 +675,9 @@ define_Conf! { /// The maximum number of bounds a trait can have to be linted #[lints(type_repetition_in_bounds)] max_trait_bounds: u64 = 3, + /// The maximum length of a variable + #[lints(long_variable_names)] + max_variable_name_length: u32 = 100, /// Minimum chars an ident can have, anything below or equal to this will be linted. #[lints(min_ident_chars)] min_ident_chars_threshold: u64 = 1, diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index bb825c7655f8..c6f5effb6bf2 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -260,6 +260,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::literal_representation::UNREADABLE_LITERAL_INFO, crate::literal_representation::UNUSUAL_BYTE_GROUPINGS_INFO, crate::literal_string_with_formatting_args::LITERAL_STRING_WITH_FORMATTING_ARGS_INFO, + crate::long_variable_names::LONG_VARIABLE_NAMES_INFO, crate::loops::CHAR_INDICES_AS_BYTE_INDICES_INFO, crate::loops::EMPTY_LOOP_INFO, crate::loops::EXPLICIT_COUNTER_LOOP_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 006145cc623c..ace8c7b7f1c9 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -199,6 +199,7 @@ mod lifetimes; mod lines_filter_map_ok; mod literal_representation; mod literal_string_with_formatting_args; +mod long_variable_names; mod loops; mod macro_metavars_in_unsafe; mod macro_use; @@ -944,5 +945,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(single_option_map::SingleOptionMap)); store.register_late_pass(move |_| Box::new(redundant_test_prefix::RedundantTestPrefix)); store.register_late_pass(|_| Box::new(cloned_ref_to_slice_refs::ClonedRefToSliceRefs::new(conf))); + store.register_late_pass(move |_| Box::new(long_variable_names::LongVariableNames::new(conf))); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/long_variable_names.rs b/clippy_lints/src/long_variable_names.rs new file mode 100644 index 000000000000..a1017eaa63d9 --- /dev/null +++ b/clippy_lints/src/long_variable_names.rs @@ -0,0 +1,76 @@ +use crate::rustc_lint::LintContext; +use crate::rustc_span::Pos; +use clippy_config::Conf; +use clippy_utils::diagnostics::span_lint_and_help; +use rustc_hir::{Pat, PatKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::impl_lint_pass; + +declare_clippy_lint! { + /// ### What it does + /// Checks for variable that exceeds a configurable number characters. + /// + /// ### Why is this bad? + /// Long variable names can make code harder to read and thus to maintain. + /// + /// ### Example + /// ```no_run + /// let ferris_fixes_more_bugs_than_your_entire_devops_team_does = "content of a string"; + /// ``` + /// Use instead: + /// ```no_run + /// let ferris_fixes_more_bugs = "content of a string"; + /// ``` + #[clippy::version = "1.88.0"] + pub LONG_VARIABLE_NAMES, + style, + "usage of a long variable" +} +pub struct LongVariableNames { + pub max_variable_name_length: u32, +} + +impl LongVariableNames { + pub fn new(conf: &'static Conf) -> Self { + Self { + max_variable_name_length: conf.max_variable_name_length, + } + } +} + +impl_lint_pass!(LongVariableNames => [LONG_VARIABLE_NAMES]); + +impl<'tcx> LateLintPass<'tcx> for LongVariableNames { + fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) { + if let PatKind::Binding(.., ident, _) = pat.kind { + let length_bytes = if ident.span.from_expansion() { + // since the span can't be calculated, we can't lint this + // so we just return + return; + } else { + let higher_bound = cx.sess().source_map().lookup_char_pos(ident.span.hi()).col; + let lower_bound = cx.sess().source_map().lookup_char_pos(ident.span.lo()).col; + (higher_bound - lower_bound).to_u32() + 1 + }; + if length_bytes > self.max_variable_name_length { + let variable_name_length = u32::try_from(ident.name.to_ident_string().chars().count()) + .expect("the variable name length exceeds u32::MAX"); + if variable_name_length > self.max_variable_name_length { + let length_diff = variable_name_length - self.max_variable_name_length; + + span_lint_and_help( + cx, + LONG_VARIABLE_NAMES, + ident.span, + format!( + "variable name is longer than the configured `max-variable-name-length` of ({} characters)", + self.max_variable_name_length + ), + None, + format!("reduce the length of the variable name with at least {length_diff} characters"), + ); + } + } + } + } +} diff --git a/tests/clippy.toml b/tests/clippy.toml index 91a2e55180b9..c8f1bd8229fe 100644 --- a/tests/clippy.toml +++ b/tests/clippy.toml @@ -1,2 +1,5 @@ # default config for tests, overrides clippy.toml at the project root lint-commented-code = false + +# override the default length of variables to test the configuration +max-variable-name-length = 50 diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 6ee77ebd8ece..d7f6d9bdcdae 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -57,6 +57,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect max-struct-bools max-suggested-slice-pattern-length max-trait-bounds + max-variable-name-length min-ident-chars-threshold missing-docs-allow-unused missing-docs-in-crate-items @@ -151,6 +152,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect max-struct-bools max-suggested-slice-pattern-length max-trait-bounds + max-variable-name-length min-ident-chars-threshold missing-docs-allow-unused missing-docs-in-crate-items @@ -245,6 +247,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni max-struct-bools max-suggested-slice-pattern-length max-trait-bounds + max-variable-name-length min-ident-chars-threshold missing-docs-allow-unused missing-docs-in-crate-items diff --git a/tests/ui/long_variable_names.rs b/tests/ui/long_variable_names.rs new file mode 100644 index 000000000000..d4f80f02e126 --- /dev/null +++ b/tests/ui/long_variable_names.rs @@ -0,0 +1,15 @@ +#![warn(clippy::long_variable_names)] + +fn a_function(ferris_singlehandedly_refactored_the_monolith_while_juggling_crates_and_lifetimes: &str) { + //~^ long_variable_names +} + +fn another_function(just_a_short_name: &str) { + // should not cause a problem +} + +fn main() { + // `ferris_singlehandedly_refactored_the_monolith_while_juggling_crates_and_lifetimes` is too long + let ferris_singlehandedly_refactored_the_monolith_while_juggling_crates_and_lifetimes = "very long indeed"; + //~^ long_variable_names +} diff --git a/tests/ui/long_variable_names.stderr b/tests/ui/long_variable_names.stderr new file mode 100644 index 000000000000..41548292e4b3 --- /dev/null +++ b/tests/ui/long_variable_names.stderr @@ -0,0 +1,20 @@ +error: variable name is longer than the configured `max-variable-name-length` of (50 characters) + --> tests/ui/long_variable_names.rs:3:15 + | +LL | fn a_function(ferris_singlehandedly_refactored_the_monolith_while_juggling_crates_and_lifetimes: &str) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: reduce the length of the variable name with at least 31 characters + = note: `-D clippy::long-variable-names` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::long_variable_names)]` + +error: variable name is longer than the configured `max-variable-name-length` of (50 characters) + --> tests/ui/long_variable_names.rs:13:9 + | +LL | let ferris_singlehandedly_refactored_the_monolith_while_juggling_crates_and_lifetimes = "very long indeed"; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: reduce the length of the variable name with at least 31 characters + +error: aborting due to 2 previous errors +