diff options
author | Kirill Bulatov <[email protected]> | 2020-07-27 13:53:57 +0100 |
---|---|---|
committer | Kirill Bulatov <[email protected]> | 2020-08-11 13:09:08 +0100 |
commit | 26e102a567aadcf86f2e5b575cb6b915991ba088 (patch) | |
tree | 2075de8433e6f99ee79849724cb402cf232d8426 /crates/ra_ide | |
parent | e0de2475208765a171f335dfffde764f96243d41 (diff) |
Separate diagnostics and diagnostics fix ranges
Diffstat (limited to 'crates/ra_ide')
-rw-r--r-- | crates/ra_ide/src/diagnostics.rs | 98 | ||||
-rw-r--r-- | crates/ra_ide/src/lib.rs | 2 |
2 files changed, 56 insertions, 44 deletions
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs index 73c0b8275..5c8ea46ab 100644 --- a/crates/ra_ide/src/diagnostics.rs +++ b/crates/ra_ide/src/diagnostics.rs | |||
@@ -60,14 +60,16 @@ pub(crate) fn diagnostics( | |||
60 | FileSystemEdit::CreateFile { anchor: original_file, dst: d.candidate.clone() } | 60 | FileSystemEdit::CreateFile { anchor: original_file, dst: d.candidate.clone() } |
61 | .into(), | 61 | .into(), |
62 | ); | 62 | ); |
63 | let range = sema.diagnostics_range(d).range; | ||
63 | res.borrow_mut().push(Diagnostic { | 64 | res.borrow_mut().push(Diagnostic { |
64 | range: sema.diagnostics_range(d).range, | 65 | range, |
65 | message: d.message(), | 66 | message: d.message(), |
66 | severity: Severity::Error, | 67 | severity: Severity::Error, |
67 | fix: Some(fix), | 68 | fix: Some((fix, range)), |
68 | }) | 69 | }) |
69 | }) | 70 | }) |
70 | .on::<hir::diagnostics::MissingFields, _>(|d| { | 71 | .on::<hir::diagnostics::MissingFields, _>(|d| { |
72 | let range = sema.diagnostics_range(d).range; | ||
71 | // Note that although we could add a diagnostics to | 73 | // Note that although we could add a diagnostics to |
72 | // fill the missing tuple field, e.g : | 74 | // fill the missing tuple field, e.g : |
73 | // `struct A(usize);` | 75 | // `struct A(usize);` |
@@ -91,11 +93,15 @@ pub(crate) fn diagnostics( | |||
91 | .into_text_edit(&mut builder); | 93 | .into_text_edit(&mut builder); |
92 | builder.finish() | 94 | builder.finish() |
93 | }; | 95 | }; |
94 | Some(Fix::new("Fill struct fields", SourceFileEdit { file_id, edit }.into())) | 96 | Some(( |
97 | Fix::new("Fill struct fields", SourceFileEdit { file_id, edit }.into()), | ||
98 | range, | ||
99 | )) | ||
95 | }; | 100 | }; |
96 | 101 | ||
97 | res.borrow_mut().push(Diagnostic { | 102 | res.borrow_mut().push(Diagnostic { |
98 | range: sema.diagnostics_range(d).range, | 103 | // TODO kb use a smaller range here |
104 | range, | ||
99 | message: d.message(), | 105 | message: d.message(), |
100 | severity: Severity::Error, | 106 | severity: Severity::Error, |
101 | fix, | 107 | fix, |
@@ -106,20 +112,21 @@ pub(crate) fn diagnostics( | |||
106 | let replacement = format!("Ok({})", node.syntax()); | 112 | let replacement = format!("Ok({})", node.syntax()); |
107 | let edit = TextEdit::replace(node.syntax().text_range(), replacement); | 113 | let edit = TextEdit::replace(node.syntax().text_range(), replacement); |
108 | let source_change = SourceFileEdit { file_id, edit }.into(); | 114 | let source_change = SourceFileEdit { file_id, edit }.into(); |
109 | let fix = Fix::new("Wrap with ok", source_change); | 115 | let range = sema.diagnostics_range(d).range; |
110 | res.borrow_mut().push(Diagnostic { | 116 | res.borrow_mut().push(Diagnostic { |
111 | range: sema.diagnostics_range(d).range, | 117 | range, |
112 | message: d.message(), | 118 | message: d.message(), |
113 | severity: Severity::Error, | 119 | severity: Severity::Error, |
114 | fix: Some(fix), | 120 | fix: Some((Fix::new("Wrap with ok", source_change), range)), |
115 | }) | 121 | }) |
116 | }) | 122 | }) |
117 | .on::<hir::diagnostics::NoSuchField, _>(|d| { | 123 | .on::<hir::diagnostics::NoSuchField, _>(|d| { |
124 | let range = sema.diagnostics_range(d).range; | ||
118 | res.borrow_mut().push(Diagnostic { | 125 | res.borrow_mut().push(Diagnostic { |
119 | range: sema.diagnostics_range(d).range, | 126 | range, |
120 | message: d.message(), | 127 | message: d.message(), |
121 | severity: Severity::Error, | 128 | severity: Severity::Error, |
122 | fix: missing_struct_field_fix(&sema, file_id, d), | 129 | fix: missing_struct_field_fix(&sema, file_id, d).map(|fix| (fix, range)), |
123 | }) | 130 | }) |
124 | }) | 131 | }) |
125 | // Only collect experimental diagnostics when they're enabled. | 132 | // Only collect experimental diagnostics when they're enabled. |
@@ -222,24 +229,24 @@ fn check_unnecessary_braces_in_use_statement( | |||
222 | ) -> Option<()> { | 229 | ) -> Option<()> { |
223 | let use_tree_list = ast::UseTreeList::cast(node.clone())?; | 230 | let use_tree_list = ast::UseTreeList::cast(node.clone())?; |
224 | if let Some((single_use_tree,)) = use_tree_list.use_trees().collect_tuple() { | 231 | if let Some((single_use_tree,)) = use_tree_list.use_trees().collect_tuple() { |
225 | let range = use_tree_list.syntax().text_range(); | 232 | let use_range = use_tree_list.syntax().text_range(); |
226 | let edit = | 233 | let edit = |
227 | text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(&single_use_tree) | 234 | text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(&single_use_tree) |
228 | .unwrap_or_else(|| { | 235 | .unwrap_or_else(|| { |
229 | let to_replace = single_use_tree.syntax().text().to_string(); | 236 | let to_replace = single_use_tree.syntax().text().to_string(); |
230 | let mut edit_builder = TextEditBuilder::default(); | 237 | let mut edit_builder = TextEditBuilder::default(); |
231 | edit_builder.delete(range); | 238 | edit_builder.delete(use_range); |
232 | edit_builder.insert(range.start(), to_replace); | 239 | edit_builder.insert(use_range.start(), to_replace); |
233 | edit_builder.finish() | 240 | edit_builder.finish() |
234 | }); | 241 | }); |
235 | 242 | ||
236 | acc.push(Diagnostic { | 243 | acc.push(Diagnostic { |
237 | range, | 244 | range: use_range, |
238 | message: "Unnecessary braces in use statement".to_string(), | 245 | message: "Unnecessary braces in use statement".to_string(), |
239 | severity: Severity::WeakWarning, | 246 | severity: Severity::WeakWarning, |
240 | fix: Some(Fix::new( | 247 | fix: Some(( |
241 | "Remove unnecessary braces", | 248 | Fix::new("Remove unnecessary braces", SourceFileEdit { file_id, edit }.into()), |
242 | SourceFileEdit { file_id, edit }.into(), | 249 | use_range, |
243 | )), | 250 | )), |
244 | }); | 251 | }); |
245 | } | 252 | } |
@@ -254,8 +261,7 @@ fn text_edit_for_remove_unnecessary_braces_with_self_in_use_statement( | |||
254 | if single_use_tree.path()?.segment()?.syntax().first_child_or_token()?.kind() == T![self] { | 261 | if single_use_tree.path()?.segment()?.syntax().first_child_or_token()?.kind() == T![self] { |
255 | let start = use_tree_list_node.prev_sibling_or_token()?.text_range().start(); | 262 | let start = use_tree_list_node.prev_sibling_or_token()?.text_range().start(); |
256 | let end = use_tree_list_node.text_range().end(); | 263 | let end = use_tree_list_node.text_range().end(); |
257 | let range = TextRange::new(start, end); | 264 | return Some(TextEdit::delete(TextRange::new(start, end))); |
258 | return Some(TextEdit::delete(range)); | ||
259 | } | 265 | } |
260 | None | 266 | None |
261 | } | 267 | } |
@@ -278,13 +284,17 @@ fn check_struct_shorthand_initialization( | |||
278 | edit_builder.insert(record_field.syntax().text_range().start(), field_name); | 284 | edit_builder.insert(record_field.syntax().text_range().start(), field_name); |
279 | let edit = edit_builder.finish(); | 285 | let edit = edit_builder.finish(); |
280 | 286 | ||
287 | let field_range = record_field.syntax().text_range(); | ||
281 | acc.push(Diagnostic { | 288 | acc.push(Diagnostic { |
282 | range: record_field.syntax().text_range(), | 289 | range: field_range, |
283 | message: "Shorthand struct initialization".to_string(), | 290 | message: "Shorthand struct initialization".to_string(), |
284 | severity: Severity::WeakWarning, | 291 | severity: Severity::WeakWarning, |
285 | fix: Some(Fix::new( | 292 | fix: Some(( |
286 | "Use struct shorthand initialization", | 293 | Fix::new( |
287 | SourceFileEdit { file_id, edit }.into(), | 294 | "Use struct shorthand initialization", |
295 | SourceFileEdit { file_id, edit }.into(), | ||
296 | ), | ||
297 | field_range, | ||
288 | )), | 298 | )), |
289 | }); | 299 | }); |
290 | } | 300 | } |
@@ -304,14 +314,14 @@ mod tests { | |||
304 | /// Takes a multi-file input fixture with annotated cursor positions, | 314 | /// Takes a multi-file input fixture with annotated cursor positions, |
305 | /// and checks that: | 315 | /// and checks that: |
306 | /// * a diagnostic is produced | 316 | /// * a diagnostic is produced |
307 | /// * this diagnostic touches the input cursor position | 317 | /// * this diagnostic fix touches the input cursor position |
308 | /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied | 318 | /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied |
309 | fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) { | 319 | fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) { |
310 | let after = trim_indent(ra_fixture_after); | 320 | let after = trim_indent(ra_fixture_after); |
311 | 321 | ||
312 | let (analysis, file_position) = analysis_and_position(ra_fixture_before); | 322 | let (analysis, file_position) = analysis_and_position(ra_fixture_before); |
313 | let diagnostic = analysis.diagnostics(file_position.file_id, true).unwrap().pop().unwrap(); | 323 | let diagnostic = analysis.diagnostics(file_position.file_id, true).unwrap().pop().unwrap(); |
314 | let mut fix = diagnostic.fix.unwrap(); | 324 | let (mut fix, fix_range) = diagnostic.fix.unwrap(); |
315 | let edit = fix.source_change.source_file_edits.pop().unwrap().edit; | 325 | let edit = fix.source_change.source_file_edits.pop().unwrap().edit; |
316 | let target_file_contents = analysis.file_text(file_position.file_id).unwrap(); | 326 | let target_file_contents = analysis.file_text(file_position.file_id).unwrap(); |
317 | let actual = { | 327 | let actual = { |
@@ -322,10 +332,9 @@ mod tests { | |||
322 | 332 | ||
323 | assert_eq_text!(&after, &actual); | 333 | assert_eq_text!(&after, &actual); |
324 | assert!( | 334 | assert!( |
325 | diagnostic.range.start() <= file_position.offset | 335 | fix_range.start() <= file_position.offset && fix_range.end() >= file_position.offset, |
326 | && diagnostic.range.end() >= file_position.offset, | 336 | "diagnostic fix range {:?} does not touch cursor position {:?}", |
327 | "diagnostic range {:?} does not touch cursor position {:?}", | 337 | fix_range, |
328 | diagnostic.range, | ||
329 | file_position.offset | 338 | file_position.offset |
330 | ); | 339 | ); |
331 | } | 340 | } |
@@ -337,7 +346,7 @@ mod tests { | |||
337 | let (analysis, file_pos) = analysis_and_position(ra_fixture_before); | 346 | let (analysis, file_pos) = analysis_and_position(ra_fixture_before); |
338 | let current_file_id = file_pos.file_id; | 347 | let current_file_id = file_pos.file_id; |
339 | let diagnostic = analysis.diagnostics(current_file_id, true).unwrap().pop().unwrap(); | 348 | let diagnostic = analysis.diagnostics(current_file_id, true).unwrap().pop().unwrap(); |
340 | let mut fix = diagnostic.fix.unwrap(); | 349 | let mut fix = diagnostic.fix.unwrap().0; |
341 | let edit = fix.source_change.source_file_edits.pop().unwrap(); | 350 | let edit = fix.source_change.source_file_edits.pop().unwrap(); |
342 | let changed_file_id = edit.file_id; | 351 | let changed_file_id = edit.file_id; |
343 | let before = analysis.file_text(changed_file_id).unwrap(); | 352 | let before = analysis.file_text(changed_file_id).unwrap(); |
@@ -628,21 +637,24 @@ fn test_fn() { | |||
628 | range: 0..8, | 637 | range: 0..8, |
629 | severity: Error, | 638 | severity: Error, |
630 | fix: Some( | 639 | fix: Some( |
631 | Fix { | 640 | ( |
632 | label: "Create module", | 641 | Fix { |
633 | source_change: SourceChange { | 642 | label: "Create module", |
634 | source_file_edits: [], | 643 | source_change: SourceChange { |
635 | file_system_edits: [ | 644 | source_file_edits: [], |
636 | CreateFile { | 645 | file_system_edits: [ |
637 | anchor: FileId( | 646 | CreateFile { |
638 | 1, | 647 | anchor: FileId( |
639 | ), | 648 | 1, |
640 | dst: "foo.rs", | 649 | ), |
641 | }, | 650 | dst: "foo.rs", |
642 | ], | 651 | }, |
643 | is_snippet: false, | 652 | ], |
653 | is_snippet: false, | ||
654 | }, | ||
644 | }, | 655 | }, |
645 | }, | 656 | 0..8, |
657 | ), | ||
646 | ), | 658 | ), |
647 | }, | 659 | }, |
648 | ] | 660 | ] |
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 0fede0d87..45a4b2421 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs | |||
@@ -105,7 +105,7 @@ pub struct Diagnostic { | |||
105 | pub message: String, | 105 | pub message: String, |
106 | pub range: TextRange, | 106 | pub range: TextRange, |
107 | pub severity: Severity, | 107 | pub severity: Severity, |
108 | pub fix: Option<Fix>, | 108 | pub fix: Option<(Fix, TextRange)>, |
109 | } | 109 | } |
110 | 110 | ||
111 | #[derive(Debug)] | 111 | #[derive(Debug)] |