aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_ty
diff options
context:
space:
mode:
authorDawer <[email protected]>2021-04-30 15:12:04 +0100
committerDawer <[email protected]>2021-05-31 20:03:46 +0100
commit5a8a0b62697c01ef881e7e2a0387e3649cab2034 (patch)
treee534317fe74f4cc90e33c6c7fb66a5e5289e6459 /crates/hir_ty
parentb4f41973326a684844ffe23c5816e17d485b4203 (diff)
Check enum patterns
Diffstat (limited to 'crates/hir_ty')
-rw-r--r--crates/hir_ty/src/diagnostics/pattern.rs45
-rw-r--r--crates/hir_ty/src/diagnostics/pattern/deconstruct_pat.rs64
-rw-r--r--crates/hir_ty/src/diagnostics/pattern/usefulness.rs21
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#"
84enum E { A, B }
85fn 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#"
101struct A; struct B;
102enum E { Tuple(A, B), Struct{ a: A, b: B } }
103fn 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
281impl SplitWildcard { 282impl 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
4use std::{cell::RefCell, iter::FromIterator, ops::Index, sync::Arc}; 4use std::{cell::RefCell, iter::FromIterator, ops::Index, sync::Arc};
5 5
6use hir_def::{ModuleId, body::Body, expr::{ExprId, Pat, PatId}}; 6use hir_def::{
7 body::Body,
8 expr::{ExprId, Pat, PatId},
9 HasModule, ModuleId,
10};
7use la_arena::Arena; 11use la_arena::Arena;
8use once_cell::unsync::OnceCell; 12use once_cell::unsync::OnceCell;
9use rustc_hash::FxHashMap; 13use 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 }