diff options
-rw-r--r-- | crates/ra_hir/src/diagnostics.rs | 4 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/diagnostics.rs | 43 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/diagnostics.rs | 4 | ||||
-rw-r--r-- | crates/ra_ide/src/diagnostics.rs | 139 |
4 files changed, 102 insertions, 88 deletions
diff --git a/crates/ra_hir/src/diagnostics.rs b/crates/ra_hir/src/diagnostics.rs index 11a0ecb8b..266b513dc 100644 --- a/crates/ra_hir/src/diagnostics.rs +++ b/crates/ra_hir/src/diagnostics.rs | |||
@@ -1,6 +1,8 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | pub use hir_def::diagnostics::UnresolvedModule; | 2 | pub use hir_def::diagnostics::UnresolvedModule; |
3 | pub use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink}; | 3 | pub use hir_expand::diagnostics::{ |
4 | AstDiagnostic, Diagnostic, DiagnosticSink, DiagnosticSinkBuilder, | ||
5 | }; | ||
4 | pub use hir_ty::diagnostics::{ | 6 | pub use hir_ty::diagnostics::{ |
5 | MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr, NoSuchField, | 7 | MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr, NoSuchField, |
6 | }; | 8 | }; |
diff --git a/crates/ra_hir_expand/src/diagnostics.rs b/crates/ra_hir_expand/src/diagnostics.rs index 545cff9bd..6a5844f31 100644 --- a/crates/ra_hir_expand/src/diagnostics.rs +++ b/crates/ra_hir_expand/src/diagnostics.rs | |||
@@ -48,23 +48,6 @@ pub struct DiagnosticSink<'a> { | |||
48 | } | 48 | } |
49 | 49 | ||
50 | impl<'a> DiagnosticSink<'a> { | 50 | impl<'a> DiagnosticSink<'a> { |
51 | /// FIXME: split `new` and `on` into a separate builder type | ||
52 | pub fn new(cb: impl FnMut(&dyn Diagnostic) + 'a) -> DiagnosticSink<'a> { | ||
53 | DiagnosticSink { callbacks: Vec::new(), default_callback: Box::new(cb) } | ||
54 | } | ||
55 | |||
56 | pub fn on<D: Diagnostic, F: FnMut(&D) + 'a>(mut self, mut cb: F) -> DiagnosticSink<'a> { | ||
57 | let cb = move |diag: &dyn Diagnostic| match diag.downcast_ref::<D>() { | ||
58 | Some(d) => { | ||
59 | cb(d); | ||
60 | Ok(()) | ||
61 | } | ||
62 | None => Err(()), | ||
63 | }; | ||
64 | self.callbacks.push(Box::new(cb)); | ||
65 | self | ||
66 | } | ||
67 | |||
68 | pub fn push(&mut self, d: impl Diagnostic) { | 51 | pub fn push(&mut self, d: impl Diagnostic) { |
69 | let d: &dyn Diagnostic = &d; | 52 | let d: &dyn Diagnostic = &d; |
70 | self._push(d); | 53 | self._push(d); |
@@ -80,3 +63,29 @@ impl<'a> DiagnosticSink<'a> { | |||
80 | (self.default_callback)(d) | 63 | (self.default_callback)(d) |
81 | } | 64 | } |
82 | } | 65 | } |
66 | |||
67 | pub struct DiagnosticSinkBuilder<'a> { | ||
68 | callbacks: Vec<Box<dyn FnMut(&dyn Diagnostic) -> Result<(), ()> + 'a>>, | ||
69 | } | ||
70 | |||
71 | impl<'a> DiagnosticSinkBuilder<'a> { | ||
72 | pub fn new() -> Self { | ||
73 | Self { callbacks: Vec::new() } | ||
74 | } | ||
75 | |||
76 | pub fn on<D: Diagnostic, F: FnMut(&D) + 'a>(mut self, mut cb: F) -> Self { | ||
77 | let cb = move |diag: &dyn Diagnostic| match diag.downcast_ref::<D>() { | ||
78 | Some(d) => { | ||
79 | cb(d); | ||
80 | Ok(()) | ||
81 | } | ||
82 | None => Err(()), | ||
83 | }; | ||
84 | self.callbacks.push(Box::new(cb)); | ||
85 | self | ||
86 | } | ||
87 | |||
88 | pub fn build<F: FnMut(&dyn Diagnostic) + 'a>(self, default_callback: F) -> DiagnosticSink<'a> { | ||
89 | DiagnosticSink { callbacks: self.callbacks, default_callback: Box::new(default_callback) } | ||
90 | } | ||
91 | } | ||
diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs index d3ee9cf55..a9877d867 100644 --- a/crates/ra_hir_ty/src/diagnostics.rs +++ b/crates/ra_hir_ty/src/diagnostics.rs | |||
@@ -248,7 +248,7 @@ impl AstDiagnostic for MismatchedArgCount { | |||
248 | #[cfg(test)] | 248 | #[cfg(test)] |
249 | mod tests { | 249 | mod tests { |
250 | use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId}; | 250 | use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId}; |
251 | use hir_expand::diagnostics::{Diagnostic, DiagnosticSink}; | 251 | use hir_expand::diagnostics::{Diagnostic, DiagnosticSinkBuilder}; |
252 | use ra_db::{fixture::WithFixture, FileId, SourceDatabase, SourceDatabaseExt}; | 252 | use ra_db::{fixture::WithFixture, FileId, SourceDatabase, SourceDatabaseExt}; |
253 | use ra_syntax::{TextRange, TextSize}; | 253 | use ra_syntax::{TextRange, TextSize}; |
254 | use rustc_hash::FxHashMap; | 254 | use rustc_hash::FxHashMap; |
@@ -280,7 +280,7 @@ mod tests { | |||
280 | } | 280 | } |
281 | 281 | ||
282 | for f in fns { | 282 | for f in fns { |
283 | let mut sink = DiagnosticSink::new(&mut cb); | 283 | let mut sink = DiagnosticSinkBuilder::new().build(&mut cb); |
284 | validate_body(self, f.into(), &mut sink); | 284 | validate_body(self, f.into(), &mut sink); |
285 | } | 285 | } |
286 | } | 286 | } |
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs index e029af0dc..8e715faa4 100644 --- a/crates/ra_ide/src/diagnostics.rs +++ b/crates/ra_ide/src/diagnostics.rs | |||
@@ -7,7 +7,7 @@ | |||
7 | use std::cell::RefCell; | 7 | use std::cell::RefCell; |
8 | 8 | ||
9 | use hir::{ | 9 | use 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 | }; |
13 | use itertools::Itertools; | 13 | use itertools::Itertools; |
@@ -48,79 +48,82 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> | |||
48 | check_struct_shorthand_initialization(&mut res, file_id, &node); | 48 | check_struct_shorthand_initialization(&mut res, file_id, &node); |
49 | } | 49 | } |
50 | let res = RefCell::new(res); | 50 | let res = RefCell::new(res); |
51 | let mut sink = DiagnosticSink::new(|d| { | 51 | let mut sink = DiagnosticSinkBuilder::new() |
52 | res.borrow_mut().push(Diagnostic { | 52 | .on::<hir::diagnostics::UnresolvedModule, _>(|d| { |
53 | message: d.message(), | 53 | let original_file = d.source().file_id.original_file(db); |
54 | range: sema.diagnostics_range(d).range, | 54 | let fix = Fix::new( |
55 | severity: Severity::Error, | 55 | "Create module", |
56 | fix: None, | 56 | FileSystemEdit::CreateFile { anchor: original_file, dst: d.candidate.clone() } |
57 | .into(), | ||
58 | ); | ||
59 | res.borrow_mut().push(Diagnostic { | ||
60 | range: sema.diagnostics_range(d).range, | ||
61 | message: d.message(), | ||
62 | severity: Severity::Error, | ||
63 | fix: Some(fix), | ||
64 | }) | ||
57 | }) | 65 | }) |
58 | }) | 66 | .on::<hir::diagnostics::MissingFields, _>(|d| { |
59 | .on::<hir::diagnostics::UnresolvedModule, _>(|d| { | 67 | // Note that although we could add a diagnostics to |
60 | let original_file = d.source().file_id.original_file(db); | 68 | // fill the missing tuple field, e.g : |
61 | let fix = Fix::new( | 69 | // `struct A(usize);` |
62 | "Create module", | 70 | // `let a = A { 0: () }` |
63 | FileSystemEdit::CreateFile { anchor: original_file, dst: d.candidate.clone() }.into(), | 71 | // but it is uncommon usage and it should not be encouraged. |
64 | ); | 72 | let fix = if d.missed_fields.iter().any(|it| it.as_tuple_index().is_some()) { |
65 | res.borrow_mut().push(Diagnostic { | 73 | None |
66 | range: sema.diagnostics_range(d).range, | 74 | } else { |
67 | message: d.message(), | 75 | let mut field_list = d.ast(db); |
68 | severity: Severity::Error, | 76 | for f in d.missed_fields.iter() { |
69 | fix: Some(fix), | 77 | let field = |
70 | }) | 78 | make::record_field(make::name_ref(&f.to_string()), Some(make::expr_unit())); |
71 | }) | 79 | field_list = field_list.append_field(&field); |
72 | .on::<hir::diagnostics::MissingFields, _>(|d| { | 80 | } |
73 | // Note that although we could add a diagnostics to | 81 | |
74 | // fill the missing tuple field, e.g : | 82 | let edit = { |
75 | // `struct A(usize);` | 83 | let mut builder = TextEditBuilder::default(); |
76 | // `let a = A { 0: () }` | 84 | algo::diff(&d.ast(db).syntax(), &field_list.syntax()) |
77 | // but it is uncommon usage and it should not be encouraged. | 85 | .into_text_edit(&mut builder); |
78 | let fix = if d.missed_fields.iter().any(|it| it.as_tuple_index().is_some()) { | 86 | builder.finish() |
79 | None | 87 | }; |
80 | } else { | 88 | Some(Fix::new("Fill struct fields", SourceFileEdit { file_id, edit }.into())) |
81 | let mut field_list = d.ast(db); | ||
82 | for f in d.missed_fields.iter() { | ||
83 | let field = | ||
84 | make::record_field(make::name_ref(&f.to_string()), Some(make::expr_unit())); | ||
85 | field_list = field_list.append_field(&field); | ||
86 | } | ||
87 | |||
88 | let edit = { | ||
89 | let mut builder = TextEditBuilder::default(); | ||
90 | algo::diff(&d.ast(db).syntax(), &field_list.syntax()).into_text_edit(&mut builder); | ||
91 | builder.finish() | ||
92 | }; | 89 | }; |
93 | Some(Fix::new("Fill struct fields", SourceFileEdit { file_id, edit }.into())) | ||
94 | }; | ||
95 | 90 | ||
96 | res.borrow_mut().push(Diagnostic { | 91 | res.borrow_mut().push(Diagnostic { |
97 | range: sema.diagnostics_range(d).range, | 92 | range: sema.diagnostics_range(d).range, |
98 | message: d.message(), | 93 | message: d.message(), |
99 | severity: Severity::Error, | 94 | severity: Severity::Error, |
100 | fix, | 95 | fix, |
96 | }) | ||
101 | }) | 97 | }) |
102 | }) | 98 | .on::<hir::diagnostics::MissingOkInTailExpr, _>(|d| { |
103 | .on::<hir::diagnostics::MissingOkInTailExpr, _>(|d| { | 99 | let node = d.ast(db); |
104 | let node = d.ast(db); | 100 | let replacement = format!("Ok({})", node.syntax()); |
105 | let replacement = format!("Ok({})", node.syntax()); | 101 | let edit = TextEdit::replace(node.syntax().text_range(), replacement); |
106 | let edit = TextEdit::replace(node.syntax().text_range(), replacement); | 102 | let source_change = SourceFileEdit { file_id, edit }.into(); |
107 | let source_change = SourceFileEdit { file_id, edit }.into(); | 103 | let fix = Fix::new("Wrap with ok", source_change); |
108 | let fix = Fix::new("Wrap with ok", source_change); | 104 | res.borrow_mut().push(Diagnostic { |
109 | res.borrow_mut().push(Diagnostic { | 105 | range: sema.diagnostics_range(d).range, |
110 | range: sema.diagnostics_range(d).range, | 106 | message: d.message(), |
111 | message: d.message(), | 107 | severity: Severity::Error, |
112 | severity: Severity::Error, | 108 | fix: Some(fix), |
113 | fix: Some(fix), | 109 | }) |
114 | }) | 110 | }) |
115 | }) | 111 | .on::<hir::diagnostics::NoSuchField, _>(|d| { |
116 | .on::<hir::diagnostics::NoSuchField, _>(|d| { | 112 | res.borrow_mut().push(Diagnostic { |
117 | res.borrow_mut().push(Diagnostic { | 113 | range: sema.diagnostics_range(d).range, |
118 | range: sema.diagnostics_range(d).range, | 114 | message: d.message(), |
119 | message: d.message(), | 115 | severity: Severity::Error, |
120 | severity: Severity::Error, | 116 | fix: missing_struct_field_fix(&sema, file_id, d), |
121 | fix: missing_struct_field_fix(&sema, file_id, d), | 117 | }) |
122 | }) | 118 | }) |
123 | }); | 119 | .build(|d| { |
120 | res.borrow_mut().push(Diagnostic { | ||
121 | message: d.message(), | ||
122 | range: sema.diagnostics_range(d).range, | ||
123 | severity: Severity::Error, | ||
124 | fix: None, | ||
125 | }) | ||
126 | }); | ||
124 | 127 | ||
125 | if let Some(m) = sema.to_module_def(file_id) { | 128 | if let Some(m) = sema.to_module_def(file_id) { |
126 | m.diagnostics(db, &mut sink); | 129 | m.diagnostics(db, &mut sink); |