Skip to content

Commit

Permalink
specify "upper camel case" in style lint
Browse files Browse the repository at this point in the history
Also, fix an issue where internal upper case letters were converted to
lower case.
  • Loading branch information
euclio committed Feb 13, 2019
1 parent 57d7cfc commit 2f95299
Show file tree
Hide file tree
Showing 8 changed files with 139 additions and 126 deletions.
1 change: 0 additions & 1 deletion src/librustc_lint/Cargo.toml
Expand Up @@ -8,7 +8,6 @@ edition = "2018"
name = "rustc_lint"
path = "lib.rs"
crate-type = ["dylib"]
test = false

[dependencies]
log = "0.4"
Expand Down
148 changes: 95 additions & 53 deletions src/librustc_lint/nonstandard_style.rs
Expand Up @@ -38,66 +38,87 @@ declare_lint! {
"types, variants, traits and type parameters should have camel case names"
}

#[derive(Copy, Clone)]
pub struct NonCamelCaseTypes;
fn char_has_case(c: char) -> bool {
c.is_lowercase() || c.is_uppercase()
}

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

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

// 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] == '_'
})
}
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
}

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
}
#[derive(Copy, Clone)]
pub struct NonCamelCaseTypes;

impl NonCamelCaseTypes {
fn check_case(&self, cx: &EarlyContext<'_>, sort: &str, ident: &Ident) {
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();
Expand All @@ -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;
Expand Down Expand Up @@ -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");
}
}
2 changes: 1 addition & 1 deletion src/test/ui/lint/lint-group-nonstandard-style.rs
Expand Up @@ -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
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/test/ui/lint/lint-group-nonstandard-style.stderr
@@ -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
Expand Down
24 changes: 8 additions & 16 deletions src/test/ui/lint/lint-non-camel-case-types.rs
Expand Up @@ -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() { }
66 changes: 24 additions & 42 deletions src/test/ui/lint/lint-non-camel-case-types.stderr
@@ -1,74 +1,56 @@
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
|
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

4 changes: 1 addition & 3 deletions src/test/ui/utf8_idents.rs
@@ -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 {
Expand Down

0 comments on commit 2f95299

Please sign in to comment.