mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-01 14:21:44 +00:00
Check for allow(..) attributes for case diagnostic
This commit is contained in:
parent
245e1b533b
commit
bdfe12df8f
1 changed files with 91 additions and 25 deletions
|
@ -16,7 +16,7 @@ use hir_def::{
|
||||||
adt::VariantData,
|
adt::VariantData,
|
||||||
expr::{Pat, PatId},
|
expr::{Pat, PatId},
|
||||||
src::HasSource,
|
src::HasSource,
|
||||||
AdtId, ConstId, EnumId, FunctionId, Lookup, ModuleDefId, StaticId, StructId,
|
AdtId, AttrDefId, ConstId, EnumId, FunctionId, Lookup, ModuleDefId, StaticId, StructId,
|
||||||
};
|
};
|
||||||
use hir_expand::{
|
use hir_expand::{
|
||||||
diagnostics::DiagnosticSink,
|
diagnostics::DiagnosticSink,
|
||||||
|
@ -32,6 +32,12 @@ use crate::{
|
||||||
diagnostics::{decl_check::case_conv::*, CaseType, IncorrectCase},
|
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> {
|
pub(super) struct DeclValidator<'a, 'b: 'a> {
|
||||||
owner: ModuleDefId,
|
owner: ModuleDefId,
|
||||||
sink: &'a mut DiagnosticSink<'b>,
|
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) {
|
fn validate_func(&mut self, db: &dyn HirDatabase, func: FunctionId) {
|
||||||
let data = db.function_data(func);
|
let data = db.function_data(func);
|
||||||
let body = db.body(func.into());
|
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 function_name = data.name.to_string();
|
||||||
let fn_name_replacement = if let Some(new_name) = to_lower_snake_case(&function_name) {
|
let fn_name_replacement = if let Some(new_name) = to_lower_snake_case(&function_name) {
|
||||||
let replacement = Replacement {
|
let replacement = Replacement {
|
||||||
|
@ -89,7 +113,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
// 2. Check the param names.
|
// 3. Check the param names.
|
||||||
let mut fn_param_replacements = Vec::new();
|
let mut fn_param_replacements = Vec::new();
|
||||||
|
|
||||||
for pat_id in body.params.iter().cloned() {
|
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();
|
let mut pats_replacements = Vec::new();
|
||||||
|
|
||||||
for (pat_idx, pat) in body.pats.iter() {
|
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(
|
self.create_incorrect_case_diagnostic_for_func(
|
||||||
func,
|
func,
|
||||||
db,
|
db,
|
||||||
|
@ -144,12 +168,6 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
|
||||||
fn_param_replacements,
|
fn_param_replacements,
|
||||||
);
|
);
|
||||||
self.create_incorrect_case_diagnostic_for_variables(func, db, pats_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
|
/// 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) {
|
fn validate_struct(&mut self, db: &dyn HirDatabase, struct_id: StructId) {
|
||||||
let data = db.struct_data(struct_id);
|
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.
|
// 1. Check the structure name.
|
||||||
let struct_name = data.name.to_string();
|
let struct_name = data.name.to_string();
|
||||||
let struct_name_replacement = if let Some(new_name) = to_camel_case(&struct_name) {
|
let struct_name_replacement = if let Some(new_name) = to_camel_case(&struct_name) {
|
||||||
|
@ -320,14 +342,19 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
|
||||||
suggested_text: new_name,
|
suggested_text: new_name,
|
||||||
expected_case: CaseType::UpperCamelCase,
|
expected_case: CaseType::UpperCamelCase,
|
||||||
};
|
};
|
||||||
|
if !non_camel_case_allowed {
|
||||||
Some(replacement)
|
Some(replacement)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
// 2. Check the field names.
|
// 2. Check the field names.
|
||||||
let mut struct_fields_replacements = Vec::new();
|
let mut struct_fields_replacements = Vec::new();
|
||||||
|
|
||||||
|
if !non_snake_case_allowed {
|
||||||
if let VariantData::Record(fields) = data.variant_data.as_ref() {
|
if let VariantData::Record(fields) = data.variant_data.as_ref() {
|
||||||
for (_, field) in fields.iter() {
|
for (_, field) in fields.iter() {
|
||||||
let field_name = field.name.to_string();
|
let field_name = field.name.to_string();
|
||||||
|
@ -341,6 +368,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.
|
// 3. 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_struct(
|
self.create_incorrect_case_diagnostic_for_struct(
|
||||||
|
@ -442,7 +470,12 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
|
||||||
fn validate_enum(&mut self, db: &dyn HirDatabase, enum_id: EnumId) {
|
fn validate_enum(&mut self, db: &dyn HirDatabase, enum_id: EnumId) {
|
||||||
let data = db.enum_data(enum_id);
|
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 = data.name.to_string();
|
||||||
let enum_name_replacement = if let Some(new_name) = to_camel_case(&enum_name) {
|
let enum_name_replacement = if let Some(new_name) = to_camel_case(&enum_name) {
|
||||||
let replacement = Replacement {
|
let replacement = Replacement {
|
||||||
|
@ -455,7 +488,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
// 2. Check the field names.
|
// 3. Check the field names.
|
||||||
let mut enum_fields_replacements = Vec::new();
|
let mut enum_fields_replacements = Vec::new();
|
||||||
|
|
||||||
for (_, variant) in data.variants.iter() {
|
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(
|
self.create_incorrect_case_diagnostic_for_enum(
|
||||||
enum_id,
|
enum_id,
|
||||||
db,
|
db,
|
||||||
|
@ -572,6 +605,10 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
|
||||||
fn validate_const(&mut self, db: &dyn HirDatabase, const_id: ConstId) {
|
fn validate_const(&mut self, db: &dyn HirDatabase, const_id: ConstId) {
|
||||||
let data = db.const_data(const_id);
|
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 {
|
let name = match &data.name {
|
||||||
Some(name) => name,
|
Some(name) => name,
|
||||||
None => return,
|
None => return,
|
||||||
|
@ -612,6 +649,10 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
|
||||||
fn validate_static(&mut self, db: &dyn HirDatabase, static_id: StaticId) {
|
fn validate_static(&mut self, db: &dyn HirDatabase, static_id: StaticId) {
|
||||||
let data = db.static_data(static_id);
|
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 {
|
let name = match &data.name {
|
||||||
Some(name) => name,
|
Some(name) => name,
|
||||||
None => return,
|
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;
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue