aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_hir/src/code_model_api.rs16
-rw-r--r--crates/ra_hir/src/diagnostics.rs39
-rw-r--r--crates/ra_ide_api/src/diagnostics.rs79
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 @@
1use std::{fmt, any::Any}; 1use std::{fmt, any::Any};
2 2
3use ra_syntax::{SyntaxNodePtr, AstPtr, ast}; 3use ra_syntax::{SyntaxNodePtr, AstPtr, ast};
4use relative_path::RelativePathBuf;
4 5
5use crate::HirFileId; 6use crate::HirFileId;
6use 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
27impl dyn Diagnostic { 27impl dyn Diagnostic {
@@ -30,18 +30,37 @@ impl dyn Diagnostic {
30 } 30 }
31} 31}
32 32
33#[derive(Debug, Default)] 33pub struct DiagnosticSink<'a> {
34pub 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
38impl DiagnosticSink { 38impl<'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 @@
1use std::cell::RefCell;
2
1use itertools::Itertools; 3use itertools::Itertools;
2use hir::{source_binder, diagnostics::{Diagnostic as _, DiagnosticSink}}; 4use hir::{source_binder, diagnostics::{Diagnostic as _, DiagnosticSink}};
3use ra_db::SourceDatabase; 5use 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
35fn syntax_errors(acc: &mut Vec<Diagnostic>, source_file: &SourceFile) { 62fn 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
130fn 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)]
177mod tests { 158mod tests {
178 use test_utils::assert_eq_text; 159 use test_utils::assert_eq_text;