diff options
author | Dawer <[email protected]> | 2021-04-30 15:12:04 +0100 |
---|---|---|
committer | Dawer <[email protected]> | 2021-05-31 20:03:46 +0100 |
commit | 5a8a0b62697c01ef881e7e2a0387e3649cab2034 (patch) | |
tree | e534317fe74f4cc90e33c6c7fb66a5e5289e6459 /crates/hir_ty/src | |
parent | b4f41973326a684844ffe23c5816e17d485b4203 (diff) |
Check enum patterns
Diffstat (limited to 'crates/hir_ty/src')
-rw-r--r-- | crates/hir_ty/src/diagnostics/pattern.rs | 45 | ||||
-rw-r--r-- | crates/hir_ty/src/diagnostics/pattern/deconstruct_pat.rs | 64 | ||||
-rw-r--r-- | crates/hir_ty/src/diagnostics/pattern/usefulness.rs | 21 |
3 files changed, 124 insertions, 6 deletions
diff --git a/crates/hir_ty/src/diagnostics/pattern.rs b/crates/hir_ty/src/diagnostics/pattern.rs index 4dcbd7f9f..3e90461cc 100644 --- a/crates/hir_ty/src/diagnostics/pattern.rs +++ b/crates/hir_ty/src/diagnostics/pattern.rs | |||
@@ -76,4 +76,49 @@ fn main(v: S) { | |||
76 | "#, | 76 | "#, |
77 | ); | 77 | ); |
78 | } | 78 | } |
79 | |||
80 | #[test] | ||
81 | fn c_enum() { | ||
82 | check_diagnostics( | ||
83 | r#" | ||
84 | enum E { A, B } | ||
85 | fn main(v: E) { | ||
86 | match v { E::A | E::B => {} } | ||
87 | match v { _ => {} } | ||
88 | match v { E::A => {} } | ||
89 | //^ Missing match arm | ||
90 | match v { } | ||
91 | //^ Missing match arm | ||
92 | } | ||
93 | "#, | ||
94 | ); | ||
95 | } | ||
96 | |||
97 | #[test] | ||
98 | fn enum_() { | ||
99 | check_diagnostics( | ||
100 | r#" | ||
101 | struct A; struct B; | ||
102 | enum E { Tuple(A, B), Struct{ a: A, b: B } } | ||
103 | fn main(v: E) { | ||
104 | match v { | ||
105 | E::Tuple(a, b) => {} | ||
106 | E::Struct{ a, b } => {} | ||
107 | } | ||
108 | match v { | ||
109 | E::Tuple(_, _) => {} | ||
110 | E::Struct{..} => {} | ||
111 | } | ||
112 | match v { | ||
113 | E::Tuple(..) => {} | ||
114 | _ => {} | ||
115 | } | ||
116 | match v { E::Tuple(..) => {} } | ||
117 | //^ Missing match arm | ||
118 | match v { } | ||
119 | //^ Missing match arm | ||
120 | } | ||
121 | "#, | ||
122 | ); | ||
123 | } | ||
79 | } | 124 | } |
diff --git a/crates/hir_ty/src/diagnostics/pattern/deconstruct_pat.rs b/crates/hir_ty/src/diagnostics/pattern/deconstruct_pat.rs index 60323aea3..3c1811f95 100644 --- a/crates/hir_ty/src/diagnostics/pattern/deconstruct_pat.rs +++ b/crates/hir_ty/src/diagnostics/pattern/deconstruct_pat.rs | |||
@@ -135,6 +135,7 @@ impl Constructor { | |||
135 | Pat::Bind { .. } | Pat::Wild => Wildcard, | 135 | Pat::Bind { .. } | Pat::Wild => Wildcard, |
136 | Pat::Tuple { .. } | Pat::Ref { .. } | Pat::Box { .. } => Single, | 136 | Pat::Tuple { .. } | Pat::Ref { .. } | Pat::Box { .. } => Single, |
137 | Pat::Record { .. } | Pat::Path(_) | Pat::TupleStruct { .. } => { | 137 | Pat::Record { .. } | Pat::Path(_) | Pat::TupleStruct { .. } => { |
138 | // TODO: path to const | ||
138 | let variant_id = | 139 | let variant_id = |
139 | cx.infer.variant_resolution_for_pat(pat).unwrap_or_else(|| todo!()); | 140 | cx.infer.variant_resolution_for_pat(pat).unwrap_or_else(|| todo!()); |
140 | match variant_id { | 141 | match variant_id { |
@@ -144,8 +145,8 @@ impl Constructor { | |||
144 | } | 145 | } |
145 | 146 | ||
146 | Pat::Or(..) => panic!("bug: Or-pattern should have been expanded earlier on."), | 147 | Pat::Or(..) => panic!("bug: Or-pattern should have been expanded earlier on."), |
148 | Pat::Missing => todo!("Fail gracefully when there is an error in a pattern"), | ||
147 | pat => todo!("Constructor::from_pat {:?}", pat), | 149 | pat => todo!("Constructor::from_pat {:?}", pat), |
148 | // Pat::Missing => {} | ||
149 | // Pat::Range { start, end } => {} | 150 | // Pat::Range { start, end } => {} |
150 | // Pat::Slice { prefix, slice, suffix } => {} | 151 | // Pat::Slice { prefix, slice, suffix } => {} |
151 | // Pat::Lit(_) => {} | 152 | // Pat::Lit(_) => {} |
@@ -280,7 +281,7 @@ pub(super) struct SplitWildcard { | |||
280 | 281 | ||
281 | impl SplitWildcard { | 282 | impl SplitWildcard { |
282 | pub(super) fn new(pcx: PatCtxt<'_>) -> Self { | 283 | pub(super) fn new(pcx: PatCtxt<'_>) -> Self { |
283 | // let cx = pcx.cx; | 284 | let cx = pcx.cx; |
284 | // let make_range = |start, end| IntRange(todo!()); | 285 | // let make_range = |start, end| IntRange(todo!()); |
285 | 286 | ||
286 | // This determines the set of all possible constructors for the type `pcx.ty`. For numbers, | 287 | // This determines the set of all possible constructors for the type `pcx.ty`. For numbers, |
@@ -292,9 +293,62 @@ impl SplitWildcard { | |||
292 | // Invariant: this is empty if and only if the type is uninhabited (as determined by | 293 | // Invariant: this is empty if and only if the type is uninhabited (as determined by |
293 | // `cx.is_uninhabited()`). | 294 | // `cx.is_uninhabited()`). |
294 | let all_ctors = match pcx.ty.kind(&Interner) { | 295 | let all_ctors = match pcx.ty.kind(&Interner) { |
295 | TyKind::Adt(AdtId(hir_def::AdtId::EnumId(_)), _) => todo!(), | 296 | TyKind::Scalar(Scalar::Bool) => todo!(), |
297 | // TyKind::Array(..) if ... => todo!(), | ||
298 | TyKind::Array(..) | TyKind::Slice(..) => todo!(), | ||
299 | &TyKind::Adt(AdtId(hir_def::AdtId::EnumId(enum_id)), ref _substs) => { | ||
300 | let enum_data = cx.db.enum_data(enum_id); | ||
301 | |||
302 | // If the enum is declared as `#[non_exhaustive]`, we treat it as if it had an | ||
303 | // additional "unknown" constructor. | ||
304 | // There is no point in enumerating all possible variants, because the user can't | ||
305 | // actually match against them all themselves. So we always return only the fictitious | ||
306 | // constructor. | ||
307 | // E.g., in an example like: | ||
308 | // | ||
309 | // ``` | ||
310 | // let err: io::ErrorKind = ...; | ||
311 | // match err { | ||
312 | // io::ErrorKind::NotFound => {}, | ||
313 | // } | ||
314 | // ``` | ||
315 | // | ||
316 | // we don't want to show every possible IO error, but instead have only `_` as the | ||
317 | // witness. | ||
318 | let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(enum_id); | ||
319 | |||
320 | // If `exhaustive_patterns` is disabled and our scrutinee is an empty enum, we treat it | ||
321 | // as though it had an "unknown" constructor to avoid exposing its emptiness. The | ||
322 | // exception is if the pattern is at the top level, because we want empty matches to be | ||
323 | // considered exhaustive. | ||
324 | let is_secretly_empty = enum_data.variants.is_empty() | ||
325 | && !cx.feature_exhaustive_patterns() | ||
326 | && !pcx.is_top_level; | ||
327 | |||
328 | if is_secretly_empty || is_declared_nonexhaustive { | ||
329 | smallvec![NonExhaustive] | ||
330 | } else if cx.feature_exhaustive_patterns() { | ||
331 | // If `exhaustive_patterns` is enabled, we exclude variants known to be | ||
332 | // uninhabited. | ||
333 | todo!() | ||
334 | } else { | ||
335 | enum_data | ||
336 | .variants | ||
337 | .iter() | ||
338 | .map(|(local_id, ..)| Variant(EnumVariantId { parent: enum_id, local_id })) | ||
339 | .collect() | ||
340 | } | ||
341 | } | ||
342 | TyKind::Scalar(Scalar::Char) => todo!(), | ||
343 | TyKind::Scalar(Scalar::Int(..)) | TyKind::Scalar(Scalar::Uint(..)) => todo!(), | ||
344 | TyKind::Never if !cx.feature_exhaustive_patterns() && !pcx.is_top_level => { | ||
345 | smallvec![NonExhaustive] | ||
346 | } | ||
347 | TyKind::Never => SmallVec::new(), | ||
348 | _ if cx.is_uninhabited(&pcx.ty) => SmallVec::new(), | ||
296 | TyKind::Adt(..) | TyKind::Tuple(..) | TyKind::Ref(..) => smallvec![Single], | 349 | TyKind::Adt(..) | TyKind::Tuple(..) | TyKind::Ref(..) => smallvec![Single], |
297 | _ => todo!(), | 350 | // This type is one for which we cannot list constructors, like `str` or `f64`. |
351 | _ => smallvec![NonExhaustive], | ||
298 | }; | 352 | }; |
299 | SplitWildcard { matrix_ctors: Vec::new(), all_ctors } | 353 | SplitWildcard { matrix_ctors: Vec::new(), all_ctors } |
300 | } | 354 | } |
@@ -496,7 +550,7 @@ impl Fields { | |||
496 | pub(super) fn apply(self, pcx: PatCtxt<'_>, ctor: &Constructor) -> Pat { | 550 | pub(super) fn apply(self, pcx: PatCtxt<'_>, ctor: &Constructor) -> Pat { |
497 | let subpatterns_and_indices = self.patterns_and_indices(); | 551 | let subpatterns_and_indices = self.patterns_and_indices(); |
498 | let mut subpatterns = subpatterns_and_indices.iter().map(|&(_, p)| p); | 552 | let mut subpatterns = subpatterns_and_indices.iter().map(|&(_, p)| p); |
499 | // TODO witnesses are not yet used | 553 | // TODO witnesses are not yet used |
500 | const TODO: Pat = Pat::Wild; | 554 | const TODO: Pat = Pat::Wild; |
501 | 555 | ||
502 | match ctor { | 556 | match ctor { |
diff --git a/crates/hir_ty/src/diagnostics/pattern/usefulness.rs b/crates/hir_ty/src/diagnostics/pattern/usefulness.rs index 4b55aee97..2df87ccea 100644 --- a/crates/hir_ty/src/diagnostics/pattern/usefulness.rs +++ b/crates/hir_ty/src/diagnostics/pattern/usefulness.rs | |||
@@ -3,7 +3,11 @@ | |||
3 | 3 | ||
4 | use std::{cell::RefCell, iter::FromIterator, ops::Index, sync::Arc}; | 4 | use std::{cell::RefCell, iter::FromIterator, ops::Index, sync::Arc}; |
5 | 5 | ||
6 | use hir_def::{ModuleId, body::Body, expr::{ExprId, Pat, PatId}}; | 6 | use hir_def::{ |
7 | body::Body, | ||
8 | expr::{ExprId, Pat, PatId}, | ||
9 | HasModule, ModuleId, | ||
10 | }; | ||
7 | use la_arena::Arena; | 11 | use la_arena::Arena; |
8 | use once_cell::unsync::OnceCell; | 12 | use once_cell::unsync::OnceCell; |
9 | use rustc_hash::FxHashMap; | 13 | use rustc_hash::FxHashMap; |
@@ -36,6 +40,21 @@ impl<'a> MatchCheckCtx<'a> { | |||
36 | false | 40 | false |
37 | } | 41 | } |
38 | 42 | ||
43 | /// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`. | ||
44 | pub(super) fn is_foreign_non_exhaustive_enum(&self, enum_id: hir_def::EnumId) -> bool { | ||
45 | let has_non_exhaustive_attr = | ||
46 | self.db.attrs(enum_id.into()).by_key("non_exhaustive").exists(); | ||
47 | let is_local = | ||
48 | hir_def::AdtId::from(enum_id).module(self.db.upcast()).krate() == self.module.krate(); | ||
49 | has_non_exhaustive_attr && !is_local | ||
50 | } | ||
51 | |||
52 | // Rust feature described as "Allows exhaustive pattern matching on types that contain uninhabited types." | ||
53 | pub(super) fn feature_exhaustive_patterns(&self) -> bool { | ||
54 | // TODO | ||
55 | false | ||
56 | } | ||
57 | |||
39 | pub(super) fn alloc_pat(&self, pat: Pat, ty: &Ty) -> PatId { | 58 | pub(super) fn alloc_pat(&self, pat: Pat, ty: &Ty) -> PatId { |
40 | self.pattern_arena.borrow_mut().alloc(pat, ty) | 59 | self.pattern_arena.borrow_mut().alloc(pat, ty) |
41 | } | 60 | } |