aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/diagnostics/diagnostics_with_fix.rs
diff options
context:
space:
mode:
authorKirill Bulatov <[email protected]>2020-08-11 15:13:40 +0100
committerKirill Bulatov <[email protected]>2020-08-11 15:13:40 +0100
commit188ec3459e795732ad097758f7bf6b6b95bdbf5e (patch)
treeb24e4118f73f9828616fa97f16a499fc78bae656 /crates/ra_ide/src/diagnostics/diagnostics_with_fix.rs
parent37aa68f050fae0079db7b6ebd81bacea4441fb7e (diff)
Simplify fix structure
Diffstat (limited to 'crates/ra_ide/src/diagnostics/diagnostics_with_fix.rs')
-rw-r--r--crates/ra_ide/src/diagnostics/diagnostics_with_fix.rs101
1 files changed, 50 insertions, 51 deletions
diff --git a/crates/ra_ide/src/diagnostics/diagnostics_with_fix.rs b/crates/ra_ide/src/diagnostics/diagnostics_with_fix.rs
index 1955e1521..57b54a61e 100644
--- a/crates/ra_ide/src/diagnostics/diagnostics_with_fix.rs
+++ b/crates/ra_ide/src/diagnostics/diagnostics_with_fix.rs
@@ -1,9 +1,9 @@
1//! Provides a way to derive fixes based on the diagnostic data. 1//! Provides a way to attach fix actions to the
2use crate::Fix; 2use crate::Fix;
3use ast::{edit::IndentLevel, make}; 3use ast::{edit::IndentLevel, make};
4use hir::{ 4use hir::{
5 db::AstDatabase, 5 db::AstDatabase,
6 diagnostics::{MissingFields, MissingOkInTailExpr, NoSuchField, UnresolvedModule}, 6 diagnostics::{Diagnostic, MissingFields, MissingOkInTailExpr, NoSuchField, UnresolvedModule},
7 HasSource, HirDisplay, Semantics, VariantDef, 7 HasSource, HirDisplay, Semantics, VariantDef,
8}; 8};
9use ra_db::FileId; 9use ra_db::FileId;
@@ -11,94 +11,90 @@ use ra_ide_db::{
11 source_change::{FileSystemEdit, SourceFileEdit}, 11 source_change::{FileSystemEdit, SourceFileEdit},
12 RootDatabase, 12 RootDatabase,
13}; 13};
14use ra_syntax::{algo, ast, AstNode, TextRange}; 14use ra_syntax::{algo, ast, AstNode};
15use ra_text_edit::{TextEdit, TextEditBuilder}; 15use ra_text_edit::{TextEdit, TextEditBuilder};
16 16
17/// A trait to implement fot the Diagnostic that has a fix available. 17/// A [Diagnostic] that potentially has a fix available.
18pub trait DiagnosticWithFix { 18///
19 /// Provides a fix with the fix range, if applicable in the current semantics. 19/// [Diagnostic]: hir::diagnostics::Diagnostic
20 fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<(Fix, TextRange)>; 20pub trait DiagnosticWithFix: Diagnostic {
21 fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix>;
21} 22}
22 23
23impl DiagnosticWithFix for UnresolvedModule { 24impl DiagnosticWithFix for UnresolvedModule {
24 fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<(Fix, TextRange)> { 25 fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> {
25 let fix = Fix::new( 26 let root = sema.db.parse_or_expand(self.file)?;
27 let unresolved_module = self.decl.to_node(&root);
28 Some(Fix::new(
26 "Create module", 29 "Create module",
27 FileSystemEdit::CreateFile { 30 FileSystemEdit::CreateFile {
28 anchor: self.file.original_file(sema.db), 31 anchor: self.file.original_file(sema.db),
29 dst: self.candidate.clone(), 32 dst: self.candidate.clone(),
30 } 33 }
31 .into(), 34 .into(),
32 ); 35 unresolved_module.syntax().text_range(),
33 36 ))
34 let root = sema.db.parse_or_expand(self.file)?;
35 let unresolved_module = self.decl.to_node(&root);
36 Some((fix, unresolved_module.syntax().text_range()))
37 } 37 }
38} 38}
39 39
40impl DiagnosticWithFix for NoSuchField { 40impl DiagnosticWithFix for NoSuchField {
41 fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<(Fix, TextRange)> { 41 fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> {
42 let root = sema.db.parse_or_expand(self.file)?; 42 let root = sema.db.parse_or_expand(self.file)?;
43 let record_expr_field = self.field.to_node(&root); 43 missing_record_expr_field_fix(
44 let fix = 44 &sema,
45 missing_struct_field_fix(&sema, self.file.original_file(sema.db), &record_expr_field)?; 45 self.file.original_file(sema.db),
46 Some((fix, record_expr_field.syntax().text_range())) 46 &self.field.to_node(&root),
47 )
47 } 48 }
48} 49}
49 50
50impl DiagnosticWithFix for MissingFields { 51impl DiagnosticWithFix for MissingFields {
51 fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<(Fix, TextRange)> { 52 fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> {
52 // Note that although we could add a diagnostics to 53 // Note that although we could add a diagnostics to
53 // fill the missing tuple field, e.g : 54 // fill the missing tuple field, e.g :
54 // `struct A(usize);` 55 // `struct A(usize);`
55 // `let a = A { 0: () }` 56 // `let a = A { 0: () }`
56 // but it is uncommon usage and it should not be encouraged. 57 // but it is uncommon usage and it should not be encouraged.
57 if self.missed_fields.iter().any(|it| it.as_tuple_index().is_some()) { 58 if self.missed_fields.iter().any(|it| it.as_tuple_index().is_some()) {
58 None 59 return None;
59 } else { 60 }
60 let root = sema.db.parse_or_expand(self.file)?;
61 let old_field_list = self.field_list_parent.to_node(&root).record_expr_field_list()?;
62 let mut new_field_list = old_field_list.clone();
63 for f in self.missed_fields.iter() {
64 let field = make::record_expr_field(
65 make::name_ref(&f.to_string()),
66 Some(make::expr_unit()),
67 );
68 new_field_list = new_field_list.append_field(&field);
69 }
70 61
71 let edit = { 62 let root = sema.db.parse_or_expand(self.file)?;
72 let mut builder = TextEditBuilder::default(); 63 let old_field_list = self.field_list_parent.to_node(&root).record_expr_field_list()?;
73 algo::diff(&old_field_list.syntax(), &new_field_list.syntax()) 64 let mut new_field_list = old_field_list.clone();
74 .into_text_edit(&mut builder); 65 for f in self.missed_fields.iter() {
75 builder.finish() 66 let field =
76 }; 67 make::record_expr_field(make::name_ref(&f.to_string()), Some(make::expr_unit()));
77 Some(( 68 new_field_list = new_field_list.append_field(&field);
78 Fix::new(
79 "Fill struct fields",
80 SourceFileEdit { file_id: self.file.original_file(sema.db), edit }.into(),
81 ),
82 sema.original_range(&old_field_list.syntax()).range,
83 // old_field_list.syntax().text_range(),
84 ))
85 } 69 }
70
71 let edit = {
72 let mut builder = TextEditBuilder::default();
73 algo::diff(&old_field_list.syntax(), &new_field_list.syntax())
74 .into_text_edit(&mut builder);
75 builder.finish()
76 };
77 Some(Fix::new(
78 "Fill struct fields",
79 SourceFileEdit { file_id: self.file.original_file(sema.db), edit }.into(),
80 sema.original_range(&old_field_list.syntax()).range,
81 ))
86 } 82 }
87} 83}
88 84
89impl DiagnosticWithFix for MissingOkInTailExpr { 85impl DiagnosticWithFix for MissingOkInTailExpr {
90 fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<(Fix, TextRange)> { 86 fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> {
91 let root = sema.db.parse_or_expand(self.file)?; 87 let root = sema.db.parse_or_expand(self.file)?;
92 let tail_expr = self.expr.to_node(&root); 88 let tail_expr = self.expr.to_node(&root);
93 let tail_expr_range = tail_expr.syntax().text_range(); 89 let tail_expr_range = tail_expr.syntax().text_range();
94 let edit = TextEdit::replace(tail_expr_range, format!("Ok({})", tail_expr.syntax())); 90 let edit = TextEdit::replace(tail_expr_range, format!("Ok({})", tail_expr.syntax()));
95 let source_change = 91 let source_change =
96 SourceFileEdit { file_id: self.file.original_file(sema.db), edit }.into(); 92 SourceFileEdit { file_id: self.file.original_file(sema.db), edit }.into();
97 Some((Fix::new("Wrap with ok", source_change), tail_expr_range)) 93 Some(Fix::new("Wrap with ok", source_change, tail_expr_range))
98 } 94 }
99} 95}
100 96
101fn missing_struct_field_fix( 97fn missing_record_expr_field_fix(
102 sema: &Semantics<RootDatabase>, 98 sema: &Semantics<RootDatabase>,
103 usage_file_id: FileId, 99 usage_file_id: FileId,
104 record_expr_field: &ast::RecordExprField, 100 record_expr_field: &ast::RecordExprField,
@@ -159,8 +155,11 @@ fn missing_struct_field_fix(
159 file_id: def_file_id, 155 file_id: def_file_id,
160 edit: TextEdit::insert(last_field_syntax.text_range().end(), new_field), 156 edit: TextEdit::insert(last_field_syntax.text_range().end(), new_field),
161 }; 157 };
162 let fix = Fix::new("Create field", source_change.into()); 158 return Some(Fix::new(
163 return Some(fix); 159 "Create field",
160 source_change.into(),
161 record_expr_field.syntax().text_range(),
162 ));
164 163
165 fn record_field_list(field_def_list: ast::FieldList) -> Option<ast::RecordFieldList> { 164 fn record_field_list(field_def_list: ast::FieldList) -> Option<ast::RecordFieldList> {
166 match field_def_list { 165 match field_def_list {