From 2f95299f6bf87a7b23381cda954570e72c80c159 Mon Sep 17 00:00:00 2001 From: Andy Russell Date: Tue, 12 Feb 2019 12:35:38 -0500 Subject: [PATCH] specify "upper camel case" in style lint Also, fix an issue where internal upper case letters were converted to lower case. --- src/librustc_lint/Cargo.toml | 1 - src/librustc_lint/nonstandard_style.rs | 150 +++++++++++------- .../ui/lint/lint-group-nonstandard-style.rs | 2 +- .../lint/lint-group-nonstandard-style.stderr | 6 +- src/test/ui/lint/lint-non-camel-case-types.rs | 24 +-- .../ui/lint/lint-non-camel-case-types.stderr | 66 +++----- src/test/ui/utf8_idents.rs | 4 +- src/test/ui/utf8_idents.stderr | 14 +- 8 files changed, 140 insertions(+), 127 deletions(-) diff --git a/src/librustc_lint/Cargo.toml b/src/librustc_lint/Cargo.toml index 82f7118df2d..fd2b635faef 100644 --- a/src/librustc_lint/Cargo.toml +++ b/src/librustc_lint/Cargo.toml @@ -8,7 +8,6 @@ edition = "2018" name = "rustc_lint" path = "lib.rs" crate-type = ["dylib"] -test = false [dependencies] log = "0.4" diff --git a/src/librustc_lint/nonstandard_style.rs b/src/librustc_lint/nonstandard_style.rs index 2dbafc7ede2..c2dd9a3d1b8 100644 --- a/src/librustc_lint/nonstandard_style.rs +++ b/src/librustc_lint/nonstandard_style.rs @@ -38,66 +38,87 @@ pub fn method_context(cx: &LateContext<'_, '_>, id: ast::NodeId) -> MethodLateCo "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::>().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), 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::>().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::() - } else { - c.to_lowercase().collect() - }) - .collect::() - }) - .filter(|x| !x.is_empty()) - .fold((String::new(), None), |(acc, prev): (String, Option), 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 @@ fn check_pat(&mut self, cx: &LateContext<'_, '_>, p: &hir::Pat) { } } } + +#[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"); + } +} diff --git a/src/test/ui/lint/lint-group-nonstandard-style.rs b/src/test/ui/lint/lint-group-nonstandard-style.rs index 0386daaa59b..bd7f327bc0f 100644 --- a/src/test/ui/lint/lint-group-nonstandard-style.rs +++ b/src/test/ui/lint/lint-group-nonstandard-style.rs @@ -19,7 +19,7 @@ mod warn { fn CamelCase() {} //~ WARN should have a snake - struct snake_case; //~ WARN should have a camel + struct snake_case; //~ WARN should have an upper camel } } diff --git a/src/test/ui/lint/lint-group-nonstandard-style.stderr b/src/test/ui/lint/lint-group-nonstandard-style.stderr index f3c7d70054b..ab36cda57ec 100644 --- a/src/test/ui/lint/lint-group-nonstandard-style.stderr +++ b/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 diff --git a/src/test/ui/lint/lint-non-camel-case-types.rs b/src/test/ui/lint/lint-non-camel-case-types.rs index bca1992605b..d3b119a9441 100644 --- a/src/test/ui/lint/lint-non-camel-case-types.rs +++ b/src/test/ui/lint/lint-non-camel-case-types.rs @@ -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) {} //~ ERROR type parameter `ty` should have a camel case name +fn f(_: 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() { } diff --git a/src/test/ui/lint/lint-non-camel-case-types.stderr b/src/test/ui/lint/lint-non-camel-case-types.stderr index 74f9a5993b8..7afacf64d87 100644 --- a/src/test/ui/lint/lint-non-camel-case-types.stderr +++ b/src/test/ui/lint/lint-non-camel-case-types.stderr @@ -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) {} //~ ERROR type parameter `ty` should have a camel case name - | ^^ help: convert the identifier to camel case: `Ty` +LL | fn f(_: 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 diff --git a/src/test/ui/utf8_idents.rs b/src/test/ui/utf8_idents.rs index e601c6e4555..f59d5502aae 100644 --- a/src/test/ui/utf8_idents.rs +++ b/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 { diff --git a/src/test/ui/utf8_idents.stderr b/src/test/ui/utf8_idents.stderr index 268dd99d060..52fb607af5b 100644 --- a/src/test/ui/utf8_idents.stderr +++ b/src/test/ui/utf8_idents.stderr @@ -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