aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/diagnostics.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src/diagnostics.rs')
-rw-r--r--crates/ra_ide/src/diagnostics.rs156
1 files changed, 83 insertions, 73 deletions
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
index e029af0dc..897177d05 100644
--- a/crates/ra_ide/src/diagnostics.rs
+++ b/crates/ra_ide/src/diagnostics.rs
@@ -7,7 +7,7 @@
7use std::cell::RefCell; 7use std::cell::RefCell;
8 8
9use hir::{ 9use hir::{
10 diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSink}, 10 diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSinkBuilder},
11 HasSource, HirDisplay, Semantics, VariantDef, 11 HasSource, HirDisplay, Semantics, VariantDef,
12}; 12};
13use itertools::Itertools; 13use itertools::Itertools;
@@ -29,7 +29,11 @@ pub enum Severity {
29 WeakWarning, 29 WeakWarning,
30} 30}
31 31
32pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> { 32pub(crate) fn diagnostics(
33 db: &RootDatabase,
34 file_id: FileId,
35 enable_experimental: bool,
36) -> Vec<Diagnostic> {
33 let _p = profile("diagnostics"); 37 let _p = profile("diagnostics");
34 let sema = Semantics::new(db); 38 let sema = Semantics::new(db);
35 let parse = db.parse(file_id); 39 let parse = db.parse(file_id);
@@ -48,79 +52,85 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
48 check_struct_shorthand_initialization(&mut res, file_id, &node); 52 check_struct_shorthand_initialization(&mut res, file_id, &node);
49 } 53 }
50 let res = RefCell::new(res); 54 let res = RefCell::new(res);
51 let mut sink = DiagnosticSink::new(|d| { 55 let mut sink = DiagnosticSinkBuilder::new()
52 res.borrow_mut().push(Diagnostic { 56 .on::<hir::diagnostics::UnresolvedModule, _>(|d| {
53 message: d.message(), 57 let original_file = d.source().file_id.original_file(db);
54 range: sema.diagnostics_range(d).range, 58 let fix = Fix::new(
55 severity: Severity::Error, 59 "Create module",
56 fix: None, 60 FileSystemEdit::CreateFile { anchor: original_file, dst: d.candidate.clone() }
57 }) 61 .into(),
58 }) 62 );
59 .on::<hir::diagnostics::UnresolvedModule, _>(|d| { 63 res.borrow_mut().push(Diagnostic {
60 let original_file = d.source().file_id.original_file(db); 64 range: sema.diagnostics_range(d).range,
61 let fix = Fix::new( 65 message: d.message(),
62 "Create module", 66 severity: Severity::Error,
63 FileSystemEdit::CreateFile { anchor: original_file, dst: d.candidate.clone() }.into(), 67 fix: Some(fix),
64 ); 68 })
65 res.borrow_mut().push(Diagnostic {
66 range: sema.diagnostics_range(d).range,
67 message: d.message(),
68 severity: Severity::Error,
69 fix: Some(fix),
70 }) 69 })
71 }) 70 .on::<hir::diagnostics::MissingFields, _>(|d| {
72 .on::<hir::diagnostics::MissingFields, _>(|d| { 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);` 74 // `let a = A { 0: () }`
76 // `let a = A { 0: () }` 75 // but it is uncommon usage and it should not be encouraged.
77 // but it is uncommon usage and it should not be encouraged. 76 let fix = if d.missed_fields.iter().any(|it| it.as_tuple_index().is_some()) {
78 let fix = if d.missed_fields.iter().any(|it| it.as_tuple_index().is_some()) { 77 None
79 None 78 } else {
80 } else { 79 let mut field_list = d.ast(db);
81 let mut field_list = d.ast(db); 80 for f in d.missed_fields.iter() {
82 for f in d.missed_fields.iter() { 81 let field =
83 let field = 82 make::record_field(make::name_ref(&f.to_string()), Some(make::expr_unit()));
84 make::record_field(make::name_ref(&f.to_string()), Some(make::expr_unit())); 83 field_list = field_list.append_field(&field);
85 field_list = field_list.append_field(&field); 84 }
86 } 85
87 86 let edit = {
88 let edit = { 87 let mut builder = TextEditBuilder::default();
89 let mut builder = TextEditBuilder::default(); 88 algo::diff(&d.ast(db).syntax(), &field_list.syntax())
90 algo::diff(&d.ast(db).syntax(), &field_list.syntax()).into_text_edit(&mut builder); 89 .into_text_edit(&mut builder);
91 builder.finish() 90 builder.finish()
91 };
92 Some(Fix::new("Fill struct fields", SourceFileEdit { file_id, edit }.into()))
92 }; 93 };
93 Some(Fix::new("Fill struct fields", SourceFileEdit { file_id, edit }.into()))
94 };
95 94
96 res.borrow_mut().push(Diagnostic { 95 res.borrow_mut().push(Diagnostic {
97 range: sema.diagnostics_range(d).range, 96 range: sema.diagnostics_range(d).range,
98 message: d.message(), 97 message: d.message(),
99 severity: Severity::Error, 98 severity: Severity::Error,
100 fix, 99 fix,
100 })
101 }) 101 })
102 }) 102 .on::<hir::diagnostics::MissingOkInTailExpr, _>(|d| {
103 .on::<hir::diagnostics::MissingOkInTailExpr, _>(|d| { 103 let node = d.ast(db);
104 let node = d.ast(db); 104 let replacement = format!("Ok({})", node.syntax());
105 let replacement = format!("Ok({})", node.syntax()); 105 let edit = TextEdit::replace(node.syntax().text_range(), replacement);
106 let edit = TextEdit::replace(node.syntax().text_range(), replacement); 106 let source_change = SourceFileEdit { file_id, edit }.into();
107 let source_change = SourceFileEdit { file_id, edit }.into(); 107 let fix = Fix::new("Wrap with ok", source_change);
108 let fix = Fix::new("Wrap with ok", source_change); 108 res.borrow_mut().push(Diagnostic {
109 res.borrow_mut().push(Diagnostic { 109 range: sema.diagnostics_range(d).range,
110 range: sema.diagnostics_range(d).range, 110 message: d.message(),
111 message: d.message(), 111 severity: Severity::Error,
112 severity: Severity::Error, 112 fix: Some(fix),
113 fix: Some(fix), 113 })
114 }) 114 })
115 }) 115 .on::<hir::diagnostics::NoSuchField, _>(|d| {
116 .on::<hir::diagnostics::NoSuchField, _>(|d| { 116 res.borrow_mut().push(Diagnostic {
117 res.borrow_mut().push(Diagnostic { 117 range: sema.diagnostics_range(d).range,
118 range: sema.diagnostics_range(d).range, 118 message: d.message(),
119 message: d.message(), 119 severity: Severity::Error,
120 severity: Severity::Error, 120 fix: missing_struct_field_fix(&sema, file_id, d),
121 fix: missing_struct_field_fix(&sema, file_id, d), 121 })
122 }) 122 })
123 }); 123 // Only collect experimental diagnostics when they're enabled.
124 .filter(|diag| !diag.is_experimental() || enable_experimental)
125 // Diagnostics not handled above get no fix and default treatment.
126 .build(|d| {
127 res.borrow_mut().push(Diagnostic {
128 message: d.message(),
129 range: sema.diagnostics_range(d).range,
130 severity: Severity::Error,
131 fix: None,
132 })
133 });
124 134
125 if let Some(m) = sema.to_module_def(file_id) { 135 if let Some(m) = sema.to_module_def(file_id) {
126 m.diagnostics(db, &mut sink); 136 m.diagnostics(db, &mut sink);
@@ -298,7 +308,7 @@ mod tests {
298 let after = trim_indent(ra_fixture_after); 308 let after = trim_indent(ra_fixture_after);
299 309
300 let (analysis, file_position) = analysis_and_position(ra_fixture_before); 310 let (analysis, file_position) = analysis_and_position(ra_fixture_before);
301 let diagnostic = analysis.diagnostics(file_position.file_id).unwrap().pop().unwrap(); 311 let diagnostic = analysis.diagnostics(file_position.file_id, true).unwrap().pop().unwrap();
302 let mut fix = diagnostic.fix.unwrap(); 312 let mut fix = diagnostic.fix.unwrap();
303 let edit = fix.source_change.source_file_edits.pop().unwrap().edit; 313 let edit = fix.source_change.source_file_edits.pop().unwrap().edit;
304 let target_file_contents = analysis.file_text(file_position.file_id).unwrap(); 314 let target_file_contents = analysis.file_text(file_position.file_id).unwrap();
@@ -324,7 +334,7 @@ mod tests {
324 let ra_fixture_after = &trim_indent(ra_fixture_after); 334 let ra_fixture_after = &trim_indent(ra_fixture_after);
325 let (analysis, file_pos) = analysis_and_position(ra_fixture_before); 335 let (analysis, file_pos) = analysis_and_position(ra_fixture_before);
326 let current_file_id = file_pos.file_id; 336 let current_file_id = file_pos.file_id;
327 let diagnostic = analysis.diagnostics(current_file_id).unwrap().pop().unwrap(); 337 let diagnostic = analysis.diagnostics(current_file_id, true).unwrap().pop().unwrap();
328 let mut fix = diagnostic.fix.unwrap(); 338 let mut fix = diagnostic.fix.unwrap();
329 let edit = fix.source_change.source_file_edits.pop().unwrap(); 339 let edit = fix.source_change.source_file_edits.pop().unwrap();
330 let changed_file_id = edit.file_id; 340 let changed_file_id = edit.file_id;
@@ -345,14 +355,14 @@ mod tests {
345 let analysis = mock.analysis(); 355 let analysis = mock.analysis();
346 let diagnostics = files 356 let diagnostics = files
347 .into_iter() 357 .into_iter()
348 .flat_map(|file_id| analysis.diagnostics(file_id).unwrap()) 358 .flat_map(|file_id| analysis.diagnostics(file_id, true).unwrap())
349 .collect::<Vec<_>>(); 359 .collect::<Vec<_>>();
350 assert_eq!(diagnostics.len(), 0, "unexpected diagnostics:\n{:#?}", diagnostics); 360 assert_eq!(diagnostics.len(), 0, "unexpected diagnostics:\n{:#?}", diagnostics);
351 } 361 }
352 362
353 fn check_expect(ra_fixture: &str, expect: Expect) { 363 fn check_expect(ra_fixture: &str, expect: Expect) {
354 let (analysis, file_id) = single_file(ra_fixture); 364 let (analysis, file_id) = single_file(ra_fixture);
355 let diagnostics = analysis.diagnostics(file_id).unwrap(); 365 let diagnostics = analysis.diagnostics(file_id, true).unwrap();
356 expect.assert_debug_eq(&diagnostics) 366 expect.assert_debug_eq(&diagnostics)
357 } 367 }
358 368