From e24e22f288eba33928a9e579f13653d6f04fcdfa Mon Sep 17 00:00:00 2001 From: Igor Aleksanov Date: Sat, 3 Oct 2020 17:34:52 +0300 Subject: Add fix for incorrect case diagnostic --- crates/ide/src/diagnostics.rs | 64 +++++++++++++++++++++++++++++++++++++ crates/ide/src/diagnostics/fixes.rs | 20 ++++++++++-- crates/ide/src/references.rs | 2 +- crates/ide/src/references/rename.rs | 7 ++++ 4 files changed, 90 insertions(+), 3 deletions(-) (limited to 'crates/ide/src') diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index f5d627b6e..71ab98c1f 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs @@ -96,6 +96,9 @@ pub(crate) fn diagnostics( .on::(|d| { res.borrow_mut().push(diagnostic_with_fix(d, &sema)); }) + .on::(|d| { + res.borrow_mut().push(warning_with_fix(d, &sema)); + }) // Only collect experimental diagnostics when they're enabled. .filter(|diag| !(diag.is_experimental() && config.disable_experimental)) .filter(|diag| !config.disabled.contains(diag.code().as_str())); @@ -130,6 +133,16 @@ fn diagnostic_with_fix(d: &D, sema: &Semantics(d: &D, sema: &Semantics) -> Diagnostic { + Diagnostic { + // name: Some(d.name().into()), + range: sema.diagnostics_display_range(d).range, + message: d.message(), + severity: Severity::WeakWarning, + fix: d.fix(&sema), + } +} + fn check_unnecessary_braces_in_use_statement( acc: &mut Vec, file_id: FileId, @@ -253,6 +266,37 @@ mod tests { ); } + /// Similar to `check_fix`, but applies all the available fixes. + fn check_fixes(ra_fixture_before: &str, ra_fixture_after: &str) { + let after = trim_indent(ra_fixture_after); + + let (analysis, file_position) = fixture::position(ra_fixture_before); + let diagnostic = analysis + .diagnostics(&DiagnosticsConfig::default(), file_position.file_id) + .unwrap() + .pop() + .unwrap(); + let fix = diagnostic.fix.unwrap(); + let target_file_contents = analysis.file_text(file_position.file_id).unwrap(); + let actual = { + let mut actual = target_file_contents.to_string(); + // Go from the last one to the first one, so that ranges won't be affected by previous edits. + for edit in fix.source_change.source_file_edits.iter().rev() { + edit.edit.apply(&mut actual); + } + actual + }; + + assert_eq_text!(&after, &actual); + assert!( + fix.fix_trigger_range.start() <= file_position.offset + && fix.fix_trigger_range.end() >= file_position.offset, + "diagnostic fix range {:?} does not touch cursor position {:?}", + fix.fix_trigger_range, + file_position.offset + ); + } + /// Checks that a diagnostic applies to the file containing the `<|>` cursor marker /// which has a fix that can apply to other files. fn check_apply_diagnostic_fix_in_other_file(ra_fixture_before: &str, ra_fixture_after: &str) { @@ -790,4 +834,24 @@ struct Foo { let diagnostics = analysis.diagnostics(&DiagnosticsConfig::default(), file_id).unwrap(); assert!(!diagnostics.is_empty()); } + + #[test] + fn test_rename_incorrect_case() { + check_fixes( + r#" +pub struct test_struct<|> { 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 } +} +"#, + ); + } } diff --git a/crates/ide/src/diagnostics/fixes.rs b/crates/ide/src/diagnostics/fixes.rs index 68ae1c239..286ef0785 100644 --- a/crates/ide/src/diagnostics/fixes.rs +++ b/crates/ide/src/diagnostics/fixes.rs @@ -3,7 +3,10 @@ use base_db::FileId; use hir::{ db::AstDatabase, - diagnostics::{Diagnostic, MissingFields, MissingOkInTailExpr, NoSuchField, UnresolvedModule}, + diagnostics::{ + Diagnostic, IncorrectCase, MissingFields, MissingOkInTailExpr, NoSuchField, + UnresolvedModule, + }, HasSource, HirDisplay, Semantics, VariantDef, }; use ide_db::{ @@ -17,7 +20,7 @@ use syntax::{ }; use text_edit::TextEdit; -use crate::diagnostics::Fix; +use crate::{diagnostics::Fix, references::rename::rename_with_semantics, FilePosition}; /// A [Diagnostic] that potentially has a fix available. /// @@ -99,6 +102,19 @@ impl DiagnosticWithFix for MissingOkInTailExpr { } } +impl DiagnosticWithFix for IncorrectCase { + fn fix(&self, sema: &Semantics) -> Option { + let file_id = self.file.original_file(sema.db); + let offset = self.ident.text_range().start(); + let file_position = FilePosition { file_id, offset }; + + let rename_changes = rename_with_semantics(sema, file_position, &self.suggested_text)?; + + let label = format!("Rename to {}", self.suggested_text); + Some(Fix::new(&label, rename_changes.info, rename_changes.range)) + } +} + fn missing_record_expr_field_fix( sema: &Semantics, usage_file_id: FileId, diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index f65a05ea3..88e2f2db3 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs @@ -9,7 +9,7 @@ //! at the index that the match starts at and its tree parent is //! resolved to the search element definition, we get a reference. -mod rename; +pub(crate) mod rename; use hir::Semantics; use ide_db::{ diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs index f3b5cfc8c..f9a11e43d 100644 --- a/crates/ide/src/references/rename.rs +++ b/crates/ide/src/references/rename.rs @@ -42,7 +42,14 @@ pub(crate) fn rename( new_name: &str, ) -> Result, RenameError> { let sema = Semantics::new(db); + rename_with_semantics(&sema, position, new_name) +} +pub(crate) fn rename_with_semantics( + sema: &Semantics, + position: FilePosition, + new_name: &str, +) -> Result, RenameError> { match lex_single_syntax_kind(new_name) { Some(res) => match res { (SyntaxKind::IDENT, _) => (), -- cgit v1.2.3 From fb96bba87895c062a78e6599cea161e461ff607d Mon Sep 17 00:00:00 2001 From: Igor Aleksanov Date: Sat, 3 Oct 2020 18:01:25 +0300 Subject: Add diagnostics for enum names and variants --- crates/ide/src/diagnostics.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'crates/ide/src') 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 @@ -851,6 +851,32 @@ pub struct TestStruct { one: i32 } pub fn some_fn(val: TestStruct) -> TestStruct { TestStruct { one: val.one + 1 } } +"#, + ); + + check_fixes( + r#" +pub fn some_fn(NonSnakeCase<|>: u8) -> u8 { + NonSnakeCase +} +"#, + r#" +pub fn some_fn(non_snake_case: u8) -> u8 { + non_snake_case +} +"#, + ); + + check_fixes( + r#" +pub fn SomeFn<|>(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 } +} "#, ); } -- cgit v1.2.3 From b42562b5dee4f4ce23094c7bab6406e3b00f90ad Mon Sep 17 00:00:00 2001 From: Igor Aleksanov Date: Sun, 4 Oct 2020 07:39:35 +0300 Subject: Make incorrect case diagnostic work inside of functions --- crates/ide/src/diagnostics.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'crates/ide/src') diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index ad1b265fd..70d5cbd38 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs @@ -877,6 +877,32 @@ pub fn SomeFn<|>(val: u8) -> u8 { pub fn some_fn(val: u8) -> u8 { if val != 0 { some_fn(val - 1) } else { val } } +"#, + ); + + check_fixes( + r#" +fn some_fn() { + let whatAWeird_Formatting<|> = 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_no_diagnostics( + r#" +fn foo() { + const ANOTHER_ITEM<|>: &str = "some_item"; +} "#, ); } -- cgit v1.2.3 From 50a147dcdfd0df462f0c24e5d7bcfe60abadac32 Mon Sep 17 00:00:00 2001 From: Igor Aleksanov Date: Sun, 4 Oct 2020 09:12:00 +0300 Subject: Apply case check diagnostic to impl items --- crates/ide/src/diagnostics.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'crates/ide/src') diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index 70d5cbd38..8e508639b 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs @@ -903,6 +903,30 @@ fn some_fn() { fn foo() { const ANOTHER_ITEM<|>: &str = "some_item"; } +"#, + ); + } + + #[test] + fn test_rename_incorrect_case_struct_method() { + check_fixes( + r#" +pub struct TestStruct; + +impl TestStruct { + pub fn SomeFn<|>() -> TestStruct { + TestStruct + } +} +"#, + r#" +pub struct TestStruct; + +impl TestStruct { + pub fn some_fn() -> TestStruct { + TestStruct + } +} "#, ); } -- cgit v1.2.3 From 9ea57cac0e9779ac0749ef568eeb3977fe3adacd Mon Sep 17 00:00:00 2001 From: Igor Aleksanov Date: Sun, 4 Oct 2020 09:26:39 +0300 Subject: Fix code style issues --- crates/ide/src/diagnostics.rs | 1 - 1 file changed, 1 deletion(-) (limited to 'crates/ide/src') diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index 8e508639b..add102ff2 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs @@ -135,7 +135,6 @@ fn diagnostic_with_fix(d: &D, sema: &Semantics(d: &D, sema: &Semantics) -> Diagnostic { Diagnostic { - // name: Some(d.name().into()), range: sema.diagnostics_display_range(d).range, message: d.message(), severity: Severity::WeakWarning, -- cgit v1.2.3 From fb0ab9f7456018ff0bac628e05366f976c5af1a7 Mon Sep 17 00:00:00 2001 From: Igor Aleksanov Date: Thu, 8 Oct 2020 09:27:38 +0300 Subject: Keep SyntaxNodePtr::range private --- crates/ide/src/diagnostics/fixes.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'crates/ide/src') diff --git a/crates/ide/src/diagnostics/fixes.rs b/crates/ide/src/diagnostics/fixes.rs index 286ef0785..b47fe0469 100644 --- a/crates/ide/src/diagnostics/fixes.rs +++ b/crates/ide/src/diagnostics/fixes.rs @@ -104,8 +104,11 @@ impl DiagnosticWithFix for MissingOkInTailExpr { impl DiagnosticWithFix for IncorrectCase { fn fix(&self, sema: &Semantics) -> Option { + let root = sema.db.parse_or_expand(self.file)?; + let name_node = self.ident.to_node(&root); + let file_id = self.file.original_file(sema.db); - let offset = self.ident.text_range().start(); + let offset = name_node.syntax().text_range().start(); let file_position = FilePosition { file_id, offset }; let rename_changes = rename_with_semantics(sema, file_position, &self.suggested_text)?; -- cgit v1.2.3 From 991d0190968662f23220d8aefaf28bd03b1dbe41 Mon Sep 17 00:00:00 2001 From: Igor Aleksanov Date: Thu, 8 Oct 2020 09:30:01 +0300 Subject: Use TextRange::contains_inclusive in fixes check --- crates/ide/src/diagnostics.rs | 6 ++---- crates/ide/src/diagnostics/fixes.rs | 3 ++- 2 files changed, 4 insertions(+), 5 deletions(-) (limited to 'crates/ide/src') diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index add102ff2..b30cdb6ed 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs @@ -257,8 +257,7 @@ mod tests { assert_eq_text!(&after, &actual); assert!( - fix.fix_trigger_range.start() <= file_position.offset - && fix.fix_trigger_range.end() >= file_position.offset, + fix.fix_trigger_range.contains_inclusive(file_position.offset), "diagnostic fix range {:?} does not touch cursor position {:?}", fix.fix_trigger_range, file_position.offset @@ -288,8 +287,7 @@ mod tests { assert_eq_text!(&after, &actual); assert!( - fix.fix_trigger_range.start() <= file_position.offset - && fix.fix_trigger_range.end() >= file_position.offset, + fix.fix_trigger_range.contains_inclusive(file_position.offset), "diagnostic fix range {:?} does not touch cursor position {:?}", fix.fix_trigger_range, file_position.offset diff --git a/crates/ide/src/diagnostics/fixes.rs b/crates/ide/src/diagnostics/fixes.rs index b47fe0469..0c75e50b0 100644 --- a/crates/ide/src/diagnostics/fixes.rs +++ b/crates/ide/src/diagnostics/fixes.rs @@ -111,7 +111,8 @@ impl DiagnosticWithFix for IncorrectCase { let offset = name_node.syntax().text_range().start(); let file_position = FilePosition { file_id, offset }; - let rename_changes = rename_with_semantics(sema, file_position, &self.suggested_text)?; + let rename_changes = + rename_with_semantics(sema, file_position, &self.suggested_text).ok()?; let label = format!("Rename to {}", self.suggested_text); Some(Fix::new(&label, rename_changes.info, rename_changes.range)) -- cgit v1.2.3