diff options
Diffstat (limited to 'crates/ra_ide_api/src')
-rw-r--r-- | crates/ra_ide_api/src/diagnostics.rs | 123 |
1 files changed, 122 insertions, 1 deletions
diff --git a/crates/ra_ide_api/src/diagnostics.rs b/crates/ra_ide_api/src/diagnostics.rs index b27cb690a..855a3ff0f 100644 --- a/crates/ra_ide_api/src/diagnostics.rs +++ b/crates/ra_ide_api/src/diagnostics.rs | |||
@@ -5,8 +5,9 @@ use hir::{source_binder, diagnostics::{Diagnostic as _, DiagnosticSink}}; | |||
5 | use ra_db::SourceDatabase; | 5 | use ra_db::SourceDatabase; |
6 | use ra_syntax::{ | 6 | use ra_syntax::{ |
7 | Location, SourceFile, SyntaxKind, TextRange, SyntaxNode, | 7 | Location, SourceFile, SyntaxKind, TextRange, SyntaxNode, |
8 | ast::{self, AstNode}, | 8 | ast::{self, AstNode, NamedFieldList, NamedField}, |
9 | }; | 9 | }; |
10 | use ra_assists::ast_editor::{AstEditor, AstBuilder}; | ||
10 | use ra_text_edit::{TextEdit, TextEditBuilder}; | 11 | use ra_text_edit::{TextEdit, TextEditBuilder}; |
11 | use ra_prof::profile; | 12 | use ra_prof::profile; |
12 | 13 | ||
@@ -48,6 +49,27 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> | |||
48 | severity: Severity::Error, | 49 | severity: Severity::Error, |
49 | fix: Some(fix), | 50 | fix: Some(fix), |
50 | }) | 51 | }) |
52 | }) | ||
53 | .on::<hir::diagnostics::MissingFields, _>(|d| { | ||
54 | let file_id = d.file().original_file(db); | ||
55 | let source_file = db.parse(file_id); | ||
56 | let syntax_node = d.syntax_node_ptr(); | ||
57 | let node = NamedFieldList::cast(syntax_node.to_node(&source_file)).unwrap(); | ||
58 | let mut ast_editor = AstEditor::new(node); | ||
59 | for f in d.missed_fields.iter() { | ||
60 | ast_editor.append_field(&AstBuilder::<NamedField>::from_name(f)); | ||
61 | } | ||
62 | |||
63 | let mut builder = TextEditBuilder::default(); | ||
64 | ast_editor.into_text_edit(&mut builder); | ||
65 | let fix = | ||
66 | SourceChange::source_file_edit_from("fill struct fields", file_id, builder.finish()); | ||
67 | res.borrow_mut().push(Diagnostic { | ||
68 | range: d.highlight_range(), | ||
69 | message: d.message(), | ||
70 | severity: Severity::Error, | ||
71 | fix: Some(fix), | ||
72 | }) | ||
51 | }); | 73 | }); |
52 | if let Some(m) = source_binder::module_from_file_id(db, file_id) { | 74 | if let Some(m) = source_binder::module_from_file_id(db, file_id) { |
53 | m.diagnostics(db, &mut sink); | 75 | m.diagnostics(db, &mut sink); |
@@ -187,6 +209,105 @@ mod tests { | |||
187 | assert_eq_text!(after, &actual); | 209 | assert_eq_text!(after, &actual); |
188 | } | 210 | } |
189 | 211 | ||
212 | fn check_apply_diagnostic_fix(before: &str, after: &str) { | ||
213 | let (analysis, file_id) = single_file(before); | ||
214 | let diagnostic = analysis.diagnostics(file_id).unwrap().pop().unwrap(); | ||
215 | let mut fix = diagnostic.fix.unwrap(); | ||
216 | let edit = fix.source_file_edits.pop().unwrap().edit; | ||
217 | let actual = edit.apply(&before); | ||
218 | assert_eq_text!(after, &actual); | ||
219 | } | ||
220 | |||
221 | fn check_no_diagnostic(content: &str) { | ||
222 | let (analysis, file_id) = single_file(content); | ||
223 | let diagnostics = analysis.diagnostics(file_id).unwrap(); | ||
224 | assert_eq!(diagnostics.len(), 0); | ||
225 | } | ||
226 | |||
227 | #[test] | ||
228 | fn test_fill_struct_fields_empty() { | ||
229 | let before = r" | ||
230 | struct TestStruct { | ||
231 | one: i32, | ||
232 | two: i64, | ||
233 | } | ||
234 | |||
235 | fn test_fn() { | ||
236 | let s = TestStruct{}; | ||
237 | } | ||
238 | "; | ||
239 | let after = r" | ||
240 | struct TestStruct { | ||
241 | one: i32, | ||
242 | two: i64, | ||
243 | } | ||
244 | |||
245 | fn test_fn() { | ||
246 | let s = TestStruct{ one: (), two: ()}; | ||
247 | } | ||
248 | "; | ||
249 | check_apply_diagnostic_fix(before, after); | ||
250 | } | ||
251 | |||
252 | #[test] | ||
253 | fn test_fill_struct_fields_partial() { | ||
254 | let before = r" | ||
255 | struct TestStruct { | ||
256 | one: i32, | ||
257 | two: i64, | ||
258 | } | ||
259 | |||
260 | fn test_fn() { | ||
261 | let s = TestStruct{ two: 2 }; | ||
262 | } | ||
263 | "; | ||
264 | let after = r" | ||
265 | struct TestStruct { | ||
266 | one: i32, | ||
267 | two: i64, | ||
268 | } | ||
269 | |||
270 | fn test_fn() { | ||
271 | let s = TestStruct{ two: 2, one: () }; | ||
272 | } | ||
273 | "; | ||
274 | check_apply_diagnostic_fix(before, after); | ||
275 | } | ||
276 | |||
277 | #[test] | ||
278 | fn test_fill_struct_fields_no_diagnostic() { | ||
279 | let content = r" | ||
280 | struct TestStruct { | ||
281 | one: i32, | ||
282 | two: i64, | ||
283 | } | ||
284 | |||
285 | fn test_fn() { | ||
286 | let one = 1; | ||
287 | let s = TestStruct{ one, two: 2 }; | ||
288 | } | ||
289 | "; | ||
290 | |||
291 | check_no_diagnostic(content); | ||
292 | } | ||
293 | |||
294 | #[test] | ||
295 | fn test_fill_struct_fields_no_diagnostic_on_spread() { | ||
296 | let content = r" | ||
297 | struct TestStruct { | ||
298 | one: i32, | ||
299 | two: i64, | ||
300 | } | ||
301 | |||
302 | fn test_fn() { | ||
303 | let one = 1; | ||
304 | let s = TestStruct{ ..a }; | ||
305 | } | ||
306 | "; | ||
307 | |||
308 | check_no_diagnostic(content); | ||
309 | } | ||
310 | |||
190 | #[test] | 311 | #[test] |
191 | fn test_unresolved_module_diagnostic() { | 312 | fn test_unresolved_module_diagnostic() { |
192 | let (analysis, file_id) = single_file("mod foo;"); | 313 | let (analysis, file_id) = single_file("mod foo;"); |