aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/diagnostics.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir/src/diagnostics.rs')
-rw-r--r--crates/ra_hir/src/diagnostics.rs115
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 @@
1use std::{fmt, any::Any};
2
3use ra_syntax::{SyntaxNodePtr, TreeArc, AstPtr, TextRange, ast, SyntaxNode};
4use relative_path::RelativePathBuf;
5
6use 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.
20pub 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
30impl 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
40pub struct DiagnosticSink<'a> {
41 callbacks: Vec<Box<dyn FnMut(&dyn Diagnostic) -> Result<(), ()> + 'a>>,
42 default_callback: Box<dyn FnMut(&dyn Diagnostic) + 'a>,
43}
44
45impl<'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)]
75pub struct NoSuchField {
76 pub file: HirFileId,
77 pub field: AstPtr<ast::NamedField>,
78}
79
80impl 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)]
96pub struct UnresolvedModule {
97 pub file: HirFileId,
98 pub decl: AstPtr<ast::Module>,
99 pub candidate: RelativePathBuf,
100}
101
102impl 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}