use hir::{db::AstDatabase, InFile}; use ide_assists::Assist; use ide_db::base_db::FilePosition; use syntax::AstNode; use crate::{ diagnostics::{unresolved_fix, Diagnostic, DiagnosticsContext}, references::rename::rename_with_semantics, Severity, }; // Diagnostic: incorrect-ident-case // // This diagnostic is triggered if an item name doesn't follow https://doc.rust-lang.org/1.0.0/style/style/naming/README.html[Rust naming convention]. pub(super) fn incorrect_case(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Diagnostic { Diagnostic::new( "incorrect-ident-case", format!( "{} `{}` should have {} name, e.g. `{}`", d.ident_type, d.ident_text, d.expected_case, d.suggested_text ), ctx.sema.diagnostics_display_range(InFile::new(d.file, d.ident.clone().into())).range, ) .severity(Severity::WeakWarning) .with_fixes(fixes(ctx, d)) } fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Option> { let root = ctx.sema.db.parse_or_expand(d.file)?; let name_node = d.ident.to_node(&root); let name_node = InFile::new(d.file, name_node.syntax()); let frange = name_node.original_file_range(ctx.sema.db); let file_position = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; let label = format!("Rename to {}", d.suggested_text); let mut res = unresolved_fix("change_case", &label, frange.range); if ctx.resolve.should_resolve(&res.id) { let source_change = rename_with_semantics(&ctx.sema, file_position, &d.suggested_text); res.source_change = Some(source_change.ok().unwrap_or_default()); } Some(vec![res]) } #[cfg(test)] mod change_case { use crate::{ diagnostics::tests::{check_diagnostics, check_fix}, fixture, AssistResolveStrategy, DiagnosticsConfig, }; #[test] fn test_rename_incorrect_case() { check_fix( r#" pub struct test_struct$0 { one: i32 } pub fn some_fn(val: test_struct) -> test_struct { test_struct { one: val.one + 1 } } "#, r#" pub struct TestStruct { one: i32 } pub fn some_fn(val: TestStruct) -> TestStruct { TestStruct { one: val.one + 1 } } "#, ); check_fix( r#" pub fn some_fn(NonSnakeCase$0: u8) -> u8 { NonSnakeCase } "#, r#" pub fn some_fn(non_snake_case: u8) -> u8 { non_snake_case } "#, ); check_fix( r#" pub fn SomeFn$0(val: u8) -> u8 { if val != 0 { SomeFn(val - 1) } else { val } } "#, r#" pub fn some_fn(val: u8) -> u8 { if val != 0 { some_fn(val - 1) } else { val } } "#, ); check_fix( r#" fn some_fn() { let whatAWeird_Formatting$0 = 10; another_func(whatAWeird_Formatting); } "#, r#" fn some_fn() { let what_a_weird_formatting = 10; another_func(what_a_weird_formatting); } "#, ); } #[test] fn test_uppercase_const_no_diagnostics() { check_diagnostics( r#" fn foo() { const ANOTHER_ITEM$0: &str = "some_item"; } "#, ); } #[test] fn test_rename_incorrect_case_struct_method() { check_fix( r#" pub struct TestStruct; impl TestStruct { pub fn SomeFn$0() -> TestStruct { TestStruct } } "#, r#" pub struct TestStruct; impl TestStruct { pub fn some_fn() -> TestStruct { TestStruct } } "#, ); } #[test] fn test_single_incorrect_case_diagnostic_in_function_name_issue_6970() { let input = r#"fn FOO$0() {}"#; let expected = r#"fn foo() {}"#; let (analysis, file_position) = fixture::position(input); let diagnostics = analysis .diagnostics( &DiagnosticsConfig::default(), AssistResolveStrategy::All, file_position.file_id, ) .unwrap(); assert_eq!(diagnostics.len(), 1); check_fix(input, expected); } }