aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIgor Aleksanov <[email protected]>2020-10-03 15:34:52 +0100
committerIgor Aleksanov <[email protected]>2020-10-12 09:04:59 +0100
commite24e22f288eba33928a9e579f13653d6f04fcdfa (patch)
tree4fd9d128cf510dd7478f6a1fd955461cca70d2f4
parent17f1026c46e6e3797caf3c69737f66bd612c58e1 (diff)
Add fix for incorrect case diagnostic
-rw-r--r--crates/hir/src/code_model.rs40
-rw-r--r--crates/hir/src/diagnostics.rs3
-rw-r--r--crates/hir_ty/src/diagnostics.rs2
-rw-r--r--crates/ide/src/diagnostics.rs64
-rw-r--r--crates/ide/src/diagnostics/fixes.rs20
-rw-r--r--crates/ide/src/references.rs2
-rw-r--r--crates/ide/src/references/rename.rs7
-rw-r--r--crates/syntax/src/ptr.rs4
8 files changed, 112 insertions, 30 deletions
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs
index 19ea26e36..c134356ef 100644
--- a/crates/hir/src/code_model.rs
+++ b/crates/hir/src/code_model.rs
@@ -257,34 +257,22 @@ impl ModuleDef {
257 } 257 }
258 258
259 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { 259 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) {
260 match self { 260 let id = match self {
261 ModuleDef::Adt(it) => match it { 261 ModuleDef::Adt(it) => match it {
262 Adt::Struct(it) => { 262 Adt::Struct(it) => it.id.into(),
263 hir_ty::diagnostics::validate_module_item(db, it.id.into(), sink) 263 Adt::Enum(it) => it.id.into(),
264 } 264 Adt::Union(it) => it.id.into(),
265 Adt::Enum(it) => hir_ty::diagnostics::validate_module_item(db, it.id.into(), sink),
266 Adt::Union(it) => hir_ty::diagnostics::validate_module_item(db, it.id.into(), sink),
267 }, 265 },
268 ModuleDef::Trait(it) => { 266 ModuleDef::Trait(it) => it.id.into(),
269 hir_ty::diagnostics::validate_module_item(db, it.id.into(), sink) 267 ModuleDef::Function(it) => it.id.into(),
270 } 268 ModuleDef::TypeAlias(it) => it.id.into(),
271 ModuleDef::Function(it) => { 269 ModuleDef::Module(it) => it.id.into(),
272 hir_ty::diagnostics::validate_module_item(db, it.id.into(), sink) 270 ModuleDef::Const(it) => it.id.into(),
273 } 271 ModuleDef::Static(it) => it.id.into(),
274 ModuleDef::TypeAlias(it) => {
275 hir_ty::diagnostics::validate_module_item(db, it.id.into(), sink)
276 }
277 ModuleDef::Module(it) => {
278 hir_ty::diagnostics::validate_module_item(db, it.id.into(), sink)
279 }
280 ModuleDef::Const(it) => {
281 hir_ty::diagnostics::validate_module_item(db, it.id.into(), sink)
282 }
283 ModuleDef::Static(it) => {
284 hir_ty::diagnostics::validate_module_item(db, it.id.into(), sink)
285 }
286 _ => return, 272 _ => return,
287 } 273 };
274
275 hir_ty::diagnostics::validate_module_item(db, id, sink)
288 } 276 }
289} 277}
290 278
@@ -389,6 +377,8 @@ impl Module {
389 let crate_def_map = db.crate_def_map(self.id.krate); 377 let crate_def_map = db.crate_def_map(self.id.krate);
390 crate_def_map.add_diagnostics(db.upcast(), self.id.local_id, sink); 378 crate_def_map.add_diagnostics(db.upcast(), self.id.local_id, sink);
391 for decl in self.declarations(db) { 379 for decl in self.declarations(db) {
380 decl.diagnostics(db, sink);
381
392 match decl { 382 match decl {
393 crate::ModuleDef::Function(f) => f.diagnostics(db, sink), 383 crate::ModuleDef::Function(f) => f.diagnostics(db, sink),
394 crate::ModuleDef::Module(m) => { 384 crate::ModuleDef::Module(m) => {
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index 363164b9b..da2b40849 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -2,5 +2,6 @@
2pub use hir_def::diagnostics::UnresolvedModule; 2pub use hir_def::diagnostics::UnresolvedModule;
3pub use hir_expand::diagnostics::{Diagnostic, DiagnosticSink, DiagnosticSinkBuilder}; 3pub use hir_expand::diagnostics::{Diagnostic, DiagnosticSink, DiagnosticSinkBuilder};
4pub use hir_ty::diagnostics::{ 4pub use hir_ty::diagnostics::{
5 MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr, NoSuchField, 5 IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr,
6 NoSuchField,
6}; 7};
diff --git a/crates/hir_ty/src/diagnostics.rs b/crates/hir_ty/src/diagnostics.rs
index bd370e3b2..66762b90e 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 true 301 false
302 } 302 }
303} 303}
304 304
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(
96 .on::<hir::diagnostics::NoSuchField, _>(|d| { 96 .on::<hir::diagnostics::NoSuchField, _>(|d| {
97 res.borrow_mut().push(diagnostic_with_fix(d, &sema)); 97 res.borrow_mut().push(diagnostic_with_fix(d, &sema));
98 }) 98 })
99 .on::<hir::diagnostics::IncorrectCase, _>(|d| {
100 res.borrow_mut().push(warning_with_fix(d, &sema));
101 })
99 // Only collect experimental diagnostics when they're enabled. 102 // Only collect experimental diagnostics when they're enabled.
100 .filter(|diag| !(diag.is_experimental() && config.disable_experimental)) 103 .filter(|diag| !(diag.is_experimental() && config.disable_experimental))
101 .filter(|diag| !config.disabled.contains(diag.code().as_str())); 104 .filter(|diag| !config.disabled.contains(diag.code().as_str()));
@@ -130,6 +133,16 @@ fn diagnostic_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabas
130 } 133 }
131} 134}
132 135
136fn warning_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic {
137 Diagnostic {
138 // name: Some(d.name().into()),
139 range: sema.diagnostics_display_range(d).range,
140 message: d.message(),
141 severity: Severity::WeakWarning,
142 fix: d.fix(&sema),
143 }
144}
145
133fn check_unnecessary_braces_in_use_statement( 146fn check_unnecessary_braces_in_use_statement(
134 acc: &mut Vec<Diagnostic>, 147 acc: &mut Vec<Diagnostic>,
135 file_id: FileId, 148 file_id: FileId,
@@ -253,6 +266,37 @@ mod tests {
253 ); 266 );
254 } 267 }
255 268
269 /// Similar to `check_fix`, but applies all the available fixes.
270 fn check_fixes(ra_fixture_before: &str, ra_fixture_after: &str) {
271 let after = trim_indent(ra_fixture_after);
272
273 let (analysis, file_position) = fixture::position(ra_fixture_before);
274 let diagnostic = analysis
275 .diagnostics(&DiagnosticsConfig::default(), file_position.file_id)
276 .unwrap()
277 .pop()
278 .unwrap();
279 let fix = diagnostic.fix.unwrap();
280 let target_file_contents = analysis.file_text(file_position.file_id).unwrap();
281 let actual = {
282 let mut actual = target_file_contents.to_string();
283 // Go from the last one to the first one, so that ranges won't be affected by previous edits.
284 for edit in fix.source_change.source_file_edits.iter().rev() {
285 edit.edit.apply(&mut actual);
286 }
287 actual
288 };
289
290 assert_eq_text!(&after, &actual);
291 assert!(
292 fix.fix_trigger_range.start() <= file_position.offset
293 && fix.fix_trigger_range.end() >= file_position.offset,
294 "diagnostic fix range {:?} does not touch cursor position {:?}",
295 fix.fix_trigger_range,
296 file_position.offset
297 );
298 }
299
256 /// Checks that a diagnostic applies to the file containing the `<|>` cursor marker 300 /// Checks that a diagnostic applies to the file containing the `<|>` cursor marker
257 /// which has a fix that can apply to other files. 301 /// which has a fix that can apply to other files.
258 fn check_apply_diagnostic_fix_in_other_file(ra_fixture_before: &str, ra_fixture_after: &str) { 302 fn check_apply_diagnostic_fix_in_other_file(ra_fixture_before: &str, ra_fixture_after: &str) {
@@ -790,4 +834,24 @@ struct Foo {
790 let diagnostics = analysis.diagnostics(&DiagnosticsConfig::default(), file_id).unwrap(); 834 let diagnostics = analysis.diagnostics(&DiagnosticsConfig::default(), file_id).unwrap();
791 assert!(!diagnostics.is_empty()); 835 assert!(!diagnostics.is_empty());
792 } 836 }
837
838 #[test]
839 fn test_rename_incorrect_case() {
840 check_fixes(
841 r#"
842pub struct test_struct<|> { one: i32 }
843
844pub fn some_fn(val: test_struct) -> test_struct {
845 test_struct { one: val.one + 1 }
846}
847"#,
848 r#"
849pub struct TestStruct { one: i32 }
850
851pub fn some_fn(val: TestStruct) -> TestStruct {
852 TestStruct { one: val.one + 1 }
853}
854"#,
855 );
856 }
793} 857}
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 @@
3use base_db::FileId; 3use base_db::FileId;
4use hir::{ 4use hir::{
5 db::AstDatabase, 5 db::AstDatabase,
6 diagnostics::{Diagnostic, MissingFields, MissingOkInTailExpr, NoSuchField, UnresolvedModule}, 6 diagnostics::{
7 Diagnostic, IncorrectCase, MissingFields, MissingOkInTailExpr, NoSuchField,
8 UnresolvedModule,
9 },
7 HasSource, HirDisplay, Semantics, VariantDef, 10 HasSource, HirDisplay, Semantics, VariantDef,
8}; 11};
9use ide_db::{ 12use ide_db::{
@@ -17,7 +20,7 @@ use syntax::{
17}; 20};
18use text_edit::TextEdit; 21use text_edit::TextEdit;
19 22
20use crate::diagnostics::Fix; 23use crate::{diagnostics::Fix, references::rename::rename_with_semantics, FilePosition};
21 24
22/// A [Diagnostic] that potentially has a fix available. 25/// A [Diagnostic] that potentially has a fix available.
23/// 26///
@@ -99,6 +102,19 @@ impl DiagnosticWithFix for MissingOkInTailExpr {
99 } 102 }
100} 103}
101 104
105impl DiagnosticWithFix for IncorrectCase {
106 fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> {
107 let file_id = self.file.original_file(sema.db);
108 let offset = self.ident.text_range().start();
109 let file_position = FilePosition { file_id, offset };
110
111 let rename_changes = rename_with_semantics(sema, file_position, &self.suggested_text)?;
112
113 let label = format!("Rename to {}", self.suggested_text);
114 Some(Fix::new(&label, rename_changes.info, rename_changes.range))
115 }
116}
117
102fn missing_record_expr_field_fix( 118fn missing_record_expr_field_fix(
103 sema: &Semantics<RootDatabase>, 119 sema: &Semantics<RootDatabase>,
104 usage_file_id: FileId, 120 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 @@
9//! at the index that the match starts at and its tree parent is 9//! at the index that the match starts at and its tree parent is
10//! resolved to the search element definition, we get a reference. 10//! resolved to the search element definition, we get a reference.
11 11
12mod rename; 12pub(crate) mod rename;
13 13
14use hir::Semantics; 14use hir::Semantics;
15use ide_db::{ 15use 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(
42 new_name: &str, 42 new_name: &str,
43) -> Result<RangeInfo<SourceChange>, RenameError> { 43) -> Result<RangeInfo<SourceChange>, RenameError> {
44 let sema = Semantics::new(db); 44 let sema = Semantics::new(db);
45 rename_with_semantics(&sema, position, new_name)
46}
45 47
48pub(crate) fn rename_with_semantics(
49 sema: &Semantics<RootDatabase>,
50 position: FilePosition,
51 new_name: &str,
52) -> Result<RangeInfo<SourceChange>, RenameError> {
46 match lex_single_syntax_kind(new_name) { 53 match lex_single_syntax_kind(new_name) {
47 Some(res) => match res { 54 Some(res) => match res {
48 (SyntaxKind::IDENT, _) => (), 55 (SyntaxKind::IDENT, _) => (),
diff --git a/crates/syntax/src/ptr.rs b/crates/syntax/src/ptr.rs
index d3fb7a5d9..34e20464a 100644
--- a/crates/syntax/src/ptr.rs
+++ b/crates/syntax/src/ptr.rs
@@ -23,6 +23,10 @@ impl SyntaxNodePtr {
23 SyntaxNodePtr { range: node.text_range(), kind: node.kind() } 23 SyntaxNodePtr { range: node.text_range(), kind: node.kind() }
24 } 24 }
25 25
26 pub fn text_range(&self) -> TextRange {
27 self.range.clone()
28 }
29
26 pub fn to_node(&self, root: &SyntaxNode) -> SyntaxNode { 30 pub fn to_node(&self, root: &SyntaxNode) -> SyntaxNode {
27 assert!(root.parent().is_none()); 31 assert!(root.parent().is_none());
28 successors(Some(root.clone()), |node| { 32 successors(Some(root.clone()), |node| {