aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_ty/src
diff options
context:
space:
mode:
authorIgor Aleksanov <[email protected]>2020-10-31 08:53:47 +0000
committerIgor Aleksanov <[email protected]>2020-11-03 07:11:02 +0000
commitbdfe12df8fd055bfa4bbcd0493c285e2e48dfd13 (patch)
treef69f5d74580c9bdb078b15112dcd8a28baf83a2b /crates/hir_ty/src
parent245e1b533b5be5ea4a917957fb02d7f57e6b4661 (diff)
Check for allow(..) attributes for case diagnostic
Diffstat (limited to 'crates/hir_ty/src')
-rw-r--r--crates/hir_ty/src/diagnostics/decl_check.rs116
1 files changed, 91 insertions, 25 deletions
diff --git a/crates/hir_ty/src/diagnostics/decl_check.rs b/crates/hir_ty/src/diagnostics/decl_check.rs
index f179c62b7..bc33ad09a 100644
--- a/crates/hir_ty/src/diagnostics/decl_check.rs
+++ b/crates/hir_ty/src/diagnostics/decl_check.rs
@@ -16,7 +16,7 @@ use hir_def::{
16 adt::VariantData, 16 adt::VariantData,
17 expr::{Pat, PatId}, 17 expr::{Pat, PatId},
18 src::HasSource, 18 src::HasSource,
19 AdtId, ConstId, EnumId, FunctionId, Lookup, ModuleDefId, StaticId, StructId, 19 AdtId, AttrDefId, ConstId, EnumId, FunctionId, Lookup, ModuleDefId, StaticId, StructId,
20}; 20};
21use hir_expand::{ 21use hir_expand::{
22 diagnostics::DiagnosticSink, 22 diagnostics::DiagnosticSink,
@@ -32,6 +32,12 @@ use crate::{
32 diagnostics::{decl_check::case_conv::*, CaseType, IncorrectCase}, 32 diagnostics::{decl_check::case_conv::*, CaseType, IncorrectCase},
33}; 33};
34 34
35mod allow {
36 pub const NON_SNAKE_CASE: &str = "non_snake_case";
37 pub const NON_UPPER_CASE_GLOBAL: &str = "non_upper_case_globals";
38 pub const NON_CAMEL_CASE_TYPES: &str = "non_camel_case_types";
39}
40
35pub(super) struct DeclValidator<'a, 'b: 'a> { 41pub(super) struct DeclValidator<'a, 'b: 'a> {
36 owner: ModuleDefId, 42 owner: ModuleDefId,
37 sink: &'a mut DiagnosticSink<'b>, 43 sink: &'a mut DiagnosticSink<'b>,
@@ -72,11 +78,29 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
72 } 78 }
73 } 79 }
74 80
81 /// Checks whether not following the convention is allowed for this item.
82 ///
83 /// Currently this method doesn't check parent attributes.
84 fn allowed(&self, db: &dyn HirDatabase, id: AttrDefId, allow_name: &str) -> bool {
85 db.attrs(id).by_key("allow").tt_values().any(|tt| tt.to_string().contains(allow_name))
86 }
87
75 fn validate_func(&mut self, db: &dyn HirDatabase, func: FunctionId) { 88 fn validate_func(&mut self, db: &dyn HirDatabase, func: FunctionId) {
76 let data = db.function_data(func); 89 let data = db.function_data(func);
77 let body = db.body(func.into()); 90 let body = db.body(func.into());
78 91
79 // 1. Check the function name. 92 // 1. Recursively validate inner scope items, such as static variables and constants.
93 for (item_id, _) in body.item_scope.values() {
94 let mut validator = DeclValidator::new(item_id, self.sink);
95 validator.validate_item(db);
96 }
97
98 // 2. Check whether non-snake case identifiers are allowed for this function.
99 if self.allowed(db, func.into(), allow::NON_SNAKE_CASE) {
100 return;
101 }
102
103 // 2. Check the function name.
80 let function_name = data.name.to_string(); 104 let function_name = data.name.to_string();
81 let fn_name_replacement = if let Some(new_name) = to_lower_snake_case(&function_name) { 105 let fn_name_replacement = if let Some(new_name) = to_lower_snake_case(&function_name) {
82 let replacement = Replacement { 106 let replacement = Replacement {
@@ -89,7 +113,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
89 None 113 None
90 }; 114 };
91 115
92 // 2. Check the param names. 116 // 3. Check the param names.
93 let mut fn_param_replacements = Vec::new(); 117 let mut fn_param_replacements = Vec::new();
94 118
95 for pat_id in body.params.iter().cloned() { 119 for pat_id in body.params.iter().cloned() {
@@ -111,7 +135,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
111 } 135 }
112 } 136 }
113 137
114 // 3. Check the patterns inside the function body. 138 // 4. Check the patterns inside the function body.
115 let mut pats_replacements = Vec::new(); 139 let mut pats_replacements = Vec::new();
116 140
117 for (pat_idx, pat) in body.pats.iter() { 141 for (pat_idx, pat) in body.pats.iter() {
@@ -136,7 +160,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
136 } 160 }
137 } 161 }
138 162
139 // 4. If there is at least one element to spawn a warning on, go to the source map and generate a warning. 163 // 5. If there is at least one element to spawn a warning on, go to the source map and generate a warning.
140 self.create_incorrect_case_diagnostic_for_func( 164 self.create_incorrect_case_diagnostic_for_func(
141 func, 165 func,
142 db, 166 db,
@@ -144,12 +168,6 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
144 fn_param_replacements, 168 fn_param_replacements,
145 ); 169 );
146 self.create_incorrect_case_diagnostic_for_variables(func, db, pats_replacements); 170 self.create_incorrect_case_diagnostic_for_variables(func, db, pats_replacements);
147
148 // 5. Recursively validate inner scope items, such as static variables and constants.
149 for (item_id, _) in body.item_scope.values() {
150 let mut validator = DeclValidator::new(item_id, self.sink);
151 validator.validate_item(db);
152 }
153 } 171 }
154 172
155 /// Given the information about incorrect names in the function declaration, looks up into the source code 173 /// Given the information about incorrect names in the function declaration, looks up into the source code
@@ -312,6 +330,10 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
312 fn validate_struct(&mut self, db: &dyn HirDatabase, struct_id: StructId) { 330 fn validate_struct(&mut self, db: &dyn HirDatabase, struct_id: StructId) {
313 let data = db.struct_data(struct_id); 331 let data = db.struct_data(struct_id);
314 332
333 let non_camel_case_allowed =
334 self.allowed(db, struct_id.into(), allow::NON_CAMEL_CASE_TYPES);
335 let non_snake_case_allowed = self.allowed(db, struct_id.into(), allow::NON_SNAKE_CASE);
336
315 // 1. Check the structure name. 337 // 1. Check the structure name.
316 let struct_name = data.name.to_string(); 338 let struct_name = data.name.to_string();
317 let struct_name_replacement = if let Some(new_name) = to_camel_case(&struct_name) { 339 let struct_name_replacement = if let Some(new_name) = to_camel_case(&struct_name) {
@@ -320,7 +342,11 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
320 suggested_text: new_name, 342 suggested_text: new_name,
321 expected_case: CaseType::UpperCamelCase, 343 expected_case: CaseType::UpperCamelCase,
322 }; 344 };
323 Some(replacement) 345 if !non_camel_case_allowed {
346 Some(replacement)
347 } else {
348 None
349 }
324 } else { 350 } else {
325 None 351 None
326 }; 352 };
@@ -328,16 +354,18 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
328 // 2. Check the field names. 354 // 2. Check the field names.
329 let mut struct_fields_replacements = Vec::new(); 355 let mut struct_fields_replacements = Vec::new();
330 356
331 if let VariantData::Record(fields) = data.variant_data.as_ref() { 357 if !non_snake_case_allowed {
332 for (_, field) in fields.iter() { 358 if let VariantData::Record(fields) = data.variant_data.as_ref() {
333 let field_name = field.name.to_string(); 359 for (_, field) in fields.iter() {
334 if let Some(new_name) = to_lower_snake_case(&field_name) { 360 let field_name = field.name.to_string();
335 let replacement = Replacement { 361 if let Some(new_name) = to_lower_snake_case(&field_name) {
336 current_name: field.name.clone(), 362 let replacement = Replacement {
337 suggested_text: new_name, 363 current_name: field.name.clone(),
338 expected_case: CaseType::LowerSnakeCase, 364 suggested_text: new_name,
339 }; 365 expected_case: CaseType::LowerSnakeCase,
340 struct_fields_replacements.push(replacement); 366 };
367 struct_fields_replacements.push(replacement);
368 }
341 } 369 }
342 } 370 }
343 } 371 }
@@ -442,7 +470,12 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
442 fn validate_enum(&mut self, db: &dyn HirDatabase, enum_id: EnumId) { 470 fn validate_enum(&mut self, db: &dyn HirDatabase, enum_id: EnumId) {
443 let data = db.enum_data(enum_id); 471 let data = db.enum_data(enum_id);
444 472
445 // 1. Check the enum name. 473 // 1. Check whether non-camel case names are allowed for this enum.
474 if self.allowed(db, enum_id.into(), allow::NON_CAMEL_CASE_TYPES) {
475 return;
476 }
477
478 // 2. Check the enum name.
446 let enum_name = data.name.to_string(); 479 let enum_name = data.name.to_string();
447 let enum_name_replacement = if let Some(new_name) = to_camel_case(&enum_name) { 480 let enum_name_replacement = if let Some(new_name) = to_camel_case(&enum_name) {
448 let replacement = Replacement { 481 let replacement = Replacement {
@@ -455,7 +488,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
455 None 488 None
456 }; 489 };
457 490
458 // 2. Check the field names. 491 // 3. Check the field names.
459 let mut enum_fields_replacements = Vec::new(); 492 let mut enum_fields_replacements = Vec::new();
460 493
461 for (_, variant) in data.variants.iter() { 494 for (_, variant) in data.variants.iter() {
@@ -470,7 +503,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
470 } 503 }
471 } 504 }
472 505
473 // 3. If there is at least one element to spawn a warning on, go to the source map and generate a warning. 506 // 4. If there is at least one element to spawn a warning on, go to the source map and generate a warning.
474 self.create_incorrect_case_diagnostic_for_enum( 507 self.create_incorrect_case_diagnostic_for_enum(
475 enum_id, 508 enum_id,
476 db, 509 db,
@@ -572,6 +605,10 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
572 fn validate_const(&mut self, db: &dyn HirDatabase, const_id: ConstId) { 605 fn validate_const(&mut self, db: &dyn HirDatabase, const_id: ConstId) {
573 let data = db.const_data(const_id); 606 let data = db.const_data(const_id);
574 607
608 if self.allowed(db, const_id.into(), allow::NON_UPPER_CASE_GLOBAL) {
609 return;
610 }
611
575 let name = match &data.name { 612 let name = match &data.name {
576 Some(name) => name, 613 Some(name) => name,
577 None => return, 614 None => return,
@@ -612,6 +649,10 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
612 fn validate_static(&mut self, db: &dyn HirDatabase, static_id: StaticId) { 649 fn validate_static(&mut self, db: &dyn HirDatabase, static_id: StaticId) {
613 let data = db.static_data(static_id); 650 let data = db.static_data(static_id);
614 651
652 if self.allowed(db, static_id.into(), allow::NON_UPPER_CASE_GLOBAL) {
653 return;
654 }
655
615 let name = match &data.name { 656 let name = match &data.name {
616 Some(name) => name, 657 Some(name) => name,
617 None => return, 658 None => return,
@@ -854,4 +895,29 @@ fn main() {
854"#, 895"#,
855 ); 896 );
856 } 897 }
898
899 #[test]
900 fn allow_attributes() {
901 check_diagnostics(
902 r#"
903 #[allow(non_snake_case)]
904 fn NonSnakeCaseName(SOME_VAR: u8) -> u8{
905 let OtherVar = SOME_VAR + 1;
906 OtherVar
907 }
908
909 #[allow(non_snake_case, non_camel_case_types)]
910 pub struct some_type {
911 SOME_FIELD: u8,
912 SomeField: u16,
913 }
914
915 #[allow(non_upper_case_globals)]
916 pub const some_const: u8 = 10;
917
918 #[allow(non_upper_case_globals)]
919 pub static SomeStatic: u8 = 10;
920 "#,
921 );
922 }
857} 923}