aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_ty/src/diagnostics
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_ty/src/diagnostics')
-rw-r--r--crates/hir_ty/src/diagnostics/decl_check.rs357
-rw-r--r--crates/hir_ty/src/diagnostics/expr.rs499
-rw-r--r--crates/hir_ty/src/diagnostics/match_check.rs957
-rw-r--r--crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs1
-rw-r--r--crates/hir_ty/src/diagnostics/unsafe_check.rs151
5 files changed, 113 insertions, 1852 deletions
diff --git a/crates/hir_ty/src/diagnostics/decl_check.rs b/crates/hir_ty/src/diagnostics/decl_check.rs
index cfb5d7320..f26150b77 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) {
@@ -131,7 +126,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
131 for (_, block_def_map) in body.blocks(self.db.upcast()) { 126 for (_, block_def_map) in body.blocks(self.db.upcast()) {
132 for (_, module) in block_def_map.modules() { 127 for (_, module) in block_def_map.modules() {
133 for def_id in module.scope.declarations() { 128 for def_id in module.scope.declarations() {
134 let mut validator = DeclValidator::new(self.db, self.krate, self.sink); 129 let mut validator = DeclValidator::new(self.db, self.krate);
135 validator.validate_item(def_id); 130 validator.validate_item(def_id);
136 } 131 }
137 } 132 }
@@ -623,343 +618,3 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
623 self.sink.push(diagnostic); 618 self.sink.push(diagnostic);
624 } 619 }
625} 620}
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/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs
index a2a4d61db..c480ed352 100644
--- a/crates/hir_ty/src/diagnostics/expr.rs
+++ b/crates/hir_ty/src/diagnostics/expr.rs
@@ -9,19 +9,13 @@ use hir_def::{
9}; 9};
10use hir_expand::name; 10use hir_expand::name;
11use rustc_hash::FxHashSet; 11use rustc_hash::FxHashSet;
12use syntax::{ast, AstPtr};
13 12
14use crate::{ 13use crate::{
15 db::HirDatabase, 14 db::HirDatabase,
16 diagnostics::{ 15 diagnostics::match_check::{
17 match_check::{ 16 self,
18 self, 17 usefulness::{compute_match_usefulness, expand_pattern, MatchCheckCtx, PatternArena},
19 usefulness::{compute_match_usefulness, expand_pattern, MatchCheckCtx, PatternArena},
20 },
21 MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr,
22 MissingPatFields, RemoveThisSemicolon,
23 }, 18 },
24 diagnostics_sink::DiagnosticSink,
25 AdtId, InferenceResult, Interner, TyExt, TyKind, 19 AdtId, InferenceResult, Interner, TyExt, TyKind,
26}; 20};
27 21
@@ -31,38 +25,81 @@ pub(crate) use hir_def::{
31 LocalFieldId, VariantId, 25 LocalFieldId, VariantId,
32}; 26};
33 27
34use super::ReplaceFilterMapNextWithFindMap; 28pub enum BodyValidationDiagnostic {
29 RecordLiteralMissingFields {
30 record_expr: ExprId,
31 variant: VariantId,
32 missed_fields: Vec<LocalFieldId>,
33 },
34 RecordPatMissingFields {
35 record_pat: PatId,
36 variant: VariantId,
37 missed_fields: Vec<LocalFieldId>,
38 },
39 ReplaceFilterMapNextWithFindMap {
40 method_call_expr: ExprId,
41 },
42 MismatchedArgCount {
43 call_expr: ExprId,
44 expected: usize,
45 found: usize,
46 },
47 RemoveThisSemicolon {
48 expr: ExprId,
49 },
50 MissingOkOrSomeInTailExpr {
51 expr: ExprId,
52 required: String,
53 },
54 MissingMatchArms {
55 match_expr: ExprId,
56 },
57 InternalBailedOut {
58 pat: PatId,
59 },
60}
35 61
36pub(super) struct ExprValidator<'a, 'b: 'a> { 62impl BodyValidationDiagnostic {
63 pub fn collect(
64 db: &dyn HirDatabase,
65 owner: DefWithBodyId,
66 internal_diagnostics: bool,
67 ) -> Vec<BodyValidationDiagnostic> {
68 let _p = profile::span("BodyValidationDiagnostic::collect");
69 let infer = db.infer(owner);
70 let mut validator = ExprValidator::new(owner, infer.clone());
71 validator.internal_diagnostics = internal_diagnostics;
72 validator.validate_body(db);
73 validator.diagnostics
74 }
75}
76
77struct ExprValidator {
37 owner: DefWithBodyId, 78 owner: DefWithBodyId,
38 infer: Arc<InferenceResult>, 79 infer: Arc<InferenceResult>,
39 sink: &'a mut DiagnosticSink<'b>, 80 pub(super) diagnostics: Vec<BodyValidationDiagnostic>,
81 internal_diagnostics: bool,
40} 82}
41 83
42impl<'a, 'b> ExprValidator<'a, 'b> { 84impl ExprValidator {
43 pub(super) fn new( 85 fn new(owner: DefWithBodyId, infer: Arc<InferenceResult>) -> ExprValidator {
44 owner: DefWithBodyId, 86 ExprValidator { owner, infer, diagnostics: Vec::new(), internal_diagnostics: false }
45 infer: Arc<InferenceResult>,
46 sink: &'a mut DiagnosticSink<'b>,
47 ) -> ExprValidator<'a, 'b> {
48 ExprValidator { owner, infer, sink }
49 } 87 }
50 88
51 pub(super) fn validate_body(&mut self, db: &dyn HirDatabase) { 89 fn validate_body(&mut self, db: &dyn HirDatabase) {
52 self.check_for_filter_map_next(db); 90 self.check_for_filter_map_next(db);
53 91
54 let body = db.body(self.owner); 92 let body = db.body(self.owner);
55 93
56 for (id, expr) in body.exprs.iter() { 94 for (id, expr) in body.exprs.iter() {
57 if let Some((variant_def, missed_fields, true)) = 95 if let Some((variant, missed_fields, true)) =
58 record_literal_missing_fields(db, &self.infer, id, expr) 96 record_literal_missing_fields(db, &self.infer, id, expr)
59 { 97 {
60 self.create_record_literal_missing_fields_diagnostic( 98 self.diagnostics.push(BodyValidationDiagnostic::RecordLiteralMissingFields {
61 id, 99 record_expr: id,
62 db, 100 variant,
63 variant_def,
64 missed_fields, 101 missed_fields,
65 ); 102 });
66 } 103 }
67 104
68 match expr { 105 match expr {
@@ -76,15 +113,14 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
76 } 113 }
77 } 114 }
78 for (id, pat) in body.pats.iter() { 115 for (id, pat) in body.pats.iter() {
79 if let Some((variant_def, missed_fields, true)) = 116 if let Some((variant, missed_fields, true)) =
80 record_pattern_missing_fields(db, &self.infer, id, pat) 117 record_pattern_missing_fields(db, &self.infer, id, pat)
81 { 118 {
82 self.create_record_pattern_missing_fields_diagnostic( 119 self.diagnostics.push(BodyValidationDiagnostic::RecordPatMissingFields {
83 id, 120 record_pat: id,
84 db, 121 variant,
85 variant_def,
86 missed_fields, 122 missed_fields,
87 ); 123 });
88 } 124 }
89 } 125 }
90 let body_expr = &body[body.body_expr]; 126 let body_expr = &body[body.body_expr];
@@ -92,71 +128,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
92 if let Some(t) = tail { 128 if let Some(t) = tail {
93 self.validate_results_in_tail_expr(body.body_expr, *t, db); 129 self.validate_results_in_tail_expr(body.body_expr, *t, db);
94 } else if let Some(Statement::Expr { expr: id, .. }) = statements.last() { 130 } else if let Some(Statement::Expr { expr: id, .. }) = statements.last() {
95 self.validate_missing_tail_expr(body.body_expr, *id, db); 131 self.validate_missing_tail_expr(body.body_expr, *id);
96 }
97 }
98 }
99
100 fn create_record_literal_missing_fields_diagnostic(
101 &mut self,
102 id: ExprId,
103 db: &dyn HirDatabase,
104 variant_def: VariantId,
105 missed_fields: Vec<LocalFieldId>,
106 ) {
107 // XXX: only look at source_map if we do have missing fields
108 let (_, source_map) = db.body_with_source_map(self.owner);
109
110 if let Ok(source_ptr) = source_map.expr_syntax(id) {
111 let root = source_ptr.file_syntax(db.upcast());
112 if let ast::Expr::RecordExpr(record_expr) = &source_ptr.value.to_node(&root) {
113 if let Some(_) = record_expr.record_expr_field_list() {
114 let variant_data = variant_def.variant_data(db.upcast());
115 let missed_fields = missed_fields
116 .into_iter()
117 .map(|idx| variant_data.fields()[idx].name.clone())
118 .collect();
119 self.sink.push(MissingFields {
120 file: source_ptr.file_id,
121 field_list_parent: AstPtr::new(&record_expr),
122 field_list_parent_path: record_expr.path().map(|path| AstPtr::new(&path)),
123 missed_fields,
124 })
125 }
126 }
127 }
128 }
129
130 fn create_record_pattern_missing_fields_diagnostic(
131 &mut self,
132 id: PatId,
133 db: &dyn HirDatabase,
134 variant_def: VariantId,
135 missed_fields: Vec<LocalFieldId>,
136 ) {
137 // XXX: only look at source_map if we do have missing fields
138 let (_, source_map) = db.body_with_source_map(self.owner);
139
140 if let Ok(source_ptr) = source_map.pat_syntax(id) {
141 if let Some(expr) = source_ptr.value.as_ref().left() {
142 let root = source_ptr.file_syntax(db.upcast());
143 if let ast::Pat::RecordPat(record_pat) = expr.to_node(&root) {
144 if let Some(_) = record_pat.record_pat_field_list() {
145 let variant_data = variant_def.variant_data(db.upcast());
146 let missed_fields = missed_fields
147 .into_iter()
148 .map(|idx| variant_data.fields()[idx].name.clone())
149 .collect();
150 self.sink.push(MissingPatFields {
151 file: source_ptr.file_id,
152 field_list_parent: AstPtr::new(&record_pat),
153 field_list_parent_path: record_pat
154 .path()
155 .map(|path| AstPtr::new(&path)),
156 missed_fields,
157 })
158 }
159 }
160 } 132 }
161 } 133 }
162 } 134 }
@@ -199,13 +171,11 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
199 if function_id == *next_function_id { 171 if function_id == *next_function_id {
200 if let Some(filter_map_id) = prev { 172 if let Some(filter_map_id) = prev {
201 if *receiver == filter_map_id { 173 if *receiver == filter_map_id {
202 let (_, source_map) = db.body_with_source_map(self.owner); 174 self.diagnostics.push(
203 if let Ok(next_source_ptr) = source_map.expr_syntax(id) { 175 BodyValidationDiagnostic::ReplaceFilterMapNextWithFindMap {
204 self.sink.push(ReplaceFilterMapNextWithFindMap { 176 method_call_expr: id,
205 file: next_source_ptr.file_id, 177 },
206 next_expr: next_source_ptr.value, 178 );
207 });
208 }
209 } 179 }
210 } 180 }
211 } 181 }
@@ -266,19 +236,15 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
266 let mut arg_count = args.len(); 236 let mut arg_count = args.len();
267 237
268 if arg_count != param_count { 238 if arg_count != param_count {
269 let (_, source_map) = db.body_with_source_map(self.owner); 239 if is_method_call {
270 if let Ok(source_ptr) = source_map.expr_syntax(call_id) { 240 param_count -= 1;
271 if is_method_call { 241 arg_count -= 1;
272 param_count -= 1;
273 arg_count -= 1;
274 }
275 self.sink.push(MismatchedArgCount {
276 file: source_ptr.file_id,
277 call_expr: source_ptr.value,
278 expected: param_count,
279 found: arg_count,
280 });
281 } 242 }
243 self.diagnostics.push(BodyValidationDiagnostic::MismatchedArgCount {
244 call_expr: call_id,
245 expected: param_count,
246 found: arg_count,
247 });
282 } 248 }
283 } 249 }
284 250
@@ -346,8 +312,9 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
346 // fit the match expression, we skip this diagnostic. Skipping the entire 312 // fit the match expression, we skip this diagnostic. Skipping the entire
347 // diagnostic rather than just not including this match arm is preferred 313 // diagnostic rather than just not including this match arm is preferred
348 // to avoid the chance of false positives. 314 // to avoid the chance of false positives.
349 #[cfg(test)] 315 if self.internal_diagnostics {
350 match_check::tests::report_bail_out(db, self.owner, arm.pat, self.sink); 316 self.diagnostics.push(BodyValidationDiagnostic::InternalBailedOut { pat: arm.pat })
317 }
351 return; 318 return;
352 } 319 }
353 320
@@ -382,20 +349,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
382 // FIXME Report witnesses 349 // FIXME Report witnesses
383 // eprintln!("compute_match_usefulness(..) -> {:?}", &witnesses); 350 // eprintln!("compute_match_usefulness(..) -> {:?}", &witnesses);
384 if !witnesses.is_empty() { 351 if !witnesses.is_empty() {
385 if let Ok(source_ptr) = source_map.expr_syntax(id) { 352 self.diagnostics.push(BodyValidationDiagnostic::MissingMatchArms { match_expr: id });
386 let root = source_ptr.file_syntax(db.upcast());
387 if let ast::Expr::MatchExpr(match_expr) = &source_ptr.value.to_node(&root) {
388 if let (Some(match_expr), Some(arms)) =
389 (match_expr.expr(), match_expr.match_arm_list())
390 {
391 self.sink.push(MissingMatchArms {
392 file: source_ptr.file_id,
393 match_expr: AstPtr::new(&match_expr),
394 arms: AstPtr::new(&arms),
395 })
396 }
397 }
398 }
399 } 353 }
400 } 354 }
401 355
@@ -453,24 +407,12 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
453 if params.len(&Interner) > 0 407 if params.len(&Interner) > 0
454 && params.at(&Interner, 0).ty(&Interner) == Some(&mismatch.actual) 408 && params.at(&Interner, 0).ty(&Interner) == Some(&mismatch.actual)
455 { 409 {
456 let (_, source_map) = db.body_with_source_map(self.owner); 410 self.diagnostics
457 411 .push(BodyValidationDiagnostic::MissingOkOrSomeInTailExpr { expr: id, required });
458 if let Ok(source_ptr) = source_map.expr_syntax(id) {
459 self.sink.push(MissingOkOrSomeInTailExpr {
460 file: source_ptr.file_id,
461 expr: source_ptr.value,
462 required,
463 });
464 }
465 } 412 }
466 } 413 }
467 414
468 fn validate_missing_tail_expr( 415 fn validate_missing_tail_expr(&mut self, body_id: ExprId, possible_tail_id: ExprId) {
469 &mut self,
470 body_id: ExprId,
471 possible_tail_id: ExprId,
472 db: &dyn HirDatabase,
473 ) {
474 let mismatch = match self.infer.type_mismatch_for_expr(body_id) { 416 let mismatch = match self.infer.type_mismatch_for_expr(body_id) {
475 Some(m) => m, 417 Some(m) => m,
476 None => return, 418 None => return,
@@ -485,12 +427,8 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
485 return; 427 return;
486 } 428 }
487 429
488 let (_, source_map) = db.body_with_source_map(self.owner); 430 self.diagnostics
489 431 .push(BodyValidationDiagnostic::RemoveThisSemicolon { expr: possible_tail_id });
490 if let Ok(source_ptr) = source_map.expr_syntax(possible_tail_id) {
491 self.sink
492 .push(RemoveThisSemicolon { file: source_ptr.file_id, expr: source_ptr.value });
493 }
494 } 432 }
495} 433}
496 434
@@ -568,258 +506,3 @@ fn types_of_subpatterns_do_match(pat: PatId, body: &Body, infer: &InferenceResul
568 walk(pat, body, infer, &mut has_type_mismatches); 506 walk(pat, body, infer, &mut has_type_mismatches);
569 !has_type_mismatches 507 !has_type_mismatches
570} 508}
571
572#[cfg(test)]
573mod tests {
574 use crate::diagnostics::tests::check_diagnostics;
575
576 #[test]
577 fn simple_free_fn_zero() {
578 check_diagnostics(
579 r#"
580fn zero() {}
581fn f() { zero(1); }
582 //^^^^^^^ Expected 0 arguments, found 1
583"#,
584 );
585
586 check_diagnostics(
587 r#"
588fn zero() {}
589fn f() { zero(); }
590"#,
591 );
592 }
593
594 #[test]
595 fn simple_free_fn_one() {
596 check_diagnostics(
597 r#"
598fn one(arg: u8) {}
599fn f() { one(); }
600 //^^^^^ Expected 1 argument, found 0
601"#,
602 );
603
604 check_diagnostics(
605 r#"
606fn one(arg: u8) {}
607fn f() { one(1); }
608"#,
609 );
610 }
611
612 #[test]
613 fn method_as_fn() {
614 check_diagnostics(
615 r#"
616struct S;
617impl S { fn method(&self) {} }
618
619fn f() {
620 S::method();
621} //^^^^^^^^^^^ Expected 1 argument, found 0
622"#,
623 );
624
625 check_diagnostics(
626 r#"
627struct S;
628impl S { fn method(&self) {} }
629
630fn f() {
631 S::method(&S);
632 S.method();
633}
634"#,
635 );
636 }
637
638 #[test]
639 fn method_with_arg() {
640 check_diagnostics(
641 r#"
642struct S;
643impl S { fn method(&self, arg: u8) {} }
644
645 fn f() {
646 S.method();
647 } //^^^^^^^^^^ Expected 1 argument, found 0
648 "#,
649 );
650
651 check_diagnostics(
652 r#"
653struct S;
654impl S { fn method(&self, arg: u8) {} }
655
656fn f() {
657 S::method(&S, 0);
658 S.method(1);
659}
660"#,
661 );
662 }
663
664 #[test]
665 fn method_unknown_receiver() {
666 // note: this is incorrect code, so there might be errors on this in the
667 // future, but we shouldn't emit an argument count diagnostic here
668 check_diagnostics(
669 r#"
670trait Foo { fn method(&self, arg: usize) {} }
671
672fn f() {
673 let x;
674 x.method();
675}
676"#,
677 );
678 }
679
680 #[test]
681 fn tuple_struct() {
682 check_diagnostics(
683 r#"
684struct Tup(u8, u16);
685fn f() {
686 Tup(0);
687} //^^^^^^ Expected 2 arguments, found 1
688"#,
689 )
690 }
691
692 #[test]
693 fn enum_variant() {
694 check_diagnostics(
695 r#"
696enum En { Variant(u8, u16), }
697fn f() {
698 En::Variant(0);
699} //^^^^^^^^^^^^^^ Expected 2 arguments, found 1
700"#,
701 )
702 }
703
704 #[test]
705 fn enum_variant_type_macro() {
706 check_diagnostics(
707 r#"
708macro_rules! Type {
709 () => { u32 };
710}
711enum Foo {
712 Bar(Type![])
713}
714impl Foo {
715 fn new() {
716 Foo::Bar(0);
717 Foo::Bar(0, 1);
718 //^^^^^^^^^^^^^^ Expected 1 argument, found 2
719 Foo::Bar();
720 //^^^^^^^^^^ Expected 1 argument, found 0
721 }
722}
723 "#,
724 );
725 }
726
727 #[test]
728 fn varargs() {
729 check_diagnostics(
730 r#"
731extern "C" {
732 fn fixed(fixed: u8);
733 fn varargs(fixed: u8, ...);
734 fn varargs2(...);
735}
736
737fn f() {
738 unsafe {
739 fixed(0);
740 fixed(0, 1);
741 //^^^^^^^^^^^ Expected 1 argument, found 2
742 varargs(0);
743 varargs(0, 1);
744 varargs2();
745 varargs2(0);
746 varargs2(0, 1);
747 }
748}
749 "#,
750 )
751 }
752
753 #[test]
754 fn arg_count_lambda() {
755 check_diagnostics(
756 r#"
757fn main() {
758 let f = |()| ();
759 f();
760 //^^^ Expected 1 argument, found 0
761 f(());
762 f((), ());
763 //^^^^^^^^^ Expected 1 argument, found 2
764}
765"#,
766 )
767 }
768
769 #[test]
770 fn cfgd_out_call_arguments() {
771 check_diagnostics(
772 r#"
773struct C(#[cfg(FALSE)] ());
774impl C {
775 fn new() -> Self {
776 Self(
777 #[cfg(FALSE)]
778 (),
779 )
780 }
781
782 fn method(&self) {}
783}
784
785fn main() {
786 C::new().method(#[cfg(FALSE)] 0);
787}
788 "#,
789 );
790 }
791
792 #[test]
793 fn cfgd_out_fn_params() {
794 check_diagnostics(
795 r#"
796fn foo(#[cfg(NEVER)] x: ()) {}
797
798struct S;
799
800impl S {
801 fn method(#[cfg(NEVER)] self) {}
802 fn method2(#[cfg(NEVER)] self, arg: u8) {}
803 fn method3(self, #[cfg(NEVER)] arg: u8) {}
804}
805
806extern "C" {
807 fn fixed(fixed: u8, #[cfg(NEVER)] ...);
808 fn varargs(#[cfg(not(NEVER))] ...);
809}
810
811fn main() {
812 foo();
813 S::method();
814 S::method2(0);
815 S::method3(S);
816 S.method3();
817 unsafe {
818 fixed(0);
819 varargs(1, 2, 3);
820 }
821}
822 "#,
823 )
824 }
825}
diff --git a/crates/hir_ty/src/diagnostics/match_check.rs b/crates/hir_ty/src/diagnostics/match_check.rs
index c8e1b23de..a30e42699 100644
--- a/crates/hir_ty/src/diagnostics/match_check.rs
+++ b/crates/hir_ty/src/diagnostics/match_check.rs
@@ -364,960 +364,3 @@ impl PatternFoldable for PatKind {
364 } 364 }
365 } 365 }
366} 366}
367
368#[cfg(test)]
369pub(super) mod tests {
370 mod report {
371 use std::any::Any;
372
373 use hir_def::{expr::PatId, DefWithBodyId};
374 use hir_expand::{HirFileId, InFile};
375 use syntax::SyntaxNodePtr;
376
377 use crate::{
378 db::HirDatabase,
379 diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink},
380 };
381
382 /// In tests, match check bails out loudly.
383 /// This helps to catch incorrect tests that pass due to false negatives.
384 pub(crate) fn report_bail_out(
385 db: &dyn HirDatabase,
386 def: DefWithBodyId,
387 pat: PatId,
388 sink: &mut DiagnosticSink,
389 ) {
390 let (_, source_map) = db.body_with_source_map(def);
391 if let Ok(source_ptr) = source_map.pat_syntax(pat) {
392 let pat_syntax_ptr = source_ptr.value.either(Into::into, Into::into);
393 sink.push(BailedOut { file: source_ptr.file_id, pat_syntax_ptr });
394 }
395 }
396
397 #[derive(Debug)]
398 struct BailedOut {
399 file: HirFileId,
400 pat_syntax_ptr: SyntaxNodePtr,
401 }
402
403 impl Diagnostic for BailedOut {
404 fn code(&self) -> DiagnosticCode {
405 DiagnosticCode("internal:match-check-bailed-out")
406 }
407 fn message(&self) -> String {
408 format!("Internal: match check bailed out")
409 }
410 fn display_source(&self) -> InFile<SyntaxNodePtr> {
411 InFile { file_id: self.file, value: self.pat_syntax_ptr.clone() }
412 }
413 fn as_any(&self) -> &(dyn Any + Send + 'static) {
414 self
415 }
416 }
417 }
418
419 use crate::diagnostics::tests::check_diagnostics;
420
421 pub(crate) use self::report::report_bail_out;
422
423 #[test]
424 fn empty_tuple() {
425 check_diagnostics(
426 r#"
427fn main() {
428 match () { }
429 //^^ Missing match arm
430 match (()) { }
431 //^^^^ Missing match arm
432
433 match () { _ => (), }
434 match () { () => (), }
435 match (()) { (()) => (), }
436}
437"#,
438 );
439 }
440
441 #[test]
442 fn tuple_of_two_empty_tuple() {
443 check_diagnostics(
444 r#"
445fn main() {
446 match ((), ()) { }
447 //^^^^^^^^ Missing match arm
448
449 match ((), ()) { ((), ()) => (), }
450}
451"#,
452 );
453 }
454
455 #[test]
456 fn boolean() {
457 check_diagnostics(
458 r#"
459fn test_main() {
460 match false { }
461 //^^^^^ Missing match arm
462 match false { true => (), }
463 //^^^^^ Missing match arm
464 match (false, true) {}
465 //^^^^^^^^^^^^^ Missing match arm
466 match (false, true) { (true, true) => (), }
467 //^^^^^^^^^^^^^ Missing match arm
468 match (false, true) {
469 //^^^^^^^^^^^^^ Missing match arm
470 (false, true) => (),
471 (false, false) => (),
472 (true, false) => (),
473 }
474 match (false, true) { (true, _x) => (), }
475 //^^^^^^^^^^^^^ Missing match arm
476
477 match false { true => (), false => (), }
478 match (false, true) {
479 (false, _) => (),
480 (true, false) => (),
481 (_, true) => (),
482 }
483 match (false, true) {
484 (true, true) => (),
485 (true, false) => (),
486 (false, true) => (),
487 (false, false) => (),
488 }
489 match (false, true) {
490 (true, _x) => (),
491 (false, true) => (),
492 (false, false) => (),
493 }
494 match (false, true, false) {
495 (false, ..) => (),
496 (true, ..) => (),
497 }
498 match (false, true, false) {
499 (.., false) => (),
500 (.., true) => (),
501 }
502 match (false, true, false) { (..) => (), }
503}
504"#,
505 );
506 }
507
508 #[test]
509 fn tuple_of_tuple_and_bools() {
510 check_diagnostics(
511 r#"
512fn main() {
513 match (false, ((), false)) {}
514 //^^^^^^^^^^^^^^^^^^^^ Missing match arm
515 match (false, ((), false)) { (true, ((), true)) => (), }
516 //^^^^^^^^^^^^^^^^^^^^ Missing match arm
517 match (false, ((), false)) { (true, _) => (), }
518 //^^^^^^^^^^^^^^^^^^^^ Missing match arm
519
520 match (false, ((), false)) {
521 (true, ((), true)) => (),
522 (true, ((), false)) => (),
523 (false, ((), true)) => (),
524 (false, ((), false)) => (),
525 }
526 match (false, ((), false)) {
527 (true, ((), true)) => (),
528 (true, ((), false)) => (),
529 (false, _) => (),
530 }
531}
532"#,
533 );
534 }
535
536 #[test]
537 fn enums() {
538 check_diagnostics(
539 r#"
540enum Either { A, B, }
541
542fn main() {
543 match Either::A { }
544 //^^^^^^^^^ Missing match arm
545 match Either::B { Either::A => (), }
546 //^^^^^^^^^ Missing match arm
547
548 match &Either::B {
549 //^^^^^^^^^^ Missing match arm
550 Either::A => (),
551 }
552
553 match Either::B {
554 Either::A => (), Either::B => (),
555 }
556 match &Either::B {
557 Either::A => (), Either::B => (),
558 }
559}
560"#,
561 );
562 }
563
564 #[test]
565 fn enum_containing_bool() {
566 check_diagnostics(
567 r#"
568enum Either { A(bool), B }
569
570fn main() {
571 match Either::B { }
572 //^^^^^^^^^ Missing match arm
573 match Either::B {
574 //^^^^^^^^^ Missing match arm
575 Either::A(true) => (), Either::B => ()
576 }
577
578 match Either::B {
579 Either::A(true) => (),
580 Either::A(false) => (),
581 Either::B => (),
582 }
583 match Either::B {
584 Either::B => (),
585 _ => (),
586 }
587 match Either::B {
588 Either::A(_) => (),
589 Either::B => (),
590 }
591
592}
593 "#,
594 );
595 }
596
597 #[test]
598 fn enum_different_sizes() {
599 check_diagnostics(
600 r#"
601enum Either { A(bool), B(bool, bool) }
602
603fn main() {
604 match Either::A(false) {
605 //^^^^^^^^^^^^^^^^ Missing match arm
606 Either::A(_) => (),
607 Either::B(false, _) => (),
608 }
609
610 match Either::A(false) {
611 Either::A(_) => (),
612 Either::B(true, _) => (),
613 Either::B(false, _) => (),
614 }
615 match Either::A(false) {
616 Either::A(true) | Either::A(false) => (),
617 Either::B(true, _) => (),
618 Either::B(false, _) => (),
619 }
620}
621"#,
622 );
623 }
624
625 #[test]
626 fn tuple_of_enum_no_diagnostic() {
627 check_diagnostics(
628 r#"
629enum Either { A(bool), B(bool, bool) }
630enum Either2 { C, D }
631
632fn main() {
633 match (Either::A(false), Either2::C) {
634 (Either::A(true), _) | (Either::A(false), _) => (),
635 (Either::B(true, _), Either2::C) => (),
636 (Either::B(false, _), Either2::C) => (),
637 (Either::B(_, _), Either2::D) => (),
638 }
639}
640"#,
641 );
642 }
643
644 #[test]
645 fn or_pattern_no_diagnostic() {
646 check_diagnostics(
647 r#"
648enum Either {A, B}
649
650fn main() {
651 match (Either::A, Either::B) {
652 (Either::A | Either::B, _) => (),
653 }
654}"#,
655 )
656 }
657
658 #[test]
659 fn mismatched_types() {
660 // Match statements with arms that don't match the
661 // expression pattern do not fire this diagnostic.
662 check_diagnostics(
663 r#"
664enum Either { A, B }
665enum Either2 { C, D }
666
667fn main() {
668 match Either::A {
669 Either2::C => (),
670 // ^^^^^^^^^^ Internal: match check bailed out
671 Either2::D => (),
672 }
673 match (true, false) {
674 (true, false, true) => (),
675 // ^^^^^^^^^^^^^^^^^^^ Internal: match check bailed out
676 (true) => (),
677 }
678 match (true, false) { (true,) => {} }
679 // ^^^^^^^ Internal: match check bailed out
680 match (0) { () => () }
681 // ^^ Internal: match check bailed out
682 match Unresolved::Bar { Unresolved::Baz => () }
683}
684 "#,
685 );
686 }
687
688 #[test]
689 fn mismatched_types_in_or_patterns() {
690 check_diagnostics(
691 r#"
692fn main() {
693 match false { true | () => {} }
694 // ^^^^^^^^^ Internal: match check bailed out
695 match (false,) { (true | (),) => {} }
696 // ^^^^^^^^^^^^ Internal: match check bailed out
697}
698"#,
699 );
700 }
701
702 #[test]
703 fn malformed_match_arm_tuple_enum_missing_pattern() {
704 // We are testing to be sure we don't panic here when the match
705 // arm `Either::B` is missing its pattern.
706 check_diagnostics(
707 r#"
708enum Either { A, B(u32) }
709
710fn main() {
711 match Either::A {
712 Either::A => (),
713 Either::B() => (),
714 }
715}
716"#,
717 );
718 }
719
720 #[test]
721 fn malformed_match_arm_extra_fields() {
722 check_diagnostics(
723 r#"
724enum A { B(isize, isize), C }
725fn main() {
726 match A::B(1, 2) {
727 A::B(_, _, _) => (),
728 // ^^^^^^^^^^^^^ Internal: match check bailed out
729 }
730 match A::B(1, 2) {
731 A::C(_) => (),
732 // ^^^^^^^ Internal: match check bailed out
733 }
734}
735"#,
736 );
737 }
738
739 #[test]
740 fn expr_diverges() {
741 check_diagnostics(
742 r#"
743enum Either { A, B }
744
745fn main() {
746 match loop {} {
747 Either::A => (),
748 // ^^^^^^^^^ Internal: match check bailed out
749 Either::B => (),
750 }
751 match loop {} {
752 Either::A => (),
753 // ^^^^^^^^^ Internal: match check bailed out
754 }
755 match loop { break Foo::A } {
756 //^^^^^^^^^^^^^^^^^^^^^ Missing match arm
757 Either::A => (),
758 }
759 match loop { break Foo::A } {
760 Either::A => (),
761 Either::B => (),
762 }
763}
764"#,
765 );
766 }
767
768 #[test]
769 fn expr_partially_diverges() {
770 check_diagnostics(
771 r#"
772enum Either<T> { A(T), B }
773
774fn foo() -> Either<!> { Either::B }
775fn main() -> u32 {
776 match foo() {
777 Either::A(val) => val,
778 Either::B => 0,
779 }
780}
781"#,
782 );
783 }
784
785 #[test]
786 fn enum_record() {
787 check_diagnostics(
788 r#"
789enum Either { A { foo: bool }, B }
790
791fn main() {
792 let a = Either::A { foo: true };
793 match a { }
794 //^ Missing match arm
795 match a { Either::A { foo: true } => () }
796 //^ Missing match arm
797 match a {
798 Either::A { } => (),
799 //^^^^^^^^^ Missing structure fields:
800 // | - foo
801 Either::B => (),
802 }
803 match a {
804 //^ Missing match arm
805 Either::A { } => (),
806 } //^^^^^^^^^ Missing structure fields:
807 // | - foo
808
809 match a {
810 Either::A { foo: true } => (),
811 Either::A { foo: false } => (),
812 Either::B => (),
813 }
814 match a {
815 Either::A { foo: _ } => (),
816 Either::B => (),
817 }
818}
819"#,
820 );
821 }
822
823 #[test]
824 fn enum_record_fields_out_of_order() {
825 check_diagnostics(
826 r#"
827enum Either {
828 A { foo: bool, bar: () },
829 B,
830}
831
832fn main() {
833 let a = Either::A { foo: true, bar: () };
834 match a {
835 //^ Missing match arm
836 Either::A { bar: (), foo: false } => (),
837 Either::A { foo: true, bar: () } => (),
838 }
839
840 match a {
841 Either::A { bar: (), foo: false } => (),
842 Either::A { foo: true, bar: () } => (),
843 Either::B => (),
844 }
845}
846"#,
847 );
848 }
849
850 #[test]
851 fn enum_record_ellipsis() {
852 check_diagnostics(
853 r#"
854enum Either {
855 A { foo: bool, bar: bool },
856 B,
857}
858
859fn main() {
860 let a = Either::B;
861 match a {
862 //^ Missing match arm
863 Either::A { foo: true, .. } => (),
864 Either::B => (),
865 }
866 match a {
867 //^ Missing match arm
868 Either::A { .. } => (),
869 }
870
871 match a {
872 Either::A { foo: true, .. } => (),
873 Either::A { foo: false, .. } => (),
874 Either::B => (),
875 }
876
877 match a {
878 Either::A { .. } => (),
879 Either::B => (),
880 }
881}
882"#,
883 );
884 }
885
886 #[test]
887 fn enum_tuple_partial_ellipsis() {
888 check_diagnostics(
889 r#"
890enum Either {
891 A(bool, bool, bool, bool),
892 B,
893}
894
895fn main() {
896 match Either::B {
897 //^^^^^^^^^ Missing match arm
898 Either::A(true, .., true) => (),
899 Either::A(true, .., false) => (),
900 Either::A(false, .., false) => (),
901 Either::B => (),
902 }
903 match Either::B {
904 //^^^^^^^^^ Missing match arm
905 Either::A(true, .., true) => (),
906 Either::A(true, .., false) => (),
907 Either::A(.., true) => (),
908 Either::B => (),
909 }
910
911 match Either::B {
912 Either::A(true, .., true) => (),
913 Either::A(true, .., false) => (),
914 Either::A(false, .., true) => (),
915 Either::A(false, .., false) => (),
916 Either::B => (),
917 }
918 match Either::B {
919 Either::A(true, .., true) => (),
920 Either::A(true, .., false) => (),
921 Either::A(.., true) => (),
922 Either::A(.., false) => (),
923 Either::B => (),
924 }
925}
926"#,
927 );
928 }
929
930 #[test]
931 fn never() {
932 check_diagnostics(
933 r#"
934enum Never {}
935
936fn enum_(never: Never) {
937 match never {}
938}
939fn enum_ref(never: &Never) {
940 match never {}
941 //^^^^^ Missing match arm
942}
943fn bang(never: !) {
944 match never {}
945}
946"#,
947 );
948 }
949
950 #[test]
951 fn unknown_type() {
952 check_diagnostics(
953 r#"
954enum Option<T> { Some(T), None }
955
956fn main() {
957 // `Never` is deliberately not defined so that it's an uninferred type.
958 match Option::<Never>::None {
959 None => (),
960 Some(never) => match never {},
961 // ^^^^^^^^^^^ Internal: match check bailed out
962 }
963 match Option::<Never>::None {
964 //^^^^^^^^^^^^^^^^^^^^^ Missing match arm
965 Option::Some(_never) => {},
966 }
967}
968"#,
969 );
970 }
971
972 #[test]
973 fn tuple_of_bools_with_ellipsis_at_end_missing_arm() {
974 check_diagnostics(
975 r#"
976fn main() {
977 match (false, true, false) {
978 //^^^^^^^^^^^^^^^^^^^^ Missing match arm
979 (false, ..) => (),
980 }
981}"#,
982 );
983 }
984
985 #[test]
986 fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() {
987 check_diagnostics(
988 r#"
989fn main() {
990 match (false, true, false) {
991 //^^^^^^^^^^^^^^^^^^^^ Missing match arm
992 (.., false) => (),
993 }
994}"#,
995 );
996 }
997
998 #[test]
999 fn tuple_of_bools_with_ellipsis_in_middle_missing_arm() {
1000 check_diagnostics(
1001 r#"
1002fn main() {
1003 match (false, true, false) {
1004 //^^^^^^^^^^^^^^^^^^^^ Missing match arm
1005 (true, .., false) => (),
1006 }
1007}"#,
1008 );
1009 }
1010
1011 #[test]
1012 fn record_struct() {
1013 check_diagnostics(
1014 r#"struct Foo { a: bool }
1015fn main(f: Foo) {
1016 match f {}
1017 //^ Missing match arm
1018 match f { Foo { a: true } => () }
1019 //^ Missing match arm
1020 match &f { Foo { a: true } => () }
1021 //^^ Missing match arm
1022 match f { Foo { a: _ } => () }
1023 match f {
1024 Foo { a: true } => (),
1025 Foo { a: false } => (),
1026 }
1027 match &f {
1028 Foo { a: true } => (),
1029 Foo { a: false } => (),
1030 }
1031}
1032"#,
1033 );
1034 }
1035
1036 #[test]
1037 fn tuple_struct() {
1038 check_diagnostics(
1039 r#"struct Foo(bool);
1040fn main(f: Foo) {
1041 match f {}
1042 //^ Missing match arm
1043 match f { Foo(true) => () }
1044 //^ Missing match arm
1045 match f {
1046 Foo(true) => (),
1047 Foo(false) => (),
1048 }
1049}
1050"#,
1051 );
1052 }
1053
1054 #[test]
1055 fn unit_struct() {
1056 check_diagnostics(
1057 r#"struct Foo;
1058fn main(f: Foo) {
1059 match f {}
1060 //^ Missing match arm
1061 match f { Foo => () }
1062}
1063"#,
1064 );
1065 }
1066
1067 #[test]
1068 fn record_struct_ellipsis() {
1069 check_diagnostics(
1070 r#"struct Foo { foo: bool, bar: bool }
1071fn main(f: Foo) {
1072 match f { Foo { foo: true, .. } => () }
1073 //^ Missing match arm
1074 match f {
1075 //^ Missing match arm
1076 Foo { foo: true, .. } => (),
1077 Foo { bar: false, .. } => ()
1078 }
1079 match f { Foo { .. } => () }
1080 match f {
1081 Foo { foo: true, .. } => (),
1082 Foo { foo: false, .. } => ()
1083 }
1084}
1085"#,
1086 );
1087 }
1088
1089 #[test]
1090 fn internal_or() {
1091 check_diagnostics(
1092 r#"
1093fn main() {
1094 enum Either { A(bool), B }
1095 match Either::B {
1096 //^^^^^^^^^ Missing match arm
1097 Either::A(true | false) => (),
1098 }
1099}
1100"#,
1101 );
1102 }
1103
1104 #[test]
1105 fn no_panic_at_unimplemented_subpattern_type() {
1106 check_diagnostics(
1107 r#"
1108struct S { a: char}
1109fn main(v: S) {
1110 match v { S{ a } => {} }
1111 match v { S{ a: _x } => {} }
1112 match v { S{ a: 'a' } => {} }
1113 //^^^^^^^^^^^ Internal: match check bailed out
1114 match v { S{..} => {} }
1115 match v { _ => {} }
1116 match v { }
1117 //^ Missing match arm
1118}
1119"#,
1120 );
1121 }
1122
1123 #[test]
1124 fn binding() {
1125 check_diagnostics(
1126 r#"
1127fn main() {
1128 match true {
1129 _x @ true => {}
1130 false => {}
1131 }
1132 match true { _x @ true => {} }
1133 //^^^^ Missing match arm
1134}
1135"#,
1136 );
1137 }
1138
1139 #[test]
1140 fn binding_ref_has_correct_type() {
1141 // Asserts `PatKind::Binding(ref _x): bool`, not &bool.
1142 // If that's not true match checking will panic with "incompatible constructors"
1143 // FIXME: make facilities to test this directly like `tests::check_infer(..)`
1144 check_diagnostics(
1145 r#"
1146enum Foo { A }
1147fn main() {
1148 // FIXME: this should not bail out but current behavior is such as the old algorithm.
1149 // ExprValidator::validate_match(..) checks types of top level patterns incorrecly.
1150 match Foo::A {
1151 ref _x => {}
1152 // ^^^^^^ Internal: match check bailed out
1153 Foo::A => {}
1154 }
1155 match (true,) {
1156 (ref _x,) => {}
1157 (true,) => {}
1158 }
1159}
1160"#,
1161 );
1162 }
1163
1164 #[test]
1165 fn enum_non_exhaustive() {
1166 check_diagnostics(
1167 r#"
1168//- /lib.rs crate:lib
1169#[non_exhaustive]
1170pub enum E { A, B }
1171fn _local() {
1172 match E::A { _ => {} }
1173 match E::A {
1174 E::A => {}
1175 E::B => {}
1176 }
1177 match E::A {
1178 E::A | E::B => {}
1179 }
1180}
1181
1182//- /main.rs crate:main deps:lib
1183use lib::E;
1184fn main() {
1185 match E::A { _ => {} }
1186 match E::A {
1187 //^^^^ Missing match arm
1188 E::A => {}
1189 E::B => {}
1190 }
1191 match E::A {
1192 //^^^^ Missing match arm
1193 E::A | E::B => {}
1194 }
1195}
1196"#,
1197 );
1198 }
1199
1200 #[test]
1201 fn match_guard() {
1202 check_diagnostics(
1203 r#"
1204fn main() {
1205 match true {
1206 true if false => {}
1207 true => {}
1208 false => {}
1209 }
1210 match true {
1211 //^^^^ Missing match arm
1212 true if false => {}
1213 false => {}
1214}
1215"#,
1216 );
1217 }
1218
1219 #[test]
1220 fn pattern_type_is_of_substitution() {
1221 cov_mark::check!(match_check_wildcard_expanded_to_substitutions);
1222 check_diagnostics(
1223 r#"
1224struct Foo<T>(T);
1225struct Bar;
1226fn main() {
1227 match Foo(Bar) {
1228 _ | Foo(Bar) => {}
1229 }
1230}
1231"#,
1232 );
1233 }
1234
1235 #[test]
1236 fn record_struct_no_such_field() {
1237 check_diagnostics(
1238 r#"
1239struct Foo { }
1240fn main(f: Foo) {
1241 match f { Foo { bar } => () }
1242 // ^^^^^^^^^^^ Internal: match check bailed out
1243}
1244"#,
1245 );
1246 }
1247
1248 #[test]
1249 fn match_ergonomics_issue_9095() {
1250 check_diagnostics(
1251 r#"
1252enum Foo<T> { A(T) }
1253fn main() {
1254 match &Foo::A(true) {
1255 _ => {}
1256 Foo::A(_) => {}
1257 }
1258}
1259"#,
1260 );
1261 }
1262
1263 mod false_negatives {
1264 //! The implementation of match checking here is a work in progress. As we roll this out, we
1265 //! prefer false negatives to false positives (ideally there would be no false positives). This
1266 //! test module should document known false negatives. Eventually we will have a complete
1267 //! implementation of match checking and this module will be empty.
1268 //!
1269 //! The reasons for documenting known false negatives:
1270 //!
1271 //! 1. It acts as a backlog of work that can be done to improve the behavior of the system.
1272 //! 2. It ensures the code doesn't panic when handling these cases.
1273 use super::*;
1274
1275 #[test]
1276 fn integers() {
1277 // We don't currently check integer exhaustiveness.
1278 check_diagnostics(
1279 r#"
1280fn main() {
1281 match 5 {
1282 10 => (),
1283 // ^^ Internal: match check bailed out
1284 11..20 => (),
1285 }
1286}
1287"#,
1288 );
1289 }
1290
1291 #[test]
1292 fn reference_patterns_at_top_level() {
1293 check_diagnostics(
1294 r#"
1295fn main() {
1296 match &false {
1297 &true => {}
1298 // ^^^^^ Internal: match check bailed out
1299 }
1300}
1301 "#,
1302 );
1303 }
1304
1305 #[test]
1306 fn reference_patterns_in_fields() {
1307 check_diagnostics(
1308 r#"
1309fn main() {
1310 match (&false,) {
1311 (true,) => {}
1312 // ^^^^^^^ Internal: match check bailed out
1313 }
1314 match (&false,) {
1315 (&true,) => {}
1316 // ^^^^^^^^ Internal: match check bailed out
1317 }
1318}
1319 "#,
1320 );
1321 }
1322 }
1323}
diff --git a/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs b/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs
index 1f4219b42..222141bd6 100644
--- a/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs
+++ b/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs
@@ -664,7 +664,6 @@ impl Fields {
664 let is_non_exhaustive = 664 let is_non_exhaustive =
665 is_field_list_non_exhaustive(variant_id, cx) && !adt_is_local; 665 is_field_list_non_exhaustive(variant_id, cx) && !adt_is_local;
666 666
667 cov_mark::hit!(match_check_wildcard_expanded_to_substitutions);
668 let field_ty_data = cx.db.field_types(variant_id); 667 let field_ty_data = cx.db.field_types(variant_id);
669 let field_tys = || { 668 let field_tys = || {
670 field_ty_data 669 field_ty_data
diff --git a/crates/hir_ty/src/diagnostics/unsafe_check.rs b/crates/hir_ty/src/diagnostics/unsafe_check.rs
index c3c483425..777f347b8 100644
--- a/crates/hir_ty/src/diagnostics/unsafe_check.rs
+++ b/crates/hir_ty/src/diagnostics/unsafe_check.rs
@@ -1,8 +1,6 @@
1//! Provides validations for unsafe code. Currently checks if unsafe functions are missing 1//! Provides validations for unsafe code. Currently checks if unsafe functions are missing
2//! unsafe blocks. 2//! unsafe blocks.
3 3
4use std::sync::Arc;
5
6use hir_def::{ 4use hir_def::{
7 body::Body, 5 body::Body,
8 expr::{Expr, ExprId, UnaryOp}, 6 expr::{Expr, ExprId, UnaryOp},
@@ -10,60 +8,32 @@ use hir_def::{
10 DefWithBodyId, 8 DefWithBodyId,
11}; 9};
12 10
13use crate::{ 11use crate::{db::HirDatabase, InferenceResult, Interner, TyExt, TyKind};
14 db::HirDatabase, diagnostics::MissingUnsafe, diagnostics_sink::DiagnosticSink, InferenceResult,
15 Interner, TyExt, TyKind,
16};
17 12
18pub(super) struct UnsafeValidator<'a, 'b: 'a> { 13pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec<ExprId> {
19 owner: DefWithBodyId, 14 let infer = db.infer(def);
20 infer: Arc<InferenceResult>,
21 sink: &'a mut DiagnosticSink<'b>,
22}
23 15
24impl<'a, 'b> UnsafeValidator<'a, 'b> { 16 let is_unsafe = match def {
25 pub(super) fn new( 17 DefWithBodyId::FunctionId(it) => db.function_data(it).is_unsafe(),
26 owner: DefWithBodyId, 18 DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) => false,
27 infer: Arc<InferenceResult>, 19 };
28 sink: &'a mut DiagnosticSink<'b>, 20 if is_unsafe {
29 ) -> UnsafeValidator<'a, 'b> { 21 return Vec::new();
30 UnsafeValidator { owner, infer, sink }
31 } 22 }
32 23
33 pub(super) fn validate_body(&mut self, db: &dyn HirDatabase) { 24 unsafe_expressions(db, &infer, def)
34 let def = self.owner; 25 .into_iter()
35 let unsafe_expressions = unsafe_expressions(db, self.infer.as_ref(), def); 26 .filter(|it| !it.inside_unsafe_block)
36 let is_unsafe = match self.owner { 27 .map(|it| it.expr)
37 DefWithBodyId::FunctionId(it) => db.function_data(it).is_unsafe(), 28 .collect()
38 DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) => false,
39 };
40 if is_unsafe
41 || unsafe_expressions
42 .iter()
43 .filter(|unsafe_expr| !unsafe_expr.inside_unsafe_block)
44 .count()
45 == 0
46 {
47 return;
48 }
49
50 let (_, body_source) = db.body_with_source_map(def);
51 for unsafe_expr in unsafe_expressions {
52 if !unsafe_expr.inside_unsafe_block {
53 if let Ok(in_file) = body_source.as_ref().expr_syntax(unsafe_expr.expr) {
54 self.sink.push(MissingUnsafe { file: in_file.file_id, expr: in_file.value })
55 }
56 }
57 }
58 }
59} 29}
60 30
61pub(crate) struct UnsafeExpr { 31struct UnsafeExpr {
62 pub(crate) expr: ExprId, 32 pub(crate) expr: ExprId,
63 pub(crate) inside_unsafe_block: bool, 33 pub(crate) inside_unsafe_block: bool,
64} 34}
65 35
66pub(crate) fn unsafe_expressions( 36fn unsafe_expressions(
67 db: &dyn HirDatabase, 37 db: &dyn HirDatabase,
68 infer: &InferenceResult, 38 infer: &InferenceResult,
69 def: DefWithBodyId, 39 def: DefWithBodyId,
@@ -126,92 +96,3 @@ fn walk_unsafe(
126 walk_unsafe(unsafe_exprs, db, infer, def, body, child, inside_unsafe_block); 96 walk_unsafe(unsafe_exprs, db, infer, def, body, child, inside_unsafe_block);
127 }); 97 });
128} 98}
129
130#[cfg(test)]
131mod tests {
132 use crate::diagnostics::tests::check_diagnostics;
133
134 #[test]
135 fn missing_unsafe_diagnostic_with_raw_ptr() {
136 check_diagnostics(
137 r#"
138fn main() {
139 let x = &5 as *const usize;
140 unsafe { let y = *x; }
141 let z = *x;
142} //^^ This operation is unsafe and requires an unsafe function or block
143"#,
144 )
145 }
146
147 #[test]
148 fn missing_unsafe_diagnostic_with_unsafe_call() {
149 check_diagnostics(
150 r#"
151struct HasUnsafe;
152
153impl HasUnsafe {
154 unsafe fn unsafe_fn(&self) {
155 let x = &5 as *const usize;
156 let y = *x;
157 }
158}
159
160unsafe fn unsafe_fn() {
161 let x = &5 as *const usize;
162 let y = *x;
163}
164
165fn main() {
166 unsafe_fn();
167 //^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block
168 HasUnsafe.unsafe_fn();
169 //^^^^^^^^^^^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block
170 unsafe {
171 unsafe_fn();
172 HasUnsafe.unsafe_fn();
173 }
174}
175"#,
176 );
177 }
178
179 #[test]
180 fn missing_unsafe_diagnostic_with_static_mut() {
181 check_diagnostics(
182 r#"
183struct Ty {
184 a: u8,
185}
186
187static mut STATIC_MUT: Ty = Ty { a: 0 };
188
189fn main() {
190 let x = STATIC_MUT.a;
191 //^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block
192 unsafe {
193 let x = STATIC_MUT.a;
194 }
195}
196"#,
197 );
198 }
199
200 #[test]
201 fn no_missing_unsafe_diagnostic_with_safe_intrinsic() {
202 check_diagnostics(
203 r#"
204extern "rust-intrinsic" {
205 pub fn bitreverse(x: u32) -> u32; // Safe intrinsic
206 pub fn floorf32(x: f32) -> f32; // Unsafe intrinsic
207}
208
209fn main() {
210 let _ = bitreverse(12);
211 let _ = floorf32(12.0);
212 //^^^^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block
213}
214"#,
215 );
216 }
217}