aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_ty/src
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2021-06-12 20:05:58 +0100
committerGitHub <[email protected]>2021-06-12 20:05:58 +0100
commit124123a53bfbd32be5d63315885d2d5c9e3a1ee6 (patch)
tree536162f91898d588ddc48b4ab9eb51ee3994d11c /crates/hir_ty/src
parent907d4c3e956b6437d7754d674088fb7ccd92a560 (diff)
parent7731714578d4ae6eb7a54ead2e51ae032e9a700a (diff)
Merge #9237
9237: internal: move diagnostics infra to hir r=matklad a=matklad bors r+ 🤖 Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates/hir_ty/src')
-rw-r--r--crates/hir_ty/src/diagnostics.rs156
-rw-r--r--crates/hir_ty/src/diagnostics/decl_check.rs359
-rw-r--r--crates/hir_ty/src/diagnostics_sink.rs109
-rw-r--r--crates/hir_ty/src/lib.rs1
4 files changed, 13 insertions, 612 deletions
diff --git a/crates/hir_ty/src/diagnostics.rs b/crates/hir_ty/src/diagnostics.rs
index f3236bc06..407273943 100644
--- a/crates/hir_ty/src/diagnostics.rs
+++ b/crates/hir_ty/src/diagnostics.rs
@@ -4,17 +4,14 @@ mod match_check;
4mod unsafe_check; 4mod unsafe_check;
5mod decl_check; 5mod decl_check;
6 6
7use std::{any::Any, fmt}; 7use std::fmt;
8 8
9use base_db::CrateId; 9use base_db::CrateId;
10use hir_def::ModuleDefId; 10use hir_def::ModuleDefId;
11use hir_expand::{HirFileId, InFile}; 11use hir_expand::HirFileId;
12use syntax::{ast, AstPtr, SyntaxNodePtr}; 12use syntax::{ast, AstPtr};
13 13
14use crate::{ 14use crate::db::HirDatabase;
15 db::HirDatabase,
16 diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink},
17};
18 15
19pub use crate::diagnostics::{ 16pub use crate::diagnostics::{
20 expr::{ 17 expr::{
@@ -27,11 +24,11 @@ pub fn validate_module_item(
27 db: &dyn HirDatabase, 24 db: &dyn HirDatabase,
28 krate: CrateId, 25 krate: CrateId,
29 owner: ModuleDefId, 26 owner: ModuleDefId,
30 sink: &mut DiagnosticSink<'_>, 27) -> Vec<IncorrectCase> {
31) {
32 let _p = profile::span("validate_module_item"); 28 let _p = profile::span("validate_module_item");
33 let mut validator = decl_check::DeclValidator::new(db, krate, sink); 29 let mut validator = decl_check::DeclValidator::new(db, krate);
34 validator.validate_item(owner); 30 validator.validate_item(owner);
31 validator.sink
35} 32}
36 33
37#[derive(Debug)] 34#[derive(Debug)]
@@ -99,142 +96,3 @@ pub struct IncorrectCase {
99 pub ident_text: String, 96 pub ident_text: String,
100 pub suggested_text: String, 97 pub suggested_text: String,
101} 98}
102
103impl Diagnostic for IncorrectCase {
104 fn code(&self) -> DiagnosticCode {
105 DiagnosticCode("incorrect-ident-case")
106 }
107
108 fn message(&self) -> String {
109 format!(
110 "{} `{}` should have {} name, e.g. `{}`",
111 self.ident_type,
112 self.ident_text,
113 self.expected_case.to_string(),
114 self.suggested_text
115 )
116 }
117
118 fn display_source(&self) -> InFile<SyntaxNodePtr> {
119 InFile::new(self.file, self.ident.clone().into())
120 }
121
122 fn as_any(&self) -> &(dyn Any + Send + 'static) {
123 self
124 }
125
126 fn is_experimental(&self) -> bool {
127 true
128 }
129}
130
131#[cfg(test)]
132mod tests {
133 use base_db::{fixture::WithFixture, FileId, SourceDatabase, SourceDatabaseExt};
134 use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId};
135 use hir_expand::db::AstDatabase;
136 use rustc_hash::FxHashMap;
137 use syntax::{TextRange, TextSize};
138
139 use crate::{
140 diagnostics::validate_module_item,
141 diagnostics_sink::{Diagnostic, DiagnosticSinkBuilder},
142 test_db::TestDB,
143 };
144
145 impl TestDB {
146 fn diagnostics<F: FnMut(&dyn Diagnostic)>(&self, mut cb: F) {
147 let crate_graph = self.crate_graph();
148 for krate in crate_graph.iter() {
149 let crate_def_map = self.crate_def_map(krate);
150
151 let mut fns = Vec::new();
152 for (module_id, _) in crate_def_map.modules() {
153 for decl in crate_def_map[module_id].scope.declarations() {
154 let mut sink = DiagnosticSinkBuilder::new().build(&mut cb);
155 validate_module_item(self, krate, decl, &mut sink);
156
157 if let ModuleDefId::FunctionId(f) = decl {
158 fns.push(f)
159 }
160 }
161
162 for impl_id in crate_def_map[module_id].scope.impls() {
163 let impl_data = self.impl_data(impl_id);
164 for item in impl_data.items.iter() {
165 if let AssocItemId::FunctionId(f) = item {
166 let mut sink = DiagnosticSinkBuilder::new().build(&mut cb);
167 validate_module_item(
168 self,
169 krate,
170 ModuleDefId::FunctionId(*f),
171 &mut sink,
172 );
173 fns.push(*f)
174 }
175 }
176 }
177 }
178 }
179 }
180 }
181
182 pub(crate) fn check_diagnostics(ra_fixture: &str) {
183 let db = TestDB::with_files(ra_fixture);
184 let annotations = db.extract_annotations();
185
186 let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default();
187 db.diagnostics(|d| {
188 let src = d.display_source();
189 let root = db.parse_or_expand(src.file_id).unwrap();
190 // FIXME: macros...
191 let file_id = src.file_id.original_file(&db);
192 let range = src.value.to_node(&root).text_range();
193 let message = d.message();
194 actual.entry(file_id).or_default().push((range, message));
195 });
196
197 for (file_id, diags) in actual.iter_mut() {
198 diags.sort_by_key(|it| it.0.start());
199 let text = db.file_text(*file_id);
200 // For multiline spans, place them on line start
201 for (range, content) in diags {
202 if text[*range].contains('\n') {
203 *range = TextRange::new(range.start(), range.start() + TextSize::from(1));
204 *content = format!("... {}", content);
205 }
206 }
207 }
208
209 assert_eq!(annotations, actual);
210 }
211
212 #[test]
213 fn import_extern_crate_clash_with_inner_item() {
214 // This is more of a resolver test, but doesn't really work with the hir_def testsuite.
215
216 check_diagnostics(
217 r#"
218//- /lib.rs crate:lib deps:jwt
219mod permissions;
220
221use permissions::jwt;
222
223fn f() {
224 fn inner() {}
225 jwt::Claims {}; // should resolve to the local one with 0 fields, and not get a diagnostic
226}
227
228//- /permissions.rs
229pub mod jwt {
230 pub struct Claims {}
231}
232
233//- /jwt/lib.rs crate:jwt
234pub struct Claims {
235 field: u8,
236}
237 "#,
238 );
239 }
240}
diff --git a/crates/hir_ty/src/diagnostics/decl_check.rs b/crates/hir_ty/src/diagnostics/decl_check.rs
index cfb5d7320..ca452e879 100644
--- a/crates/hir_ty/src/diagnostics/decl_check.rs
+++ b/crates/hir_ty/src/diagnostics/decl_check.rs
@@ -29,7 +29,6 @@ use syntax::{
29use crate::{ 29use crate::{
30 db::HirDatabase, 30 db::HirDatabase,
31 diagnostics::{decl_check::case_conv::*, CaseType, IdentType, IncorrectCase}, 31 diagnostics::{decl_check::case_conv::*, CaseType, IdentType, IncorrectCase},
32 diagnostics_sink::DiagnosticSink,
33}; 32};
34 33
35mod allow { 34mod allow {
@@ -40,10 +39,10 @@ mod allow {
40 pub(super) const NON_CAMEL_CASE_TYPES: &str = "non_camel_case_types"; 39 pub(super) const NON_CAMEL_CASE_TYPES: &str = "non_camel_case_types";
41} 40}
42 41
43pub(super) struct DeclValidator<'a, 'b> { 42pub(super) struct DeclValidator<'a> {
44 db: &'a dyn HirDatabase, 43 db: &'a dyn HirDatabase,
45 krate: CrateId, 44 krate: CrateId,
46 sink: &'a mut DiagnosticSink<'b>, 45 pub(super) sink: Vec<IncorrectCase>,
47} 46}
48 47
49#[derive(Debug)] 48#[derive(Debug)]
@@ -53,13 +52,9 @@ struct Replacement {
53 expected_case: CaseType, 52 expected_case: CaseType,
54} 53}
55 54
56impl<'a, 'b> DeclValidator<'a, 'b> { 55impl<'a> DeclValidator<'a> {
57 pub(super) fn new( 56 pub(super) fn new(db: &'a dyn HirDatabase, krate: CrateId) -> DeclValidator<'a> {
58 db: &'a dyn HirDatabase, 57 DeclValidator { db, krate, sink: Vec::new() }
59 krate: CrateId,
60 sink: &'a mut DiagnosticSink<'b>,
61 ) -> DeclValidator<'a, 'b> {
62 DeclValidator { db, krate, sink }
63 } 58 }
64 59
65 pub(super) fn validate_item(&mut self, item: ModuleDefId) { 60 pub(super) fn validate_item(&mut self, item: ModuleDefId) {
@@ -121,7 +116,6 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
121 fn validate_func(&mut self, func: FunctionId) { 116 fn validate_func(&mut self, func: FunctionId) {
122 let data = self.db.function_data(func); 117 let data = self.db.function_data(func);
123 if data.is_in_extern_block() { 118 if data.is_in_extern_block() {
124 cov_mark::hit!(extern_func_incorrect_case_ignored);
125 return; 119 return;
126 } 120 }
127 121
@@ -131,7 +125,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
131 for (_, block_def_map) in body.blocks(self.db.upcast()) { 125 for (_, block_def_map) in body.blocks(self.db.upcast()) {
132 for (_, module) in block_def_map.modules() { 126 for (_, module) in block_def_map.modules() {
133 for def_id in module.scope.declarations() { 127 for def_id in module.scope.declarations() {
134 let mut validator = DeclValidator::new(self.db, self.krate, self.sink); 128 let mut validator = DeclValidator::new(self.db, self.krate);
135 validator.validate_item(def_id); 129 validator.validate_item(def_id);
136 } 130 }
137 } 131 }
@@ -578,7 +572,6 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
578 fn validate_static(&mut self, static_id: StaticId) { 572 fn validate_static(&mut self, static_id: StaticId) {
579 let data = self.db.static_data(static_id); 573 let data = self.db.static_data(static_id);
580 if data.is_extern { 574 if data.is_extern {
581 cov_mark::hit!(extern_static_incorrect_case_ignored);
582 return; 575 return;
583 } 576 }
584 577
@@ -623,343 +616,3 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
623 self.sink.push(diagnostic); 616 self.sink.push(diagnostic);
624 } 617 }
625} 618}
626
627#[cfg(test)]
628mod tests {
629 use crate::diagnostics::tests::check_diagnostics;
630
631 #[test]
632 fn incorrect_function_name() {
633 check_diagnostics(
634 r#"
635fn NonSnakeCaseName() {}
636// ^^^^^^^^^^^^^^^^ Function `NonSnakeCaseName` should have snake_case name, e.g. `non_snake_case_name`
637"#,
638 );
639 }
640
641 #[test]
642 fn incorrect_function_params() {
643 check_diagnostics(
644 r#"
645fn foo(SomeParam: u8) {}
646 // ^^^^^^^^^ Parameter `SomeParam` should have snake_case name, e.g. `some_param`
647
648fn foo2(ok_param: &str, CAPS_PARAM: u8) {}
649 // ^^^^^^^^^^ Parameter `CAPS_PARAM` should have snake_case name, e.g. `caps_param`
650"#,
651 );
652 }
653
654 #[test]
655 fn incorrect_variable_names() {
656 check_diagnostics(
657 r#"
658fn foo() {
659 let SOME_VALUE = 10;
660 // ^^^^^^^^^^ Variable `SOME_VALUE` should have snake_case name, e.g. `some_value`
661 let AnotherValue = 20;
662 // ^^^^^^^^^^^^ Variable `AnotherValue` should have snake_case name, e.g. `another_value`
663}
664"#,
665 );
666 }
667
668 #[test]
669 fn incorrect_struct_names() {
670 check_diagnostics(
671 r#"
672struct non_camel_case_name {}
673 // ^^^^^^^^^^^^^^^^^^^ Structure `non_camel_case_name` should have CamelCase name, e.g. `NonCamelCaseName`
674
675struct SCREAMING_CASE {}
676 // ^^^^^^^^^^^^^^ Structure `SCREAMING_CASE` should have CamelCase name, e.g. `ScreamingCase`
677"#,
678 );
679 }
680
681 #[test]
682 fn no_diagnostic_for_camel_cased_acronyms_in_struct_name() {
683 check_diagnostics(
684 r#"
685struct AABB {}
686"#,
687 );
688 }
689
690 #[test]
691 fn incorrect_struct_field() {
692 check_diagnostics(
693 r#"
694struct SomeStruct { SomeField: u8 }
695 // ^^^^^^^^^ Field `SomeField` should have snake_case name, e.g. `some_field`
696"#,
697 );
698 }
699
700 #[test]
701 fn incorrect_enum_names() {
702 check_diagnostics(
703 r#"
704enum some_enum { Val(u8) }
705 // ^^^^^^^^^ Enum `some_enum` should have CamelCase name, e.g. `SomeEnum`
706
707enum SOME_ENUM
708 // ^^^^^^^^^ Enum `SOME_ENUM` should have CamelCase name, e.g. `SomeEnum`
709"#,
710 );
711 }
712
713 #[test]
714 fn no_diagnostic_for_camel_cased_acronyms_in_enum_name() {
715 check_diagnostics(
716 r#"
717enum AABB {}
718"#,
719 );
720 }
721
722 #[test]
723 fn incorrect_enum_variant_name() {
724 check_diagnostics(
725 r#"
726enum SomeEnum { SOME_VARIANT(u8) }
727 // ^^^^^^^^^^^^ Variant `SOME_VARIANT` should have CamelCase name, e.g. `SomeVariant`
728"#,
729 );
730 }
731
732 #[test]
733 fn incorrect_const_name() {
734 check_diagnostics(
735 r#"
736const some_weird_const: u8 = 10;
737 // ^^^^^^^^^^^^^^^^ Constant `some_weird_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_WEIRD_CONST`
738
739fn func() {
740 const someConstInFunc: &str = "hi there";
741 // ^^^^^^^^^^^^^^^ Constant `someConstInFunc` should have UPPER_SNAKE_CASE name, e.g. `SOME_CONST_IN_FUNC`
742
743}
744"#,
745 );
746 }
747
748 #[test]
749 fn incorrect_static_name() {
750 check_diagnostics(
751 r#"
752static some_weird_const: u8 = 10;
753 // ^^^^^^^^^^^^^^^^ Static variable `some_weird_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_WEIRD_CONST`
754
755fn func() {
756 static someConstInFunc: &str = "hi there";
757 // ^^^^^^^^^^^^^^^ Static variable `someConstInFunc` should have UPPER_SNAKE_CASE name, e.g. `SOME_CONST_IN_FUNC`
758}
759"#,
760 );
761 }
762
763 #[test]
764 fn fn_inside_impl_struct() {
765 check_diagnostics(
766 r#"
767struct someStruct;
768 // ^^^^^^^^^^ Structure `someStruct` should have CamelCase name, e.g. `SomeStruct`
769
770impl someStruct {
771 fn SomeFunc(&self) {
772 // ^^^^^^^^ Function `SomeFunc` should have snake_case name, e.g. `some_func`
773 static someConstInFunc: &str = "hi there";
774 // ^^^^^^^^^^^^^^^ Static variable `someConstInFunc` should have UPPER_SNAKE_CASE name, e.g. `SOME_CONST_IN_FUNC`
775 let WHY_VAR_IS_CAPS = 10;
776 // ^^^^^^^^^^^^^^^ Variable `WHY_VAR_IS_CAPS` should have snake_case name, e.g. `why_var_is_caps`
777 }
778}
779"#,
780 );
781 }
782
783 #[test]
784 fn no_diagnostic_for_enum_varinats() {
785 check_diagnostics(
786 r#"
787enum Option { Some, None }
788
789fn main() {
790 match Option::None {
791 None => (),
792 Some => (),
793 }
794}
795"#,
796 );
797 }
798
799 #[test]
800 fn non_let_bind() {
801 check_diagnostics(
802 r#"
803enum Option { Some, None }
804
805fn main() {
806 match Option::None {
807 SOME_VAR @ None => (),
808 // ^^^^^^^^ Variable `SOME_VAR` should have snake_case name, e.g. `some_var`
809 Some => (),
810 }
811}
812"#,
813 );
814 }
815
816 #[test]
817 fn allow_attributes() {
818 check_diagnostics(
819 r#"
820#[allow(non_snake_case)]
821fn NonSnakeCaseName(SOME_VAR: u8) -> u8{
822 // cov_flags generated output from elsewhere in this file
823 extern "C" {
824 #[no_mangle]
825 static lower_case: u8;
826 }
827
828 let OtherVar = SOME_VAR + 1;
829 OtherVar
830}
831
832#[allow(nonstandard_style)]
833mod CheckNonstandardStyle {
834 fn HiImABadFnName() {}
835}
836
837#[allow(bad_style)]
838mod CheckBadStyle {
839 fn HiImABadFnName() {}
840}
841
842mod F {
843 #![allow(non_snake_case)]
844 fn CheckItWorksWithModAttr(BAD_NAME_HI: u8) {}
845}
846
847#[allow(non_snake_case, non_camel_case_types)]
848pub struct some_type {
849 SOME_FIELD: u8,
850 SomeField: u16,
851}
852
853#[allow(non_upper_case_globals)]
854pub const some_const: u8 = 10;
855
856#[allow(non_upper_case_globals)]
857pub static SomeStatic: u8 = 10;
858 "#,
859 );
860 }
861
862 #[test]
863 fn allow_attributes_crate_attr() {
864 check_diagnostics(
865 r#"
866#![allow(non_snake_case)]
867
868mod F {
869 fn CheckItWorksWithCrateAttr(BAD_NAME_HI: u8) {}
870}
871 "#,
872 );
873 }
874
875 #[test]
876 #[ignore]
877 fn bug_trait_inside_fn() {
878 // FIXME:
879 // This is broken, and in fact, should not even be looked at by this
880 // lint in the first place. There's weird stuff going on in the
881 // collection phase.
882 // It's currently being brought in by:
883 // * validate_func on `a` recursing into modules
884 // * then it finds the trait and then the function while iterating
885 // through modules
886 // * then validate_func is called on Dirty
887 // * ... which then proceeds to look at some unknown module taking no
888 // attrs from either the impl or the fn a, and then finally to the root
889 // module
890 //
891 // It should find the attribute on the trait, but it *doesn't even see
892 // the trait* as far as I can tell.
893
894 check_diagnostics(
895 r#"
896trait T { fn a(); }
897struct U {}
898impl T for U {
899 fn a() {
900 // this comes out of bitflags, mostly
901 #[allow(non_snake_case)]
902 trait __BitFlags {
903 const HiImAlsoBad: u8 = 2;
904 #[inline]
905 fn Dirty(&self) -> bool {
906 false
907 }
908 }
909
910 }
911}
912 "#,
913 );
914 }
915
916 #[test]
917 #[ignore]
918 fn bug_traits_arent_checked() {
919 // FIXME: Traits and functions in traits aren't currently checked by
920 // r-a, even though rustc will complain about them.
921 check_diagnostics(
922 r#"
923trait BAD_TRAIT {
924 // ^^^^^^^^^ Trait `BAD_TRAIT` should have CamelCase name, e.g. `BadTrait`
925 fn BAD_FUNCTION();
926 // ^^^^^^^^^^^^ Function `BAD_FUNCTION` should have snake_case name, e.g. `bad_function`
927 fn BadFunction();
928 // ^^^^^^^^^^^^ Function `BadFunction` should have snake_case name, e.g. `bad_function`
929}
930 "#,
931 );
932 }
933
934 #[test]
935 fn ignores_extern_items() {
936 cov_mark::check!(extern_func_incorrect_case_ignored);
937 cov_mark::check!(extern_static_incorrect_case_ignored);
938 check_diagnostics(
939 r#"
940extern {
941 fn NonSnakeCaseName(SOME_VAR: u8) -> u8;
942 pub static SomeStatic: u8 = 10;
943}
944 "#,
945 );
946 }
947
948 #[test]
949 fn infinite_loop_inner_items() {
950 check_diagnostics(
951 r#"
952fn qualify() {
953 mod foo {
954 use super::*;
955 }
956}
957 "#,
958 )
959 }
960
961 #[test] // Issue #8809.
962 fn parenthesized_parameter() {
963 check_diagnostics(r#"fn f((O): _) {}"#)
964 }
965}
diff --git a/crates/hir_ty/src/diagnostics_sink.rs b/crates/hir_ty/src/diagnostics_sink.rs
deleted file mode 100644
index 084fa8b06..000000000
--- a/crates/hir_ty/src/diagnostics_sink.rs
+++ /dev/null
@@ -1,109 +0,0 @@
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/lib.rs b/crates/hir_ty/src/lib.rs
index 0c6b19653..128cae830 100644
--- a/crates/hir_ty/src/lib.rs
+++ b/crates/hir_ty/src/lib.rs
@@ -21,7 +21,6 @@ mod utils;
21mod walk; 21mod walk;
22pub mod db; 22pub mod db;
23pub mod diagnostics; 23pub mod diagnostics;
24pub mod diagnostics_sink;
25pub mod display; 24pub mod display;
26pub mod method_resolution; 25pub mod method_resolution;
27pub mod primitive; 26pub mod primitive;