aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_hir/src/code_model.rs6
-rw-r--r--crates/ra_hir/src/diagnostics.rs81
-rw-r--r--crates/ra_hir/src/expr/validation.rs3
-rw-r--r--crates/ra_hir/src/mock.rs3
-rw-r--r--crates/ra_hir/src/nameres.rs5
-rw-r--r--crates/ra_hir/src/ty/infer.rs12
-rw-r--r--crates/ra_hir_expand/src/diagnostics.rs85
-rw-r--r--crates/ra_hir_expand/src/lib.rs1
8 files changed, 107 insertions, 89 deletions
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index c97ea18a2..5b78bdfef 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -11,14 +11,16 @@ use hir_def::{
11 type_ref::{Mutability, TypeRef}, 11 type_ref::{Mutability, TypeRef},
12 CrateModuleId, LocalEnumVariantId, LocalStructFieldId, ModuleId, 12 CrateModuleId, LocalEnumVariantId, LocalStructFieldId, ModuleId,
13}; 13};
14use hir_expand::name::{self, AsName}; 14use hir_expand::{
15 diagnostics::DiagnosticSink,
16 name::{self, AsName},
17};
15use ra_db::{CrateId, Edition}; 18use ra_db::{CrateId, Edition};
16use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner}; 19use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner};
17 20
18use crate::{ 21use crate::{
19 adt::VariantDef, 22 adt::VariantDef,
20 db::{AstDatabase, DefDatabase, HirDatabase}, 23 db::{AstDatabase, DefDatabase, HirDatabase},
21 diagnostics::DiagnosticSink,
22 expr::{validation::ExprValidator, Body, BodySourceMap}, 24 expr::{validation::ExprValidator, Body, BodySourceMap},
23 generics::HasGenericParams, 25 generics::HasGenericParams,
24 ids::{ 26 ids::{
diff --git a/crates/ra_hir/src/diagnostics.rs b/crates/ra_hir/src/diagnostics.rs
index 9acdaf8ed..a33af8f46 100644
--- a/crates/ra_hir/src/diagnostics.rs
+++ b/crates/ra_hir/src/diagnostics.rs
@@ -1,82 +1,13 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use std::{any::Any, fmt}; 3use std::any::Any;
4 4
5use ra_syntax::{ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr, TextRange}; 5use ra_syntax::{ast, AstNode, AstPtr, SyntaxNodePtr};
6use relative_path::RelativePathBuf; 6use relative_path::RelativePathBuf;
7 7
8use crate::{db::HirDatabase, HirFileId, Name, Source}; 8use crate::{db::AstDatabase, HirFileId, Name, Source};
9
10/// Diagnostic defines hir API for errors and warnings.
11///
12/// It is used as a `dyn` object, which you can downcast to a concrete
13/// diagnostic. DiagnosticSink are structured, meaning that they include rich
14/// information which can be used by IDE to create fixes. DiagnosticSink are
15/// expressed in terms of macro-expanded syntax tree nodes (so, it's a bad idea
16/// to diagnostic in a salsa value).
17///
18/// Internally, various subsystems of hir produce diagnostics specific to a
19/// subsystem (typically, an `enum`), which are safe to store in salsa but do not
20/// include source locations. Such internal diagnostic are transformed into an
21/// instance of `Diagnostic` on demand.
22pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static {
23 fn message(&self) -> String;
24 fn source(&self) -> Source<SyntaxNodePtr>;
25 fn highlight_range(&self) -> TextRange {
26 self.source().ast.range()
27 }
28 fn as_any(&self) -> &(dyn Any + Send + 'static);
29}
30
31pub trait AstDiagnostic {
32 type AST;
33 fn ast(&self, db: &impl HirDatabase) -> Self::AST;
34}
35
36impl dyn Diagnostic {
37 pub fn syntax_node(&self, db: &impl HirDatabase) -> SyntaxNode {
38 let node = db.parse_or_expand(self.source().file_id).unwrap();
39 self.source().ast.to_node(&node)
40 }
41
42 pub fn downcast_ref<D: Diagnostic>(&self) -> Option<&D> {
43 self.as_any().downcast_ref()
44 }
45}
46 9
47pub struct DiagnosticSink<'a> { 10pub use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink};
48 callbacks: Vec<Box<dyn FnMut(&dyn Diagnostic) -> Result<(), ()> + 'a>>,
49 default_callback: Box<dyn FnMut(&dyn Diagnostic) + 'a>,
50}
51
52impl<'a> DiagnosticSink<'a> {
53 pub fn new(cb: impl FnMut(&dyn Diagnostic) + 'a) -> DiagnosticSink<'a> {
54 DiagnosticSink { callbacks: Vec::new(), default_callback: Box::new(cb) }
55 }
56
57 pub fn on<D: Diagnostic, F: FnMut(&D) + 'a>(mut self, mut cb: F) -> DiagnosticSink<'a> {
58 let cb = move |diag: &dyn Diagnostic| match diag.downcast_ref::<D>() {
59 Some(d) => {
60 cb(d);
61 Ok(())
62 }
63 None => Err(()),
64 };
65 self.callbacks.push(Box::new(cb));
66 self
67 }
68
69 pub(crate) fn push(&mut self, d: impl Diagnostic) {
70 let d: &dyn Diagnostic = &d;
71 for cb in self.callbacks.iter_mut() {
72 match cb(d) {
73 Ok(()) => return,
74 Err(()) => (),
75 }
76 }
77 (self.default_callback)(d)
78 }
79}
80 11
81#[derive(Debug)] 12#[derive(Debug)]
82pub struct NoSuchField { 13pub struct NoSuchField {
@@ -139,7 +70,7 @@ impl Diagnostic for MissingFields {
139impl AstDiagnostic for MissingFields { 70impl AstDiagnostic for MissingFields {
140 type AST = ast::RecordFieldList; 71 type AST = ast::RecordFieldList;
141 72
142 fn ast(&self, db: &impl HirDatabase) -> Self::AST { 73 fn ast(&self, db: &impl AstDatabase) -> Self::AST {
143 let root = db.parse_or_expand(self.source().file_id).unwrap(); 74 let root = db.parse_or_expand(self.source().file_id).unwrap();
144 let node = self.source().ast.to_node(&root); 75 let node = self.source().ast.to_node(&root);
145 ast::RecordFieldList::cast(node).unwrap() 76 ast::RecordFieldList::cast(node).unwrap()
@@ -167,7 +98,7 @@ impl Diagnostic for MissingOkInTailExpr {
167impl AstDiagnostic for MissingOkInTailExpr { 98impl AstDiagnostic for MissingOkInTailExpr {
168 type AST = ast::Expr; 99 type AST = ast::Expr;
169 100
170 fn ast(&self, db: &impl HirDatabase) -> Self::AST { 101 fn ast(&self, db: &impl AstDatabase) -> Self::AST {
171 let root = db.parse_or_expand(self.file).unwrap(); 102 let root = db.parse_or_expand(self.file).unwrap();
172 let node = self.source().ast.to_node(&root); 103 let node = self.source().ast.to_node(&root);
173 ast::Expr::cast(node).unwrap() 104 ast::Expr::cast(node).unwrap()
diff --git a/crates/ra_hir/src/expr/validation.rs b/crates/ra_hir/src/expr/validation.rs
index c685edda1..3054f1dce 100644
--- a/crates/ra_hir/src/expr/validation.rs
+++ b/crates/ra_hir/src/expr/validation.rs
@@ -3,12 +3,13 @@
3use std::sync::Arc; 3use std::sync::Arc;
4 4
5use hir_def::path::known; 5use hir_def::path::known;
6use hir_expand::diagnostics::DiagnosticSink;
6use ra_syntax::ast; 7use ra_syntax::ast;
7use rustc_hash::FxHashSet; 8use rustc_hash::FxHashSet;
8 9
9use crate::{ 10use crate::{
10 db::HirDatabase, 11 db::HirDatabase,
11 diagnostics::{DiagnosticSink, MissingFields, MissingOkInTailExpr}, 12 diagnostics::{MissingFields, MissingOkInTailExpr},
12 expr::AstPtr, 13 expr::AstPtr,
13 ty::{ApplicationTy, InferenceResult, Ty, TypeCtor}, 14 ty::{ApplicationTy, InferenceResult, Ty, TypeCtor},
14 Adt, Function, Name, Path, 15 Adt, Function, Name, Path,
diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs
index 35dfaf3ba..4c89c8d38 100644
--- a/crates/ra_hir/src/mock.rs
+++ b/crates/ra_hir/src/mock.rs
@@ -2,6 +2,7 @@
2 2
3use std::{panic, sync::Arc}; 3use std::{panic, sync::Arc};
4 4
5use hir_expand::diagnostics::DiagnosticSink;
5use parking_lot::Mutex; 6use parking_lot::Mutex;
6use ra_cfg::CfgOptions; 7use ra_cfg::CfgOptions;
7use ra_db::{ 8use ra_db::{
@@ -12,7 +13,7 @@ use relative_path::{RelativePath, RelativePathBuf};
12use rustc_hash::FxHashMap; 13use rustc_hash::FxHashMap;
13use test_utils::{extract_offset, parse_fixture, CURSOR_MARKER}; 14use test_utils::{extract_offset, parse_fixture, CURSOR_MARKER};
14 15
15use crate::{db, debug::HirDebugHelper, diagnostics::DiagnosticSink}; 16use crate::{db, debug::HirDebugHelper};
16 17
17pub const WORKSPACE: SourceRootId = SourceRootId(0); 18pub const WORKSPACE: SourceRootId = SourceRootId(0);
18 19
diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs
index 7ba031827..32a6ab474 100644
--- a/crates/ra_hir/src/nameres.rs
+++ b/crates/ra_hir/src/nameres.rs
@@ -55,6 +55,7 @@ mod tests;
55use std::sync::Arc; 55use std::sync::Arc;
56 56
57use hir_def::{builtin_type::BuiltinType, CrateModuleId}; 57use hir_def::{builtin_type::BuiltinType, CrateModuleId};
58use hir_expand::diagnostics::DiagnosticSink;
58use once_cell::sync::Lazy; 59use once_cell::sync::Lazy;
59use ra_arena::Arena; 60use ra_arena::Arena;
60use ra_db::{Edition, FileId}; 61use ra_db::{Edition, FileId};
@@ -65,7 +66,6 @@ use test_utils::tested_by;
65 66
66use crate::{ 67use crate::{
67 db::{AstDatabase, DefDatabase}, 68 db::{AstDatabase, DefDatabase},
68 diagnostics::DiagnosticSink,
69 ids::MacroDefId, 69 ids::MacroDefId,
70 nameres::diagnostics::DefDiagnostic, 70 nameres::diagnostics::DefDiagnostic,
71 Adt, AstId, Crate, HirFileId, MacroDef, Module, ModuleDef, Name, Path, PathKind, Trait, 71 Adt, AstId, Crate, HirFileId, MacroDef, Module, ModuleDef, Name, Path, PathKind, Trait,
@@ -513,12 +513,13 @@ impl CrateDefMap {
513} 513}
514 514
515mod diagnostics { 515mod diagnostics {
516 use hir_expand::diagnostics::DiagnosticSink;
516 use ra_syntax::{ast, AstPtr}; 517 use ra_syntax::{ast, AstPtr};
517 use relative_path::RelativePathBuf; 518 use relative_path::RelativePathBuf;
518 519
519 use crate::{ 520 use crate::{
520 db::{AstDatabase, DefDatabase}, 521 db::{AstDatabase, DefDatabase},
521 diagnostics::{DiagnosticSink, UnresolvedModule}, 522 diagnostics::UnresolvedModule,
522 nameres::CrateModuleId, 523 nameres::CrateModuleId,
523 AstId, 524 AstId,
524 }; 525 };
diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs
index 6694467a3..2370e8d4f 100644
--- a/crates/ra_hir/src/ty/infer.rs
+++ b/crates/ra_hir/src/ty/infer.rs
@@ -25,7 +25,7 @@ use hir_def::{
25 path::known, 25 path::known,
26 type_ref::{Mutability, TypeRef}, 26 type_ref::{Mutability, TypeRef},
27}; 27};
28use hir_expand::name; 28use hir_expand::{diagnostics::DiagnosticSink, name};
29use ra_arena::map::ArenaMap; 29use ra_arena::map::ArenaMap;
30use ra_prof::profile; 30use ra_prof::profile;
31use test_utils::tested_by; 31use test_utils::tested_by;
@@ -40,7 +40,6 @@ use crate::{
40 adt::VariantDef, 40 adt::VariantDef,
41 code_model::TypeAlias, 41 code_model::TypeAlias,
42 db::HirDatabase, 42 db::HirDatabase,
43 diagnostics::DiagnosticSink,
44 expr::{BindingAnnotation, Body, ExprId, PatId}, 43 expr::{BindingAnnotation, Body, ExprId, PatId},
45 resolve::{Resolver, TypeNs}, 44 resolve::{Resolver, TypeNs},
46 ty::infer::diagnostics::InferenceDiagnostic, 45 ty::infer::diagnostics::InferenceDiagnostic,
@@ -719,12 +718,9 @@ impl Expectation {
719} 718}
720 719
721mod diagnostics { 720mod diagnostics {
722 use crate::{ 721 use hir_expand::diagnostics::DiagnosticSink;
723 db::HirDatabase, 722
724 diagnostics::{DiagnosticSink, NoSuchField}, 723 use crate::{db::HirDatabase, diagnostics::NoSuchField, expr::ExprId, Function, HasSource};
725 expr::ExprId,
726 Function, HasSource,
727 };
728 724
729 #[derive(Debug, PartialEq, Eq, Clone)] 725 #[derive(Debug, PartialEq, Eq, Clone)]
730 pub(super) enum InferenceDiagnostic { 726 pub(super) enum InferenceDiagnostic {
diff --git a/crates/ra_hir_expand/src/diagnostics.rs b/crates/ra_hir_expand/src/diagnostics.rs
new file mode 100644
index 000000000..201884b95
--- /dev/null
+++ b/crates/ra_hir_expand/src/diagnostics.rs
@@ -0,0 +1,85 @@
1//! Semantic errors and warnings.
2//!
3//! The `Diagnostic` trait defines a trait object which can represent any
4//! diagnostic.
5//!
6//! `DiagnosticSink` struct is used as an emitter for diagnostic. When creating
7//! a `DiagnosticSink`, you supply a callback which can react to a `dyn
8//! Diagnostic` or to any concrete diagnostic (downcasting is sued internally).
9//!
10//! Because diagnostics store file offsets, it's a bad idea to store them
11//! directly in salsa. For this reason, every hir subsytem defines it's own
12//! strongly-typed closed set of diagnostics which use hir ids internally, are
13//! stored in salsa and do *not* implement the `Diagnostic` trait. Instead, a
14//! subsystem provides a separate, non-query-based API which can walk all stored
15//! values and transform them into instances of `Diagnostic`.
16
17use std::{any::Any, fmt};
18
19use ra_syntax::{SyntaxNode, SyntaxNodePtr, TextRange};
20
21use crate::{db::AstDatabase, Source};
22
23pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static {
24 fn message(&self) -> String;
25 fn source(&self) -> Source<SyntaxNodePtr>;
26 fn highlight_range(&self) -> TextRange {
27 self.source().ast.range()
28 }
29 fn as_any(&self) -> &(dyn Any + Send + 'static);
30}
31
32pub trait AstDiagnostic {
33 type AST;
34 fn ast(&self, db: &impl AstDatabase) -> Self::AST;
35}
36
37impl dyn Diagnostic {
38 pub fn syntax_node(&self, db: &impl AstDatabase) -> SyntaxNode {
39 let node = db.parse_or_expand(self.source().file_id).unwrap();
40 self.source().ast.to_node(&node)
41 }
42
43 pub fn downcast_ref<D: Diagnostic>(&self) -> Option<&D> {
44 self.as_any().downcast_ref()
45 }
46}
47
48pub struct DiagnosticSink<'a> {
49 callbacks: Vec<Box<dyn FnMut(&dyn Diagnostic) -> Result<(), ()> + 'a>>,
50 default_callback: Box<dyn FnMut(&dyn Diagnostic) + 'a>,
51}
52
53impl<'a> DiagnosticSink<'a> {
54 /// FIXME: split `new` and `on` into a separate builder type
55 pub fn new(cb: impl FnMut(&dyn Diagnostic) + 'a) -> DiagnosticSink<'a> {
56 DiagnosticSink { callbacks: Vec::new(), default_callback: Box::new(cb) }
57 }
58
59 pub fn on<D: Diagnostic, F: FnMut(&D) + 'a>(mut self, mut cb: F) -> DiagnosticSink<'a> {
60 let cb = move |diag: &dyn Diagnostic| match diag.downcast_ref::<D>() {
61 Some(d) => {
62 cb(d);
63 Ok(())
64 }
65 None => Err(()),
66 };
67 self.callbacks.push(Box::new(cb));
68 self
69 }
70
71 pub fn push(&mut self, d: impl Diagnostic) {
72 let d: &dyn Diagnostic = &d;
73 self._push(d);
74 }
75
76 fn _push(&mut self, d: &dyn Diagnostic) {
77 for cb in self.callbacks.iter_mut() {
78 match cb(d) {
79 Ok(()) => return,
80 Err(()) => (),
81 }
82 }
83 (self.default_callback)(d)
84 }
85}
diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs
index 85c2b22ac..dd07a16b4 100644
--- a/crates/ra_hir_expand/src/lib.rs
+++ b/crates/ra_hir_expand/src/lib.rs
@@ -9,6 +9,7 @@ pub mod ast_id_map;
9pub mod either; 9pub mod either;
10pub mod name; 10pub mod name;
11pub mod hygiene; 11pub mod hygiene;
12pub mod diagnostics;
12 13
13use std::hash::{Hash, Hasher}; 14use std::hash::{Hash, Hasher};
14 15