about summary refs log tree commit diff
diff options
context:
space:
mode:
authordavidsemakula <hello@davidsemakula.com>2024-02-02 13:16:49 +0300
committerdavidsemakula <hello@davidsemakula.com>2024-02-02 17:07:08 +0300
commit080d223dd4775cddebe496d86c419d72ab52e4e5 (patch)
treec0440bbddf1efac518514d65a37daf056ed6063d
parent0ffc088439fbbb31992016cc9cc348c2156d19b4 (diff)
downloadrust-080d223dd4775cddebe496d86c419d72ab52e4e5.tar.gz
rust-080d223dd4775cddebe496d86c419d72ab52e4e5.zip
incorrect case diagnostics for type aliases
-rw-r--r--crates/hir-ty/src/diagnostics/decl_check.rs53
-rw-r--r--crates/hir/src/lib.rs32
-rw-r--r--crates/ide-diagnostics/src/handlers/incorrect_case.rs6
3 files changed, 76 insertions, 15 deletions
diff --git a/crates/hir-ty/src/diagnostics/decl_check.rs b/crates/hir-ty/src/diagnostics/decl_check.rs
index 7ff06bb218c..4909b82f88c 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 a741da78d9c..997cb436432 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<AnyDiagnostic>) {
+        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 84929c63d15..59031e44cc8 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`