Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/diagnostics/decl_check.rs')
-rw-r--r--crates/hir-ty/src/diagnostics/decl_check.rs125
1 files changed, 111 insertions, 14 deletions
diff --git a/crates/hir-ty/src/diagnostics/decl_check.rs b/crates/hir-ty/src/diagnostics/decl_check.rs
index 29da1b0c51..89d8c0e91d 100644
--- a/crates/hir-ty/src/diagnostics/decl_check.rs
+++ b/crates/hir-ty/src/diagnostics/decl_check.rs
@@ -17,8 +17,17 @@ use std::fmt;
use hir_def::{
AdtId, ConstId, EnumId, EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup,
- ModuleDefId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, attrs::AttrFlags,
- db::DefDatabase, hir::Pat, item_tree::FieldsShape, signatures::StaticFlags, src::HasSource,
+ ModuleDefId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, UnionId,
+ attrs::AttrFlags,
+ db::DefDatabase,
+ expr_store::Body,
+ hir::Pat,
+ item_tree::FieldsShape,
+ signatures::{
+ ConstSignature, EnumSignature, FunctionSignature, StaticFlags, StaticSignature,
+ StructSignature, TraitSignature, TypeAliasSignature, UnionSignature,
+ },
+ src::HasSource,
};
use hir_expand::{
HirFileId,
@@ -77,6 +86,7 @@ pub enum IdentType {
Structure,
Trait,
TypeAlias,
+ Union,
Variable,
Variant,
}
@@ -94,6 +104,7 @@ impl fmt::Display for IdentType {
IdentType::Structure => "Structure",
IdentType::Trait => "Trait",
IdentType::TypeAlias => "Type alias",
+ IdentType::Union => "Union",
IdentType::Variable => "Variable",
IdentType::Variant => "Variant",
};
@@ -146,9 +157,7 @@ impl<'a> DeclValidator<'a> {
match adt {
AdtId::StructId(struct_id) => self.validate_struct(struct_id),
AdtId::EnumId(enum_id) => self.validate_enum(enum_id),
- AdtId::UnionId(_) => {
- // FIXME: Unions aren't yet supported by this validator.
- }
+ AdtId::UnionId(union_id) => self.validate_union(union_id),
}
}
@@ -178,7 +187,7 @@ impl<'a> DeclValidator<'a> {
fn validate_trait(&mut self, trait_id: TraitId) {
// Check the trait name.
- let data = self.db.trait_signature(trait_id);
+ let data = TraitSignature::of(self.db, trait_id);
self.create_incorrect_case_diagnostic_for_item_name(
trait_id,
&data.name,
@@ -197,7 +206,7 @@ impl<'a> DeclValidator<'a> {
// Check the function name.
// Skipped if function is an associated item of a trait implementation.
if !self.is_trait_impl_container(container) {
- let data = self.db.function_signature(func);
+ let data = FunctionSignature::of(self.db, func);
// Don't run the lint on extern "[not Rust]" fn items with the
// #[no_mangle] attribute.
@@ -223,7 +232,7 @@ impl<'a> DeclValidator<'a> {
/// Check incorrect names for patterns inside the function body.
/// This includes function parameters except for trait implementation associated functions.
fn validate_func_body(&mut self, func: FunctionId) {
- let body = self.db.body(func.into());
+ let body = Body::of(self.db, func.into());
let edition = self.edition(func);
let mut pats_replacements = body
.pats()
@@ -250,7 +259,7 @@ impl<'a> DeclValidator<'a> {
return;
}
- let source_map = self.db.body_with_source_map(func.into()).1;
+ let source_map = &Body::with_source_map(self.db, func.into()).1;
for (id, replacement) in pats_replacements {
let Ok(source_ptr) = source_map.pat_syntax(id) else {
continue;
@@ -292,7 +301,7 @@ impl<'a> DeclValidator<'a> {
fn validate_struct(&mut self, struct_id: StructId) {
// Check the structure name.
- let data = self.db.struct_signature(struct_id);
+ let data = StructSignature::of(self.db, struct_id);
// rustc implementation excuses repr(C) since C structs predominantly don't
// use camel case.
@@ -383,9 +392,97 @@ impl<'a> DeclValidator<'a> {
}
}
+ fn validate_union(&mut self, union_id: UnionId) {
+ // Check the union name.
+ let data = UnionSignature::of(self.db, union_id);
+
+ // rustc implementation excuses repr(C) since C unions predominantly don't
+ // use camel case.
+ let has_repr_c = AttrFlags::repr(self.db, union_id.into()).is_some_and(|repr| repr.c());
+ if !has_repr_c {
+ self.create_incorrect_case_diagnostic_for_item_name(
+ union_id,
+ &data.name,
+ CaseType::UpperCamelCase,
+ IdentType::Union,
+ );
+ }
+
+ // Check the field names.
+ self.validate_union_fields(union_id);
+ }
+
+ /// Check incorrect names for union fields.
+ fn validate_union_fields(&mut self, union_id: UnionId) {
+ let data = union_id.fields(self.db);
+ let edition = self.edition(union_id);
+ let mut union_fields_replacements = data
+ .fields()
+ .iter()
+ .filter_map(|(_, field)| {
+ to_lower_snake_case(&field.name.display_no_db(edition).to_smolstr()).map(
+ |new_name| Replacement {
+ current_name: field.name.clone(),
+ suggested_text: new_name,
+ expected_case: CaseType::LowerSnakeCase,
+ },
+ )
+ })
+ .peekable();
+
+ // XXX: Only look at sources if we do have incorrect names.
+ if union_fields_replacements.peek().is_none() {
+ return;
+ }
+
+ let union_loc = union_id.lookup(self.db);
+ let union_src = union_loc.source(self.db);
+
+ let Some(union_fields_list) = union_src.value.record_field_list() else {
+ always!(
+ union_fields_replacements.peek().is_none(),
+ "Replacements ({:?}) were generated for a union fields \
+ which had no fields list: {:?}",
+ union_fields_replacements.collect::<Vec<_>>(),
+ union_src
+ );
+ return;
+ };
+ let mut union_fields_iter = union_fields_list.fields();
+ for field_replacement in union_fields_replacements {
+ // We assume that parameters in replacement are in the same order as in the
+ // actual params list, but just some of them (ones that named correctly) are skipped.
+ let field = loop {
+ if let Some(field) = union_fields_iter.next() {
+ let Some(field_name) = field.name() else {
+ continue;
+ };
+ if field_name.as_name() == field_replacement.current_name {
+ break field;
+ }
+ } else {
+ never!(
+ "Replacement ({:?}) was generated for a union field \
+ which was not found: {:?}",
+ field_replacement,
+ union_src
+ );
+ return;
+ }
+ };
+
+ self.create_incorrect_case_diagnostic_for_ast_node(
+ field_replacement,
+ union_src.file_id,
+ &field,
+ IdentType::Field,
+ );
+ }
+ }
+
fn validate_enum(&mut self, enum_id: EnumId) {
// Check the enum name.
- let data = self.db.enum_signature(enum_id);
+ let data = EnumSignature::of(self.db, enum_id);
// rustc implementation excuses repr(C) since C structs predominantly don't
// use camel case.
@@ -556,7 +653,7 @@ impl<'a> DeclValidator<'a> {
return;
}
- let data = self.db.const_signature(const_id);
+ let data = ConstSignature::of(self.db, const_id);
let Some(name) = &data.name else {
return;
};
@@ -569,7 +666,7 @@ impl<'a> DeclValidator<'a> {
}
fn validate_static(&mut self, static_id: StaticId) {
- let data = self.db.static_signature(static_id);
+ let data = StaticSignature::of(self.db, static_id);
if data.flags.contains(StaticFlags::EXTERN) {
cov_mark::hit!(extern_static_incorrect_case_ignored);
return;
@@ -595,7 +692,7 @@ impl<'a> DeclValidator<'a> {
}
// Check the type alias name.
- let data = self.db.type_alias_signature(type_alias_id);
+ let data = TypeAliasSignature::of(self.db, type_alias_id);
self.create_incorrect_case_diagnostic_for_item_name(
type_alias_id,
&data.name,