diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/hir_ty/src/diagnostics/decl_check.rs | 49 | ||||
-rw-r--r-- | crates/hir_ty/src/diagnostics/decl_check/str_helpers.rs | 92 |
2 files changed, 112 insertions, 29 deletions
diff --git a/crates/hir_ty/src/diagnostics/decl_check.rs b/crates/hir_ty/src/diagnostics/decl_check.rs index 083df3772..1a0906492 100644 --- a/crates/hir_ty/src/diagnostics/decl_check.rs +++ b/crates/hir_ty/src/diagnostics/decl_check.rs | |||
@@ -9,6 +9,8 @@ | |||
9 | // If you see these lines in the pull request, feel free to call me stupid :P. | 9 | // If you see these lines in the pull request, feel free to call me stupid :P. |
10 | #![allow(dead_code, unused_imports, unused_variables)] | 10 | #![allow(dead_code, unused_imports, unused_variables)] |
11 | 11 | ||
12 | mod str_helpers; | ||
13 | |||
12 | use std::sync::Arc; | 14 | use std::sync::Arc; |
13 | 15 | ||
14 | use hir_def::{ | 16 | use hir_def::{ |
@@ -18,7 +20,7 @@ use hir_def::{ | |||
18 | item_tree::ItemTreeNode, | 20 | item_tree::ItemTreeNode, |
19 | resolver::{resolver_for_expr, ResolveValueResult, ValueNs}, | 21 | resolver::{resolver_for_expr, ResolveValueResult, ValueNs}, |
20 | src::HasSource, | 22 | src::HasSource, |
21 | AdtId, FunctionId, Lookup, ModuleDefId, | 23 | AdtId, EnumId, FunctionId, Lookup, ModuleDefId, StructId, |
22 | }; | 24 | }; |
23 | use hir_expand::{diagnostics::DiagnosticSink, name::Name}; | 25 | use hir_expand::{diagnostics::DiagnosticSink, name::Name}; |
24 | use syntax::{ | 26 | use syntax::{ |
@@ -28,7 +30,7 @@ use syntax::{ | |||
28 | 30 | ||
29 | use crate::{ | 31 | use crate::{ |
30 | db::HirDatabase, | 32 | db::HirDatabase, |
31 | diagnostics::{CaseType, IncorrectCase}, | 33 | diagnostics::{decl_check::str_helpers::*, CaseType, IncorrectCase}, |
32 | lower::CallableDefId, | 34 | lower::CallableDefId, |
33 | ApplicationTy, InferenceResult, Ty, TypeCtor, | 35 | ApplicationTy, InferenceResult, Ty, TypeCtor, |
34 | }; | 36 | }; |
@@ -191,41 +193,30 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
191 | } | 193 | } |
192 | } | 194 | } |
193 | 195 | ||
194 | fn validate_adt(&mut self, db: &dyn HirDatabase, adt: AdtId) {} | 196 | fn validate_adt(&mut self, db: &dyn HirDatabase, adt: AdtId) { |
195 | } | 197 | match adt { |
196 | 198 | AdtId::StructId(struct_id) => self.validate_struct(db, struct_id), | |
197 | fn pat_equals_to_name(pat: Option<ast::Pat>, name: &Name) -> bool { | 199 | AdtId::EnumId(enum_id) => self.validate_enum(db, enum_id), |
198 | if let Some(ast::Pat::IdentPat(ident)) = pat { | 200 | AdtId::UnionId(_) => { |
199 | ident.to_string() == name.to_string() | 201 | // Unions aren't yet supported by this validator. |
200 | } else { | 202 | } |
201 | false | 203 | } |
202 | } | 204 | } |
203 | } | ||
204 | 205 | ||
205 | fn to_lower_snake_case(ident: &str) -> Option<String> { | 206 | fn validate_struct(&mut self, db: &dyn HirDatabase, struct_id: StructId) { |
206 | // First, assume that it's UPPER_SNAKE_CASE. | 207 | let data = db.struct_data(struct_id); |
207 | if let Some(normalized) = to_lower_snake_case_from_upper_snake_case(ident) { | ||
208 | return Some(normalized); | ||
209 | } | 208 | } |
210 | 209 | ||
211 | // Otherwise, assume that it's CamelCase. | 210 | fn validate_enum(&mut self, db: &dyn HirDatabase, enum_id: EnumId) { |
212 | let lower_snake_case = stdx::to_lower_snake_case(ident); | 211 | let data = db.enum_data(enum_id); |
213 | |||
214 | if lower_snake_case == ident { | ||
215 | None | ||
216 | } else { | ||
217 | Some(lower_snake_case) | ||
218 | } | 212 | } |
219 | } | 213 | } |
220 | 214 | ||
221 | fn to_lower_snake_case_from_upper_snake_case(ident: &str) -> Option<String> { | 215 | fn pat_equals_to_name(pat: Option<ast::Pat>, name: &Name) -> bool { |
222 | let is_upper_snake_case = ident.chars().all(|c| c.is_ascii_uppercase() || c == '_'); | 216 | if let Some(ast::Pat::IdentPat(ident)) = pat { |
223 | 217 | ident.to_string() == name.to_string() | |
224 | if is_upper_snake_case { | ||
225 | let string = ident.chars().map(|c| c.to_ascii_lowercase()).collect(); | ||
226 | Some(string) | ||
227 | } else { | 218 | } else { |
228 | None | 219 | false |
229 | } | 220 | } |
230 | } | 221 | } |
231 | 222 | ||
diff --git a/crates/hir_ty/src/diagnostics/decl_check/str_helpers.rs b/crates/hir_ty/src/diagnostics/decl_check/str_helpers.rs new file mode 100644 index 000000000..3d8f1b5f2 --- /dev/null +++ b/crates/hir_ty/src/diagnostics/decl_check/str_helpers.rs | |||
@@ -0,0 +1,92 @@ | |||
1 | pub fn to_camel_case(ident: &str) -> Option<String> { | ||
2 | let mut output = String::new(); | ||
3 | |||
4 | if is_camel_case(ident) { | ||
5 | return None; | ||
6 | } | ||
7 | |||
8 | let mut capital_added = false; | ||
9 | for chr in ident.chars() { | ||
10 | if chr.is_alphabetic() { | ||
11 | if !capital_added { | ||
12 | output.push(chr.to_ascii_uppercase()); | ||
13 | capital_added = true; | ||
14 | } else { | ||
15 | output.push(chr.to_ascii_lowercase()); | ||
16 | } | ||
17 | } else if chr == '_' { | ||
18 | // Skip this character and make the next one capital. | ||
19 | capital_added = false; | ||
20 | } else { | ||
21 | // Put the characted as-is. | ||
22 | output.push(chr); | ||
23 | } | ||
24 | } | ||
25 | |||
26 | if output == ident { | ||
27 | None | ||
28 | } else { | ||
29 | Some(output) | ||
30 | } | ||
31 | } | ||
32 | |||
33 | pub fn to_lower_snake_case(ident: &str) -> Option<String> { | ||
34 | // First, assume that it's UPPER_SNAKE_CASE. | ||
35 | if let Some(normalized) = to_lower_snake_case_from_upper_snake_case(ident) { | ||
36 | return Some(normalized); | ||
37 | } | ||
38 | |||
39 | // Otherwise, assume that it's CamelCase. | ||
40 | let lower_snake_case = stdx::to_lower_snake_case(ident); | ||
41 | |||
42 | if lower_snake_case == ident { | ||
43 | None | ||
44 | } else { | ||
45 | Some(lower_snake_case) | ||
46 | } | ||
47 | } | ||
48 | |||
49 | fn to_lower_snake_case_from_upper_snake_case(ident: &str) -> Option<String> { | ||
50 | if is_upper_snake_case(ident) { | ||
51 | let string = ident.chars().map(|c| c.to_ascii_lowercase()).collect(); | ||
52 | Some(string) | ||
53 | } else { | ||
54 | None | ||
55 | } | ||
56 | } | ||
57 | |||
58 | fn is_upper_snake_case(ident: &str) -> bool { | ||
59 | ident.chars().all(|c| c.is_ascii_uppercase() || c == '_') | ||
60 | } | ||
61 | |||
62 | fn is_camel_case(ident: &str) -> bool { | ||
63 | // We assume that the string is either snake case or camel case. | ||
64 | ident.chars().all(|c| c != '_') | ||
65 | } | ||
66 | |||
67 | #[cfg(test)] | ||
68 | mod tests { | ||
69 | use super::*; | ||
70 | use expect_test::{expect, Expect}; | ||
71 | |||
72 | fn check<F: Fn(&str) -> Option<String>>(fun: F, input: &str, expect: Expect) { | ||
73 | // `None` is translated to empty string, meaning that there is nothing to fix. | ||
74 | let output = fun(input).unwrap_or_default(); | ||
75 | |||
76 | expect.assert_eq(&output); | ||
77 | } | ||
78 | |||
79 | #[test] | ||
80 | fn test_to_lower_snake_case() { | ||
81 | check(to_lower_snake_case, "lower_snake_case", expect![[""]]); | ||
82 | check(to_lower_snake_case, "UPPER_SNAKE_CASE", expect![["upper_snake_case"]]); | ||
83 | check(to_lower_snake_case, "CamelCase", expect![["camel_case"]]); | ||
84 | } | ||
85 | |||
86 | #[test] | ||
87 | fn test_to_camel_case() { | ||
88 | check(to_camel_case, "CamelCase", expect![[""]]); | ||
89 | check(to_camel_case, "lower_snake_case", expect![["LowerSnakeCase"]]); | ||
90 | check(to_camel_case, "UPPER_SNAKE_CASE", expect![["UpperSnakeCase"]]); | ||
91 | } | ||
92 | } | ||