From 328d2c76260631aa8da7d0c64eeb95f2592630d1 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 1 Feb 2016 12:47:31 +0100 Subject: [PATCH] add lint to check for enums where all variants have the same pre-/postfix --- README.md | 3 +- src/enum_variants.rs | 90 ++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 ++ 3 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 src/enum_variants.rs diff --git a/README.md b/README.md index 36037036147..9b249636c66 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ A collection of lints to catch common mistakes and improve your Rust code. [Jump to usage instructions](#usage) ##Lints -There are 105 lints included in this crate: +There are 106 lints included in this crate: name | default | meaning ---------------------------------------------------------------------------------------------------------------|---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ @@ -30,6 +30,7 @@ name [derive_hash_not_eq](https://github.com/Manishearth/rust-clippy/wiki#derive_hash_not_eq) | warn | deriving `Hash` but implementing `PartialEq` explicitly [duplicate_underscore_argument](https://github.com/Manishearth/rust-clippy/wiki#duplicate_underscore_argument) | warn | Function arguments having names which only differ by an underscore [empty_loop](https://github.com/Manishearth/rust-clippy/wiki#empty_loop) | warn | empty `loop {}` detected +[enum_variant_names](https://github.com/Manishearth/rust-clippy/wiki#enum_variant_names) | warn | finds enums where all variants share a prefix/postfix [eq_op](https://github.com/Manishearth/rust-clippy/wiki#eq_op) | warn | equal operands on both sides of a comparison or bitwise combination (e.g. `x == x`) [expl_impl_clone_on_copy](https://github.com/Manishearth/rust-clippy/wiki#expl_impl_clone_on_copy) | warn | implementing `Clone` explicitly on `Copy` types [explicit_counter_loop](https://github.com/Manishearth/rust-clippy/wiki#explicit_counter_loop) | warn | for-looping with an explicit counter when `_.enumerate()` would do diff --git a/src/enum_variants.rs b/src/enum_variants.rs new file mode 100644 index 00000000000..bf0025e021c --- /dev/null +++ b/src/enum_variants.rs @@ -0,0 +1,90 @@ +//! lint on enum variants that are prefixed or suffixed by the same characters + +use rustc::lint::*; +use syntax::attr::*; +use syntax::ast::*; +use syntax::parse::token::InternedString; + +use utils::span_help_and_lint; + +/// **What it does:** Warns on enum variants that are prefixed or suffixed by the same characters +/// +/// **Why is this bad?** Enum variant names should specify their variant, not the enum, too. +/// +/// **Known problems:** None +/// +/// **Example:** enum Cake { BlackForestCake, HummingbirdCake } +declare_lint! { pub ENUM_VARIANT_NAMES, Warn, + "finds enums where all variants share a prefix/postfix" } + +pub struct EnumVariantNames; + +impl LintPass for EnumVariantNames { + fn get_lints(&self) -> LintArray { + lint_array!(ENUM_VARIANT_NAMES) + } +} + +fn var2str(var: &Variant) -> InternedString { + var.node.name.name.as_str() +} + +fn partial_match(left: &str, right: &str) -> usize { + left.chars().zip(right.chars()).take_while(|&(l, r)| l == r).count() +} + +fn partial_rmatch(left: &str, right: &str) -> usize { + left.chars().rev().zip(right.chars().rev()).take_while(|&(l, r)| l == r).count() +} + +impl EarlyLintPass for EnumVariantNames { + fn check_item(&mut self, cx: &EarlyContext, item: &Item) { + if let ItemEnum(ref def, _) = item.node { + if def.variants.len() < 2 { + return; + } + let first = var2str(&*def.variants[0]); + let mut pre = first.to_string(); + let mut post = pre.clone(); + for var in &def.variants[1..] { + let name = var2str(var); + let pre_match = partial_match(&pre, &name); + let post_match = partial_rmatch(&post, &name); + pre.truncate(pre_match); + let post_end = post.len() - post_match; + post.drain(..post_end); + } + if let Some(c) = first[pre.len()..].chars().next() { + if !c.is_uppercase() { + // non camel case prefix + pre.clear() + } + } + if let Some(c) = first[..(first.len() - post.len())].chars().rev().next() { + if let Some(c1) = post.chars().next() { + if !c.is_lowercase() || !c1.is_uppercase() { + // non camel case postfix + post.clear() + } + } + } + if pre == "_" { + // don't lint on underscores which are meant to allow dead code + pre.clear(); + } + let (what, value) = if !pre.is_empty() { + ("pre", pre) + } else if !post.is_empty() { + ("post", post) + } else { + return + }; + span_help_and_lint(cx, + ENUM_VARIANT_NAMES, + item.span, + &format!("All variants have the same {}fix: `{}`", what, value), + &format!("remove the {}fixes and use full paths to \ + the variants instead of glob imports", what)); + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 6d8ea99ebcc..15d1db19d97 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,6 +43,7 @@ pub mod ptr_arg; pub mod needless_bool; pub mod approx_const; pub mod eta_reduction; +pub mod enum_variants; pub mod identity_op; pub mod items_after_statements; pub mod minmax; @@ -93,6 +94,7 @@ pub fn plugin_registrar(reg: &mut Registry) { reg.register_late_lint_pass(box misc::TopLevelRefPass); reg.register_late_lint_pass(box misc::CmpNan); reg.register_late_lint_pass(box eq_op::EqOp); + reg.register_early_lint_pass(box enum_variants::EnumVariantNames); reg.register_late_lint_pass(box bit_mask::BitMask); reg.register_late_lint_pass(box ptr_arg::PtrArg); reg.register_late_lint_pass(box needless_bool::NeedlessBool); @@ -183,6 +185,7 @@ pub fn plugin_registrar(reg: &mut Registry) { derive::DERIVE_HASH_NOT_EQ, derive::EXPL_IMPL_CLONE_ON_COPY, entry::MAP_ENTRY, + enum_variants::ENUM_VARIANT_NAMES, eq_op::EQ_OP, escape::BOXED_LOCAL, eta_reduction::REDUNDANT_CLOSURE,