aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide
diff options
context:
space:
mode:
authorKirill Bulatov <[email protected]>2020-07-27 13:53:57 +0100
committerKirill Bulatov <[email protected]>2020-08-11 13:09:08 +0100
commit26e102a567aadcf86f2e5b575cb6b915991ba088 (patch)
tree2075de8433e6f99ee79849724cb402cf232d8426 /crates/ra_ide
parente0de2475208765a171f335dfffde764f96243d41 (diff)
Separate diagnostics and diagnostics fix ranges
Diffstat (limited to 'crates/ra_ide')
-rw-r--r--crates/ra_ide/src/diagnostics.rs98
-rw-r--r--crates/ra_ide/src/lib.rs2
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)]