aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_hir/src/diagnostics.rs4
-rw-r--r--crates/ra_hir_expand/src/diagnostics.rs43
-rw-r--r--crates/ra_hir_ty/src/diagnostics.rs4
-rw-r--r--crates/ra_ide/src/diagnostics.rs139
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
2pub use hir_def::diagnostics::UnresolvedModule; 2pub use hir_def::diagnostics::UnresolvedModule;
3pub use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink}; 3pub use hir_expand::diagnostics::{
4 AstDiagnostic, Diagnostic, DiagnosticSink, DiagnosticSinkBuilder,
5};
4pub use hir_ty::diagnostics::{ 6pub 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
50impl<'a> DiagnosticSink<'a> { 50impl<'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
67pub struct DiagnosticSinkBuilder<'a> {
68 callbacks: Vec<Box<dyn FnMut(&dyn Diagnostic) -> Result<(), ()> + 'a>>,
69}
70
71impl<'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)]
249mod tests { 249mod 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 @@
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;
@@ -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);