diff options
author | Igor Aleksanov <[email protected]> | 2020-10-03 12:47:46 +0100 |
---|---|---|
committer | Igor Aleksanov <[email protected]> | 2020-10-12 08:59:54 +0100 |
commit | 329626124f360feadb47e83be5690861c62a4b70 (patch) | |
tree | 56a1f8e3b351ae739b4c3c373ebd93ba81c18dc4 /crates/hir_ty/src | |
parent | 1773c6d154abe5da00b31bb16139addcaa443bbb (diff) |
Add check for structure names to be CamelCase
Diffstat (limited to 'crates/hir_ty/src')
-rw-r--r-- | crates/hir_ty/src/diagnostics.rs | 2 | ||||
-rw-r--r-- | crates/hir_ty/src/diagnostics/decl_check.rs | 138 |
2 files changed, 139 insertions, 1 deletions
diff --git a/crates/hir_ty/src/diagnostics.rs b/crates/hir_ty/src/diagnostics.rs index 24fff690a..bd370e3b2 100644 --- a/crates/hir_ty/src/diagnostics.rs +++ b/crates/hir_ty/src/diagnostics.rs | |||
@@ -257,7 +257,7 @@ impl fmt::Display for CaseType { | |||
257 | let repr = match self { | 257 | let repr = match self { |
258 | CaseType::LowerSnakeCase => "snake_case", | 258 | CaseType::LowerSnakeCase => "snake_case", |
259 | CaseType::UpperSnakeCase => "UPPER_SNAKE_CASE", | 259 | CaseType::UpperSnakeCase => "UPPER_SNAKE_CASE", |
260 | CaseType::UpperCamelCase => "UpperCamelCase", | 260 | CaseType::UpperCamelCase => "CamelCase", |
261 | }; | 261 | }; |
262 | 262 | ||
263 | write!(f, "{}", repr) | 263 | write!(f, "{}", repr) |
diff --git a/crates/hir_ty/src/diagnostics/decl_check.rs b/crates/hir_ty/src/diagnostics/decl_check.rs index 1a0906492..b7f511fd8 100644 --- a/crates/hir_ty/src/diagnostics/decl_check.rs +++ b/crates/hir_ty/src/diagnostics/decl_check.rs | |||
@@ -14,6 +14,7 @@ mod str_helpers; | |||
14 | use std::sync::Arc; | 14 | use std::sync::Arc; |
15 | 15 | ||
16 | use hir_def::{ | 16 | use hir_def::{ |
17 | adt::VariantData, | ||
17 | body::Body, | 18 | body::Body, |
18 | db::DefDatabase, | 19 | db::DefDatabase, |
19 | expr::{Expr, ExprId, UnaryOp}, | 20 | expr::{Expr, ExprId, UnaryOp}, |
@@ -205,6 +206,133 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
205 | 206 | ||
206 | fn validate_struct(&mut self, db: &dyn HirDatabase, struct_id: StructId) { | 207 | fn validate_struct(&mut self, db: &dyn HirDatabase, struct_id: StructId) { |
207 | let data = db.struct_data(struct_id); | 208 | let data = db.struct_data(struct_id); |
209 | |||
210 | // 1. Check the structure name. | ||
211 | let struct_name = data.name.to_string(); | ||
212 | let struct_name_replacement = if let Some(new_name) = to_camel_case(&struct_name) { | ||
213 | let replacement = Replacement { | ||
214 | current_name: data.name.clone(), | ||
215 | suggested_text: new_name, | ||
216 | expected_case: CaseType::UpperCamelCase, | ||
217 | }; | ||
218 | Some(replacement) | ||
219 | } else { | ||
220 | None | ||
221 | }; | ||
222 | |||
223 | // 2. Check the field names. | ||
224 | let mut struct_fields_replacements = Vec::new(); | ||
225 | |||
226 | if let VariantData::Record(fields) = data.variant_data.as_ref() { | ||
227 | for (_, field) in fields.iter() { | ||
228 | let field_name = field.name.to_string(); | ||
229 | if let Some(new_name) = to_lower_snake_case(&field_name) { | ||
230 | let replacement = Replacement { | ||
231 | current_name: field.name.clone(), | ||
232 | suggested_text: new_name, | ||
233 | expected_case: CaseType::LowerSnakeCase, | ||
234 | }; | ||
235 | struct_fields_replacements.push(replacement); | ||
236 | } | ||
237 | } | ||
238 | } | ||
239 | |||
240 | // 3. If there is at least one element to spawn a warning on, go to the source map and generate a warning. | ||
241 | self.create_incorrect_case_diagnostic_for_struct( | ||
242 | struct_id, | ||
243 | db, | ||
244 | struct_name_replacement, | ||
245 | struct_fields_replacements, | ||
246 | ) | ||
247 | } | ||
248 | |||
249 | /// Given the information about incorrect names in the struct declaration, looks up into the source code | ||
250 | /// for exact locations and adds diagnostics into the sink. | ||
251 | fn create_incorrect_case_diagnostic_for_struct( | ||
252 | &mut self, | ||
253 | struct_id: StructId, | ||
254 | db: &dyn HirDatabase, | ||
255 | struct_name_replacement: Option<Replacement>, | ||
256 | struct_fields_replacements: Vec<Replacement>, | ||
257 | ) { | ||
258 | // XXX: only look at sources if we do have incorrect names | ||
259 | if struct_name_replacement.is_none() && struct_fields_replacements.is_empty() { | ||
260 | return; | ||
261 | } | ||
262 | |||
263 | let struct_loc = struct_id.lookup(db.upcast()); | ||
264 | let struct_src = struct_loc.source(db.upcast()); | ||
265 | |||
266 | if let Some(replacement) = struct_name_replacement { | ||
267 | let ast_ptr = if let Some(name) = struct_src.value.name() { | ||
268 | name | ||
269 | } else { | ||
270 | // We don't want rust-analyzer to panic over this, but it is definitely some kind of error in the logic. | ||
271 | log::error!( | ||
272 | "Replacement ({:?}) was generated for a structure without a name: {:?}", | ||
273 | replacement, | ||
274 | struct_src | ||
275 | ); | ||
276 | return; | ||
277 | }; | ||
278 | |||
279 | let diagnostic = IncorrectCase { | ||
280 | file: struct_src.file_id, | ||
281 | ident_type: "Structure".to_string(), | ||
282 | ident: AstPtr::new(&ast_ptr).into(), | ||
283 | expected_case: replacement.expected_case, | ||
284 | ident_text: replacement.current_name.to_string(), | ||
285 | suggested_text: replacement.suggested_text, | ||
286 | }; | ||
287 | |||
288 | self.sink.push(diagnostic); | ||
289 | } | ||
290 | |||
291 | // let fn_params_list = match fn_src.value.param_list() { | ||
292 | // Some(params) => params, | ||
293 | // None => { | ||
294 | // if !fn_param_replacements.is_empty() { | ||
295 | // log::error!( | ||
296 | // "Replacements ({:?}) were generated for a function parameters which had no parameters list: {:?}", | ||
297 | // fn_param_replacements, fn_src | ||
298 | // ); | ||
299 | // } | ||
300 | // return; | ||
301 | // } | ||
302 | // }; | ||
303 | // let mut fn_params_iter = fn_params_list.params(); | ||
304 | // for param_to_rename in fn_param_replacements { | ||
305 | // // We assume that parameters in replacement are in the same order as in the | ||
306 | // // actual params list, but just some of them (ones that named correctly) are skipped. | ||
307 | // let ast_ptr = loop { | ||
308 | // match fn_params_iter.next() { | ||
309 | // Some(element) | ||
310 | // if pat_equals_to_name(element.pat(), ¶m_to_rename.current_name) => | ||
311 | // { | ||
312 | // break element.pat().unwrap() | ||
313 | // } | ||
314 | // Some(_) => {} | ||
315 | // None => { | ||
316 | // log::error!( | ||
317 | // "Replacement ({:?}) was generated for a function parameter which was not found: {:?}", | ||
318 | // param_to_rename, fn_src | ||
319 | // ); | ||
320 | // return; | ||
321 | // } | ||
322 | // } | ||
323 | // }; | ||
324 | |||
325 | // let diagnostic = IncorrectCase { | ||
326 | // file: fn_src.file_id, | ||
327 | // ident_type: "Argument".to_string(), | ||
328 | // ident: AstPtr::new(&ast_ptr).into(), | ||
329 | // expected_case: param_to_rename.expected_case, | ||
330 | // ident_text: param_to_rename.current_name.to_string(), | ||
331 | // suggested_text: param_to_rename.suggested_text, | ||
332 | // }; | ||
333 | |||
334 | // self.sink.push(diagnostic); | ||
335 | // } | ||
208 | } | 336 | } |
209 | 337 | ||
210 | fn validate_enum(&mut self, db: &dyn HirDatabase, enum_id: EnumId) { | 338 | fn validate_enum(&mut self, db: &dyn HirDatabase, enum_id: EnumId) { |
@@ -246,4 +374,14 @@ fn foo2(ok_param: &str, CAPS_PARAM: u8) {} | |||
246 | "#, | 374 | "#, |
247 | ); | 375 | ); |
248 | } | 376 | } |
377 | |||
378 | #[test] | ||
379 | fn incorrect_struct_name() { | ||
380 | check_diagnostics( | ||
381 | r#" | ||
382 | struct non_camel_case_name {} | ||
383 | // ^^^^^^^^^^^^^^^^^^^ Structure `non_camel_case_name` should have a CamelCase name, e.g. `NonCamelCaseName` | ||
384 | "#, | ||
385 | ); | ||
386 | } | ||
249 | } | 387 | } |