aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide
diff options
context:
space:
mode:
authorTimo Freiberg <[email protected]>2020-06-09 22:11:16 +0100
committerTimo Freiberg <[email protected]>2020-06-12 17:52:44 +0100
commitf5ac3130001ab7fe64dd542fd866a18012ee9971 (patch)
treefb084408f5cce4addbb44806301f0526c5277356 /crates/ra_ide
parentb56ad148db0c69eb279c225f45d324b4e80e7367 (diff)
Add quickfix to add a struct field
Diffstat (limited to 'crates/ra_ide')
-rw-r--r--crates/ra_ide/src/diagnostics.rs98
1 files changed, 96 insertions, 2 deletions
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
index e1bfd72f9..365d52168 100644
--- a/crates/ra_ide/src/diagnostics.rs
+++ b/crates/ra_ide/src/diagnostics.rs
@@ -8,7 +8,7 @@ use std::cell::RefCell;
8 8
9use hir::{ 9use hir::{
10 diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSink}, 10 diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSink},
11 Semantics, 11 HasSource, HirDisplay, Semantics, VariantDef,
12}; 12};
13use itertools::Itertools; 13use itertools::Itertools;
14use ra_db::{RelativePath, SourceDatabase, SourceDatabaseExt}; 14use ra_db::{RelativePath, SourceDatabase, SourceDatabaseExt};
@@ -16,7 +16,7 @@ use ra_ide_db::RootDatabase;
16use ra_prof::profile; 16use ra_prof::profile;
17use ra_syntax::{ 17use ra_syntax::{
18 algo, 18 algo,
19 ast::{self, make, AstNode}, 19 ast::{self, edit::IndentLevel, make, AstNode},
20 SyntaxNode, TextRange, T, 20 SyntaxNode, TextRange, T,
21}; 21};
22use ra_text_edit::{TextEdit, TextEditBuilder}; 22use ra_text_edit::{TextEdit, TextEditBuilder};
@@ -123,7 +123,16 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
123 severity: Severity::Error, 123 severity: Severity::Error,
124 fix: Some(fix), 124 fix: Some(fix),
125 }) 125 })
126 })
127 .on::<hir::diagnostics::NoSuchField, _>(|d| {
128 res.borrow_mut().push(Diagnostic {
129 range: sema.diagnostics_range(d).range,
130 message: d.message(),
131 severity: Severity::Error,
132 fix: missing_struct_field_fix(&sema, file_id, d),
133 })
126 }); 134 });
135
127 if let Some(m) = sema.to_module_def(file_id) { 136 if let Some(m) = sema.to_module_def(file_id) {
128 m.diagnostics(db, &mut sink); 137 m.diagnostics(db, &mut sink);
129 }; 138 };
@@ -131,6 +140,68 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
131 res.into_inner() 140 res.into_inner()
132} 141}
133 142
143fn missing_struct_field_fix(
144 sema: &Semantics<RootDatabase>,
145 file_id: FileId,
146 d: &hir::diagnostics::NoSuchField,
147) -> Option<Fix> {
148 let record_expr = sema.ast(d);
149
150 let record_lit = ast::RecordLit::cast(record_expr.syntax().parent()?.parent()?)?;
151 let def_id = sema.resolve_variant(record_lit)?;
152 let module;
153 let record_fields = match VariantDef::from(def_id) {
154 VariantDef::Struct(s) => {
155 module = s.module(sema.db);
156 let source = s.source(sema.db);
157 let fields = source.value.field_def_list()?;
158 record_field_def_list(fields)?
159 }
160 VariantDef::Union(u) => {
161 module = u.module(sema.db);
162 let source = u.source(sema.db);
163 source.value.record_field_def_list()?
164 }
165 VariantDef::EnumVariant(e) => {
166 module = e.module(sema.db);
167 let source = e.source(sema.db);
168 let fields = source.value.field_def_list()?;
169 record_field_def_list(fields)?
170 }
171 };
172
173 let new_field_type = sema.type_of_expr(&record_expr.expr()?)?;
174 let new_field = make::record_field_def(
175 record_expr.field_name()?,
176 make::type_ref(&new_field_type.display_source_code(sema.db, module.into()).ok()?),
177 );
178
179 let last_field = record_fields.fields().last()?;
180 let last_field_syntax = last_field.syntax();
181 let indent = IndentLevel::from_node(last_field_syntax);
182
183 let mut new_field = format!("\n{}{}", indent, new_field);
184
185 let needs_comma = !last_field_syntax.to_string().ends_with(",");
186 if needs_comma {
187 new_field = format!(",{}", new_field);
188 }
189
190 let source_change = SourceFileEdit {
191 file_id,
192 edit: TextEdit::insert(last_field_syntax.text_range().end(), new_field),
193 };
194 let fix = Fix::new("Create field", source_change.into());
195 return Some(fix);
196
197 fn record_field_def_list(field_def_list: ast::FieldDefList) -> Option<ast::RecordFieldDefList> {
198 match field_def_list {
199 ast::FieldDefList::RecordFieldDefList(it) => Some(it),
200 ast::FieldDefList::TupleFieldDefList(_) => None,
201 }
202 }
203}
204
134fn check_unnecessary_braces_in_use_statement( 205fn check_unnecessary_braces_in_use_statement(
135 acc: &mut Vec<Diagnostic>, 206 acc: &mut Vec<Diagnostic>,
136 file_id: FileId, 207 file_id: FileId,
@@ -795,4 +866,27 @@ fn main() {
795 check_struct_shorthand_initialization, 866 check_struct_shorthand_initialization,
796 ); 867 );
797 } 868 }
869
870 #[test]
871 fn test_add_field_from_usage() {
872 check_apply_diagnostic_fix(
873 r"
874 fn main() {
875 Foo { bar: 3, baz: false};
876 }
877 struct Foo {
878 bar: i32
879 }
880 ",
881 r"
882 fn main() {
883 Foo { bar: 3, baz: false};
884 }
885 struct Foo {
886 bar: i32,
887 baz: bool
888 }
889 ",
890 )
891 }
798} 892}