From bdfe12df8fd055bfa4bbcd0493c285e2e48dfd13 Mon Sep 17 00:00:00 2001 From: Igor Aleksanov Date: Sat, 31 Oct 2020 11:53:47 +0300 Subject: Check for allow(..) attributes for case diagnostic --- crates/hir_ty/src/diagnostics/decl_check.rs | 116 ++++++++++++++++++++++------ 1 file changed, 91 insertions(+), 25 deletions(-) (limited to 'crates/hir_ty/src') 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::{ adt::VariantData, expr::{Pat, PatId}, src::HasSource, - AdtId, ConstId, EnumId, FunctionId, Lookup, ModuleDefId, StaticId, StructId, + AdtId, AttrDefId, ConstId, EnumId, FunctionId, Lookup, ModuleDefId, StaticId, StructId, }; use hir_expand::{ diagnostics::DiagnosticSink, @@ -32,6 +32,12 @@ use crate::{ diagnostics::{decl_check::case_conv::*, CaseType, IncorrectCase}, }; +mod allow { + pub const NON_SNAKE_CASE: &str = "non_snake_case"; + pub const NON_UPPER_CASE_GLOBAL: &str = "non_upper_case_globals"; + pub const NON_CAMEL_CASE_TYPES: &str = "non_camel_case_types"; +} + pub(super) struct DeclValidator<'a, 'b: 'a> { owner: ModuleDefId, sink: &'a mut DiagnosticSink<'b>, @@ -72,11 +78,29 @@ impl<'a, 'b> DeclValidator<'a, 'b> { } } + /// Checks whether not following the convention is allowed for this item. + /// + /// Currently this method doesn't check parent attributes. + fn allowed(&self, db: &dyn HirDatabase, id: AttrDefId, allow_name: &str) -> bool { + db.attrs(id).by_key("allow").tt_values().any(|tt| tt.to_string().contains(allow_name)) + } + fn validate_func(&mut self, db: &dyn HirDatabase, func: FunctionId) { let data = db.function_data(func); let body = db.body(func.into()); - // 1. Check the function name. + // 1. Recursively validate inner scope items, such as static variables and constants. + for (item_id, _) in body.item_scope.values() { + let mut validator = DeclValidator::new(item_id, self.sink); + validator.validate_item(db); + } + + // 2. Check whether non-snake case identifiers are allowed for this function. + if self.allowed(db, func.into(), allow::NON_SNAKE_CASE) { + return; + } + + // 2. Check the function name. let function_name = data.name.to_string(); let fn_name_replacement = if let Some(new_name) = to_lower_snake_case(&function_name) { let replacement = Replacement { @@ -89,7 +113,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> { None }; - // 2. Check the param names. + // 3. Check the param names. let mut fn_param_replacements = Vec::new(); for pat_id in body.params.iter().cloned() { @@ -111,7 +135,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> { } } - // 3. Check the patterns inside the function body. + // 4. Check the patterns inside the function body. let mut pats_replacements = Vec::new(); for (pat_idx, pat) in body.pats.iter() { @@ -136,7 +160,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> { } } - // 4. If there is at least one element to spawn a warning on, go to the source map and generate a warning. + // 5. If there is at least one element to spawn a warning on, go to the source map and generate a warning. self.create_incorrect_case_diagnostic_for_func( func, db, @@ -144,12 +168,6 @@ impl<'a, 'b> DeclValidator<'a, 'b> { fn_param_replacements, ); self.create_incorrect_case_diagnostic_for_variables(func, db, pats_replacements); - - // 5. Recursively validate inner scope items, such as static variables and constants. - for (item_id, _) in body.item_scope.values() { - let mut validator = DeclValidator::new(item_id, self.sink); - validator.validate_item(db); - } } /// 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> { fn validate_struct(&mut self, db: &dyn HirDatabase, struct_id: StructId) { let data = db.struct_data(struct_id); + let non_camel_case_allowed = + self.allowed(db, struct_id.into(), allow::NON_CAMEL_CASE_TYPES); + let non_snake_case_allowed = self.allowed(db, struct_id.into(), allow::NON_SNAKE_CASE); + // 1. Check the structure name. let struct_name = data.name.to_string(); let struct_name_replacement = if let Some(new_name) = to_camel_case(&struct_name) { @@ -320,7 +342,11 @@ impl<'a, 'b> DeclValidator<'a, 'b> { suggested_text: new_name, expected_case: CaseType::UpperCamelCase, }; - Some(replacement) + if !non_camel_case_allowed { + Some(replacement) + } else { + None + } } else { None }; @@ -328,16 +354,18 @@ impl<'a, 'b> DeclValidator<'a, 'b> { // 2. Check the field names. let mut struct_fields_replacements = Vec::new(); - if let VariantData::Record(fields) = data.variant_data.as_ref() { - for (_, field) in fields.iter() { - let field_name = field.name.to_string(); - if let Some(new_name) = to_lower_snake_case(&field_name) { - let replacement = Replacement { - current_name: field.name.clone(), - suggested_text: new_name, - expected_case: CaseType::LowerSnakeCase, - }; - struct_fields_replacements.push(replacement); + if !non_snake_case_allowed { + if let VariantData::Record(fields) = data.variant_data.as_ref() { + for (_, field) in fields.iter() { + let field_name = field.name.to_string(); + if let Some(new_name) = to_lower_snake_case(&field_name) { + let replacement = Replacement { + current_name: field.name.clone(), + suggested_text: new_name, + expected_case: CaseType::LowerSnakeCase, + }; + struct_fields_replacements.push(replacement); + } } } } @@ -442,7 +470,12 @@ impl<'a, 'b> DeclValidator<'a, 'b> { fn validate_enum(&mut self, db: &dyn HirDatabase, enum_id: EnumId) { let data = db.enum_data(enum_id); - // 1. Check the enum name. + // 1. Check whether non-camel case names are allowed for this enum. + if self.allowed(db, enum_id.into(), allow::NON_CAMEL_CASE_TYPES) { + return; + } + + // 2. Check the enum name. let enum_name = data.name.to_string(); let enum_name_replacement = if let Some(new_name) = to_camel_case(&enum_name) { let replacement = Replacement { @@ -455,7 +488,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> { None }; - // 2. Check the field names. + // 3. Check the field names. let mut enum_fields_replacements = Vec::new(); for (_, variant) in data.variants.iter() { @@ -470,7 +503,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> { } } - // 3. If there is at least one element to spawn a warning on, go to the source map and generate a warning. + // 4. If there is at least one element to spawn a warning on, go to the source map and generate a warning. self.create_incorrect_case_diagnostic_for_enum( enum_id, db, @@ -572,6 +605,10 @@ impl<'a, 'b> DeclValidator<'a, 'b> { fn validate_const(&mut self, db: &dyn HirDatabase, const_id: ConstId) { let data = db.const_data(const_id); + if self.allowed(db, const_id.into(), allow::NON_UPPER_CASE_GLOBAL) { + return; + } + let name = match &data.name { Some(name) => name, None => return, @@ -612,6 +649,10 @@ impl<'a, 'b> DeclValidator<'a, 'b> { fn validate_static(&mut self, db: &dyn HirDatabase, static_id: StaticId) { let data = db.static_data(static_id); + if self.allowed(db, static_id.into(), allow::NON_UPPER_CASE_GLOBAL) { + return; + } + let name = match &data.name { Some(name) => name, None => return, @@ -854,4 +895,29 @@ fn main() { "#, ); } + + #[test] + fn allow_attributes() { + check_diagnostics( + r#" + #[allow(non_snake_case)] + fn NonSnakeCaseName(SOME_VAR: u8) -> u8{ + let OtherVar = SOME_VAR + 1; + OtherVar + } + + #[allow(non_snake_case, non_camel_case_types)] + pub struct some_type { + SOME_FIELD: u8, + SomeField: u16, + } + + #[allow(non_upper_case_globals)] + pub const some_const: u8 = 10; + + #[allow(non_upper_case_globals)] + pub static SomeStatic: u8 = 10; + "#, + ); + } } -- cgit v1.2.3