diff options
author | Aleksey Kladov <[email protected]> | 2021-04-12 15:58:01 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2021-04-12 15:58:01 +0100 |
commit | 426d098bd6a032cb03e61d4b3d091caeaecbd4d0 (patch) | |
tree | eb54a5d4e72a875b072af5f266673c6265057f3d | |
parent | cae920a1bb827726aa142e5c81da9e6b0ca38d97 (diff) |
internal: prepare for lazy diagnostics
-rw-r--r-- | crates/ide/src/diagnostics.rs | 83 | ||||
-rw-r--r-- | crates/ide/src/diagnostics/field_shorthand.rs | 8 | ||||
-rw-r--r-- | crates/ide/src/diagnostics/fixes.rs | 42 | ||||
-rw-r--r-- | crates/ide/src/diagnostics/unlinked_file.rs | 14 | ||||
-rw-r--r-- | crates/ide/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/rust-analyzer/src/handlers.rs | 24 |
6 files changed, 96 insertions, 77 deletions
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index 0ace80a1e..4f0b4a62e 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs | |||
@@ -25,7 +25,7 @@ use syntax::{ | |||
25 | use text_edit::TextEdit; | 25 | use text_edit::TextEdit; |
26 | use unlinked_file::UnlinkedFile; | 26 | use unlinked_file::UnlinkedFile; |
27 | 27 | ||
28 | use crate::{FileId, Label, SourceChange}; | 28 | use crate::{Assist, AssistId, AssistKind, FileId, Label, SourceChange}; |
29 | 29 | ||
30 | use self::fixes::DiagnosticWithFix; | 30 | use self::fixes::DiagnosticWithFix; |
31 | 31 | ||
@@ -35,7 +35,7 @@ pub struct Diagnostic { | |||
35 | pub message: String, | 35 | pub message: String, |
36 | pub range: TextRange, | 36 | pub range: TextRange, |
37 | pub severity: Severity, | 37 | pub severity: Severity, |
38 | pub fix: Option<Fix>, | 38 | pub fix: Option<Assist>, |
39 | pub unused: bool, | 39 | pub unused: bool, |
40 | pub code: Option<DiagnosticCode>, | 40 | pub code: Option<DiagnosticCode>, |
41 | } | 41 | } |
@@ -56,7 +56,7 @@ impl Diagnostic { | |||
56 | } | 56 | } |
57 | } | 57 | } |
58 | 58 | ||
59 | fn with_fix(self, fix: Option<Fix>) -> Self { | 59 | fn with_fix(self, fix: Option<Assist>) -> Self { |
60 | Self { fix, ..self } | 60 | Self { fix, ..self } |
61 | } | 61 | } |
62 | 62 | ||
@@ -69,21 +69,6 @@ impl Diagnostic { | |||
69 | } | 69 | } |
70 | } | 70 | } |
71 | 71 | ||
72 | #[derive(Debug)] | ||
73 | pub struct Fix { | ||
74 | pub label: Label, | ||
75 | pub source_change: SourceChange, | ||
76 | /// Allows to trigger the fix only when the caret is in the range given | ||
77 | pub fix_trigger_range: TextRange, | ||
78 | } | ||
79 | |||
80 | impl Fix { | ||
81 | fn new(label: &str, source_change: SourceChange, fix_trigger_range: TextRange) -> Self { | ||
82 | let label = Label::new(label); | ||
83 | Self { label, source_change, fix_trigger_range } | ||
84 | } | ||
85 | } | ||
86 | |||
87 | #[derive(Debug, Copy, Clone)] | 72 | #[derive(Debug, Copy, Clone)] |
88 | pub enum Severity { | 73 | pub enum Severity { |
89 | Error, | 74 | Error, |
@@ -261,7 +246,8 @@ fn check_unnecessary_braces_in_use_statement( | |||
261 | 246 | ||
262 | acc.push( | 247 | acc.push( |
263 | Diagnostic::hint(use_range, "Unnecessary braces in use statement".to_string()) | 248 | Diagnostic::hint(use_range, "Unnecessary braces in use statement".to_string()) |
264 | .with_fix(Some(Fix::new( | 249 | .with_fix(Some(fix( |
250 | "remove_braces", | ||
265 | "Remove unnecessary braces", | 251 | "Remove unnecessary braces", |
266 | SourceChange::from_text_edit(file_id, edit), | 252 | SourceChange::from_text_edit(file_id, edit), |
267 | use_range, | 253 | use_range, |
@@ -284,6 +270,17 @@ fn text_edit_for_remove_unnecessary_braces_with_self_in_use_statement( | |||
284 | None | 270 | None |
285 | } | 271 | } |
286 | 272 | ||
273 | fn fix(id: &'static str, label: &str, source_change: SourceChange, target: TextRange) -> Assist { | ||
274 | assert!(!id.contains(' ')); | ||
275 | Assist { | ||
276 | id: AssistId(id, AssistKind::QuickFix), | ||
277 | label: Label::new(label), | ||
278 | group: None, | ||
279 | target, | ||
280 | source_change: Some(source_change), | ||
281 | } | ||
282 | } | ||
283 | |||
287 | #[cfg(test)] | 284 | #[cfg(test)] |
288 | mod tests { | 285 | mod tests { |
289 | use expect_test::{expect, Expect}; | 286 | use expect_test::{expect, Expect}; |
@@ -308,10 +305,11 @@ mod tests { | |||
308 | .unwrap(); | 305 | .unwrap(); |
309 | let fix = diagnostic.fix.unwrap(); | 306 | let fix = diagnostic.fix.unwrap(); |
310 | let actual = { | 307 | let actual = { |
311 | let file_id = *fix.source_change.source_file_edits.keys().next().unwrap(); | 308 | let source_change = fix.source_change.unwrap(); |
309 | let file_id = *source_change.source_file_edits.keys().next().unwrap(); | ||
312 | let mut actual = analysis.file_text(file_id).unwrap().to_string(); | 310 | let mut actual = analysis.file_text(file_id).unwrap().to_string(); |
313 | 311 | ||
314 | for edit in fix.source_change.source_file_edits.values() { | 312 | for edit in source_change.source_file_edits.values() { |
315 | edit.apply(&mut actual); | 313 | edit.apply(&mut actual); |
316 | } | 314 | } |
317 | actual | 315 | actual |
@@ -319,9 +317,9 @@ mod tests { | |||
319 | 317 | ||
320 | assert_eq_text!(&after, &actual); | 318 | assert_eq_text!(&after, &actual); |
321 | assert!( | 319 | assert!( |
322 | fix.fix_trigger_range.contains_inclusive(file_position.offset), | 320 | fix.target.contains_inclusive(file_position.offset), |
323 | "diagnostic fix range {:?} does not touch cursor position {:?}", | 321 | "diagnostic fix range {:?} does not touch cursor position {:?}", |
324 | fix.fix_trigger_range, | 322 | fix.target, |
325 | file_position.offset | 323 | file_position.offset |
326 | ); | 324 | ); |
327 | } | 325 | } |
@@ -665,24 +663,31 @@ fn test_fn() { | |||
665 | range: 0..8, | 663 | range: 0..8, |
666 | severity: Error, | 664 | severity: Error, |
667 | fix: Some( | 665 | fix: Some( |
668 | Fix { | 666 | Assist { |
667 | id: AssistId( | ||
668 | "create_module", | ||
669 | QuickFix, | ||
670 | ), | ||
669 | label: "Create module", | 671 | label: "Create module", |
670 | source_change: SourceChange { | 672 | group: None, |
671 | source_file_edits: {}, | 673 | target: 0..8, |
672 | file_system_edits: [ | 674 | source_change: Some( |
673 | CreateFile { | 675 | SourceChange { |
674 | dst: AnchoredPathBuf { | 676 | source_file_edits: {}, |
675 | anchor: FileId( | 677 | file_system_edits: [ |
676 | 0, | 678 | CreateFile { |
677 | ), | 679 | dst: AnchoredPathBuf { |
678 | path: "foo.rs", | 680 | anchor: FileId( |
681 | 0, | ||
682 | ), | ||
683 | path: "foo.rs", | ||
684 | }, | ||
685 | initial_contents: "", | ||
679 | }, | 686 | }, |
680 | initial_contents: "", | 687 | ], |
681 | }, | 688 | is_snippet: false, |
682 | ], | 689 | }, |
683 | is_snippet: false, | 690 | ), |
684 | }, | ||
685 | fix_trigger_range: 0..8, | ||
686 | }, | 691 | }, |
687 | ), | 692 | ), |
688 | unused: false, | 693 | unused: false, |
diff --git a/crates/ide/src/diagnostics/field_shorthand.rs b/crates/ide/src/diagnostics/field_shorthand.rs index 5c89e2170..2b1787f9b 100644 --- a/crates/ide/src/diagnostics/field_shorthand.rs +++ b/crates/ide/src/diagnostics/field_shorthand.rs | |||
@@ -5,7 +5,7 @@ use ide_db::{base_db::FileId, source_change::SourceChange}; | |||
5 | use syntax::{ast, match_ast, AstNode, SyntaxNode}; | 5 | use syntax::{ast, match_ast, AstNode, SyntaxNode}; |
6 | use text_edit::TextEdit; | 6 | use text_edit::TextEdit; |
7 | 7 | ||
8 | use crate::{Diagnostic, Fix}; | 8 | use crate::{diagnostics::fix, Diagnostic}; |
9 | 9 | ||
10 | pub(super) fn check(acc: &mut Vec<Diagnostic>, file_id: FileId, node: &SyntaxNode) { | 10 | pub(super) fn check(acc: &mut Vec<Diagnostic>, file_id: FileId, node: &SyntaxNode) { |
11 | match_ast! { | 11 | match_ast! { |
@@ -47,7 +47,8 @@ fn check_expr_field_shorthand( | |||
47 | let field_range = record_field.syntax().text_range(); | 47 | let field_range = record_field.syntax().text_range(); |
48 | acc.push( | 48 | acc.push( |
49 | Diagnostic::hint(field_range, "Shorthand struct initialization".to_string()).with_fix( | 49 | Diagnostic::hint(field_range, "Shorthand struct initialization".to_string()).with_fix( |
50 | Some(Fix::new( | 50 | Some(fix( |
51 | "use_expr_field_shorthand", | ||
51 | "Use struct shorthand initialization", | 52 | "Use struct shorthand initialization", |
52 | SourceChange::from_text_edit(file_id, edit), | 53 | SourceChange::from_text_edit(file_id, edit), |
53 | field_range, | 54 | field_range, |
@@ -86,7 +87,8 @@ fn check_pat_field_shorthand( | |||
86 | 87 | ||
87 | let field_range = record_pat_field.syntax().text_range(); | 88 | let field_range = record_pat_field.syntax().text_range(); |
88 | acc.push(Diagnostic::hint(field_range, "Shorthand struct pattern".to_string()).with_fix( | 89 | acc.push(Diagnostic::hint(field_range, "Shorthand struct pattern".to_string()).with_fix( |
89 | Some(Fix::new( | 90 | Some(fix( |
91 | "use_pat_field_shorthand", | ||
90 | "Use struct field shorthand", | 92 | "Use struct field shorthand", |
91 | SourceChange::from_text_edit(file_id, edit), | 93 | SourceChange::from_text_edit(file_id, edit), |
92 | field_range, | 94 | field_range, |
diff --git a/crates/ide/src/diagnostics/fixes.rs b/crates/ide/src/diagnostics/fixes.rs index 5fb3e2d91..69cf5288c 100644 --- a/crates/ide/src/diagnostics/fixes.rs +++ b/crates/ide/src/diagnostics/fixes.rs | |||
@@ -20,20 +20,21 @@ use syntax::{ | |||
20 | }; | 20 | }; |
21 | use text_edit::TextEdit; | 21 | use text_edit::TextEdit; |
22 | 22 | ||
23 | use crate::{diagnostics::Fix, references::rename::rename_with_semantics, FilePosition}; | 23 | use crate::{diagnostics::fix, references::rename::rename_with_semantics, Assist, FilePosition}; |
24 | 24 | ||
25 | /// A [Diagnostic] that potentially has a fix available. | 25 | /// A [Diagnostic] that potentially has a fix available. |
26 | /// | 26 | /// |
27 | /// [Diagnostic]: hir::diagnostics::Diagnostic | 27 | /// [Diagnostic]: hir::diagnostics::Diagnostic |
28 | pub(crate) trait DiagnosticWithFix: Diagnostic { | 28 | pub(crate) trait DiagnosticWithFix: Diagnostic { |
29 | fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix>; | 29 | fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Assist>; |
30 | } | 30 | } |
31 | 31 | ||
32 | impl DiagnosticWithFix for UnresolvedModule { | 32 | impl DiagnosticWithFix for UnresolvedModule { |
33 | fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> { | 33 | fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Assist> { |
34 | let root = sema.db.parse_or_expand(self.file)?; | 34 | let root = sema.db.parse_or_expand(self.file)?; |
35 | let unresolved_module = self.decl.to_node(&root); | 35 | let unresolved_module = self.decl.to_node(&root); |
36 | Some(Fix::new( | 36 | Some(fix( |
37 | "create_module", | ||
37 | "Create module", | 38 | "Create module", |
38 | FileSystemEdit::CreateFile { | 39 | FileSystemEdit::CreateFile { |
39 | dst: AnchoredPathBuf { | 40 | dst: AnchoredPathBuf { |
@@ -49,7 +50,7 @@ impl DiagnosticWithFix for UnresolvedModule { | |||
49 | } | 50 | } |
50 | 51 | ||
51 | impl DiagnosticWithFix for NoSuchField { | 52 | impl DiagnosticWithFix for NoSuchField { |
52 | fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> { | 53 | fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Assist> { |
53 | let root = sema.db.parse_or_expand(self.file)?; | 54 | let root = sema.db.parse_or_expand(self.file)?; |
54 | missing_record_expr_field_fix( | 55 | missing_record_expr_field_fix( |
55 | &sema, | 56 | &sema, |
@@ -60,7 +61,7 @@ impl DiagnosticWithFix for NoSuchField { | |||
60 | } | 61 | } |
61 | 62 | ||
62 | impl DiagnosticWithFix for MissingFields { | 63 | impl DiagnosticWithFix for MissingFields { |
63 | fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> { | 64 | fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Assist> { |
64 | // Note that although we could add a diagnostics to | 65 | // Note that although we could add a diagnostics to |
65 | // fill the missing tuple field, e.g : | 66 | // fill the missing tuple field, e.g : |
66 | // `struct A(usize);` | 67 | // `struct A(usize);` |
@@ -86,7 +87,8 @@ impl DiagnosticWithFix for MissingFields { | |||
86 | .into_text_edit(&mut builder); | 87 | .into_text_edit(&mut builder); |
87 | builder.finish() | 88 | builder.finish() |
88 | }; | 89 | }; |
89 | Some(Fix::new( | 90 | Some(fix( |
91 | "fill_missing_fields", | ||
90 | "Fill struct fields", | 92 | "Fill struct fields", |
91 | SourceChange::from_text_edit(self.file.original_file(sema.db), edit), | 93 | SourceChange::from_text_edit(self.file.original_file(sema.db), edit), |
92 | sema.original_range(&field_list_parent.syntax()).range, | 94 | sema.original_range(&field_list_parent.syntax()).range, |
@@ -95,7 +97,7 @@ impl DiagnosticWithFix for MissingFields { | |||
95 | } | 97 | } |
96 | 98 | ||
97 | impl DiagnosticWithFix for MissingOkOrSomeInTailExpr { | 99 | impl DiagnosticWithFix for MissingOkOrSomeInTailExpr { |
98 | fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> { | 100 | fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Assist> { |
99 | let root = sema.db.parse_or_expand(self.file)?; | 101 | let root = sema.db.parse_or_expand(self.file)?; |
100 | let tail_expr = self.expr.to_node(&root); | 102 | let tail_expr = self.expr.to_node(&root); |
101 | let tail_expr_range = tail_expr.syntax().text_range(); | 103 | let tail_expr_range = tail_expr.syntax().text_range(); |
@@ -103,12 +105,12 @@ impl DiagnosticWithFix for MissingOkOrSomeInTailExpr { | |||
103 | let edit = TextEdit::replace(tail_expr_range, replacement); | 105 | let edit = TextEdit::replace(tail_expr_range, replacement); |
104 | let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit); | 106 | let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit); |
105 | let name = if self.required == "Ok" { "Wrap with Ok" } else { "Wrap with Some" }; | 107 | let name = if self.required == "Ok" { "Wrap with Ok" } else { "Wrap with Some" }; |
106 | Some(Fix::new(name, source_change, tail_expr_range)) | 108 | Some(fix("wrap_tail_expr", name, source_change, tail_expr_range)) |
107 | } | 109 | } |
108 | } | 110 | } |
109 | 111 | ||
110 | impl DiagnosticWithFix for RemoveThisSemicolon { | 112 | impl DiagnosticWithFix for RemoveThisSemicolon { |
111 | fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> { | 113 | fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Assist> { |
112 | let root = sema.db.parse_or_expand(self.file)?; | 114 | let root = sema.db.parse_or_expand(self.file)?; |
113 | 115 | ||
114 | let semicolon = self | 116 | let semicolon = self |
@@ -123,12 +125,12 @@ impl DiagnosticWithFix for RemoveThisSemicolon { | |||
123 | let edit = TextEdit::delete(semicolon); | 125 | let edit = TextEdit::delete(semicolon); |
124 | let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit); | 126 | let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit); |
125 | 127 | ||
126 | Some(Fix::new("Remove this semicolon", source_change, semicolon)) | 128 | Some(fix("remove_semicolon", "Remove this semicolon", source_change, semicolon)) |
127 | } | 129 | } |
128 | } | 130 | } |
129 | 131 | ||
130 | impl DiagnosticWithFix for IncorrectCase { | 132 | impl DiagnosticWithFix for IncorrectCase { |
131 | fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> { | 133 | fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Assist> { |
132 | let root = sema.db.parse_or_expand(self.file)?; | 134 | let root = sema.db.parse_or_expand(self.file)?; |
133 | let name_node = self.ident.to_node(&root); | 135 | let name_node = self.ident.to_node(&root); |
134 | 136 | ||
@@ -140,12 +142,12 @@ impl DiagnosticWithFix for IncorrectCase { | |||
140 | rename_with_semantics(sema, file_position, &self.suggested_text).ok()?; | 142 | rename_with_semantics(sema, file_position, &self.suggested_text).ok()?; |
141 | 143 | ||
142 | let label = format!("Rename to {}", self.suggested_text); | 144 | let label = format!("Rename to {}", self.suggested_text); |
143 | Some(Fix::new(&label, rename_changes, frange.range)) | 145 | Some(fix("change_case", &label, rename_changes, frange.range)) |
144 | } | 146 | } |
145 | } | 147 | } |
146 | 148 | ||
147 | impl DiagnosticWithFix for ReplaceFilterMapNextWithFindMap { | 149 | impl DiagnosticWithFix for ReplaceFilterMapNextWithFindMap { |
148 | fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> { | 150 | fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Assist> { |
149 | let root = sema.db.parse_or_expand(self.file)?; | 151 | let root = sema.db.parse_or_expand(self.file)?; |
150 | let next_expr = self.next_expr.to_node(&root); | 152 | let next_expr = self.next_expr.to_node(&root); |
151 | let next_call = ast::MethodCallExpr::cast(next_expr.syntax().clone())?; | 153 | let next_call = ast::MethodCallExpr::cast(next_expr.syntax().clone())?; |
@@ -163,7 +165,8 @@ impl DiagnosticWithFix for ReplaceFilterMapNextWithFindMap { | |||
163 | 165 | ||
164 | let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit); | 166 | let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit); |
165 | 167 | ||
166 | Some(Fix::new( | 168 | Some(fix( |
169 | "replace_with_find_map", | ||
167 | "Replace filter_map(..).next() with find_map()", | 170 | "Replace filter_map(..).next() with find_map()", |
168 | source_change, | 171 | source_change, |
169 | trigger_range, | 172 | trigger_range, |
@@ -175,7 +178,7 @@ fn missing_record_expr_field_fix( | |||
175 | sema: &Semantics<RootDatabase>, | 178 | sema: &Semantics<RootDatabase>, |
176 | usage_file_id: FileId, | 179 | usage_file_id: FileId, |
177 | record_expr_field: &ast::RecordExprField, | 180 | record_expr_field: &ast::RecordExprField, |
178 | ) -> Option<Fix> { | 181 | ) -> Option<Assist> { |
179 | let record_lit = ast::RecordExpr::cast(record_expr_field.syntax().parent()?.parent()?)?; | 182 | let record_lit = ast::RecordExpr::cast(record_expr_field.syntax().parent()?.parent()?)?; |
180 | let def_id = sema.resolve_variant(record_lit)?; | 183 | let def_id = sema.resolve_variant(record_lit)?; |
181 | let module; | 184 | let module; |
@@ -233,7 +236,12 @@ fn missing_record_expr_field_fix( | |||
233 | def_file_id, | 236 | def_file_id, |
234 | TextEdit::insert(last_field_syntax.text_range().end(), new_field), | 237 | TextEdit::insert(last_field_syntax.text_range().end(), new_field), |
235 | ); | 238 | ); |
236 | return Some(Fix::new("Create field", source_change, record_expr_field.syntax().text_range())); | 239 | return Some(fix( |
240 | "create_field", | ||
241 | "Create field", | ||
242 | source_change, | ||
243 | record_expr_field.syntax().text_range(), | ||
244 | )); | ||
237 | 245 | ||
238 | fn record_field_list(field_def_list: ast::FieldList) -> Option<ast::RecordFieldList> { | 246 | fn record_field_list(field_def_list: ast::FieldList) -> Option<ast::RecordFieldList> { |
239 | match field_def_list { | 247 | match field_def_list { |
diff --git a/crates/ide/src/diagnostics/unlinked_file.rs b/crates/ide/src/diagnostics/unlinked_file.rs index e174fb767..5482b7287 100644 --- a/crates/ide/src/diagnostics/unlinked_file.rs +++ b/crates/ide/src/diagnostics/unlinked_file.rs | |||
@@ -16,9 +16,10 @@ use syntax::{ | |||
16 | }; | 16 | }; |
17 | use text_edit::TextEdit; | 17 | use text_edit::TextEdit; |
18 | 18 | ||
19 | use crate::Fix; | 19 | use crate::{ |
20 | 20 | diagnostics::{fix, fixes::DiagnosticWithFix}, | |
21 | use super::fixes::DiagnosticWithFix; | 21 | Assist, |
22 | }; | ||
22 | 23 | ||
23 | // Diagnostic: unlinked-file | 24 | // Diagnostic: unlinked-file |
24 | // | 25 | // |
@@ -49,7 +50,7 @@ impl Diagnostic for UnlinkedFile { | |||
49 | } | 50 | } |
50 | 51 | ||
51 | impl DiagnosticWithFix for UnlinkedFile { | 52 | impl DiagnosticWithFix for UnlinkedFile { |
52 | fn fix(&self, sema: &hir::Semantics<RootDatabase>) -> Option<Fix> { | 53 | fn fix(&self, sema: &hir::Semantics<RootDatabase>) -> Option<Assist> { |
53 | // If there's an existing module that could add a `mod` item to include the unlinked file, | 54 | // If there's an existing module that could add a `mod` item to include the unlinked file, |
54 | // suggest that as a fix. | 55 | // suggest that as a fix. |
55 | 56 | ||
@@ -100,7 +101,7 @@ fn make_fix( | |||
100 | parent_file_id: FileId, | 101 | parent_file_id: FileId, |
101 | new_mod_name: &str, | 102 | new_mod_name: &str, |
102 | added_file_id: FileId, | 103 | added_file_id: FileId, |
103 | ) -> Option<Fix> { | 104 | ) -> Option<Assist> { |
104 | fn is_outline_mod(item: &ast::Item) -> bool { | 105 | fn is_outline_mod(item: &ast::Item) -> bool { |
105 | matches!(item, ast::Item::Module(m) if m.item_list().is_none()) | 106 | matches!(item, ast::Item::Module(m) if m.item_list().is_none()) |
106 | } | 107 | } |
@@ -152,7 +153,8 @@ fn make_fix( | |||
152 | 153 | ||
153 | let edit = builder.finish(); | 154 | let edit = builder.finish(); |
154 | let trigger_range = db.parse(added_file_id).tree().syntax().text_range(); | 155 | let trigger_range = db.parse(added_file_id).tree().syntax().text_range(); |
155 | Some(Fix::new( | 156 | Some(fix( |
157 | "add_mod_declaration", | ||
156 | &format!("Insert `{}`", mod_decl), | 158 | &format!("Insert `{}`", mod_decl), |
157 | SourceChange::from_text_edit(parent_file_id, edit), | 159 | SourceChange::from_text_edit(parent_file_id, edit), |
158 | trigger_range, | 160 | trigger_range, |
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 3f73c0632..0615b26d3 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs | |||
@@ -69,7 +69,7 @@ use crate::display::ToNav; | |||
69 | pub use crate::{ | 69 | pub use crate::{ |
70 | annotations::{Annotation, AnnotationConfig, AnnotationKind}, | 70 | annotations::{Annotation, AnnotationConfig, AnnotationKind}, |
71 | call_hierarchy::CallItem, | 71 | call_hierarchy::CallItem, |
72 | diagnostics::{Diagnostic, DiagnosticsConfig, Fix, Severity}, | 72 | diagnostics::{Diagnostic, DiagnosticsConfig, Severity}, |
73 | display::navigation_target::NavigationTarget, | 73 | display::navigation_target::NavigationTarget, |
74 | expand_macro::ExpandedMacro, | 74 | expand_macro::ExpandedMacro, |
75 | file_structure::{StructureNode, StructureNodeKind}, | 75 | file_structure::{StructureNode, StructureNodeKind}, |
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index edfa42eb5..107685c63 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs | |||
@@ -1039,18 +1039,20 @@ fn add_quick_fixes( | |||
1039 | for fix in diagnostics | 1039 | for fix in diagnostics |
1040 | .into_iter() | 1040 | .into_iter() |
1041 | .filter_map(|d| d.fix) | 1041 | .filter_map(|d| d.fix) |
1042 | .filter(|fix| fix.fix_trigger_range.intersect(frange.range).is_some()) | 1042 | .filter(|fix| fix.target.intersect(frange.range).is_some()) |
1043 | { | 1043 | { |
1044 | let edit = to_proto::snippet_workspace_edit(&snap, fix.source_change)?; | 1044 | if let Some(source_change) = fix.source_change { |
1045 | let action = lsp_ext::CodeAction { | 1045 | let edit = to_proto::snippet_workspace_edit(&snap, source_change)?; |
1046 | title: fix.label.to_string(), | 1046 | let action = lsp_ext::CodeAction { |
1047 | group: None, | 1047 | title: fix.label.to_string(), |
1048 | kind: Some(CodeActionKind::QUICKFIX), | 1048 | group: None, |
1049 | edit: Some(edit), | 1049 | kind: Some(CodeActionKind::QUICKFIX), |
1050 | is_preferred: Some(false), | 1050 | edit: Some(edit), |
1051 | data: None, | 1051 | is_preferred: Some(false), |
1052 | }; | 1052 | data: None, |
1053 | acc.push(action); | 1053 | }; |
1054 | acc.push(action); | ||
1055 | } | ||
1054 | } | 1056 | } |
1055 | 1057 | ||
1056 | for fix in snap.check_fixes.get(&frange.file_id).into_iter().flatten() { | 1058 | for fix in snap.check_fixes.get(&frange.file_id).into_iter().flatten() { |