Merge pull request #673 from oli-obk/fix/665
don't lint on all caps enum variants starting with the same char
This commit is contained in:
commit
05afde821c
@ -15,16 +15,16 @@ use syntax::ptr::P;
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum FloatWidth {
|
||||
Fw32,
|
||||
Fw64,
|
||||
FwAny,
|
||||
F32,
|
||||
F64,
|
||||
Any,
|
||||
}
|
||||
|
||||
impl From<FloatTy> for FloatWidth {
|
||||
fn from(ty: FloatTy) -> FloatWidth {
|
||||
match ty {
|
||||
FloatTy::F32 => FloatWidth::Fw32,
|
||||
FloatTy::F64 => FloatWidth::Fw64,
|
||||
FloatTy::F32 => FloatWidth::F32,
|
||||
FloatTy::F64 => FloatWidth::F64,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -200,7 +200,7 @@ fn lit_to_constant(lit: &LitKind) -> Constant {
|
||||
LitKind::Char(c) => Constant::Char(c),
|
||||
LitKind::Int(value, ty) => Constant::Int(value, ty, Sign::Plus),
|
||||
LitKind::Float(ref is, ty) => Constant::Float(is.to_string(), ty.into()),
|
||||
LitKind::FloatUnsuffixed(ref is) => Constant::Float(is.to_string(), FloatWidth::FwAny),
|
||||
LitKind::FloatUnsuffixed(ref is) => Constant::Float(is.to_string(), FloatWidth::Any),
|
||||
LitKind::Bool(b) => Constant::Bool(b),
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ use syntax::ast::*;
|
||||
use syntax::parse::token::InternedString;
|
||||
|
||||
use utils::span_help_and_lint;
|
||||
use utils::{camel_case_from, camel_case_until};
|
||||
|
||||
/// **What it does:** Warns on enum variants that are prefixed or suffixed by the same characters
|
||||
///
|
||||
@ -31,48 +32,63 @@ 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()
|
||||
/*
|
||||
FIXME: waiting for https://github.com/rust-lang/rust/pull/31700
|
||||
fn partial_match(pre: &str, name: &str) -> usize {
|
||||
// skip(1) to ensure that the prefix never takes the whole variant name
|
||||
pre.chars().zip(name.chars().rev().skip(1).rev()).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()
|
||||
fn partial_rmatch(post: &str, name: &str) -> usize {
|
||||
// skip(1) to ensure that the postfix never takes the whole variant name
|
||||
post.chars().rev().zip(name.chars().skip(1).rev()).take_while(|&(l, r)| l == r).count()
|
||||
}*/
|
||||
|
||||
fn partial_match(pre: &str, name: &str) -> usize {
|
||||
let mut name_iter = name.chars();
|
||||
let _ = name_iter.next_back(); // make sure the name is never fully matched
|
||||
pre.chars().zip(name_iter).take_while(|&(l, r)| l == r).count()
|
||||
}
|
||||
|
||||
fn partial_rmatch(post: &str, name: &str) -> usize {
|
||||
let mut name_iter = name.chars();
|
||||
let _ = name_iter.next(); // make sure the name is never fully matched
|
||||
post.chars().rev().zip(name_iter.rev()).take_while(|&(l, r)| l == r).count()
|
||||
}
|
||||
|
||||
impl EarlyLintPass for EnumVariantNames {
|
||||
// FIXME: #600
|
||||
#[allow(while_let_on_iterator)]
|
||||
fn check_item(&mut self, cx: &EarlyContext, item: &Item) {
|
||||
if let ItemKind::Enum(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 mut pre = &first[..camel_case_until(&*first)];
|
||||
let mut post = &first[camel_case_from(&*first)..];
|
||||
for var in &def.variants {
|
||||
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()
|
||||
pre = &pre[..pre_match];
|
||||
let pre_camel = camel_case_until(&pre);
|
||||
pre = &pre[..pre_camel];
|
||||
while let Some((next, last)) = name[pre.len()..].chars().zip(pre.chars().rev()).next() {
|
||||
if next.is_lowercase() {
|
||||
let last = pre.len() - last.len_utf8();
|
||||
let last_camel = camel_case_until(&pre[..last]);
|
||||
pre = &pre[..last_camel];
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if pre == "_" {
|
||||
// don't lint on underscores which are meant to allow dead code
|
||||
pre.clear();
|
||||
|
||||
let post_match = partial_rmatch(&post, &name);
|
||||
let post_end = post.len() - post_match;
|
||||
post = &post[post_end..];
|
||||
let post_camel = camel_case_from(&post);
|
||||
post = &post[post_camel..];
|
||||
}
|
||||
let (what, value) = if !pre.is_empty() {
|
||||
("pre", pre)
|
||||
|
@ -607,3 +607,65 @@ pub fn is_expn_of(cx: &LateContext, mut span: Span, name: &str) -> Option<Span>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns index of character after first CamelCase component of `s`
|
||||
pub fn camel_case_until(s: &str) -> usize {
|
||||
let mut iter = s.char_indices();
|
||||
if let Some((_, first)) = iter.next() {
|
||||
if !first.is_uppercase() {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
let mut up = true;
|
||||
let mut last_i = 0;
|
||||
for (i, c) in iter {
|
||||
if up {
|
||||
if c.is_lowercase() {
|
||||
up = false;
|
||||
} else {
|
||||
return last_i;
|
||||
}
|
||||
} else if c.is_uppercase() {
|
||||
up = true;
|
||||
last_i = i;
|
||||
} else if !c.is_lowercase() {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
if up {
|
||||
last_i
|
||||
} else {
|
||||
s.len()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns index of last CamelCase component of `s`.
|
||||
pub fn camel_case_from(s: &str) -> usize {
|
||||
let mut iter = s.char_indices().rev();
|
||||
if let Some((_, first)) = iter.next() {
|
||||
if !first.is_lowercase() {
|
||||
return s.len();
|
||||
}
|
||||
} else {
|
||||
return s.len();
|
||||
}
|
||||
let mut down = true;
|
||||
let mut last_i = s.len();
|
||||
for (i, c) in iter {
|
||||
if down {
|
||||
if c.is_uppercase() {
|
||||
down = false;
|
||||
last_i = i;
|
||||
} else if !c.is_lowercase() {
|
||||
return last_i;
|
||||
}
|
||||
} else if c.is_lowercase() {
|
||||
down = true;
|
||||
} else {
|
||||
return last_i;
|
||||
}
|
||||
}
|
||||
last_i
|
||||
}
|
||||
|
@ -47,8 +47,8 @@ impl LateLintPass for ZeroDivZeroPass {
|
||||
// since we're about to suggest a use of std::f32::NaN or std::f64::NaN,
|
||||
// match the precision of the literals that are given.
|
||||
let float_type = match (lhs_width, rhs_width) {
|
||||
(FloatWidth::Fw64, _)
|
||||
| (_, FloatWidth::Fw64) => "f64",
|
||||
(FloatWidth::F64, _)
|
||||
| (_, FloatWidth::F64) => "f64",
|
||||
_ => "f32"
|
||||
};
|
||||
span_help_and_lint(cx, ZERO_DIVIDED_BY_ZERO, expr.span,
|
||||
|
51
tests/camel_case.rs
Normal file
51
tests/camel_case.rs
Normal file
@ -0,0 +1,51 @@
|
||||
#[allow(plugin_as_library)]
|
||||
extern crate clippy;
|
||||
|
||||
use clippy::utils::{camel_case_from, camel_case_until};
|
||||
|
||||
#[test]
|
||||
fn from_full() {
|
||||
assert_eq!(camel_case_from("AbcDef"), 0);
|
||||
assert_eq!(camel_case_from("Abc"), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_partial() {
|
||||
assert_eq!(camel_case_from("abcDef"), 3);
|
||||
assert_eq!(camel_case_from("aDbc"), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_not() {
|
||||
assert_eq!(camel_case_from("AbcDef_"), 7);
|
||||
assert_eq!(camel_case_from("AbcDD"), 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_caps() {
|
||||
assert_eq!(camel_case_from("ABCD"), 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn until_full() {
|
||||
assert_eq!(camel_case_until("AbcDef"), 6);
|
||||
assert_eq!(camel_case_until("Abc"), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn until_not() {
|
||||
assert_eq!(camel_case_until("abcDef"), 0);
|
||||
assert_eq!(camel_case_until("aDbc"), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn until_partial() {
|
||||
assert_eq!(camel_case_until("AbcDef_"), 6);
|
||||
assert_eq!(camel_case_until("CallTypeC"), 8);
|
||||
assert_eq!(camel_case_until("AbcDD"), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn until_caps() {
|
||||
assert_eq!(camel_case_until("ABCD"), 0);
|
||||
}
|
71
tests/compile-fail/enum_variants.rs
Normal file
71
tests/compile-fail/enum_variants.rs
Normal file
@ -0,0 +1,71 @@
|
||||
#![feature(plugin, non_ascii_idents)]
|
||||
#![plugin(clippy)]
|
||||
#![deny(clippy)]
|
||||
|
||||
enum FakeCallType {
|
||||
CALL, CREATE
|
||||
}
|
||||
|
||||
enum FakeCallType2 {
|
||||
CALL, CREATELL
|
||||
}
|
||||
|
||||
enum Foo {
|
||||
cFoo, cBar,
|
||||
}
|
||||
|
||||
enum BadCallType { //~ ERROR: All variants have the same prefix: `CallType`
|
||||
CallTypeCall,
|
||||
CallTypeCreate,
|
||||
CallTypeDestroy,
|
||||
}
|
||||
|
||||
enum TwoCallType { //~ ERROR: All variants have the same prefix: `CallType`
|
||||
CallTypeCall,
|
||||
CallTypeCreate,
|
||||
}
|
||||
|
||||
enum Consts { //~ ERROR: All variants have the same prefix: `Constant`
|
||||
ConstantInt,
|
||||
ConstantCake,
|
||||
ConstantLie,
|
||||
}
|
||||
|
||||
enum Two { //~ ERROR: All variants have the same prefix: `Constant`
|
||||
ConstantInt,
|
||||
ConstantInfer,
|
||||
}
|
||||
|
||||
enum Something {
|
||||
CCall,
|
||||
CCreate,
|
||||
CCryogenize,
|
||||
}
|
||||
|
||||
enum Seal {
|
||||
With,
|
||||
Without,
|
||||
}
|
||||
|
||||
enum Seall {
|
||||
With,
|
||||
WithOut,
|
||||
Withbroken,
|
||||
}
|
||||
|
||||
enum Sealll {
|
||||
With,
|
||||
WithOut,
|
||||
}
|
||||
|
||||
enum Seallll { //~ ERROR: All variants have the same prefix: `With`
|
||||
WithOutCake,
|
||||
WithOut,
|
||||
}
|
||||
|
||||
enum NonCaps { //~ ERROR: All variants have the same prefix: `Prefix`
|
||||
Prefix的,
|
||||
PrefixCake,
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -77,9 +77,9 @@ fn test_ops() {
|
||||
check(ONE, &binop(BiMul, litone.clone(), litone.clone()));
|
||||
check(ONE, &binop(BiDiv, litone.clone(), litone.clone()));
|
||||
|
||||
let half_any = Constant::Float("0.5".into(), FloatWidth::FwAny);
|
||||
let half32 = Constant::Float("0.5".into(), FloatWidth::Fw32);
|
||||
let half64 = Constant::Float("0.5".into(), FloatWidth::Fw64);
|
||||
let half_any = Constant::Float("0.5".into(), FloatWidth::Any);
|
||||
let half32 = Constant::Float("0.5".into(), FloatWidth::F32);
|
||||
let half64 = Constant::Float("0.5".into(), FloatWidth::F64);
|
||||
|
||||
assert_eq!(half_any, half32);
|
||||
assert_eq!(half_any, half64);
|
||||
|
Loading…
x
Reference in New Issue
Block a user