diff options
-rw-r--r-- | crates/hir/src/code_model.rs | 40 | ||||
-rw-r--r-- | crates/hir/src/diagnostics.rs | 3 | ||||
-rw-r--r-- | crates/hir_ty/src/diagnostics.rs | 2 | ||||
-rw-r--r-- | crates/ide/src/diagnostics.rs | 64 | ||||
-rw-r--r-- | crates/ide/src/diagnostics/fixes.rs | 20 | ||||
-rw-r--r-- | crates/ide/src/references.rs | 2 | ||||
-rw-r--r-- | crates/ide/src/references/rename.rs | 7 | ||||
-rw-r--r-- | crates/syntax/src/ptr.rs | 4 |
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 @@ | |||
2 | pub use hir_def::diagnostics::UnresolvedModule; | 2 | pub use hir_def::diagnostics::UnresolvedModule; |
3 | pub use hir_expand::diagnostics::{Diagnostic, DiagnosticSink, DiagnosticSinkBuilder}; | 3 | pub use hir_expand::diagnostics::{Diagnostic, DiagnosticSink, DiagnosticSinkBuilder}; |
4 | pub use hir_ty::diagnostics::{ | 4 | pub 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 | ||
136 | fn 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 | |||
133 | fn check_unnecessary_braces_in_use_statement( | 146 | fn 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#" | ||
842 | pub struct test_struct<|> { one: i32 } | ||
843 | |||
844 | pub fn some_fn(val: test_struct) -> test_struct { | ||
845 | test_struct { one: val.one + 1 } | ||
846 | } | ||
847 | "#, | ||
848 | r#" | ||
849 | pub struct TestStruct { one: i32 } | ||
850 | |||
851 | pub 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 @@ | |||
3 | use base_db::FileId; | 3 | use base_db::FileId; |
4 | use hir::{ | 4 | use 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 | }; |
9 | use ide_db::{ | 12 | use ide_db::{ |
@@ -17,7 +20,7 @@ use syntax::{ | |||
17 | }; | 20 | }; |
18 | use text_edit::TextEdit; | 21 | use text_edit::TextEdit; |
19 | 22 | ||
20 | use crate::diagnostics::Fix; | 23 | use 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 | ||
105 | impl 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 | |||
102 | fn missing_record_expr_field_fix( | 118 | fn 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 | ||
12 | mod rename; | 12 | pub(crate) mod rename; |
13 | 13 | ||
14 | use hir::Semantics; | 14 | use hir::Semantics; |
15 | use ide_db::{ | 15 | 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( | |||
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 | ||
48 | pub(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| { |