diff --git a/crates/hir-ty/src/diagnostics/decl_check.rs b/crates/hir-ty/src/diagnostics/decl_check.rs index 7ff06bb218..4909b82f88 100644 --- a/crates/hir-ty/src/diagnostics/decl_check.rs +++ b/crates/hir-ty/src/diagnostics/decl_check.rs @@ -20,7 +20,7 @@ use hir_def::{ hir::{Pat, PatId}, src::HasSource, AdtId, AttrDefId, ConstId, EnumId, FunctionId, ItemContainerId, Lookup, ModuleDefId, ModuleId, - StaticId, StructId, TraitId, + StaticId, StructId, TraitId, TypeAliasId, }; use hir_expand::{ name::{AsName, Name}, @@ -84,6 +84,7 @@ pub enum IdentType { StaticVariable, Structure, Trait, + TypeAlias, Variable, Variant, } @@ -100,6 +101,7 @@ impl fmt::Display for IdentType { IdentType::StaticVariable => "Static variable", IdentType::Structure => "Structure", IdentType::Trait => "Trait", + IdentType::TypeAlias => "Type alias", IdentType::Variable => "Variable", IdentType::Variant => "Variant", }; @@ -143,6 +145,7 @@ impl<'a> DeclValidator<'a> { ModuleDefId::AdtId(adt) => self.validate_adt(adt), ModuleDefId::ConstId(const_id) => self.validate_const(const_id), ModuleDefId::StaticId(static_id) => self.validate_static(static_id), + ModuleDefId::TypeAliasId(type_alias_id) => self.validate_type_alias(type_alias_id), _ => (), } } @@ -845,6 +848,54 @@ impl<'a> DeclValidator<'a> { self.sink.push(diagnostic); } + fn validate_type_alias(&mut self, type_alias_id: TypeAliasId) { + let container = type_alias_id.lookup(self.db.upcast()).container; + if self.is_trait_impl_container(container) { + cov_mark::hit!(trait_impl_assoc_type_incorrect_case_ignored); + return; + } + + // Check whether non-snake case identifiers are allowed for this type alias. + if self.allowed(type_alias_id.into(), allow::NON_CAMEL_CASE_TYPES, false) { + return; + } + + // Check the type alias name. + let data = self.db.type_alias_data(type_alias_id); + let type_alias_name = data.name.display(self.db.upcast()).to_string(); + let type_alias_name_replacement = + to_camel_case(&type_alias_name).map(|new_name| Replacement { + current_name: data.name.clone(), + suggested_text: new_name, + expected_case: CaseType::UpperCamelCase, + }); + + if let Some(replacement) = type_alias_name_replacement { + let type_alias_loc = type_alias_id.lookup(self.db.upcast()); + let type_alias_src = type_alias_loc.source(self.db.upcast()); + + let Some(ast_ptr) = type_alias_src.value.name() else { + never!( + "Replacement ({:?}) was generated for a type alias without a name: {:?}", + replacement, + type_alias_src + ); + return; + }; + + let diagnostic = IncorrectCase { + file: type_alias_src.file_id, + ident_type: IdentType::TypeAlias, + ident: AstPtr::new(&ast_ptr), + expected_case: replacement.expected_case, + ident_text: type_alias_name, + suggested_text: replacement.suggested_text, + }; + + self.sink.push(diagnostic); + } + } + fn is_trait_impl_container(&self, container_id: ItemContainerId) -> bool { if let ItemContainerId::ImplId(impl_id) = container_id { if self.db.impl_trait(impl_id).is_some() { diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index a741da78d9..997cb43643 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -565,13 +565,7 @@ impl Module { } for item in t.items(db) { - let def: DefWithBody = match item { - AssocItem::Function(it) => it.into(), - AssocItem::Const(it) => it.into(), - AssocItem::TypeAlias(_) => continue, - }; - - def.diagnostics(db, acc); + item.diagnostics(db, acc); } acc.extend(def.diagnostics(db)) @@ -741,13 +735,7 @@ impl Module { } for &item in &db.impl_data(impl_def.id).items { - let def: DefWithBody = match AssocItem::from(item) { - AssocItem::Function(it) => it.into(), - AssocItem::Const(it) => it.into(), - AssocItem::TypeAlias(_) => continue, - }; - - def.diagnostics(db, acc); + AssocItem::from(item).diagnostics(db, acc); } } } @@ -2662,6 +2650,22 @@ impl AssocItem { _ => None, } } + + pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec) { + match self { + AssocItem::Function(func) => { + DefWithBody::from(func).diagnostics(db, acc); + } + AssocItem::Const(const_) => { + DefWithBody::from(const_).diagnostics(db, acc); + } + AssocItem::TypeAlias(type_alias) => { + for diag in hir_ty::diagnostics::incorrect_case(db, type_alias.id.into()) { + acc.push(diag.into()); + } + } + } + } } impl HasVisibility for AssocItem { diff --git a/crates/ide-diagnostics/src/handlers/incorrect_case.rs b/crates/ide-diagnostics/src/handlers/incorrect_case.rs index 84929c63d1..59031e44cc 100644 --- a/crates/ide-diagnostics/src/handlers/incorrect_case.rs +++ b/crates/ide-diagnostics/src/handlers/incorrect_case.rs @@ -470,6 +470,8 @@ trait BAD_TRAIT { // ^^^^^^^^^ 💡 warn: Trait `BAD_TRAIT` should have CamelCase name, e.g. `BadTrait` const bad_const: u8; // ^^^^^^^^^ 💡 warn: Constant `bad_const` should have UPPER_SNAKE_CASE name, e.g. `BAD_CONST` + type BAD_TYPE; + // ^^^^^^^^ 💡 warn: Type alias `BAD_TYPE` should have CamelCase name, e.g. `BadType` fn BAD_FUNCTION(); // ^^^^^^^^^^^^ 💡 warn: Function `BAD_FUNCTION` should have snake_case name, e.g. `bad_function` fn BadFunction(); @@ -482,6 +484,7 @@ trait BAD_TRAIT { #[test] fn no_diagnostics_for_trait_impl_assoc_items_except_pats_in_body() { cov_mark::check!(trait_impl_assoc_const_incorrect_case_ignored); + cov_mark::check!(trait_impl_assoc_type_incorrect_case_ignored); cov_mark::check_count!(trait_impl_assoc_func_name_incorrect_case_ignored, 2); cov_mark::check!(trait_impl_assoc_func_param_incorrect_case_ignored); check_diagnostics_with_disabled( @@ -490,6 +493,8 @@ trait BAD_TRAIT { // ^^^^^^^^^ 💡 warn: Trait `BAD_TRAIT` should have CamelCase name, e.g. `BadTrait` const bad_const: u8; // ^^^^^^^^^ 💡 warn: Constant `bad_const` should have UPPER_SNAKE_CASE name, e.g. `BAD_CONST` + type BAD_TYPE; + // ^^^^^^^^ 💡 warn: Type alias `BAD_TYPE` should have CamelCase name, e.g. `BadType` fn BAD_FUNCTION(BAD_PARAM: u8); // ^^^^^^^^^^^^ 💡 warn: Function `BAD_FUNCTION` should have snake_case name, e.g. `bad_function` // ^^^^^^^^^ 💡 warn: Parameter `BAD_PARAM` should have snake_case name, e.g. `bad_param` @@ -499,6 +504,7 @@ trait BAD_TRAIT { impl BAD_TRAIT for () { const bad_const: u8 = 1; + type BAD_TYPE = (); fn BAD_FUNCTION(BAD_PARAM: u8) { let BAD_VAR = 10; // ^^^^^^^ 💡 warn: Variable `BAD_VAR` should have snake_case name, e.g. `bad_var`