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.rs507
-rw-r--r--crates/hir_ty/src/diagnostics/match_check.rs955
-rw-r--r--crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs2
-rw-r--r--crates/hir_ty/src/diagnostics/match_check/usefulness.rs13
-rw-r--r--crates/hir_ty/src/diagnostics/unsafe_check.rs151
6 files changed, 129 insertions, 1856 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 3efbce773..b809b96a0 100644
--- a/crates/hir_ty/src/diagnostics/expr.rs
+++ b/crates/hir_ty/src/diagnostics/expr.rs
@@ -8,20 +8,15 @@ use hir_def::{
8 expr::Statement, path::path, resolver::HasResolver, AssocItemId, DefWithBodyId, HasModule, 8 expr::Statement, path::path, resolver::HasResolver, AssocItemId, DefWithBodyId, HasModule,
9}; 9};
10use hir_expand::name; 10use hir_expand::name;
11use itertools::Either;
11use rustc_hash::FxHashSet; 12use rustc_hash::FxHashSet;
12use syntax::{ast, AstPtr};
13 13
14use crate::{ 14use crate::{
15 db::HirDatabase, 15 db::HirDatabase,
16 diagnostics::{ 16 diagnostics::match_check::{
17 match_check::{ 17 self,
18 self, 18 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 }, 19 },
24 diagnostics_sink::DiagnosticSink,
25 AdtId, InferenceResult, Interner, TyExt, TyKind, 20 AdtId, InferenceResult, Interner, TyExt, TyKind,
26}; 21};
27 22
@@ -31,38 +26,67 @@ pub(crate) use hir_def::{
31 LocalFieldId, VariantId, 26 LocalFieldId, VariantId,
32}; 27};
33 28
34use super::ReplaceFilterMapNextWithFindMap; 29pub enum BodyValidationDiagnostic {
30 RecordMissingFields {
31 record: Either<ExprId, PatId>,
32 variant: VariantId,
33 missed_fields: Vec<LocalFieldId>,
34 },
35 ReplaceFilterMapNextWithFindMap {
36 method_call_expr: ExprId,
37 },
38 MismatchedArgCount {
39 call_expr: ExprId,
40 expected: usize,
41 found: usize,
42 },
43 RemoveThisSemicolon {
44 expr: ExprId,
45 },
46 MissingOkOrSomeInTailExpr {
47 expr: ExprId,
48 required: String,
49 },
50 MissingMatchArms {
51 match_expr: ExprId,
52 },
53}
54
55impl BodyValidationDiagnostic {
56 pub fn collect(db: &dyn HirDatabase, owner: DefWithBodyId) -> Vec<BodyValidationDiagnostic> {
57 let _p = profile::span("BodyValidationDiagnostic::collect");
58 let infer = db.infer(owner);
59 let mut validator = ExprValidator::new(owner, infer.clone());
60 validator.validate_body(db);
61 validator.diagnostics
62 }
63}
35 64
36pub(super) struct ExprValidator<'a, 'b: 'a> { 65struct ExprValidator {
37 owner: DefWithBodyId, 66 owner: DefWithBodyId,
38 infer: Arc<InferenceResult>, 67 infer: Arc<InferenceResult>,
39 sink: &'a mut DiagnosticSink<'b>, 68 pub(super) diagnostics: Vec<BodyValidationDiagnostic>,
40} 69}
41 70
42impl<'a, 'b> ExprValidator<'a, 'b> { 71impl ExprValidator {
43 pub(super) fn new( 72 fn new(owner: DefWithBodyId, infer: Arc<InferenceResult>) -> ExprValidator {
44 owner: DefWithBodyId, 73 ExprValidator { owner, infer, diagnostics: Vec::new() }
45 infer: Arc<InferenceResult>,
46 sink: &'a mut DiagnosticSink<'b>,
47 ) -> ExprValidator<'a, 'b> {
48 ExprValidator { owner, infer, sink }
49 } 74 }
50 75
51 pub(super) fn validate_body(&mut self, db: &dyn HirDatabase) { 76 fn validate_body(&mut self, db: &dyn HirDatabase) {
52 self.check_for_filter_map_next(db); 77 self.check_for_filter_map_next(db);
53 78
54 let body = db.body(self.owner); 79 let body = db.body(self.owner);
55 80
56 for (id, expr) in body.exprs.iter() { 81 for (id, expr) in body.exprs.iter() {
57 if let Some((variant_def, missed_fields, true)) = 82 if let Some((variant, missed_fields, true)) =
58 record_literal_missing_fields(db, &self.infer, id, expr) 83 record_literal_missing_fields(db, &self.infer, id, expr)
59 { 84 {
60 self.create_record_literal_missing_fields_diagnostic( 85 self.diagnostics.push(BodyValidationDiagnostic::RecordMissingFields {
61 id, 86 record: Either::Left(id),
62 db, 87 variant,
63 variant_def,
64 missed_fields, 88 missed_fields,
65 ); 89 });
66 } 90 }
67 91
68 match expr { 92 match expr {
@@ -76,15 +100,14 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
76 } 100 }
77 } 101 }
78 for (id, pat) in body.pats.iter() { 102 for (id, pat) in body.pats.iter() {
79 if let Some((variant_def, missed_fields, true)) = 103 if let Some((variant, missed_fields, true)) =
80 record_pattern_missing_fields(db, &self.infer, id, pat) 104 record_pattern_missing_fields(db, &self.infer, id, pat)
81 { 105 {
82 self.create_record_pattern_missing_fields_diagnostic( 106 self.diagnostics.push(BodyValidationDiagnostic::RecordMissingFields {
83 id, 107 record: Either::Right(id),
84 db, 108 variant,
85 variant_def,
86 missed_fields, 109 missed_fields,
87 ); 110 });
88 } 111 }
89 } 112 }
90 let body_expr = &body[body.body_expr]; 113 let body_expr = &body[body.body_expr];
@@ -92,71 +115,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
92 if let Some(t) = tail { 115 if let Some(t) = tail {
93 self.validate_results_in_tail_expr(body.body_expr, *t, db); 116 self.validate_results_in_tail_expr(body.body_expr, *t, db);
94 } else if let Some(Statement::Expr { expr: id, .. }) = statements.last() { 117 } else if let Some(Statement::Expr { expr: id, .. }) = statements.last() {
95 self.validate_missing_tail_expr(body.body_expr, *id, db); 118 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 } 119 }
161 } 120 }
162 } 121 }
@@ -199,13 +158,11 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
199 if function_id == *next_function_id { 158 if function_id == *next_function_id {
200 if let Some(filter_map_id) = prev { 159 if let Some(filter_map_id) = prev {
201 if *receiver == filter_map_id { 160 if *receiver == filter_map_id {
202 let (_, source_map) = db.body_with_source_map(self.owner); 161 self.diagnostics.push(
203 if let Ok(next_source_ptr) = source_map.expr_syntax(id) { 162 BodyValidationDiagnostic::ReplaceFilterMapNextWithFindMap {
204 self.sink.push(ReplaceFilterMapNextWithFindMap { 163 method_call_expr: id,
205 file: next_source_ptr.file_id, 164 },
206 next_expr: next_source_ptr.value, 165 );
207 });
208 }
209 } 166 }
210 } 167 }
211 } 168 }
@@ -266,19 +223,15 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
266 let mut arg_count = args.len(); 223 let mut arg_count = args.len();
267 224
268 if arg_count != param_count { 225 if arg_count != param_count {
269 let (_, source_map) = db.body_with_source_map(self.owner); 226 if is_method_call {
270 if let Ok(source_ptr) = source_map.expr_syntax(call_id) { 227 param_count -= 1;
271 if is_method_call { 228 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 } 229 }
230 self.diagnostics.push(BodyValidationDiagnostic::MismatchedArgCount {
231 call_expr: call_id,
232 expected: param_count,
233 found: arg_count,
234 });
282 } 235 }
283 } 236 }
284 237
@@ -346,8 +299,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
346 // fit the match expression, we skip this diagnostic. Skipping the entire 299 // fit the match expression, we skip this diagnostic. Skipping the entire
347 // diagnostic rather than just not including this match arm is preferred 300 // diagnostic rather than just not including this match arm is preferred
348 // to avoid the chance of false positives. 301 // to avoid the chance of false positives.
349 #[cfg(test)] 302 cov_mark::hit!(validate_match_bailed_out);
350 match_check::tests::report_bail_out(db, self.owner, arm.pat, self.sink);
351 return; 303 return;
352 } 304 }
353 305
@@ -357,17 +309,20 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
357 infer: &infer, 309 infer: &infer,
358 db, 310 db,
359 pattern_arena: &pattern_arena, 311 pattern_arena: &pattern_arena,
360 eprint_panic_context: &|| { 312 panic_context: &|| {
361 use syntax::AstNode; 313 use syntax::AstNode;
362 if let Ok(scrutinee_sptr) = source_map.expr_syntax(match_expr) { 314 let match_expr_text = source_map
363 let root = scrutinee_sptr.file_syntax(db.upcast()); 315 .expr_syntax(match_expr)
364 if let Some(match_ast) = scrutinee_sptr.value.to_node(&root).syntax().parent() { 316 .ok()
365 eprintln!( 317 .and_then(|scrutinee_sptr| {
366 "Match checking is about to panic on this expression:\n{}", 318 let root = scrutinee_sptr.file_syntax(db.upcast());
367 match_ast.to_string(), 319 scrutinee_sptr.value.to_node(&root).syntax().parent()
368 ); 320 })
369 } 321 .map(|node| node.to_string());
370 } 322 format!(
323 "expression:\n{}",
324 match_expr_text.as_deref().unwrap_or("<synthesized expr>")
325 )
371 }, 326 },
372 }; 327 };
373 let report = compute_match_usefulness(&cx, &m_arms); 328 let report = compute_match_usefulness(&cx, &m_arms);
@@ -379,20 +334,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
379 // FIXME Report witnesses 334 // FIXME Report witnesses
380 // eprintln!("compute_match_usefulness(..) -> {:?}", &witnesses); 335 // eprintln!("compute_match_usefulness(..) -> {:?}", &witnesses);
381 if !witnesses.is_empty() { 336 if !witnesses.is_empty() {
382 if let Ok(source_ptr) = source_map.expr_syntax(id) { 337 self.diagnostics.push(BodyValidationDiagnostic::MissingMatchArms { match_expr: id });
383 let root = source_ptr.file_syntax(db.upcast());
384 if let ast::Expr::MatchExpr(match_expr) = &source_ptr.value.to_node(&root) {
385 if let (Some(match_expr), Some(arms)) =
386 (match_expr.expr(), match_expr.match_arm_list())
387 {
388 self.sink.push(MissingMatchArms {
389 file: source_ptr.file_id,
390 match_expr: AstPtr::new(&match_expr),
391 arms: AstPtr::new(&arms),
392 })
393 }
394 }
395 }
396 } 338 }
397 } 339 }
398 340
@@ -450,24 +392,12 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
450 if params.len(&Interner) > 0 392 if params.len(&Interner) > 0
451 && params.at(&Interner, 0).ty(&Interner) == Some(&mismatch.actual) 393 && params.at(&Interner, 0).ty(&Interner) == Some(&mismatch.actual)
452 { 394 {
453 let (_, source_map) = db.body_with_source_map(self.owner); 395 self.diagnostics
454 396 .push(BodyValidationDiagnostic::MissingOkOrSomeInTailExpr { expr: id, required });
455 if let Ok(source_ptr) = source_map.expr_syntax(id) {
456 self.sink.push(MissingOkOrSomeInTailExpr {
457 file: source_ptr.file_id,
458 expr: source_ptr.value,
459 required,
460 });
461 }
462 } 397 }
463 } 398 }
464 399
465 fn validate_missing_tail_expr( 400 fn validate_missing_tail_expr(&mut self, body_id: ExprId, possible_tail_id: ExprId) {
466 &mut self,
467 body_id: ExprId,
468 possible_tail_id: ExprId,
469 db: &dyn HirDatabase,
470 ) {
471 let mismatch = match self.infer.type_mismatch_for_expr(body_id) { 401 let mismatch = match self.infer.type_mismatch_for_expr(body_id) {
472 Some(m) => m, 402 Some(m) => m,
473 None => return, 403 None => return,
@@ -482,12 +412,8 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
482 return; 412 return;
483 } 413 }
484 414
485 let (_, source_map) = db.body_with_source_map(self.owner); 415 self.diagnostics
486 416 .push(BodyValidationDiagnostic::RemoveThisSemicolon { expr: possible_tail_id });
487 if let Ok(source_ptr) = source_map.expr_syntax(possible_tail_id) {
488 self.sink
489 .push(RemoveThisSemicolon { file: source_ptr.file_id, expr: source_ptr.value });
490 }
491 } 417 }
492} 418}
493 419
@@ -565,258 +491,3 @@ fn types_of_subpatterns_do_match(pat: PatId, body: &Body, infer: &InferenceResul
565 walk(pat, body, infer, &mut has_type_mismatches); 491 walk(pat, body, infer, &mut has_type_mismatches);
566 !has_type_mismatches 492 !has_type_mismatches
567} 493}
568
569#[cfg(test)]
570mod tests {
571 use crate::diagnostics::tests::check_diagnostics;
572
573 #[test]
574 fn simple_free_fn_zero() {
575 check_diagnostics(
576 r#"
577fn zero() {}
578fn f() { zero(1); }
579 //^^^^^^^ Expected 0 arguments, found 1
580"#,
581 );
582
583 check_diagnostics(
584 r#"
585fn zero() {}
586fn f() { zero(); }
587"#,
588 );
589 }
590
591 #[test]
592 fn simple_free_fn_one() {
593 check_diagnostics(
594 r#"
595fn one(arg: u8) {}
596fn f() { one(); }
597 //^^^^^ Expected 1 argument, found 0
598"#,
599 );
600
601 check_diagnostics(
602 r#"
603fn one(arg: u8) {}
604fn f() { one(1); }
605"#,
606 );
607 }
608
609 #[test]
610 fn method_as_fn() {
611 check_diagnostics(
612 r#"
613struct S;
614impl S { fn method(&self) {} }
615
616fn f() {
617 S::method();
618} //^^^^^^^^^^^ Expected 1 argument, found 0
619"#,
620 );
621
622 check_diagnostics(
623 r#"
624struct S;
625impl S { fn method(&self) {} }
626
627fn f() {
628 S::method(&S);
629 S.method();
630}
631"#,
632 );
633 }
634
635 #[test]
636 fn method_with_arg() {
637 check_diagnostics(
638 r#"
639struct S;
640impl S { fn method(&self, arg: u8) {} }
641
642 fn f() {
643 S.method();
644 } //^^^^^^^^^^ Expected 1 argument, found 0
645 "#,
646 );
647
648 check_diagnostics(
649 r#"
650struct S;
651impl S { fn method(&self, arg: u8) {} }
652
653fn f() {
654 S::method(&S, 0);
655 S.method(1);
656}
657"#,
658 );
659 }
660
661 #[test]
662 fn method_unknown_receiver() {
663 // note: this is incorrect code, so there might be errors on this in the
664 // future, but we shouldn't emit an argument count diagnostic here
665 check_diagnostics(
666 r#"
667trait Foo { fn method(&self, arg: usize) {} }
668
669fn f() {
670 let x;
671 x.method();
672}
673"#,
674 );
675 }
676
677 #[test]
678 fn tuple_struct() {
679 check_diagnostics(
680 r#"
681struct Tup(u8, u16);
682fn f() {
683 Tup(0);
684} //^^^^^^ Expected 2 arguments, found 1
685"#,
686 )
687 }
688
689 #[test]
690 fn enum_variant() {
691 check_diagnostics(
692 r#"
693enum En { Variant(u8, u16), }
694fn f() {
695 En::Variant(0);
696} //^^^^^^^^^^^^^^ Expected 2 arguments, found 1
697"#,
698 )
699 }
700
701 #[test]
702 fn enum_variant_type_macro() {
703 check_diagnostics(
704 r#"
705macro_rules! Type {
706 () => { u32 };
707}
708enum Foo {
709 Bar(Type![])
710}
711impl Foo {
712 fn new() {
713 Foo::Bar(0);
714 Foo::Bar(0, 1);
715 //^^^^^^^^^^^^^^ Expected 1 argument, found 2
716 Foo::Bar();
717 //^^^^^^^^^^ Expected 1 argument, found 0
718 }
719}
720 "#,
721 );
722 }
723
724 #[test]
725 fn varargs() {
726 check_diagnostics(
727 r#"
728extern "C" {
729 fn fixed(fixed: u8);
730 fn varargs(fixed: u8, ...);
731 fn varargs2(...);
732}
733
734fn f() {
735 unsafe {
736 fixed(0);
737 fixed(0, 1);
738 //^^^^^^^^^^^ Expected 1 argument, found 2
739 varargs(0);
740 varargs(0, 1);
741 varargs2();
742 varargs2(0);
743 varargs2(0, 1);
744 }
745}
746 "#,
747 )
748 }
749
750 #[test]
751 fn arg_count_lambda() {
752 check_diagnostics(
753 r#"
754fn main() {
755 let f = |()| ();
756 f();
757 //^^^ Expected 1 argument, found 0
758 f(());
759 f((), ());
760 //^^^^^^^^^ Expected 1 argument, found 2
761}
762"#,
763 )
764 }
765
766 #[test]
767 fn cfgd_out_call_arguments() {
768 check_diagnostics(
769 r#"
770struct C(#[cfg(FALSE)] ());
771impl C {
772 fn new() -> Self {
773 Self(
774 #[cfg(FALSE)]
775 (),
776 )
777 }
778
779 fn method(&self) {}
780}
781
782fn main() {
783 C::new().method(#[cfg(FALSE)] 0);
784}
785 "#,
786 );
787 }
788
789 #[test]
790 fn cfgd_out_fn_params() {
791 check_diagnostics(
792 r#"
793fn foo(#[cfg(NEVER)] x: ()) {}
794
795struct S;
796
797impl S {
798 fn method(#[cfg(NEVER)] self) {}
799 fn method2(#[cfg(NEVER)] self, arg: u8) {}
800 fn method3(self, #[cfg(NEVER)] arg: u8) {}
801}
802
803extern "C" {
804 fn fixed(fixed: u8, #[cfg(NEVER)] ...);
805 fn varargs(#[cfg(not(NEVER))] ...);
806}
807
808fn main() {
809 foo();
810 S::method();
811 S::method2(0);
812 S::method3(S);
813 S.method3();
814 unsafe {
815 fixed(0);
816 varargs(1, 2, 3);
817 }
818}
819 "#,
820 )
821 }
822}
diff --git a/crates/hir_ty/src/diagnostics/match_check.rs b/crates/hir_ty/src/diagnostics/match_check.rs
index a9a99f57a..a30e42699 100644
--- a/crates/hir_ty/src/diagnostics/match_check.rs
+++ b/crates/hir_ty/src/diagnostics/match_check.rs
@@ -100,10 +100,19 @@ impl<'a> PatCtxt<'a> {
100 } 100 }
101 101
102 pub(crate) fn lower_pattern(&mut self, pat: hir_def::expr::PatId) -> Pat { 102 pub(crate) fn lower_pattern(&mut self, pat: hir_def::expr::PatId) -> Pat {
103 // FIXME: implement pattern adjustments (implicit pattern dereference; "RFC 2005-match-ergonomics") 103 // XXX(iDawer): Collecting pattern adjustments feels imprecise to me.
104 // When lowering of & and box patterns are implemented this should be tested
105 // in a manner of `match_ergonomics_issue_9095` test.
106 // Pattern adjustment is part of RFC 2005-match-ergonomics.
104 // More info https://github.com/rust-lang/rust/issues/42640#issuecomment-313535089 107 // More info https://github.com/rust-lang/rust/issues/42640#issuecomment-313535089
105 let unadjusted_pat = self.lower_pattern_unadjusted(pat); 108 let unadjusted_pat = self.lower_pattern_unadjusted(pat);
106 unadjusted_pat 109 self.infer.pat_adjustments.get(&pat).map(|it| &**it).unwrap_or_default().iter().rev().fold(
110 unadjusted_pat,
111 |subpattern, ref_ty| Pat {
112 ty: ref_ty.clone(),
113 kind: Box::new(PatKind::Deref { subpattern }),
114 },
115 )
107 } 116 }
108 117
109 fn lower_pattern_unadjusted(&mut self, pat: hir_def::expr::PatId) -> Pat { 118 fn lower_pattern_unadjusted(&mut self, pat: hir_def::expr::PatId) -> Pat {
@@ -355,945 +364,3 @@ impl PatternFoldable for PatKind {
355 } 364 }
356 } 365 }
357} 366}
358
359#[cfg(test)]
360pub(super) mod tests {
361 mod report {
362 use std::any::Any;
363
364 use hir_def::{expr::PatId, DefWithBodyId};
365 use hir_expand::{HirFileId, InFile};
366 use syntax::SyntaxNodePtr;
367
368 use crate::{
369 db::HirDatabase,
370 diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink},
371 };
372
373 /// In tests, match check bails out loudly.
374 /// This helps to catch incorrect tests that pass due to false negatives.
375 pub(crate) fn report_bail_out(
376 db: &dyn HirDatabase,
377 def: DefWithBodyId,
378 pat: PatId,
379 sink: &mut DiagnosticSink,
380 ) {
381 let (_, source_map) = db.body_with_source_map(def);
382 if let Ok(source_ptr) = source_map.pat_syntax(pat) {
383 let pat_syntax_ptr = source_ptr.value.either(Into::into, Into::into);
384 sink.push(BailedOut { file: source_ptr.file_id, pat_syntax_ptr });
385 }
386 }
387
388 #[derive(Debug)]
389 struct BailedOut {
390 file: HirFileId,
391 pat_syntax_ptr: SyntaxNodePtr,
392 }
393
394 impl Diagnostic for BailedOut {
395 fn code(&self) -> DiagnosticCode {
396 DiagnosticCode("internal:match-check-bailed-out")
397 }
398 fn message(&self) -> String {
399 format!("Internal: match check bailed out")
400 }
401 fn display_source(&self) -> InFile<SyntaxNodePtr> {
402 InFile { file_id: self.file, value: self.pat_syntax_ptr.clone() }
403 }
404 fn as_any(&self) -> &(dyn Any + Send + 'static) {
405 self
406 }
407 }
408 }
409
410 use crate::diagnostics::tests::check_diagnostics;
411
412 pub(crate) use self::report::report_bail_out;
413
414 #[test]
415 fn empty_tuple() {
416 check_diagnostics(
417 r#"
418fn main() {
419 match () { }
420 //^^ Missing match arm
421 match (()) { }
422 //^^^^ Missing match arm
423
424 match () { _ => (), }
425 match () { () => (), }
426 match (()) { (()) => (), }
427}
428"#,
429 );
430 }
431
432 #[test]
433 fn tuple_of_two_empty_tuple() {
434 check_diagnostics(
435 r#"
436fn main() {
437 match ((), ()) { }
438 //^^^^^^^^ Missing match arm
439
440 match ((), ()) { ((), ()) => (), }
441}
442"#,
443 );
444 }
445
446 #[test]
447 fn boolean() {
448 check_diagnostics(
449 r#"
450fn test_main() {
451 match false { }
452 //^^^^^ Missing match arm
453 match false { true => (), }
454 //^^^^^ Missing match arm
455 match (false, true) {}
456 //^^^^^^^^^^^^^ Missing match arm
457 match (false, true) { (true, true) => (), }
458 //^^^^^^^^^^^^^ Missing match arm
459 match (false, true) {
460 //^^^^^^^^^^^^^ Missing match arm
461 (false, true) => (),
462 (false, false) => (),
463 (true, false) => (),
464 }
465 match (false, true) { (true, _x) => (), }
466 //^^^^^^^^^^^^^ Missing match arm
467
468 match false { true => (), false => (), }
469 match (false, true) {
470 (false, _) => (),
471 (true, false) => (),
472 (_, true) => (),
473 }
474 match (false, true) {
475 (true, true) => (),
476 (true, false) => (),
477 (false, true) => (),
478 (false, false) => (),
479 }
480 match (false, true) {
481 (true, _x) => (),
482 (false, true) => (),
483 (false, false) => (),
484 }
485 match (false, true, false) {
486 (false, ..) => (),
487 (true, ..) => (),
488 }
489 match (false, true, false) {
490 (.., false) => (),
491 (.., true) => (),
492 }
493 match (false, true, false) { (..) => (), }
494}
495"#,
496 );
497 }
498
499 #[test]
500 fn tuple_of_tuple_and_bools() {
501 check_diagnostics(
502 r#"
503fn main() {
504 match (false, ((), false)) {}
505 //^^^^^^^^^^^^^^^^^^^^ Missing match arm
506 match (false, ((), false)) { (true, ((), true)) => (), }
507 //^^^^^^^^^^^^^^^^^^^^ Missing match arm
508 match (false, ((), false)) { (true, _) => (), }
509 //^^^^^^^^^^^^^^^^^^^^ Missing match arm
510
511 match (false, ((), false)) {
512 (true, ((), true)) => (),
513 (true, ((), false)) => (),
514 (false, ((), true)) => (),
515 (false, ((), false)) => (),
516 }
517 match (false, ((), false)) {
518 (true, ((), true)) => (),
519 (true, ((), false)) => (),
520 (false, _) => (),
521 }
522}
523"#,
524 );
525 }
526
527 #[test]
528 fn enums() {
529 check_diagnostics(
530 r#"
531enum Either { A, B, }
532
533fn main() {
534 match Either::A { }
535 //^^^^^^^^^ Missing match arm
536 match Either::B { Either::A => (), }
537 //^^^^^^^^^ Missing match arm
538
539 match &Either::B {
540 //^^^^^^^^^^ Missing match arm
541 Either::A => (),
542 }
543
544 match Either::B {
545 Either::A => (), Either::B => (),
546 }
547 match &Either::B {
548 Either::A => (), Either::B => (),
549 }
550}
551"#,
552 );
553 }
554
555 #[test]
556 fn enum_containing_bool() {
557 check_diagnostics(
558 r#"
559enum Either { A(bool), B }
560
561fn main() {
562 match Either::B { }
563 //^^^^^^^^^ Missing match arm
564 match Either::B {
565 //^^^^^^^^^ Missing match arm
566 Either::A(true) => (), Either::B => ()
567 }
568
569 match Either::B {
570 Either::A(true) => (),
571 Either::A(false) => (),
572 Either::B => (),
573 }
574 match Either::B {
575 Either::B => (),
576 _ => (),
577 }
578 match Either::B {
579 Either::A(_) => (),
580 Either::B => (),
581 }
582
583}
584 "#,
585 );
586 }
587
588 #[test]
589 fn enum_different_sizes() {
590 check_diagnostics(
591 r#"
592enum Either { A(bool), B(bool, bool) }
593
594fn main() {
595 match Either::A(false) {
596 //^^^^^^^^^^^^^^^^ Missing match arm
597 Either::A(_) => (),
598 Either::B(false, _) => (),
599 }
600
601 match Either::A(false) {
602 Either::A(_) => (),
603 Either::B(true, _) => (),
604 Either::B(false, _) => (),
605 }
606 match Either::A(false) {
607 Either::A(true) | Either::A(false) => (),
608 Either::B(true, _) => (),
609 Either::B(false, _) => (),
610 }
611}
612"#,
613 );
614 }
615
616 #[test]
617 fn tuple_of_enum_no_diagnostic() {
618 check_diagnostics(
619 r#"
620enum Either { A(bool), B(bool, bool) }
621enum Either2 { C, D }
622
623fn main() {
624 match (Either::A(false), Either2::C) {
625 (Either::A(true), _) | (Either::A(false), _) => (),
626 (Either::B(true, _), Either2::C) => (),
627 (Either::B(false, _), Either2::C) => (),
628 (Either::B(_, _), Either2::D) => (),
629 }
630}
631"#,
632 );
633 }
634
635 #[test]
636 fn or_pattern_no_diagnostic() {
637 check_diagnostics(
638 r#"
639enum Either {A, B}
640
641fn main() {
642 match (Either::A, Either::B) {
643 (Either::A | Either::B, _) => (),
644 }
645}"#,
646 )
647 }
648
649 #[test]
650 fn mismatched_types() {
651 // Match statements with arms that don't match the
652 // expression pattern do not fire this diagnostic.
653 check_diagnostics(
654 r#"
655enum Either { A, B }
656enum Either2 { C, D }
657
658fn main() {
659 match Either::A {
660 Either2::C => (),
661 // ^^^^^^^^^^ Internal: match check bailed out
662 Either2::D => (),
663 }
664 match (true, false) {
665 (true, false, true) => (),
666 // ^^^^^^^^^^^^^^^^^^^ Internal: match check bailed out
667 (true) => (),
668 }
669 match (true, false) { (true,) => {} }
670 // ^^^^^^^ Internal: match check bailed out
671 match (0) { () => () }
672 // ^^ Internal: match check bailed out
673 match Unresolved::Bar { Unresolved::Baz => () }
674}
675 "#,
676 );
677 }
678
679 #[test]
680 fn mismatched_types_in_or_patterns() {
681 check_diagnostics(
682 r#"
683fn main() {
684 match false { true | () => {} }
685 // ^^^^^^^^^ Internal: match check bailed out
686 match (false,) { (true | (),) => {} }
687 // ^^^^^^^^^^^^ Internal: match check bailed out
688}
689"#,
690 );
691 }
692
693 #[test]
694 fn malformed_match_arm_tuple_enum_missing_pattern() {
695 // We are testing to be sure we don't panic here when the match
696 // arm `Either::B` is missing its pattern.
697 check_diagnostics(
698 r#"
699enum Either { A, B(u32) }
700
701fn main() {
702 match Either::A {
703 Either::A => (),
704 Either::B() => (),
705 }
706}
707"#,
708 );
709 }
710
711 #[test]
712 fn malformed_match_arm_extra_fields() {
713 check_diagnostics(
714 r#"
715enum A { B(isize, isize), C }
716fn main() {
717 match A::B(1, 2) {
718 A::B(_, _, _) => (),
719 // ^^^^^^^^^^^^^ Internal: match check bailed out
720 }
721 match A::B(1, 2) {
722 A::C(_) => (),
723 // ^^^^^^^ Internal: match check bailed out
724 }
725}
726"#,
727 );
728 }
729
730 #[test]
731 fn expr_diverges() {
732 check_diagnostics(
733 r#"
734enum Either { A, B }
735
736fn main() {
737 match loop {} {
738 Either::A => (),
739 // ^^^^^^^^^ Internal: match check bailed out
740 Either::B => (),
741 }
742 match loop {} {
743 Either::A => (),
744 // ^^^^^^^^^ Internal: match check bailed out
745 }
746 match loop { break Foo::A } {
747 //^^^^^^^^^^^^^^^^^^^^^ Missing match arm
748 Either::A => (),
749 }
750 match loop { break Foo::A } {
751 Either::A => (),
752 Either::B => (),
753 }
754}
755"#,
756 );
757 }
758
759 #[test]
760 fn expr_partially_diverges() {
761 check_diagnostics(
762 r#"
763enum Either<T> { A(T), B }
764
765fn foo() -> Either<!> { Either::B }
766fn main() -> u32 {
767 match foo() {
768 Either::A(val) => val,
769 Either::B => 0,
770 }
771}
772"#,
773 );
774 }
775
776 #[test]
777 fn enum_record() {
778 check_diagnostics(
779 r#"
780enum Either { A { foo: bool }, B }
781
782fn main() {
783 let a = Either::A { foo: true };
784 match a { }
785 //^ Missing match arm
786 match a { Either::A { foo: true } => () }
787 //^ Missing match arm
788 match a {
789 Either::A { } => (),
790 //^^^^^^^^^ Missing structure fields:
791 // | - foo
792 Either::B => (),
793 }
794 match a {
795 //^ Missing match arm
796 Either::A { } => (),
797 } //^^^^^^^^^ Missing structure fields:
798 // | - foo
799
800 match a {
801 Either::A { foo: true } => (),
802 Either::A { foo: false } => (),
803 Either::B => (),
804 }
805 match a {
806 Either::A { foo: _ } => (),
807 Either::B => (),
808 }
809}
810"#,
811 );
812 }
813
814 #[test]
815 fn enum_record_fields_out_of_order() {
816 check_diagnostics(
817 r#"
818enum Either {
819 A { foo: bool, bar: () },
820 B,
821}
822
823fn main() {
824 let a = Either::A { foo: true, bar: () };
825 match a {
826 //^ Missing match arm
827 Either::A { bar: (), foo: false } => (),
828 Either::A { foo: true, bar: () } => (),
829 }
830
831 match a {
832 Either::A { bar: (), foo: false } => (),
833 Either::A { foo: true, bar: () } => (),
834 Either::B => (),
835 }
836}
837"#,
838 );
839 }
840
841 #[test]
842 fn enum_record_ellipsis() {
843 check_diagnostics(
844 r#"
845enum Either {
846 A { foo: bool, bar: bool },
847 B,
848}
849
850fn main() {
851 let a = Either::B;
852 match a {
853 //^ Missing match arm
854 Either::A { foo: true, .. } => (),
855 Either::B => (),
856 }
857 match a {
858 //^ Missing match arm
859 Either::A { .. } => (),
860 }
861
862 match a {
863 Either::A { foo: true, .. } => (),
864 Either::A { foo: false, .. } => (),
865 Either::B => (),
866 }
867
868 match a {
869 Either::A { .. } => (),
870 Either::B => (),
871 }
872}
873"#,
874 );
875 }
876
877 #[test]
878 fn enum_tuple_partial_ellipsis() {
879 check_diagnostics(
880 r#"
881enum Either {
882 A(bool, bool, bool, bool),
883 B,
884}
885
886fn main() {
887 match Either::B {
888 //^^^^^^^^^ Missing match arm
889 Either::A(true, .., true) => (),
890 Either::A(true, .., false) => (),
891 Either::A(false, .., false) => (),
892 Either::B => (),
893 }
894 match Either::B {
895 //^^^^^^^^^ Missing match arm
896 Either::A(true, .., true) => (),
897 Either::A(true, .., false) => (),
898 Either::A(.., true) => (),
899 Either::B => (),
900 }
901
902 match Either::B {
903 Either::A(true, .., true) => (),
904 Either::A(true, .., false) => (),
905 Either::A(false, .., true) => (),
906 Either::A(false, .., false) => (),
907 Either::B => (),
908 }
909 match Either::B {
910 Either::A(true, .., true) => (),
911 Either::A(true, .., false) => (),
912 Either::A(.., true) => (),
913 Either::A(.., false) => (),
914 Either::B => (),
915 }
916}
917"#,
918 );
919 }
920
921 #[test]
922 fn never() {
923 check_diagnostics(
924 r#"
925enum Never {}
926
927fn enum_(never: Never) {
928 match never {}
929}
930fn enum_ref(never: &Never) {
931 match never {}
932 //^^^^^ Missing match arm
933}
934fn bang(never: !) {
935 match never {}
936}
937"#,
938 );
939 }
940
941 #[test]
942 fn unknown_type() {
943 check_diagnostics(
944 r#"
945enum Option<T> { Some(T), None }
946
947fn main() {
948 // `Never` is deliberately not defined so that it's an uninferred type.
949 match Option::<Never>::None {
950 None => (),
951 Some(never) => match never {},
952 // ^^^^^^^^^^^ Internal: match check bailed out
953 }
954 match Option::<Never>::None {
955 //^^^^^^^^^^^^^^^^^^^^^ Missing match arm
956 Option::Some(_never) => {},
957 }
958}
959"#,
960 );
961 }
962
963 #[test]
964 fn tuple_of_bools_with_ellipsis_at_end_missing_arm() {
965 check_diagnostics(
966 r#"
967fn main() {
968 match (false, true, false) {
969 //^^^^^^^^^^^^^^^^^^^^ Missing match arm
970 (false, ..) => (),
971 }
972}"#,
973 );
974 }
975
976 #[test]
977 fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() {
978 check_diagnostics(
979 r#"
980fn main() {
981 match (false, true, false) {
982 //^^^^^^^^^^^^^^^^^^^^ Missing match arm
983 (.., false) => (),
984 }
985}"#,
986 );
987 }
988
989 #[test]
990 fn tuple_of_bools_with_ellipsis_in_middle_missing_arm() {
991 check_diagnostics(
992 r#"
993fn main() {
994 match (false, true, false) {
995 //^^^^^^^^^^^^^^^^^^^^ Missing match arm
996 (true, .., false) => (),
997 }
998}"#,
999 );
1000 }
1001
1002 #[test]
1003 fn record_struct() {
1004 check_diagnostics(
1005 r#"struct Foo { a: bool }
1006fn main(f: Foo) {
1007 match f {}
1008 //^ Missing match arm
1009 match f { Foo { a: true } => () }
1010 //^ Missing match arm
1011 match &f { Foo { a: true } => () }
1012 //^^ Missing match arm
1013 match f { Foo { a: _ } => () }
1014 match f {
1015 Foo { a: true } => (),
1016 Foo { a: false } => (),
1017 }
1018 match &f {
1019 Foo { a: true } => (),
1020 Foo { a: false } => (),
1021 }
1022}
1023"#,
1024 );
1025 }
1026
1027 #[test]
1028 fn tuple_struct() {
1029 check_diagnostics(
1030 r#"struct Foo(bool);
1031fn main(f: Foo) {
1032 match f {}
1033 //^ Missing match arm
1034 match f { Foo(true) => () }
1035 //^ Missing match arm
1036 match f {
1037 Foo(true) => (),
1038 Foo(false) => (),
1039 }
1040}
1041"#,
1042 );
1043 }
1044
1045 #[test]
1046 fn unit_struct() {
1047 check_diagnostics(
1048 r#"struct Foo;
1049fn main(f: Foo) {
1050 match f {}
1051 //^ Missing match arm
1052 match f { Foo => () }
1053}
1054"#,
1055 );
1056 }
1057
1058 #[test]
1059 fn record_struct_ellipsis() {
1060 check_diagnostics(
1061 r#"struct Foo { foo: bool, bar: bool }
1062fn main(f: Foo) {
1063 match f { Foo { foo: true, .. } => () }
1064 //^ Missing match arm
1065 match f {
1066 //^ Missing match arm
1067 Foo { foo: true, .. } => (),
1068 Foo { bar: false, .. } => ()
1069 }
1070 match f { Foo { .. } => () }
1071 match f {
1072 Foo { foo: true, .. } => (),
1073 Foo { foo: false, .. } => ()
1074 }
1075}
1076"#,
1077 );
1078 }
1079
1080 #[test]
1081 fn internal_or() {
1082 check_diagnostics(
1083 r#"
1084fn main() {
1085 enum Either { A(bool), B }
1086 match Either::B {
1087 //^^^^^^^^^ Missing match arm
1088 Either::A(true | false) => (),
1089 }
1090}
1091"#,
1092 );
1093 }
1094
1095 #[test]
1096 fn no_panic_at_unimplemented_subpattern_type() {
1097 check_diagnostics(
1098 r#"
1099struct S { a: char}
1100fn main(v: S) {
1101 match v { S{ a } => {} }
1102 match v { S{ a: _x } => {} }
1103 match v { S{ a: 'a' } => {} }
1104 //^^^^^^^^^^^ Internal: match check bailed out
1105 match v { S{..} => {} }
1106 match v { _ => {} }
1107 match v { }
1108 //^ Missing match arm
1109}
1110"#,
1111 );
1112 }
1113
1114 #[test]
1115 fn binding() {
1116 check_diagnostics(
1117 r#"
1118fn main() {
1119 match true {
1120 _x @ true => {}
1121 false => {}
1122 }
1123 match true { _x @ true => {} }
1124 //^^^^ Missing match arm
1125}
1126"#,
1127 );
1128 }
1129
1130 #[test]
1131 fn binding_ref_has_correct_type() {
1132 // Asserts `PatKind::Binding(ref _x): bool`, not &bool.
1133 // If that's not true match checking will panic with "incompatible constructors"
1134 // FIXME: make facilities to test this directly like `tests::check_infer(..)`
1135 check_diagnostics(
1136 r#"
1137enum Foo { A }
1138fn main() {
1139 // FIXME: this should not bail out but current behavior is such as the old algorithm.
1140 // ExprValidator::validate_match(..) checks types of top level patterns incorrecly.
1141 match Foo::A {
1142 ref _x => {}
1143 // ^^^^^^ Internal: match check bailed out
1144 Foo::A => {}
1145 }
1146 match (true,) {
1147 (ref _x,) => {}
1148 (true,) => {}
1149 }
1150}
1151"#,
1152 );
1153 }
1154
1155 #[test]
1156 fn enum_non_exhaustive() {
1157 check_diagnostics(
1158 r#"
1159//- /lib.rs crate:lib
1160#[non_exhaustive]
1161pub enum E { A, B }
1162fn _local() {
1163 match E::A { _ => {} }
1164 match E::A {
1165 E::A => {}
1166 E::B => {}
1167 }
1168 match E::A {
1169 E::A | E::B => {}
1170 }
1171}
1172
1173//- /main.rs crate:main deps:lib
1174use lib::E;
1175fn main() {
1176 match E::A { _ => {} }
1177 match E::A {
1178 //^^^^ Missing match arm
1179 E::A => {}
1180 E::B => {}
1181 }
1182 match E::A {
1183 //^^^^ Missing match arm
1184 E::A | E::B => {}
1185 }
1186}
1187"#,
1188 );
1189 }
1190
1191 #[test]
1192 fn match_guard() {
1193 check_diagnostics(
1194 r#"
1195fn main() {
1196 match true {
1197 true if false => {}
1198 true => {}
1199 false => {}
1200 }
1201 match true {
1202 //^^^^ Missing match arm
1203 true if false => {}
1204 false => {}
1205}
1206"#,
1207 );
1208 }
1209
1210 #[test]
1211 fn pattern_type_is_of_substitution() {
1212 cov_mark::check!(match_check_wildcard_expanded_to_substitutions);
1213 check_diagnostics(
1214 r#"
1215struct Foo<T>(T);
1216struct Bar;
1217fn main() {
1218 match Foo(Bar) {
1219 _ | Foo(Bar) => {}
1220 }
1221}
1222"#,
1223 );
1224 }
1225
1226 #[test]
1227 fn record_struct_no_such_field() {
1228 check_diagnostics(
1229 r#"
1230struct Foo { }
1231fn main(f: Foo) {
1232 match f { Foo { bar } => () }
1233 // ^^^^^^^^^^^ Internal: match check bailed out
1234}
1235"#,
1236 );
1237 }
1238
1239 mod false_negatives {
1240 //! The implementation of match checking here is a work in progress. As we roll this out, we
1241 //! prefer false negatives to false positives (ideally there would be no false positives). This
1242 //! test module should document known false negatives. Eventually we will have a complete
1243 //! implementation of match checking and this module will be empty.
1244 //!
1245 //! The reasons for documenting known false negatives:
1246 //!
1247 //! 1. It acts as a backlog of work that can be done to improve the behavior of the system.
1248 //! 2. It ensures the code doesn't panic when handling these cases.
1249 use super::*;
1250
1251 #[test]
1252 fn integers() {
1253 // We don't currently check integer exhaustiveness.
1254 check_diagnostics(
1255 r#"
1256fn main() {
1257 match 5 {
1258 10 => (),
1259 // ^^ Internal: match check bailed out
1260 11..20 => (),
1261 }
1262}
1263"#,
1264 );
1265 }
1266
1267 #[test]
1268 fn reference_patterns_at_top_level() {
1269 check_diagnostics(
1270 r#"
1271fn main() {
1272 match &false {
1273 &true => {}
1274 // ^^^^^ Internal: match check bailed out
1275 }
1276}
1277 "#,
1278 );
1279 }
1280
1281 #[test]
1282 fn reference_patterns_in_fields() {
1283 check_diagnostics(
1284 r#"
1285fn main() {
1286 match (&false,) {
1287 (true,) => {}
1288 // ^^^^^^^ Internal: match check bailed out
1289 }
1290 match (&false,) {
1291 (&true,) => {}
1292 // ^^^^^^^^ Internal: match check bailed out
1293 }
1294}
1295 "#,
1296 );
1297 }
1298 }
1299}
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..471cd4921 100644
--- a/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs
+++ b/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs
@@ -528,7 +528,7 @@ impl SplitWildcard {
528 smallvec![NonExhaustive] 528 smallvec![NonExhaustive]
529 } 529 }
530 TyKind::Never => SmallVec::new(), 530 TyKind::Never => SmallVec::new(),
531 _ if cx.is_uninhabited(&pcx.ty) => SmallVec::new(), 531 _ if cx.is_uninhabited(pcx.ty) => SmallVec::new(),
532 TyKind::Adt(..) | TyKind::Tuple(..) | TyKind::Ref(..) => smallvec![Single], 532 TyKind::Adt(..) | TyKind::Tuple(..) | TyKind::Ref(..) => smallvec![Single],
533 // This type is one for which we cannot list constructors, like `str` or `f64`. 533 // This type is one for which we cannot list constructors, like `str` or `f64`.
534 _ => smallvec![NonExhaustive], 534 _ => smallvec![NonExhaustive],
diff --git a/crates/hir_ty/src/diagnostics/match_check/usefulness.rs b/crates/hir_ty/src/diagnostics/match_check/usefulness.rs
index 83b094a89..8451f9df5 100644
--- a/crates/hir_ty/src/diagnostics/match_check/usefulness.rs
+++ b/crates/hir_ty/src/diagnostics/match_check/usefulness.rs
@@ -1,5 +1,5 @@
1//! Based on rust-lang/rust 1.52.0-nightly (25c15cdbe 2021-04-22) 1//! Based on rust-lang/rust 1.52.0-nightly (25c15cdbe 2021-04-22)
2//! https://github.com/rust-lang/rust/blob/25c15cdbe/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs 2//! <https://github.com/rust-lang/rust/blob/25c15cdbe/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs>
3//! 3//!
4//! ----- 4//! -----
5//! 5//!
@@ -295,7 +295,7 @@ pub(crate) struct MatchCheckCtx<'a> {
295 pub(crate) db: &'a dyn HirDatabase, 295 pub(crate) db: &'a dyn HirDatabase,
296 /// Lowered patterns from arms plus generated by the check. 296 /// Lowered patterns from arms plus generated by the check.
297 pub(crate) pattern_arena: &'a RefCell<PatternArena>, 297 pub(crate) pattern_arena: &'a RefCell<PatternArena>,
298 pub(crate) eprint_panic_context: &'a dyn Fn(), 298 pub(crate) panic_context: &'a dyn Fn() -> String,
299} 299}
300 300
301impl<'a> MatchCheckCtx<'a> { 301impl<'a> MatchCheckCtx<'a> {
@@ -331,8 +331,7 @@ impl<'a> MatchCheckCtx<'a> {
331 331
332 #[track_caller] 332 #[track_caller]
333 pub(super) fn bug(&self, info: &str) -> ! { 333 pub(super) fn bug(&self, info: &str) -> ! {
334 (self.eprint_panic_context)(); 334 panic!("bug: {}\n{}", info, (self.panic_context)());
335 panic!("bug: {}", info);
336 } 335 }
337} 336}
338 337
@@ -646,7 +645,7 @@ impl SubPatSet {
646 (Seq { subpats: s_set }, Seq { subpats: mut o_set }) => { 645 (Seq { subpats: s_set }, Seq { subpats: mut o_set }) => {
647 s_set.retain(|i, s_sub_set| { 646 s_set.retain(|i, s_sub_set| {
648 // Missing entries count as full. 647 // Missing entries count as full.
649 let o_sub_set = o_set.remove(&i).unwrap_or(Full); 648 let o_sub_set = o_set.remove(i).unwrap_or(Full);
650 s_sub_set.union(o_sub_set); 649 s_sub_set.union(o_sub_set);
651 // We drop full entries. 650 // We drop full entries.
652 !s_sub_set.is_full() 651 !s_sub_set.is_full()
@@ -657,7 +656,7 @@ impl SubPatSet {
657 (Alt { subpats: s_set, .. }, Alt { subpats: mut o_set, .. }) => { 656 (Alt { subpats: s_set, .. }, Alt { subpats: mut o_set, .. }) => {
658 s_set.retain(|i, s_sub_set| { 657 s_set.retain(|i, s_sub_set| {
659 // Missing entries count as empty. 658 // Missing entries count as empty.
660 let o_sub_set = o_set.remove(&i).unwrap_or(Empty); 659 let o_sub_set = o_set.remove(i).unwrap_or(Empty);
661 s_sub_set.union(o_sub_set); 660 s_sub_set.union(o_sub_set);
662 // We drop empty entries. 661 // We drop empty entries.
663 !s_sub_set.is_empty() 662 !s_sub_set.is_empty()
@@ -899,7 +898,7 @@ impl Usefulness {
899 } else { 898 } else {
900 witnesses 899 witnesses
901 .into_iter() 900 .into_iter()
902 .map(|witness| witness.apply_constructor(pcx, &ctor, ctor_wild_subpatterns)) 901 .map(|witness| witness.apply_constructor(pcx, ctor, ctor_wild_subpatterns))
903 .collect() 902 .collect()
904 }; 903 };
905 WithWitnesses(new_witnesses) 904 WithWitnesses(new_witnesses)
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}