Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/diagnostics/decl_check/case_conv.rs')
-rw-r--r--crates/hir-ty/src/diagnostics/decl_check/case_conv.rs199
1 files changed, 199 insertions, 0 deletions
diff --git a/crates/hir-ty/src/diagnostics/decl_check/case_conv.rs b/crates/hir-ty/src/diagnostics/decl_check/case_conv.rs
new file mode 100644
index 0000000000..88d607194f
--- /dev/null
+++ b/crates/hir-ty/src/diagnostics/decl_check/case_conv.rs
@@ -0,0 +1,199 @@
+//! Functions for string case manipulation, such as detecting the identifier case,
+//! and converting it into appropriate form.
+
+// Code that was taken from rustc was taken at commit 89fdb30,
+// from file /compiler/rustc_lint/src/nonstandard_style.rs
+
+/// Converts an identifier to an UpperCamelCase form.
+/// Returns `None` if the string is already in UpperCamelCase.
+pub(crate) fn to_camel_case(ident: &str) -> Option<String> {
+ if is_camel_case(ident) {
+ return None;
+ }
+
+ // Taken from rustc.
+ let ret = ident
+ .trim_matches('_')
+ .split('_')
+ .filter(|component| !component.is_empty())
+ .map(|component| {
+ let mut camel_cased_component = String::with_capacity(component.len());
+
+ 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.extend(c.to_uppercase());
+ } else {
+ camel_cased_component.extend(c.to_lowercase());
+ }
+
+ prev_is_lower_case = c.is_lowercase();
+ new_word = false;
+ }
+
+ camel_cased_component
+ })
+ .fold((String::new(), None), |(acc, prev): (_, Option<String>), next| {
+ // separate two components with an underscore if their boundary cannot
+ // be distinguished using an uppercase/lowercase case distinction
+ let join = prev
+ .and_then(|prev| {
+ let f = next.chars().next()?;
+ let l = prev.chars().last()?;
+ Some(!char_has_case(l) && !char_has_case(f))
+ })
+ .unwrap_or(false);
+ (acc + if join { "_" } else { "" } + &next, Some(next))
+ })
+ .0;
+ Some(ret)
+}
+
+/// Converts an identifier to a lower_snake_case form.
+/// Returns `None` if the string is already in lower_snake_case.
+pub(crate) fn to_lower_snake_case(ident: &str) -> Option<String> {
+ if is_lower_snake_case(ident) {
+ return None;
+ } else if is_upper_snake_case(ident) {
+ return Some(ident.to_lowercase());
+ }
+
+ Some(stdx::to_lower_snake_case(ident))
+}
+
+/// Converts an identifier to an UPPER_SNAKE_CASE form.
+/// Returns `None` if the string is already is UPPER_SNAKE_CASE.
+pub(crate) fn to_upper_snake_case(ident: &str) -> Option<String> {
+ if is_upper_snake_case(ident) {
+ return None;
+ } else if is_lower_snake_case(ident) {
+ return Some(ident.to_uppercase());
+ }
+
+ Some(stdx::to_upper_snake_case(ident))
+}
+
+// Taken from rustc.
+// Modified by replacing the use of unstable feature `array_windows`.
+fn is_camel_case(name: &str) -> bool {
+ let name = name.trim_matches('_');
+ if name.is_empty() {
+ return true;
+ }
+
+ let mut fst = None;
+ // start with a non-lowercase letter rather than non-uppercase
+ // ones (some scripts don't have a concept of upper/lowercase)
+ name.chars().next().map_or(true, |c| !c.is_lowercase())
+ && !name.contains("__")
+ && !name.chars().any(|snd| {
+ let ret = match fst {
+ None => false,
+ Some(fst) => char_has_case(fst) && snd == '_' || char_has_case(snd) && fst == '_',
+ };
+ fst = Some(snd);
+
+ ret
+ })
+}
+
+fn is_lower_snake_case(ident: &str) -> bool {
+ is_snake_case(ident, char::is_uppercase)
+}
+
+fn is_upper_snake_case(ident: &str) -> bool {
+ is_snake_case(ident, char::is_lowercase)
+}
+
+// Taken from rustc.
+// Modified to allow checking for both upper and lower snake case.
+fn is_snake_case<F: Fn(char) -> bool>(ident: &str, wrong_case: F) -> bool {
+ if ident.is_empty() {
+ return true;
+ }
+ let ident = ident.trim_matches('_');
+
+ let mut allow_underscore = true;
+ ident.chars().all(|c| {
+ allow_underscore = match c {
+ '_' if !allow_underscore => return false,
+ '_' => false,
+ // It would be more obvious to check for the correct case,
+ // but some characters do not have a case.
+ c if !wrong_case(c) => true,
+ _ => return false,
+ };
+ true
+ })
+}
+
+// Taken from rustc.
+fn char_has_case(c: char) -> bool {
+ c.is_lowercase() || c.is_uppercase()
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use expect_test::{expect, Expect};
+
+ fn check<F: Fn(&str) -> Option<String>>(fun: F, input: &str, expect: Expect) {
+ // `None` is translated to empty string, meaning that there is nothing to fix.
+ let output = fun(input).unwrap_or_default();
+
+ expect.assert_eq(&output);
+ }
+
+ #[test]
+ fn test_to_lower_snake_case() {
+ check(to_lower_snake_case, "lower_snake_case", expect![[""]]);
+ check(to_lower_snake_case, "UPPER_SNAKE_CASE", expect![["upper_snake_case"]]);
+ check(to_lower_snake_case, "Weird_Case", expect![["weird_case"]]);
+ check(to_lower_snake_case, "CamelCase", expect![["camel_case"]]);
+ check(to_lower_snake_case, "lowerCamelCase", expect![["lower_camel_case"]]);
+ check(to_lower_snake_case, "a", expect![[""]]);
+ check(to_lower_snake_case, "abc", expect![[""]]);
+ check(to_lower_snake_case, "foo__bar", expect![["foo_bar"]]);
+ }
+
+ #[test]
+ fn test_to_camel_case() {
+ check(to_camel_case, "CamelCase", expect![[""]]);
+ check(to_camel_case, "CamelCase_", expect![[""]]);
+ check(to_camel_case, "_CamelCase", expect![[""]]);
+ check(to_camel_case, "lowerCamelCase", expect![["LowerCamelCase"]]);
+ check(to_camel_case, "lower_snake_case", expect![["LowerSnakeCase"]]);
+ check(to_camel_case, "UPPER_SNAKE_CASE", expect![["UpperSnakeCase"]]);
+ check(to_camel_case, "Weird_Case", expect![["WeirdCase"]]);
+ check(to_camel_case, "name", expect![["Name"]]);
+ check(to_camel_case, "A", expect![[""]]);
+ check(to_camel_case, "AABB", expect![[""]]);
+ // Taken from rustc: /compiler/rustc_lint/src/nonstandard_style/tests.rs
+ check(to_camel_case, "X86_64", expect![[""]]);
+ check(to_camel_case, "x86__64", expect![["X86_64"]]);
+ check(to_camel_case, "Abc_123", expect![["Abc123"]]);
+ check(to_camel_case, "A1_b2_c3", expect![["A1B2C3"]]);
+ }
+
+ #[test]
+ fn test_to_upper_snake_case() {
+ check(to_upper_snake_case, "UPPER_SNAKE_CASE", expect![[""]]);
+ check(to_upper_snake_case, "lower_snake_case", expect![["LOWER_SNAKE_CASE"]]);
+ check(to_upper_snake_case, "Weird_Case", expect![["WEIRD_CASE"]]);
+ check(to_upper_snake_case, "CamelCase", expect![["CAMEL_CASE"]]);
+ check(to_upper_snake_case, "lowerCamelCase", expect![["LOWER_CAMEL_CASE"]]);
+ check(to_upper_snake_case, "A", expect![[""]]);
+ check(to_upper_snake_case, "ABC", expect![[""]]);
+ check(to_upper_snake_case, "X86_64", expect![[""]]);
+ check(to_upper_snake_case, "FOO_BAr", expect![["FOO_BAR"]]);
+ check(to_upper_snake_case, "FOO__BAR", expect![["FOO_BAR"]]);
+ }
+}