specify "upper camel case" in style lint

Also, fix an issue where internal upper case letters were converted to
lower case.
This commit is contained in:
Andy Russell 2019-02-12 12:35:38 -05:00
parent 57d7cfc3cf
commit 2f95299f6b
No known key found for this signature in database
GPG Key ID: BE2221033EDBC374
8 changed files with 140 additions and 127 deletions

View File

@ -8,7 +8,6 @@ edition = "2018"
name = "rustc_lint"
path = "lib.rs"
crate-type = ["dylib"]
test = false
[dependencies]
log = "0.4"

View File

@ -38,66 +38,87 @@ declare_lint! {
"types, variants, traits and type parameters should have camel case names"
}
fn char_has_case(c: char) -> bool {
c.is_lowercase() || c.is_uppercase()
}
fn is_camel_case(name: &str) -> bool {
let name = name.trim_matches('_');
if name.is_empty() {
return true;
}
// start with a non-lowercase letter rather than non-uppercase
// ones (some scripts don't have a concept of upper/lowercase)
!name.chars().next().unwrap().is_lowercase()
&& !name.contains("__")
&& !name.chars().collect::<Vec<_>>().windows(2).any(|pair| {
// contains a capitalisable character followed by, or preceded by, an underscore
char_has_case(pair[0]) && pair[1] == '_' || char_has_case(pair[1]) && pair[0] == '_'
})
}
fn to_camel_case(s: &str) -> String {
s.trim_matches('_')
.split('_')
.filter(|component| !component.is_empty())
.map(|component| {
let mut camel_cased_component = String::new();
let mut new_word = true;
let mut prev_is_lower_case = true;
for c in component.chars() {
// Preserve the case if an uppercase letter follows a lowercase letter, so that
// `camelCase` is converted to `CamelCase`.
if prev_is_lower_case && c.is_uppercase() {
new_word = true;
}
if new_word {
camel_cased_component.push_str(&c.to_uppercase().to_string());
} else {
camel_cased_component.push_str(&c.to_lowercase().to_string());
}
prev_is_lower_case = c.is_lowercase();
new_word = false;
}
camel_cased_component
})
.fold(
(String::new(), None),
|(acc, prev): (String, Option<String>), next| {
// separate two components with an underscore if their boundary cannot
// be distinguished using a uppercase/lowercase case distinction
let join = if let Some(prev) = prev {
let l = prev.chars().last().unwrap();
let f = next.chars().next().unwrap();
!char_has_case(l) && !char_has_case(f)
} else {
false
};
(acc + if join { "_" } else { "" } + &next, Some(next))
},
)
.0
}
#[derive(Copy, Clone)]
pub struct NonCamelCaseTypes;
impl NonCamelCaseTypes {
fn check_case(&self, cx: &EarlyContext<'_>, sort: &str, ident: &Ident) {
fn char_has_case(c: char) -> bool {
c.is_lowercase() || c.is_uppercase()
}
fn is_camel_case(name: &str) -> bool {
let name = name.trim_matches('_');
if name.is_empty() {
return true;
}
// start with a non-lowercase letter rather than non-uppercase
// ones (some scripts don't have a concept of upper/lowercase)
!name.is_empty() && !name.chars().next().unwrap().is_lowercase() &&
!name.contains("__") && !name.chars().collect::<Vec<_>>().windows(2).any(|pair| {
// contains a capitalisable character followed by, or preceded by, an underscore
char_has_case(pair[0]) && pair[1] == '_' ||
char_has_case(pair[1]) && pair[0] == '_'
})
}
fn to_camel_case(s: &str) -> String {
s.trim_matches('_')
.split('_')
.map(|word| {
word.chars().enumerate().map(|(i, c)| if i == 0 {
c.to_uppercase().collect::<String>()
} else {
c.to_lowercase().collect()
})
.collect::<String>()
})
.filter(|x| !x.is_empty())
.fold((String::new(), None), |(acc, prev): (String, Option<String>), next| {
// separate two components with an underscore if their boundary cannot
// be distinguished using a uppercase/lowercase case distinction
let join = if let Some(prev) = prev {
let l = prev.chars().last().unwrap();
let f = next.chars().next().unwrap();
!char_has_case(l) && !char_has_case(f)
} else { false };
(acc + if join { "_" } else { "" } + &next, Some(next))
}).0
}
let name = &ident.name.as_str();
if !is_camel_case(name) {
let c = to_camel_case(name);
let msg = format!("{} `{}` should have a camel case name", sort, name);
let msg = format!("{} `{}` should have an upper camel case name", sort, name);
cx.struct_span_lint(NON_CAMEL_CASE_TYPES, ident.span, &msg)
.span_suggestion(
ident.span,
"convert the identifier to camel case",
c,
"convert the identifier to upper camel case",
to_camel_case(name),
Applicability::MaybeIncorrect,
)
.emit();
@ -119,11 +140,7 @@ impl EarlyLintPass for NonCamelCaseTypes {
fn check_item(&mut self, cx: &EarlyContext<'_>, it: &ast::Item) {
let has_repr_c = it.attrs
.iter()
.any(|attr| {
attr::find_repr_attrs(&cx.sess.parse_sess, attr)
.iter()
.any(|r| r == &attr::ReprC)
});
.any(|attr| attr::find_repr_attrs(&cx.sess.parse_sess, attr).contains(&attr::ReprC));
if has_repr_c {
return;
@ -439,3 +456,28 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonUpperCaseGlobals {
}
}
}
#[cfg(test)]
mod tests {
use super::{is_camel_case, to_camel_case};
#[test]
fn camel_case() {
assert!(!is_camel_case("userData"));
assert_eq!(to_camel_case("userData"), "UserData");
assert!(is_camel_case("X86_64"));
assert!(!is_camel_case("X86__64"));
assert_eq!(to_camel_case("X86__64"), "X86_64");
assert!(!is_camel_case("Abc_123"));
assert_eq!(to_camel_case("Abc_123"), "Abc123");
assert!(!is_camel_case("A1_b2_c3"));
assert_eq!(to_camel_case("A1_b2_c3"), "A1B2C3");
assert!(!is_camel_case("ONE_TWO_THREE"));
assert_eq!(to_camel_case("ONE_TWO_THREE"), "OneTwoThree");
}
}

View File

@ -19,7 +19,7 @@ mod test {
fn CamelCase() {} //~ WARN should have a snake
struct snake_case; //~ WARN should have a camel
struct snake_case; //~ WARN should have an upper camel
}
}

View File

@ -1,8 +1,8 @@
warning: type `snake_case` should have a camel case name
warning: type `snake_case` should have an upper camel case name
--> $DIR/lint-group-nonstandard-style.rs:22:16
|
LL | struct snake_case; //~ WARN should have a camel
| ^^^^^^^^^^ help: convert the identifier to camel case: `SnakeCase`
LL | struct snake_case; //~ WARN should have an upper camel
| ^^^^^^^^^^ help: convert the identifier to upper camel case: `SnakeCase`
|
note: lint level defined here
--> $DIR/lint-group-nonstandard-style.rs:18:17

View File

@ -2,43 +2,35 @@
#![allow(dead_code)]
struct ONE_TWO_THREE;
//~^ ERROR type `ONE_TWO_THREE` should have a camel case name
//~^ ERROR type `ONE_TWO_THREE` should have an upper camel case name
struct foo { //~ ERROR type `foo` should have a camel case name
struct foo { //~ ERROR type `foo` should have an upper camel case name
bar: isize,
}
enum foo2 { //~ ERROR type `foo2` should have a camel case name
enum foo2 { //~ ERROR type `foo2` should have an upper camel case name
Bar
}
struct foo3 { //~ ERROR type `foo3` should have a camel case name
struct foo3 { //~ ERROR type `foo3` should have an upper camel case name
bar: isize
}
type foo4 = isize; //~ ERROR type `foo4` should have a camel case name
type foo4 = isize; //~ ERROR type `foo4` should have an upper camel case name
enum Foo5 {
bar //~ ERROR variant `bar` should have a camel case name
bar //~ ERROR variant `bar` should have an upper camel case name
}
trait foo6 { //~ ERROR trait `foo6` should have a camel case name
trait foo6 { //~ ERROR trait `foo6` should have an upper camel case name
fn dummy(&self) { }
}
fn f<ty>(_: ty) {} //~ ERROR type parameter `ty` should have a camel case name
fn f<ty>(_: ty) {} //~ ERROR type parameter `ty` should have an upper camel case name
#[repr(C)]
struct foo7 {
bar: isize,
}
struct X86_64;
struct X86__64; //~ ERROR type `X86__64` should have a camel case name
struct Abc_123; //~ ERROR type `Abc_123` should have a camel case name
struct A1_b2_c3; //~ ERROR type `A1_b2_c3` should have a camel case name
fn main() { }

View File

@ -1,8 +1,8 @@
error: type `ONE_TWO_THREE` should have a camel case name
error: type `ONE_TWO_THREE` should have an upper camel case name
--> $DIR/lint-non-camel-case-types.rs:4:8
|
LL | struct ONE_TWO_THREE;
| ^^^^^^^^^^^^^ help: convert the identifier to camel case: `OneTwoThree`
| ^^^^^^^^^^^^^ help: convert the identifier to upper camel case: `OneTwoThree`
|
note: lint level defined here
--> $DIR/lint-non-camel-case-types.rs:1:11
@ -10,65 +10,47 @@ note: lint level defined here
LL | #![forbid(non_camel_case_types)]
| ^^^^^^^^^^^^^^^^^^^^
error: type `foo` should have a camel case name
error: type `foo` should have an upper camel case name
--> $DIR/lint-non-camel-case-types.rs:7:8
|
LL | struct foo { //~ ERROR type `foo` should have a camel case name
| ^^^ help: convert the identifier to camel case: `Foo`
LL | struct foo { //~ ERROR type `foo` should have an upper camel case name
| ^^^ help: convert the identifier to upper camel case: `Foo`
error: type `foo2` should have a camel case name
error: type `foo2` should have an upper camel case name
--> $DIR/lint-non-camel-case-types.rs:11:6
|
LL | enum foo2 { //~ ERROR type `foo2` should have a camel case name
| ^^^^ help: convert the identifier to camel case: `Foo2`
LL | enum foo2 { //~ ERROR type `foo2` should have an upper camel case name
| ^^^^ help: convert the identifier to upper camel case: `Foo2`
error: type `foo3` should have a camel case name
error: type `foo3` should have an upper camel case name
--> $DIR/lint-non-camel-case-types.rs:15:8
|
LL | struct foo3 { //~ ERROR type `foo3` should have a camel case name
| ^^^^ help: convert the identifier to camel case: `Foo3`
LL | struct foo3 { //~ ERROR type `foo3` should have an upper camel case name
| ^^^^ help: convert the identifier to upper camel case: `Foo3`
error: type `foo4` should have a camel case name
error: type `foo4` should have an upper camel case name
--> $DIR/lint-non-camel-case-types.rs:19:6
|
LL | type foo4 = isize; //~ ERROR type `foo4` should have a camel case name
| ^^^^ help: convert the identifier to camel case: `Foo4`
LL | type foo4 = isize; //~ ERROR type `foo4` should have an upper camel case name
| ^^^^ help: convert the identifier to upper camel case: `Foo4`
error: variant `bar` should have a camel case name
error: variant `bar` should have an upper camel case name
--> $DIR/lint-non-camel-case-types.rs:22:5
|
LL | bar //~ ERROR variant `bar` should have a camel case name
| ^^^ help: convert the identifier to camel case: `Bar`
LL | bar //~ ERROR variant `bar` should have an upper camel case name
| ^^^ help: convert the identifier to upper camel case: `Bar`
error: trait `foo6` should have a camel case name
error: trait `foo6` should have an upper camel case name
--> $DIR/lint-non-camel-case-types.rs:25:7
|
LL | trait foo6 { //~ ERROR trait `foo6` should have a camel case name
| ^^^^ help: convert the identifier to camel case: `Foo6`
LL | trait foo6 { //~ ERROR trait `foo6` should have an upper camel case name
| ^^^^ help: convert the identifier to upper camel case: `Foo6`
error: type parameter `ty` should have a camel case name
error: type parameter `ty` should have an upper camel case name
--> $DIR/lint-non-camel-case-types.rs:29:6
|
LL | fn f<ty>(_: ty) {} //~ ERROR type parameter `ty` should have a camel case name
| ^^ help: convert the identifier to camel case: `Ty`
LL | fn f<ty>(_: ty) {} //~ ERROR type parameter `ty` should have an upper camel case name
| ^^ help: convert the identifier to upper camel case: `Ty`
error: type `X86__64` should have a camel case name
--> $DIR/lint-non-camel-case-types.rs:38:8
|
LL | struct X86__64; //~ ERROR type `X86__64` should have a camel case name
| ^^^^^^^ help: convert the identifier to camel case: `X86_64`
error: type `Abc_123` should have a camel case name
--> $DIR/lint-non-camel-case-types.rs:40:8
|
LL | struct Abc_123; //~ ERROR type `Abc_123` should have a camel case name
| ^^^^^^^ help: convert the identifier to camel case: `Abc123`
error: type `A1_b2_c3` should have a camel case name
--> $DIR/lint-non-camel-case-types.rs:42:8
|
LL | struct A1_b2_c3; //~ ERROR type `A1_b2_c3` should have a camel case name
| ^^^^^^^^ help: convert the identifier to camel case: `A1B2C3`
error: aborting due to 11 previous errors
error: aborting due to 8 previous errors

View File

@ -1,9 +1,7 @@
//
fn foo<
'β, //~ ERROR non-ascii idents are not fully supported
γ //~ ERROR non-ascii idents are not fully supported
//~^ WARN type parameter `γ` should have a camel case name
//~^ WARN type parameter `γ` should have an upper camel case name
>() {}
struct X {

View File

@ -1,5 +1,5 @@
error[E0658]: non-ascii idents are not fully supported. (see issue #55467)
--> $DIR/utf8_idents.rs:4:5
--> $DIR/utf8_idents.rs:2:5
|
LL | 'β, //~ ERROR non-ascii idents are not fully supported
| ^^
@ -7,7 +7,7 @@ LL | 'β, //~ ERROR non-ascii idents are not fully supported
= help: add #![feature(non_ascii_idents)] to the crate attributes to enable
error[E0658]: non-ascii idents are not fully supported. (see issue #55467)
--> $DIR/utf8_idents.rs:5:5
--> $DIR/utf8_idents.rs:3:5
|
LL | γ //~ ERROR non-ascii idents are not fully supported
| ^
@ -15,7 +15,7 @@ LL | γ //~ ERROR non-ascii idents are not fully supported
= help: add #![feature(non_ascii_idents)] to the crate attributes to enable
error[E0658]: non-ascii idents are not fully supported. (see issue #55467)
--> $DIR/utf8_idents.rs:10:5
--> $DIR/utf8_idents.rs:8:5
|
LL | δ: usize //~ ERROR non-ascii idents are not fully supported
| ^
@ -23,18 +23,18 @@ LL | δ: usize //~ ERROR non-ascii idents are not fully supported
= help: add #![feature(non_ascii_idents)] to the crate attributes to enable
error[E0658]: non-ascii idents are not fully supported. (see issue #55467)
--> $DIR/utf8_idents.rs:14:9
--> $DIR/utf8_idents.rs:12:9
|
LL | let α = 0.00001f64; //~ ERROR non-ascii idents are not fully supported
| ^
|
= help: add #![feature(non_ascii_idents)] to the crate attributes to enable
warning: type parameter `γ` should have a camel case name
--> $DIR/utf8_idents.rs:5:5
warning: type parameter `γ` should have an upper camel case name
--> $DIR/utf8_idents.rs:3:5
|
LL | γ //~ ERROR non-ascii idents are not fully supported
| ^ help: convert the identifier to camel case: `Γ`
| ^ help: convert the identifier to upper camel case: `Γ`
|
= note: #[warn(non_camel_case_types)] on by default