diff options
author | Igor Aleksanov <[email protected]> | 2020-10-03 16:01:25 +0100 |
---|---|---|
committer | Igor Aleksanov <[email protected]> | 2020-10-12 09:05:00 +0100 |
commit | fb96bba87895c062a78e6599cea161e461ff607d (patch) | |
tree | 17a4970bc13ccf9074cc1c8a60e8bf4e2d73c475 /crates | |
parent | e24e22f288eba33928a9e579f13653d6f04fcdfa (diff) |
Add diagnostics for enum names and variants
Diffstat (limited to 'crates')
-rw-r--r-- | crates/hir_ty/src/diagnostics.rs | 2 | ||||
-rw-r--r-- | crates/hir_ty/src/diagnostics/decl_check.rs | 147 | ||||
-rw-r--r-- | crates/ide/src/diagnostics.rs | 26 |
3 files changed, 173 insertions, 2 deletions
diff --git a/crates/hir_ty/src/diagnostics.rs b/crates/hir_ty/src/diagnostics.rs index 66762b90e..bd370e3b2 100644 --- a/crates/hir_ty/src/diagnostics.rs +++ b/crates/hir_ty/src/diagnostics.rs | |||
@@ -298,7 +298,7 @@ impl Diagnostic for IncorrectCase { | |||
298 | } | 298 | } |
299 | 299 | ||
300 | fn is_experimental(&self) -> bool { | 300 | fn is_experimental(&self) -> bool { |
301 | false | 301 | true |
302 | } | 302 | } |
303 | } | 303 | } |
304 | 304 | ||
diff --git a/crates/hir_ty/src/diagnostics/decl_check.rs b/crates/hir_ty/src/diagnostics/decl_check.rs index 260aa9607..7fc9c564e 100644 --- a/crates/hir_ty/src/diagnostics/decl_check.rs +++ b/crates/hir_ty/src/diagnostics/decl_check.rs | |||
@@ -315,7 +315,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
315 | Some(_) => {} | 315 | Some(_) => {} |
316 | None => { | 316 | None => { |
317 | log::error!( | 317 | log::error!( |
318 | "Replacement ({:?}) was generated for a function parameter which was not found: {:?}", | 318 | "Replacement ({:?}) was generated for a structure field which was not found: {:?}", |
319 | field_to_rename, struct_src | 319 | field_to_rename, struct_src |
320 | ); | 320 | ); |
321 | return; | 321 | return; |
@@ -338,6 +338,131 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
338 | 338 | ||
339 | fn validate_enum(&mut self, db: &dyn HirDatabase, enum_id: EnumId) { | 339 | fn validate_enum(&mut self, db: &dyn HirDatabase, enum_id: EnumId) { |
340 | let data = db.enum_data(enum_id); | 340 | let data = db.enum_data(enum_id); |
341 | |||
342 | // 1. Check the enum name. | ||
343 | let enum_name = data.name.to_string(); | ||
344 | let enum_name_replacement = if let Some(new_name) = to_camel_case(&enum_name) { | ||
345 | let replacement = Replacement { | ||
346 | current_name: data.name.clone(), | ||
347 | suggested_text: new_name, | ||
348 | expected_case: CaseType::UpperCamelCase, | ||
349 | }; | ||
350 | Some(replacement) | ||
351 | } else { | ||
352 | None | ||
353 | }; | ||
354 | |||
355 | // 2. Check the field names. | ||
356 | let mut enum_fields_replacements = Vec::new(); | ||
357 | |||
358 | for (_, variant) in data.variants.iter() { | ||
359 | let variant_name = variant.name.to_string(); | ||
360 | if let Some(new_name) = to_camel_case(&variant_name) { | ||
361 | let replacement = Replacement { | ||
362 | current_name: variant.name.clone(), | ||
363 | suggested_text: new_name, | ||
364 | expected_case: CaseType::UpperCamelCase, | ||
365 | }; | ||
366 | enum_fields_replacements.push(replacement); | ||
367 | } | ||
368 | } | ||
369 | |||
370 | // 3. If there is at least one element to spawn a warning on, go to the source map and generate a warning. | ||
371 | self.create_incorrect_case_diagnostic_for_enum( | ||
372 | enum_id, | ||
373 | db, | ||
374 | enum_name_replacement, | ||
375 | enum_fields_replacements, | ||
376 | ) | ||
377 | } | ||
378 | |||
379 | /// Given the information about incorrect names in the struct declaration, looks up into the source code | ||
380 | /// for exact locations and adds diagnostics into the sink. | ||
381 | fn create_incorrect_case_diagnostic_for_enum( | ||
382 | &mut self, | ||
383 | enum_id: EnumId, | ||
384 | db: &dyn HirDatabase, | ||
385 | enum_name_replacement: Option<Replacement>, | ||
386 | enum_variants_replacements: Vec<Replacement>, | ||
387 | ) { | ||
388 | // XXX: only look at sources if we do have incorrect names | ||
389 | if enum_name_replacement.is_none() && enum_variants_replacements.is_empty() { | ||
390 | return; | ||
391 | } | ||
392 | |||
393 | let enum_loc = enum_id.lookup(db.upcast()); | ||
394 | let enum_src = enum_loc.source(db.upcast()); | ||
395 | |||
396 | if let Some(replacement) = enum_name_replacement { | ||
397 | let ast_ptr = if let Some(name) = enum_src.value.name() { | ||
398 | name | ||
399 | } else { | ||
400 | // We don't want rust-analyzer to panic over this, but it is definitely some kind of error in the logic. | ||
401 | log::error!( | ||
402 | "Replacement ({:?}) was generated for a enum without a name: {:?}", | ||
403 | replacement, | ||
404 | enum_src | ||
405 | ); | ||
406 | return; | ||
407 | }; | ||
408 | |||
409 | let diagnostic = IncorrectCase { | ||
410 | file: enum_src.file_id, | ||
411 | ident_type: "Enum".to_string(), | ||
412 | ident: AstPtr::new(&ast_ptr).into(), | ||
413 | expected_case: replacement.expected_case, | ||
414 | ident_text: replacement.current_name.to_string(), | ||
415 | suggested_text: replacement.suggested_text, | ||
416 | }; | ||
417 | |||
418 | self.sink.push(diagnostic); | ||
419 | } | ||
420 | |||
421 | let enum_variants_list = match enum_src.value.variant_list() { | ||
422 | Some(variants) => variants, | ||
423 | _ => { | ||
424 | if !enum_variants_replacements.is_empty() { | ||
425 | log::error!( | ||
426 | "Replacements ({:?}) were generated for a enum variants which had no fields list: {:?}", | ||
427 | enum_variants_replacements, enum_src | ||
428 | ); | ||
429 | } | ||
430 | return; | ||
431 | } | ||
432 | }; | ||
433 | let mut enum_variants_iter = enum_variants_list.variants(); | ||
434 | for variant_to_rename in enum_variants_replacements { | ||
435 | // We assume that parameters in replacement are in the same order as in the | ||
436 | // actual params list, but just some of them (ones that named correctly) are skipped. | ||
437 | let ast_ptr = loop { | ||
438 | match enum_variants_iter.next() { | ||
439 | Some(variant) | ||
440 | if names_equal(variant.name(), &variant_to_rename.current_name) => | ||
441 | { | ||
442 | break variant.name().unwrap() | ||
443 | } | ||
444 | Some(_) => {} | ||
445 | None => { | ||
446 | log::error!( | ||
447 | "Replacement ({:?}) was generated for a enum variant which was not found: {:?}", | ||
448 | variant_to_rename, enum_src | ||
449 | ); | ||
450 | return; | ||
451 | } | ||
452 | } | ||
453 | }; | ||
454 | |||
455 | let diagnostic = IncorrectCase { | ||
456 | file: enum_src.file_id, | ||
457 | ident_type: "Variant".to_string(), | ||
458 | ident: AstPtr::new(&ast_ptr).into(), | ||
459 | expected_case: variant_to_rename.expected_case, | ||
460 | ident_text: variant_to_rename.current_name.to_string(), | ||
461 | suggested_text: variant_to_rename.suggested_text, | ||
462 | }; | ||
463 | |||
464 | self.sink.push(diagnostic); | ||
465 | } | ||
341 | } | 466 | } |
342 | } | 467 | } |
343 | 468 | ||
@@ -403,4 +528,24 @@ struct SomeStruct { SomeField: u8 } | |||
403 | "#, | 528 | "#, |
404 | ); | 529 | ); |
405 | } | 530 | } |
531 | |||
532 | #[test] | ||
533 | fn incorrect_enum_name() { | ||
534 | check_diagnostics( | ||
535 | r#" | ||
536 | enum some_enum { Val(u8) } | ||
537 | // ^^^^^^^^^ Enum `some_enum` should have a CamelCase name, e.g. `SomeEnum` | ||
538 | "#, | ||
539 | ); | ||
540 | } | ||
541 | |||
542 | #[test] | ||
543 | fn incorrect_enum_variant_name() { | ||
544 | check_diagnostics( | ||
545 | r#" | ||
546 | enum SomeEnum { SOME_VARIANT(u8) } | ||
547 | // ^^^^^^^^^^^^ Variant `SOME_VARIANT` should have a CamelCase name, e.g. `SomeVariant` | ||
548 | "#, | ||
549 | ); | ||
550 | } | ||
406 | } | 551 | } |
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index 71ab98c1f..ad1b265fd 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs | |||
@@ -853,5 +853,31 @@ pub fn some_fn(val: TestStruct) -> TestStruct { | |||
853 | } | 853 | } |
854 | "#, | 854 | "#, |
855 | ); | 855 | ); |
856 | |||
857 | check_fixes( | ||
858 | r#" | ||
859 | pub fn some_fn(NonSnakeCase<|>: u8) -> u8 { | ||
860 | NonSnakeCase | ||
861 | } | ||
862 | "#, | ||
863 | r#" | ||
864 | pub fn some_fn(non_snake_case: u8) -> u8 { | ||
865 | non_snake_case | ||
866 | } | ||
867 | "#, | ||
868 | ); | ||
869 | |||
870 | check_fixes( | ||
871 | r#" | ||
872 | pub fn SomeFn<|>(val: u8) -> u8 { | ||
873 | if val != 0 { SomeFn(val - 1) } else { val } | ||
874 | } | ||
875 | "#, | ||
876 | r#" | ||
877 | pub fn some_fn(val: u8) -> u8 { | ||
878 | if val != 0 { some_fn(val - 1) } else { val } | ||
879 | } | ||
880 | "#, | ||
881 | ); | ||
856 | } | 882 | } |
857 | } | 883 | } |