diff options
-rw-r--r-- | crates/ra_hir/src/code_model_api.rs | 16 | ||||
-rw-r--r-- | crates/ra_hir/src/diagnostics.rs | 39 | ||||
-rw-r--r-- | crates/ra_ide_api/src/diagnostics.rs | 79 |
3 files changed, 75 insertions, 59 deletions
diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs index bc0f74c89..5437133b8 100644 --- a/crates/ra_hir/src/code_model_api.rs +++ b/crates/ra_hir/src/code_model_api.rs | |||
@@ -168,6 +168,22 @@ impl Module { | |||
168 | 168 | ||
169 | pub fn diagnostics(&self, db: &impl HirDatabase, sink: &mut DiagnosticSink) { | 169 | pub fn diagnostics(&self, db: &impl HirDatabase, sink: &mut DiagnosticSink) { |
170 | db.crate_def_map(self.krate).add_diagnostics(db, self.module_id, sink); | 170 | db.crate_def_map(self.krate).add_diagnostics(db, self.module_id, sink); |
171 | for decl in self.declarations(db) { | ||
172 | match decl { | ||
173 | crate::ModuleDef::Function(f) => f.diagnostics(db, sink), | ||
174 | crate::ModuleDef::Module(f) => f.diagnostics(db, sink), | ||
175 | _ => (), | ||
176 | } | ||
177 | } | ||
178 | |||
179 | for impl_block in self.impl_blocks(db) { | ||
180 | for item in impl_block.items(db) { | ||
181 | match item { | ||
182 | crate::ImplItem::Method(f) => f.diagnostics(db, sink), | ||
183 | _ => (), | ||
184 | } | ||
185 | } | ||
186 | } | ||
171 | } | 187 | } |
172 | 188 | ||
173 | pub fn resolver(&self, db: &impl HirDatabase) -> Resolver { | 189 | pub fn resolver(&self, db: &impl HirDatabase) -> Resolver { |
diff --git a/crates/ra_hir/src/diagnostics.rs b/crates/ra_hir/src/diagnostics.rs index d6b28159e..a6ca68d86 100644 --- a/crates/ra_hir/src/diagnostics.rs +++ b/crates/ra_hir/src/diagnostics.rs | |||
@@ -1,9 +1,9 @@ | |||
1 | use std::{fmt, any::Any}; | 1 | use std::{fmt, any::Any}; |
2 | 2 | ||
3 | use ra_syntax::{SyntaxNodePtr, AstPtr, ast}; | 3 | use ra_syntax::{SyntaxNodePtr, AstPtr, ast}; |
4 | use relative_path::RelativePathBuf; | ||
4 | 5 | ||
5 | use crate::HirFileId; | 6 | use crate::HirFileId; |
6 | use relative_path::RelativePathBuf; | ||
7 | 7 | ||
8 | /// Diagnostic defines hir API for errors and warnings. | 8 | /// Diagnostic defines hir API for errors and warnings. |
9 | /// | 9 | /// |
@@ -21,7 +21,7 @@ pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static { | |||
21 | fn message(&self) -> String; | 21 | fn message(&self) -> String; |
22 | fn file(&self) -> HirFileId; | 22 | fn file(&self) -> HirFileId; |
23 | fn syntax_node(&self) -> SyntaxNodePtr; | 23 | fn syntax_node(&self) -> SyntaxNodePtr; |
24 | fn as_any(&self) -> &(Any + Send + 'static); | 24 | fn as_any(&self) -> &(dyn Any + Send + 'static); |
25 | } | 25 | } |
26 | 26 | ||
27 | impl dyn Diagnostic { | 27 | impl dyn Diagnostic { |
@@ -30,18 +30,37 @@ impl dyn Diagnostic { | |||
30 | } | 30 | } |
31 | } | 31 | } |
32 | 32 | ||
33 | #[derive(Debug, Default)] | 33 | pub struct DiagnosticSink<'a> { |
34 | pub struct DiagnosticSink { | 34 | callbacks: Vec<Box<dyn FnMut(&dyn Diagnostic) -> Result<(), ()> + 'a>>, |
35 | data: Vec<Box<dyn Diagnostic>>, | 35 | default_callback: Box<dyn FnMut(&dyn Diagnostic) + 'a>, |
36 | } | 36 | } |
37 | 37 | ||
38 | impl DiagnosticSink { | 38 | impl<'a> DiagnosticSink<'a> { |
39 | pub fn push(&mut self, d: impl Diagnostic) { | 39 | pub fn new(cb: impl FnMut(&dyn Diagnostic) + 'a) -> DiagnosticSink<'a> { |
40 | self.data.push(Box::new(d)) | 40 | DiagnosticSink { callbacks: Vec::new(), default_callback: Box::new(cb) } |
41 | } | ||
42 | |||
43 | pub fn on<D: Diagnostic, F: FnMut(&D) + 'a>(mut self, mut cb: F) -> DiagnosticSink<'a> { | ||
44 | let cb = move |diag: &dyn Diagnostic| match diag.downcast_ref::<D>() { | ||
45 | Some(d) => { | ||
46 | cb(d); | ||
47 | Ok(()) | ||
48 | } | ||
49 | None => Err(()), | ||
50 | }; | ||
51 | self.callbacks.push(Box::new(cb)); | ||
52 | self | ||
41 | } | 53 | } |
42 | 54 | ||
43 | pub fn into_diagnostics(self) -> Vec<Box<dyn Diagnostic>> { | 55 | pub(crate) fn push(&mut self, d: impl Diagnostic) { |
44 | self.data | 56 | let d: &dyn Diagnostic = &d; |
57 | for cb in self.callbacks.iter_mut() { | ||
58 | match cb(d) { | ||
59 | Ok(()) => return, | ||
60 | Err(()) => (), | ||
61 | } | ||
62 | } | ||
63 | (self.default_callback)(d) | ||
45 | } | 64 | } |
46 | } | 65 | } |
47 | 66 | ||
diff --git a/crates/ra_ide_api/src/diagnostics.rs b/crates/ra_ide_api/src/diagnostics.rs index 5b1a90c82..bf77c9ab1 100644 --- a/crates/ra_ide_api/src/diagnostics.rs +++ b/crates/ra_ide_api/src/diagnostics.rs | |||
@@ -1,3 +1,5 @@ | |||
1 | use std::cell::RefCell; | ||
2 | |||
1 | use itertools::Itertools; | 3 | use itertools::Itertools; |
2 | use hir::{source_binder, diagnostics::{Diagnostic as _, DiagnosticSink}}; | 4 | use hir::{source_binder, diagnostics::{Diagnostic as _, DiagnosticSink}}; |
3 | use ra_db::SourceDatabase; | 5 | use ra_db::SourceDatabase; |
@@ -25,11 +27,36 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> | |||
25 | check_unnecessary_braces_in_use_statement(&mut res, file_id, node); | 27 | check_unnecessary_braces_in_use_statement(&mut res, file_id, node); |
26 | check_struct_shorthand_initialization(&mut res, file_id, node); | 28 | check_struct_shorthand_initialization(&mut res, file_id, node); |
27 | } | 29 | } |
28 | 30 | let res = RefCell::new(res); | |
31 | let mut sink = DiagnosticSink::new(|d| { | ||
32 | res.borrow_mut().push(Diagnostic { | ||
33 | message: d.message(), | ||
34 | range: d.syntax_node().range(), | ||
35 | severity: Severity::Error, | ||
36 | fix: None, | ||
37 | }) | ||
38 | }) | ||
39 | .on::<hir::diagnostics::UnresolvedModule, _>(|d| { | ||
40 | let source_root = db.file_source_root(d.file().original_file(db)); | ||
41 | let create_file = FileSystemEdit::CreateFile { source_root, path: d.candidate.clone() }; | ||
42 | let fix = SourceChange { | ||
43 | label: "create module".to_string(), | ||
44 | source_file_edits: Vec::new(), | ||
45 | file_system_edits: vec![create_file], | ||
46 | cursor_position: None, | ||
47 | }; | ||
48 | res.borrow_mut().push(Diagnostic { | ||
49 | range: d.syntax_node().range(), | ||
50 | message: d.message(), | ||
51 | severity: Severity::Error, | ||
52 | fix: Some(fix), | ||
53 | }) | ||
54 | }); | ||
29 | if let Some(m) = source_binder::module_from_file_id(db, file_id) { | 55 | if let Some(m) = source_binder::module_from_file_id(db, file_id) { |
30 | check_module(&mut res, db, m); | 56 | m.diagnostics(db, &mut sink); |
31 | }; | 57 | }; |
32 | res | 58 | drop(sink); |
59 | res.into_inner() | ||
33 | } | 60 | } |
34 | 61 | ||
35 | fn syntax_errors(acc: &mut Vec<Diagnostic>, source_file: &SourceFile) { | 62 | fn syntax_errors(acc: &mut Vec<Diagnostic>, source_file: &SourceFile) { |
@@ -127,52 +154,6 @@ fn check_struct_shorthand_initialization( | |||
127 | Some(()) | 154 | Some(()) |
128 | } | 155 | } |
129 | 156 | ||
130 | fn check_module(acc: &mut Vec<Diagnostic>, db: &RootDatabase, module: hir::Module) { | ||
131 | let mut diagnostics = DiagnosticSink::default(); | ||
132 | module.diagnostics(db, &mut diagnostics); | ||
133 | for decl in module.declarations(db) { | ||
134 | match decl { | ||
135 | hir::ModuleDef::Function(f) => f.diagnostics(db, &mut diagnostics), | ||
136 | _ => (), | ||
137 | } | ||
138 | } | ||
139 | |||
140 | for impl_block in module.impl_blocks(db) { | ||
141 | for item in impl_block.items(db) { | ||
142 | match item { | ||
143 | hir::ImplItem::Method(f) => f.diagnostics(db, &mut diagnostics), | ||
144 | _ => (), | ||
145 | } | ||
146 | } | ||
147 | } | ||
148 | |||
149 | for d in diagnostics.into_diagnostics().iter() { | ||
150 | if let Some(d) = d.downcast_ref::<hir::diagnostics::UnresolvedModule>() { | ||
151 | let source_root = db.file_source_root(d.file().original_file(db)); | ||
152 | let create_file = FileSystemEdit::CreateFile { source_root, path: d.candidate.clone() }; | ||
153 | let fix = SourceChange { | ||
154 | label: "create module".to_string(), | ||
155 | source_file_edits: Vec::new(), | ||
156 | file_system_edits: vec![create_file], | ||
157 | cursor_position: None, | ||
158 | }; | ||
159 | acc.push(Diagnostic { | ||
160 | range: d.syntax_node().range(), | ||
161 | message: d.message(), | ||
162 | severity: Severity::Error, | ||
163 | fix: Some(fix), | ||
164 | }) | ||
165 | } else { | ||
166 | acc.push(Diagnostic { | ||
167 | message: d.message(), | ||
168 | range: d.syntax_node().range(), | ||
169 | severity: Severity::Error, | ||
170 | fix: None, | ||
171 | }) | ||
172 | } | ||
173 | } | ||
174 | } | ||
175 | |||
176 | #[cfg(test)] | 157 | #[cfg(test)] |
177 | mod tests { | 158 | mod tests { |
178 | use test_utils::assert_eq_text; | 159 | use test_utils::assert_eq_text; |