diff options
Diffstat (limited to 'crates/ra_hir/src/diagnostics.rs')
-rw-r--r-- | crates/ra_hir/src/diagnostics.rs | 115 |
1 files changed, 115 insertions, 0 deletions
diff --git a/crates/ra_hir/src/diagnostics.rs b/crates/ra_hir/src/diagnostics.rs new file mode 100644 index 000000000..d6a51b833 --- /dev/null +++ b/crates/ra_hir/src/diagnostics.rs | |||
@@ -0,0 +1,115 @@ | |||
1 | use std::{fmt, any::Any}; | ||
2 | |||
3 | use ra_syntax::{SyntaxNodePtr, TreeArc, AstPtr, TextRange, ast, SyntaxNode}; | ||
4 | use relative_path::RelativePathBuf; | ||
5 | |||
6 | use crate::{HirFileId, HirDatabase}; | ||
7 | |||
8 | /// Diagnostic defines hir API for errors and warnings. | ||
9 | /// | ||
10 | /// It is used as a `dyn` object, which you can downcast to a concrete | ||
11 | /// diagnostic. DiagnosticSink are structured, meaning that they include rich | ||
12 | /// information which can be used by IDE to create fixes. DiagnosticSink are | ||
13 | /// expressed in terms of macro-expanded syntax tree nodes (so, it's a bad idea | ||
14 | /// to diagnostic in a salsa value). | ||
15 | /// | ||
16 | /// Internally, various subsystems of hir produce diagnostics specific to a | ||
17 | /// subsystem (typically, an `enum`), which are safe to store in salsa but do not | ||
18 | /// include source locations. Such internal diagnostic are transformed into an | ||
19 | /// instance of `Diagnostic` on demand. | ||
20 | pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static { | ||
21 | fn message(&self) -> String; | ||
22 | fn file(&self) -> HirFileId; | ||
23 | fn syntax_node_ptr(&self) -> SyntaxNodePtr; | ||
24 | fn highlight_range(&self) -> TextRange { | ||
25 | self.syntax_node_ptr().range() | ||
26 | } | ||
27 | fn as_any(&self) -> &(dyn Any + Send + 'static); | ||
28 | } | ||
29 | |||
30 | impl dyn Diagnostic { | ||
31 | pub fn syntax_node(&self, db: &impl HirDatabase) -> TreeArc<SyntaxNode> { | ||
32 | let source_file = db.hir_parse(self.file()); | ||
33 | self.syntax_node_ptr().to_node(&source_file).to_owned() | ||
34 | } | ||
35 | pub fn downcast_ref<D: Diagnostic>(&self) -> Option<&D> { | ||
36 | self.as_any().downcast_ref() | ||
37 | } | ||
38 | } | ||
39 | |||
40 | pub struct DiagnosticSink<'a> { | ||
41 | callbacks: Vec<Box<dyn FnMut(&dyn Diagnostic) -> Result<(), ()> + 'a>>, | ||
42 | default_callback: Box<dyn FnMut(&dyn Diagnostic) + 'a>, | ||
43 | } | ||
44 | |||
45 | impl<'a> DiagnosticSink<'a> { | ||
46 | pub fn new(cb: impl FnMut(&dyn Diagnostic) + 'a) -> DiagnosticSink<'a> { | ||
47 | DiagnosticSink { callbacks: Vec::new(), default_callback: Box::new(cb) } | ||
48 | } | ||
49 | |||
50 | pub fn on<D: Diagnostic, F: FnMut(&D) + 'a>(mut self, mut cb: F) -> DiagnosticSink<'a> { | ||
51 | let cb = move |diag: &dyn Diagnostic| match diag.downcast_ref::<D>() { | ||
52 | Some(d) => { | ||
53 | cb(d); | ||
54 | Ok(()) | ||
55 | } | ||
56 | None => Err(()), | ||
57 | }; | ||
58 | self.callbacks.push(Box::new(cb)); | ||
59 | self | ||
60 | } | ||
61 | |||
62 | pub(crate) fn push(&mut self, d: impl Diagnostic) { | ||
63 | let d: &dyn Diagnostic = &d; | ||
64 | for cb in self.callbacks.iter_mut() { | ||
65 | match cb(d) { | ||
66 | Ok(()) => return, | ||
67 | Err(()) => (), | ||
68 | } | ||
69 | } | ||
70 | (self.default_callback)(d) | ||
71 | } | ||
72 | } | ||
73 | |||
74 | #[derive(Debug)] | ||
75 | pub struct NoSuchField { | ||
76 | pub file: HirFileId, | ||
77 | pub field: AstPtr<ast::NamedField>, | ||
78 | } | ||
79 | |||
80 | impl Diagnostic for NoSuchField { | ||
81 | fn message(&self) -> String { | ||
82 | "no such field".to_string() | ||
83 | } | ||
84 | fn file(&self) -> HirFileId { | ||
85 | self.file | ||
86 | } | ||
87 | fn syntax_node_ptr(&self) -> SyntaxNodePtr { | ||
88 | self.field.into() | ||
89 | } | ||
90 | fn as_any(&self) -> &(Any + Send + 'static) { | ||
91 | self | ||
92 | } | ||
93 | } | ||
94 | |||
95 | #[derive(Debug)] | ||
96 | pub struct UnresolvedModule { | ||
97 | pub file: HirFileId, | ||
98 | pub decl: AstPtr<ast::Module>, | ||
99 | pub candidate: RelativePathBuf, | ||
100 | } | ||
101 | |||
102 | impl Diagnostic for UnresolvedModule { | ||
103 | fn message(&self) -> String { | ||
104 | "unresolved module".to_string() | ||
105 | } | ||
106 | fn file(&self) -> HirFileId { | ||
107 | self.file | ||
108 | } | ||
109 | fn syntax_node_ptr(&self) -> SyntaxNodePtr { | ||
110 | self.decl.into() | ||
111 | } | ||
112 | fn as_any(&self) -> &(Any + Send + 'static) { | ||
113 | self | ||
114 | } | ||
115 | } | ||