aboutsummaryrefslogtreecommitdiff
path: root/crates/hir/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir/src')
-rw-r--r--crates/hir/src/diagnostics.rs35
-rw-r--r--crates/hir/src/diagnostics_sink.rs109
-rw-r--r--crates/hir/src/lib.rs32
3 files changed, 159 insertions, 17 deletions
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index 26dbcd86a..8a7c3a4fd 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -11,9 +11,8 @@ use hir_expand::{name::Name, HirFileId, InFile};
11use stdx::format_to; 11use stdx::format_to;
12use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange}; 12use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange};
13 13
14pub use hir_ty::{ 14pub use crate::diagnostics_sink::{
15 diagnostics::IncorrectCase, 15 Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder,
16 diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder},
17}; 16};
18 17
19// Diagnostic: unresolved-module 18// Diagnostic: unresolved-module
@@ -578,3 +577,33 @@ impl Diagnostic for InternalBailedOut {
578 self 577 self
579 } 578 }
580} 579}
580
581pub use hir_ty::diagnostics::IncorrectCase;
582
583impl Diagnostic for IncorrectCase {
584 fn code(&self) -> DiagnosticCode {
585 DiagnosticCode("incorrect-ident-case")
586 }
587
588 fn message(&self) -> String {
589 format!(
590 "{} `{}` should have {} name, e.g. `{}`",
591 self.ident_type,
592 self.ident_text,
593 self.expected_case.to_string(),
594 self.suggested_text
595 )
596 }
597
598 fn display_source(&self) -> InFile<SyntaxNodePtr> {
599 InFile::new(self.file, self.ident.clone().into())
600 }
601
602 fn as_any(&self) -> &(dyn Any + Send + 'static) {
603 self
604 }
605
606 fn is_experimental(&self) -> bool {
607 true
608 }
609}
diff --git a/crates/hir/src/diagnostics_sink.rs b/crates/hir/src/diagnostics_sink.rs
new file mode 100644
index 000000000..084fa8b06
--- /dev/null
+++ b/crates/hir/src/diagnostics_sink.rs
@@ -0,0 +1,109 @@
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 used 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 hir_expand::InFile;
20use syntax::SyntaxNodePtr;
21
22#[derive(Copy, Clone, Debug, PartialEq)]
23pub struct DiagnosticCode(pub &'static str);
24
25impl DiagnosticCode {
26 pub fn as_str(&self) -> &str {
27 self.0
28 }
29}
30
31pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static {
32 fn code(&self) -> DiagnosticCode;
33 fn message(&self) -> String;
34 /// Source element that triggered the diagnostics.
35 ///
36 /// Note that this should reflect "semantics", rather than specific span we
37 /// want to highlight. When rendering the diagnostics into an error message,
38 /// the IDE will fetch the `SyntaxNode` and will narrow the span
39 /// appropriately.
40 fn display_source(&self) -> InFile<SyntaxNodePtr>;
41 fn as_any(&self) -> &(dyn Any + Send + 'static);
42 fn is_experimental(&self) -> bool {
43 false
44 }
45}
46
47pub struct DiagnosticSink<'a> {
48 callbacks: Vec<Box<dyn FnMut(&dyn Diagnostic) -> Result<(), ()> + 'a>>,
49 filters: Vec<Box<dyn FnMut(&dyn Diagnostic) -> bool + 'a>>,
50 default_callback: Box<dyn FnMut(&dyn Diagnostic) + 'a>,
51}
52
53impl<'a> DiagnosticSink<'a> {
54 pub fn push(&mut self, d: impl Diagnostic) {
55 let d: &dyn Diagnostic = &d;
56 self._push(d);
57 }
58
59 fn _push(&mut self, d: &dyn Diagnostic) {
60 for filter in &mut self.filters {
61 if !filter(d) {
62 return;
63 }
64 }
65 for cb in &mut self.callbacks {
66 match cb(d) {
67 Ok(()) => return,
68 Err(()) => (),
69 }
70 }
71 (self.default_callback)(d)
72 }
73}
74
75pub struct DiagnosticSinkBuilder<'a> {
76 callbacks: Vec<Box<dyn FnMut(&dyn Diagnostic) -> Result<(), ()> + 'a>>,
77 filters: Vec<Box<dyn FnMut(&dyn Diagnostic) -> bool + 'a>>,
78}
79
80impl<'a> DiagnosticSinkBuilder<'a> {
81 pub fn new() -> Self {
82 Self { callbacks: Vec::new(), filters: Vec::new() }
83 }
84
85 pub fn filter<F: FnMut(&dyn Diagnostic) -> bool + 'a>(mut self, cb: F) -> Self {
86 self.filters.push(Box::new(cb));
87 self
88 }
89
90 pub fn on<D: Diagnostic, F: FnMut(&D) + 'a>(mut self, mut cb: F) -> Self {
91 let cb = move |diag: &dyn Diagnostic| match diag.as_any().downcast_ref::<D>() {
92 Some(d) => {
93 cb(d);
94 Ok(())
95 }
96 None => Err(()),
97 };
98 self.callbacks.push(Box::new(cb));
99 self
100 }
101
102 pub fn build<F: FnMut(&dyn Diagnostic) + 'a>(self, default_callback: F) -> DiagnosticSink<'a> {
103 DiagnosticSink {
104 callbacks: self.callbacks,
105 filters: self.filters,
106 default_callback: Box::new(default_callback),
107 }
108 }
109}
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index dd5515c2b..2468c0dc6 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -27,6 +27,7 @@ mod attrs;
27mod has_source; 27mod has_source;
28 28
29pub mod diagnostics; 29pub mod diagnostics;
30pub mod diagnostics_sink;
30pub mod db; 31pub mod db;
31 32
32mod display; 33mod display;
@@ -35,13 +36,6 @@ use std::{iter, sync::Arc};
35 36
36use arrayvec::ArrayVec; 37use arrayvec::ArrayVec;
37use base_db::{CrateDisplayName, CrateId, Edition, FileId}; 38use base_db::{CrateDisplayName, CrateId, Edition, FileId};
38use diagnostics::{
39 BreakOutsideOfLoop, InactiveCode, InternalBailedOut, MacroError, MismatchedArgCount,
40 MissingFields, MissingOkOrSomeInTailExpr, MissingPatFields, MissingUnsafe, NoSuchField,
41 RemoveThisSemicolon, ReplaceFilterMapNextWithFindMap, UnimplementedBuiltinMacro,
42 UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall, UnresolvedModule,
43 UnresolvedProcMacro,
44};
45use either::Either; 39use either::Either;
46use hir_def::{ 40use hir_def::{
47 adt::{ReprKind, VariantData}, 41 adt::{ReprKind, VariantData},
@@ -64,8 +58,7 @@ use hir_ty::{
64 consteval::ConstExt, 58 consteval::ConstExt,
65 could_unify, 59 could_unify,
66 diagnostics::BodyValidationDiagnostic, 60 diagnostics::BodyValidationDiagnostic,
67 diagnostics_sink::DiagnosticSink, 61 method_resolution::{self, TyFingerprint},
68 method_resolution::{self, def_crates, TyFingerprint},
69 primitive::UintTy, 62 primitive::UintTy,
70 subst_prefix, 63 subst_prefix,
71 traits::FnTrait, 64 traits::FnTrait,
@@ -87,7 +80,14 @@ use tt::{Ident, Leaf, Literal, TokenTree};
87 80
88use crate::{ 81use crate::{
89 db::{DefDatabase, HirDatabase}, 82 db::{DefDatabase, HirDatabase},
90 diagnostics::MissingMatchArms, 83 diagnostics::{
84 BreakOutsideOfLoop, InactiveCode, InternalBailedOut, MacroError, MismatchedArgCount,
85 MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr, MissingPatFields,
86 MissingUnsafe, NoSuchField, RemoveThisSemicolon, ReplaceFilterMapNextWithFindMap,
87 UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall,
88 UnresolvedModule, UnresolvedProcMacro,
89 },
90 diagnostics_sink::DiagnosticSink,
91}; 91};
92 92
93pub use crate::{ 93pub use crate::{
@@ -361,7 +361,9 @@ impl ModuleDef {
361 None => return, 361 None => return,
362 }; 362 };
363 363
364 hir_ty::diagnostics::validate_module_item(db, module.id.krate(), id, sink) 364 for diag in hir_ty::diagnostics::validate_module_item(db, module.id.krate(), id) {
365 sink.push(diag)
366 }
365 } 367 }
366} 368}
367 369
@@ -1225,7 +1227,9 @@ impl Function {
1225 } 1227 }
1226 } 1228 }
1227 1229
1228 hir_ty::diagnostics::validate_module_item(db, krate, self.id.into(), sink); 1230 for diag in hir_ty::diagnostics::validate_module_item(db, krate, self.id.into()) {
1231 sink.push(diag)
1232 }
1229 } 1233 }
1230 1234
1231 /// Whether this function declaration has a definition. 1235 /// Whether this function declaration has a definition.
@@ -1944,7 +1948,7 @@ impl Impl {
1944 } 1948 }
1945 1949
1946 pub fn all_for_type(db: &dyn HirDatabase, Type { krate, ty, .. }: Type) -> Vec<Impl> { 1950 pub fn all_for_type(db: &dyn HirDatabase, Type { krate, ty, .. }: Type) -> Vec<Impl> {
1947 let def_crates = match def_crates(db, &ty, krate) { 1951 let def_crates = match method_resolution::def_crates(db, &ty, krate) {
1948 Some(def_crates) => def_crates, 1952 Some(def_crates) => def_crates,
1949 None => return Vec::new(), 1953 None => return Vec::new(),
1950 }; 1954 };
@@ -2350,7 +2354,7 @@ impl Type {
2350 krate: Crate, 2354 krate: Crate,
2351 mut callback: impl FnMut(AssocItem) -> Option<T>, 2355 mut callback: impl FnMut(AssocItem) -> Option<T>,
2352 ) -> Option<T> { 2356 ) -> Option<T> {
2353 for krate in def_crates(db, &self.ty, krate.id)? { 2357 for krate in method_resolution::def_crates(db, &self.ty, krate.id)? {
2354 let impls = db.inherent_impls_in_crate(krate); 2358 let impls = db.inherent_impls_in_crate(krate);
2355 2359
2356 for impl_def in impls.for_self_ty(&self.ty) { 2360 for impl_def in impls.for_self_ty(&self.ty) {