aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_ty
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2021-05-23 21:31:59 +0100
committerAleksey Kladov <[email protected]>2021-05-25 15:49:59 +0100
commit5c9f31d4c28478b4373e6cf5ec155745c840ee3f (patch)
tree6d105121d271c7532170875feafaadcd7ad500ba /crates/hir_ty
parentb7414fa14a85f4acd37b5bdfdc2a4ab97a072bd2 (diff)
internal: move diagnostics to hir
The idea here is to eventually get rid of `dyn Diagnostic` and `DiagnosticSink` infrastructure altogether, and just have a `enum hir::Diagnostic` instead. The problem with `dyn Diagnostic` is that it is defined in the lowest level of the stack (hir_expand), but is used by the highest level (ide). As a first step, we free hir_expand and hir_def from `dyn Diagnostic` and kick the can up to `hir_ty`, as an intermediate state. The plan is then to move DiagnosticSink similarly to the hir crate, and, as final third step, remove its usage from the ide. One currently unsolved problem is testing. You can notice that the test which checks precise diagnostic ranges, unresolved_import_in_use_tree, was moved to the ide layer. Logically, only IDE should have the infra to render a specific range. At the same time, the range is determined with the data produced in hir_def and hir crates, so this layering is rather unfortunate. Working on hir_def shouldn't require compiling `ide` for testing.
Diffstat (limited to 'crates/hir_ty')
-rw-r--r--crates/hir_ty/src/diagnostics.rs12
-rw-r--r--crates/hir_ty/src/diagnostics/decl_check.rs6
-rw-r--r--crates/hir_ty/src/diagnostics/expr.rs3
-rw-r--r--crates/hir_ty/src/diagnostics/unsafe_check.rs4
-rw-r--r--crates/hir_ty/src/diagnostics_sink.rs109
-rw-r--r--crates/hir_ty/src/infer.rs5
-rw-r--r--crates/hir_ty/src/lib.rs1
7 files changed, 125 insertions, 15 deletions
diff --git a/crates/hir_ty/src/diagnostics.rs b/crates/hir_ty/src/diagnostics.rs
index 84fc8ce14..7598e2193 100644
--- a/crates/hir_ty/src/diagnostics.rs
+++ b/crates/hir_ty/src/diagnostics.rs
@@ -8,12 +8,14 @@ use std::{any::Any, fmt};
8 8
9use base_db::CrateId; 9use base_db::CrateId;
10use hir_def::{DefWithBodyId, ModuleDefId}; 10use hir_def::{DefWithBodyId, ModuleDefId};
11use hir_expand::diagnostics::{Diagnostic, DiagnosticCode, DiagnosticSink};
12use hir_expand::{name::Name, HirFileId, InFile}; 11use hir_expand::{name::Name, HirFileId, InFile};
13use stdx::format_to; 12use stdx::format_to;
14use syntax::{ast, AstPtr, SyntaxNodePtr}; 13use syntax::{ast, AstPtr, SyntaxNodePtr};
15 14
16use crate::db::HirDatabase; 15use crate::{
16 db::HirDatabase,
17 diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink},
18};
17 19
18pub use crate::diagnostics::expr::{record_literal_missing_fields, record_pattern_missing_fields}; 20pub use crate::diagnostics::expr::{record_literal_missing_fields, record_pattern_missing_fields};
19 21
@@ -446,15 +448,13 @@ impl Diagnostic for ReplaceFilterMapNextWithFindMap {
446mod tests { 448mod tests {
447 use base_db::{fixture::WithFixture, FileId, SourceDatabase, SourceDatabaseExt}; 449 use base_db::{fixture::WithFixture, FileId, SourceDatabase, SourceDatabaseExt};
448 use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId}; 450 use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId};
449 use hir_expand::{ 451 use hir_expand::db::AstDatabase;
450 db::AstDatabase,
451 diagnostics::{Diagnostic, DiagnosticSinkBuilder},
452 };
453 use rustc_hash::FxHashMap; 452 use rustc_hash::FxHashMap;
454 use syntax::{TextRange, TextSize}; 453 use syntax::{TextRange, TextSize};
455 454
456 use crate::{ 455 use crate::{
457 diagnostics::{validate_body, validate_module_item}, 456 diagnostics::{validate_body, validate_module_item},
457 diagnostics_sink::{Diagnostic, DiagnosticSinkBuilder},
458 test_db::TestDB, 458 test_db::TestDB,
459 }; 459 };
460 460
diff --git a/crates/hir_ty/src/diagnostics/decl_check.rs b/crates/hir_ty/src/diagnostics/decl_check.rs
index 075dc4131..ef982cbcd 100644
--- a/crates/hir_ty/src/diagnostics/decl_check.rs
+++ b/crates/hir_ty/src/diagnostics/decl_check.rs
@@ -19,10 +19,7 @@ use hir_def::{
19 src::HasSource, 19 src::HasSource,
20 AdtId, AttrDefId, ConstId, EnumId, FunctionId, Lookup, ModuleDefId, StaticId, StructId, 20 AdtId, AttrDefId, ConstId, EnumId, FunctionId, Lookup, ModuleDefId, StaticId, StructId,
21}; 21};
22use hir_expand::{ 22use hir_expand::name::{AsName, Name};
23 diagnostics::DiagnosticSink,
24 name::{AsName, Name},
25};
26use stdx::{always, never}; 23use stdx::{always, never};
27use syntax::{ 24use syntax::{
28 ast::{self, NameOwner}, 25 ast::{self, NameOwner},
@@ -32,6 +29,7 @@ use syntax::{
32use crate::{ 29use crate::{
33 db::HirDatabase, 30 db::HirDatabase,
34 diagnostics::{decl_check::case_conv::*, CaseType, IdentType, IncorrectCase}, 31 diagnostics::{decl_check::case_conv::*, CaseType, IdentType, IncorrectCase},
32 diagnostics_sink::DiagnosticSink,
35}; 33};
36 34
37mod allow { 35mod allow {
diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs
index d1f113e7f..86f82e3fa 100644
--- a/crates/hir_ty/src/diagnostics/expr.rs
+++ b/crates/hir_ty/src/diagnostics/expr.rs
@@ -5,7 +5,7 @@
5use std::sync::Arc; 5use std::sync::Arc;
6 6
7use hir_def::{expr::Statement, path::path, resolver::HasResolver, AssocItemId, DefWithBodyId}; 7use hir_def::{expr::Statement, path::path, resolver::HasResolver, AssocItemId, DefWithBodyId};
8use hir_expand::{diagnostics::DiagnosticSink, name}; 8use hir_expand::name;
9use rustc_hash::FxHashSet; 9use rustc_hash::FxHashSet;
10use syntax::{ast, AstPtr}; 10use syntax::{ast, AstPtr};
11 11
@@ -16,6 +16,7 @@ use crate::{
16 MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr, 16 MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr,
17 MissingPatFields, RemoveThisSemicolon, 17 MissingPatFields, RemoveThisSemicolon,
18 }, 18 },
19 diagnostics_sink::DiagnosticSink,
19 AdtId, InferenceResult, Interner, TyExt, TyKind, 20 AdtId, InferenceResult, Interner, TyExt, TyKind,
20}; 21};
21 22
diff --git a/crates/hir_ty/src/diagnostics/unsafe_check.rs b/crates/hir_ty/src/diagnostics/unsafe_check.rs
index 5d13bddea..c3c483425 100644
--- a/crates/hir_ty/src/diagnostics/unsafe_check.rs
+++ b/crates/hir_ty/src/diagnostics/unsafe_check.rs
@@ -9,10 +9,10 @@ use hir_def::{
9 resolver::{resolver_for_expr, ResolveValueResult, ValueNs}, 9 resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
10 DefWithBodyId, 10 DefWithBodyId,
11}; 11};
12use hir_expand::diagnostics::DiagnosticSink;
13 12
14use crate::{ 13use crate::{
15 db::HirDatabase, diagnostics::MissingUnsafe, InferenceResult, Interner, TyExt, TyKind, 14 db::HirDatabase, diagnostics::MissingUnsafe, diagnostics_sink::DiagnosticSink, InferenceResult,
15 Interner, TyExt, TyKind,
16}; 16};
17 17
18pub(super) struct UnsafeValidator<'a, 'b: 'a> { 18pub(super) struct UnsafeValidator<'a, 'b: 'a> {
diff --git a/crates/hir_ty/src/diagnostics_sink.rs b/crates/hir_ty/src/diagnostics_sink.rs
new file mode 100644
index 000000000..084fa8b06
--- /dev/null
+++ b/crates/hir_ty/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_ty/src/infer.rs b/crates/hir_ty/src/infer.rs
index db3c937ff..174b7471e 100644
--- a/crates/hir_ty/src/infer.rs
+++ b/crates/hir_ty/src/infer.rs
@@ -28,13 +28,14 @@ use hir_def::{
28 AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, HasModule, Lookup, 28 AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, HasModule, Lookup,
29 TraitId, TypeAliasId, VariantId, 29 TraitId, TypeAliasId, VariantId,
30}; 30};
31use hir_expand::{diagnostics::DiagnosticSink, name::name}; 31use hir_expand::name::name;
32use la_arena::ArenaMap; 32use la_arena::ArenaMap;
33use rustc_hash::FxHashMap; 33use rustc_hash::FxHashMap;
34use stdx::impl_from; 34use stdx::impl_from;
35use syntax::SmolStr; 35use syntax::SmolStr;
36 36
37use super::{DomainGoal, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty}; 37use super::{DomainGoal, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty};
38use crate::diagnostics_sink::DiagnosticSink;
38use crate::{ 39use crate::{
39 db::HirDatabase, fold_tys, infer::diagnostics::InferenceDiagnostic, 40 db::HirDatabase, fold_tys, infer::diagnostics::InferenceDiagnostic,
40 lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Goal, Interner, Substitution, 41 lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Goal, Interner, Substitution,
@@ -793,11 +794,11 @@ impl std::ops::BitOrAssign for Diverges {
793 794
794mod diagnostics { 795mod diagnostics {
795 use hir_def::{expr::ExprId, DefWithBodyId}; 796 use hir_def::{expr::ExprId, DefWithBodyId};
796 use hir_expand::diagnostics::DiagnosticSink;
797 797
798 use crate::{ 798 use crate::{
799 db::HirDatabase, 799 db::HirDatabase,
800 diagnostics::{BreakOutsideOfLoop, NoSuchField}, 800 diagnostics::{BreakOutsideOfLoop, NoSuchField},
801 diagnostics_sink::DiagnosticSink,
801 }; 802 };
802 803
803 #[derive(Debug, PartialEq, Eq, Clone)] 804 #[derive(Debug, PartialEq, Eq, Clone)]
diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs
index ef021978a..50e0d6333 100644
--- a/crates/hir_ty/src/lib.rs
+++ b/crates/hir_ty/src/lib.rs
@@ -21,6 +21,7 @@ mod utils;
21mod walk; 21mod walk;
22pub mod db; 22pub mod db;
23pub mod diagnostics; 23pub mod diagnostics;
24pub mod diagnostics_sink;
24pub mod display; 25pub mod display;
25pub mod method_resolution; 26pub mod method_resolution;
26pub mod primitive; 27pub mod primitive;