add lint to check for enums where all variants have the same pre-/postfix

This commit is contained in:
Oliver Schneider 2016-02-01 12:47:31 +01:00
parent fd906c043f
commit 328d2c7626
3 changed files with 95 additions and 1 deletions

View file

@ -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

90
src/enum_variants.rs Normal file
View file

@ -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));
}
}
}

View file

@ -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,