aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src/diagnostics
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2021-04-12 15:58:01 +0100
committerAleksey Kladov <[email protected]>2021-04-12 15:58:01 +0100
commit426d098bd6a032cb03e61d4b3d091caeaecbd4d0 (patch)
treeeb54a5d4e72a875b072af5f266673c6265057f3d /crates/ide/src/diagnostics
parentcae920a1bb827726aa142e5c81da9e6b0ca38d97 (diff)
internal: prepare for lazy diagnostics
Diffstat (limited to 'crates/ide/src/diagnostics')
-rw-r--r--crates/ide/src/diagnostics/field_shorthand.rs8
-rw-r--r--crates/ide/src/diagnostics/fixes.rs42
-rw-r--r--crates/ide/src/diagnostics/unlinked_file.rs14
3 files changed, 38 insertions, 26 deletions
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};
5use syntax::{ast, match_ast, AstNode, SyntaxNode}; 5use syntax::{ast, match_ast, AstNode, SyntaxNode};
6use text_edit::TextEdit; 6use text_edit::TextEdit;
7 7
8use crate::{Diagnostic, Fix}; 8use crate::{diagnostics::fix, Diagnostic};
9 9
10pub(super) fn check(acc: &mut Vec<Diagnostic>, file_id: FileId, node: &SyntaxNode) { 10pub(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};
21use text_edit::TextEdit; 21use text_edit::TextEdit;
22 22
23use crate::{diagnostics::Fix, references::rename::rename_with_semantics, FilePosition}; 23use 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
28pub(crate) trait DiagnosticWithFix: Diagnostic { 28pub(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
32impl DiagnosticWithFix for UnresolvedModule { 32impl 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
51impl DiagnosticWithFix for NoSuchField { 52impl 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
62impl DiagnosticWithFix for MissingFields { 63impl 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
97impl DiagnosticWithFix for MissingOkOrSomeInTailExpr { 99impl 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
110impl DiagnosticWithFix for RemoveThisSemicolon { 112impl 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
130impl DiagnosticWithFix for IncorrectCase { 132impl 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
147impl DiagnosticWithFix for ReplaceFilterMapNextWithFindMap { 149impl 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};
17use text_edit::TextEdit; 17use text_edit::TextEdit;
18 18
19use crate::Fix; 19use crate::{
20 20 diagnostics::{fix, fixes::DiagnosticWithFix},
21use 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
51impl DiagnosticWithFix for UnlinkedFile { 52impl 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,