diff options
author | Aleksey Kladov <[email protected]> | 2021-06-12 20:05:23 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2021-06-12 20:05:23 +0100 |
commit | 7731714578d4ae6eb7a54ead2e51ae032e9a700a (patch) | |
tree | f8dc2d68cfc72c1fcb839ec85093fb49f479663d /crates/hir_ty | |
parent | 6940cfed1e24a67e816e69e1093e04c0eb73e070 (diff) |
internal: move diagnostics infra to hir
Diffstat (limited to 'crates/hir_ty')
-rw-r--r-- | crates/hir_ty/src/diagnostics.rs | 156 | ||||
-rw-r--r-- | crates/hir_ty/src/diagnostics/decl_check.rs | 359 | ||||
-rw-r--r-- | crates/hir_ty/src/diagnostics_sink.rs | 109 | ||||
-rw-r--r-- | crates/hir_ty/src/lib.rs | 1 |
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; | |||
4 | mod unsafe_check; | 4 | mod unsafe_check; |
5 | mod decl_check; | 5 | mod decl_check; |
6 | 6 | ||
7 | use std::{any::Any, fmt}; | 7 | use std::fmt; |
8 | 8 | ||
9 | use base_db::CrateId; | 9 | use base_db::CrateId; |
10 | use hir_def::ModuleDefId; | 10 | use hir_def::ModuleDefId; |
11 | use hir_expand::{HirFileId, InFile}; | 11 | use hir_expand::HirFileId; |
12 | use syntax::{ast, AstPtr, SyntaxNodePtr}; | 12 | use syntax::{ast, AstPtr}; |
13 | 13 | ||
14 | use crate::{ | 14 | use crate::db::HirDatabase; |
15 | db::HirDatabase, | ||
16 | diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink}, | ||
17 | }; | ||
18 | 15 | ||
19 | pub use crate::diagnostics::{ | 16 | pub 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 | |||
103 | impl 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)] | ||
132 | mod 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 | ||
219 | mod permissions; | ||
220 | |||
221 | use permissions::jwt; | ||
222 | |||
223 | fn 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 | ||
229 | pub mod jwt { | ||
230 | pub struct Claims {} | ||
231 | } | ||
232 | |||
233 | //- /jwt/lib.rs crate:jwt | ||
234 | pub 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::{ | |||
29 | use crate::{ | 29 | use 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 | ||
35 | mod allow { | 34 | mod 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 | ||
43 | pub(super) struct DeclValidator<'a, 'b> { | 42 | pub(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 | ||
56 | impl<'a, 'b> DeclValidator<'a, 'b> { | 55 | impl<'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)] | ||
628 | mod tests { | ||
629 | use crate::diagnostics::tests::check_diagnostics; | ||
630 | |||
631 | #[test] | ||
632 | fn incorrect_function_name() { | ||
633 | check_diagnostics( | ||
634 | r#" | ||
635 | fn 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#" | ||
645 | fn foo(SomeParam: u8) {} | ||
646 | // ^^^^^^^^^ Parameter `SomeParam` should have snake_case name, e.g. `some_param` | ||
647 | |||
648 | fn 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#" | ||
658 | fn 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#" | ||
672 | struct non_camel_case_name {} | ||
673 | // ^^^^^^^^^^^^^^^^^^^ Structure `non_camel_case_name` should have CamelCase name, e.g. `NonCamelCaseName` | ||
674 | |||
675 | struct 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#" | ||
685 | struct AABB {} | ||
686 | "#, | ||
687 | ); | ||
688 | } | ||
689 | |||
690 | #[test] | ||
691 | fn incorrect_struct_field() { | ||
692 | check_diagnostics( | ||
693 | r#" | ||
694 | struct 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#" | ||
704 | enum some_enum { Val(u8) } | ||
705 | // ^^^^^^^^^ Enum `some_enum` should have CamelCase name, e.g. `SomeEnum` | ||
706 | |||
707 | enum 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#" | ||
717 | enum AABB {} | ||
718 | "#, | ||
719 | ); | ||
720 | } | ||
721 | |||
722 | #[test] | ||
723 | fn incorrect_enum_variant_name() { | ||
724 | check_diagnostics( | ||
725 | r#" | ||
726 | enum 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#" | ||
736 | const some_weird_const: u8 = 10; | ||
737 | // ^^^^^^^^^^^^^^^^ Constant `some_weird_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_WEIRD_CONST` | ||
738 | |||
739 | fn 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#" | ||
752 | static some_weird_const: u8 = 10; | ||
753 | // ^^^^^^^^^^^^^^^^ Static variable `some_weird_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_WEIRD_CONST` | ||
754 | |||
755 | fn 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#" | ||
767 | struct someStruct; | ||
768 | // ^^^^^^^^^^ Structure `someStruct` should have CamelCase name, e.g. `SomeStruct` | ||
769 | |||
770 | impl 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#" | ||
787 | enum Option { Some, None } | ||
788 | |||
789 | fn 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#" | ||
803 | enum Option { Some, None } | ||
804 | |||
805 | fn 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)] | ||
821 | fn 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)] | ||
833 | mod CheckNonstandardStyle { | ||
834 | fn HiImABadFnName() {} | ||
835 | } | ||
836 | |||
837 | #[allow(bad_style)] | ||
838 | mod CheckBadStyle { | ||
839 | fn HiImABadFnName() {} | ||
840 | } | ||
841 | |||
842 | mod F { | ||
843 | #![allow(non_snake_case)] | ||
844 | fn CheckItWorksWithModAttr(BAD_NAME_HI: u8) {} | ||
845 | } | ||
846 | |||
847 | #[allow(non_snake_case, non_camel_case_types)] | ||
848 | pub struct some_type { | ||
849 | SOME_FIELD: u8, | ||
850 | SomeField: u16, | ||
851 | } | ||
852 | |||
853 | #[allow(non_upper_case_globals)] | ||
854 | pub const some_const: u8 = 10; | ||
855 | |||
856 | #[allow(non_upper_case_globals)] | ||
857 | pub 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 | |||
868 | mod 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#" | ||
896 | trait T { fn a(); } | ||
897 | struct U {} | ||
898 | impl 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#" | ||
923 | trait 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#" | ||
940 | extern { | ||
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#" | ||
952 | fn 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 | |||
17 | use std::{any::Any, fmt}; | ||
18 | |||
19 | use hir_expand::InFile; | ||
20 | use syntax::SyntaxNodePtr; | ||
21 | |||
22 | #[derive(Copy, Clone, Debug, PartialEq)] | ||
23 | pub struct DiagnosticCode(pub &'static str); | ||
24 | |||
25 | impl DiagnosticCode { | ||
26 | pub fn as_str(&self) -> &str { | ||
27 | self.0 | ||
28 | } | ||
29 | } | ||
30 | |||
31 | pub 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 | |||
47 | pub 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 | |||
53 | impl<'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 | |||
75 | pub 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 | |||
80 | impl<'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; | |||
21 | mod walk; | 21 | mod walk; |
22 | pub mod db; | 22 | pub mod db; |
23 | pub mod diagnostics; | 23 | pub mod diagnostics; |
24 | pub mod diagnostics_sink; | ||
25 | pub mod display; | 24 | pub mod display; |
26 | pub mod method_resolution; | 25 | pub mod method_resolution; |
27 | pub mod primitive; | 26 | pub mod primitive; |