diff options
-rw-r--r-- | crates/hir_ty/src/diagnostics/match_check.rs | 60 |
1 files changed, 55 insertions, 5 deletions
diff --git a/crates/hir_ty/src/diagnostics/match_check.rs b/crates/hir_ty/src/diagnostics/match_check.rs index 992bb8682..1d250413d 100644 --- a/crates/hir_ty/src/diagnostics/match_check.rs +++ b/crates/hir_ty/src/diagnostics/match_check.rs | |||
@@ -24,6 +24,7 @@ pub(crate) type PatId = Idx<Pat>; | |||
24 | pub(crate) enum PatternError { | 24 | pub(crate) enum PatternError { |
25 | Unimplemented, | 25 | Unimplemented, |
26 | UnresolvedVariant, | 26 | UnresolvedVariant, |
27 | MissingField, | ||
27 | } | 28 | } |
28 | 29 | ||
29 | #[derive(Clone, Debug, PartialEq)] | 30 | #[derive(Clone, Debug, PartialEq)] |
@@ -105,7 +106,7 @@ impl<'a> PatCtxt<'a> { | |||
105 | } | 106 | } |
106 | 107 | ||
107 | fn lower_pattern_unadjusted(&mut self, pat: hir_def::expr::PatId) -> Pat { | 108 | fn lower_pattern_unadjusted(&mut self, pat: hir_def::expr::PatId) -> Pat { |
108 | let ty = &self.infer[pat]; | 109 | let mut ty = &self.infer[pat]; |
109 | let variant = self.infer.variant_resolution_for_pat(pat); | 110 | let variant = self.infer.variant_resolution_for_pat(pat); |
110 | 111 | ||
111 | let kind = match self.body[pat] { | 112 | let kind = match self.body[pat] { |
@@ -127,6 +128,9 @@ impl<'a> PatCtxt<'a> { | |||
127 | } | 128 | } |
128 | 129 | ||
129 | hir_def::expr::Pat::Bind { subpat, .. } => { | 130 | hir_def::expr::Pat::Bind { subpat, .. } => { |
131 | if let TyKind::Ref(.., rty) = ty.kind(&Interner) { | ||
132 | ty = rty; | ||
133 | } | ||
130 | PatKind::Binding { subpattern: self.lower_opt_pattern(subpat) } | 134 | PatKind::Binding { subpattern: self.lower_opt_pattern(subpat) } |
131 | } | 135 | } |
132 | 136 | ||
@@ -140,13 +144,21 @@ impl<'a> PatCtxt<'a> { | |||
140 | let variant_data = variant.unwrap().variant_data(self.db.upcast()); | 144 | let variant_data = variant.unwrap().variant_data(self.db.upcast()); |
141 | let subpatterns = args | 145 | let subpatterns = args |
142 | .iter() | 146 | .iter() |
143 | .map(|field| FieldPat { | 147 | .map(|field| { |
144 | // XXX(iDawer): field lookup is inefficient | 148 | // XXX(iDawer): field lookup is inefficient |
145 | field: variant_data.field(&field.name).unwrap(), | 149 | variant_data.field(&field.name).map(|lfield_id| FieldPat { |
146 | pattern: self.lower_pattern(field.pat), | 150 | field: lfield_id, |
151 | pattern: self.lower_pattern(field.pat), | ||
152 | }) | ||
147 | }) | 153 | }) |
148 | .collect(); | 154 | .collect(); |
149 | self.lower_variant_or_leaf(pat, ty, subpatterns) | 155 | match subpatterns { |
156 | Some(subpatterns) => self.lower_variant_or_leaf(pat, ty, subpatterns), | ||
157 | None => { | ||
158 | self.errors.push(PatternError::MissingField); | ||
159 | PatKind::Wild | ||
160 | } | ||
161 | } | ||
150 | } | 162 | } |
151 | hir_def::expr::Pat::TupleStruct { .. } | hir_def::expr::Pat::Record { .. } => { | 163 | hir_def::expr::Pat::TupleStruct { .. } | hir_def::expr::Pat::Record { .. } => { |
152 | self.errors.push(PatternError::UnresolvedVariant); | 164 | self.errors.push(PatternError::UnresolvedVariant); |
@@ -1091,6 +1103,31 @@ fn main() { | |||
1091 | } | 1103 | } |
1092 | 1104 | ||
1093 | #[test] | 1105 | #[test] |
1106 | fn binding_ref_has_correct_type() { | ||
1107 | // Asserts `PatKind::Binding(ref _x): bool`, not &bool. | ||
1108 | // If that's not true match checking will panic with "incompatible constructors" | ||
1109 | // FIXME: make facilities to test this directly like `tests::check_infer(..)` | ||
1110 | check_diagnostics( | ||
1111 | r#" | ||
1112 | enum Foo { A } | ||
1113 | fn main() { | ||
1114 | // FIXME: this should not bail out but current behavior is such as the old algorithm. | ||
1115 | // ExprValidator::validate_match(..) checks types of top level patterns incorrecly. | ||
1116 | match Foo::A { | ||
1117 | ref _x => {} | ||
1118 | // ^^^^^^ Internal: match check bailed out | ||
1119 | Foo::A => {} | ||
1120 | } | ||
1121 | match (true,) { | ||
1122 | (ref _x,) => {} | ||
1123 | (true,) => {} | ||
1124 | } | ||
1125 | } | ||
1126 | "#, | ||
1127 | ); | ||
1128 | } | ||
1129 | |||
1130 | #[test] | ||
1094 | fn enum_non_exhaustive() { | 1131 | fn enum_non_exhaustive() { |
1095 | check_diagnostics( | 1132 | check_diagnostics( |
1096 | r#" | 1133 | r#" |
@@ -1161,6 +1198,19 @@ fn main() { | |||
1161 | ); | 1198 | ); |
1162 | } | 1199 | } |
1163 | 1200 | ||
1201 | #[test] | ||
1202 | fn record_struct_no_such_field() { | ||
1203 | check_diagnostics( | ||
1204 | r#" | ||
1205 | struct Foo { } | ||
1206 | fn main(f: Foo) { | ||
1207 | match f { Foo { bar } => () } | ||
1208 | // ^^^^^^^^^^^ Internal: match check bailed out | ||
1209 | } | ||
1210 | "#, | ||
1211 | ); | ||
1212 | } | ||
1213 | |||
1164 | mod false_negatives { | 1214 | mod false_negatives { |
1165 | //! The implementation of match checking here is a work in progress. As we roll this out, we | 1215 | //! The implementation of match checking here is a work in progress. As we roll this out, we |
1166 | //! prefer false negatives to false positives (ideally there would be no false positives). This | 1216 | //! prefer false negatives to false positives (ideally there would be no false positives). This |