diff options
Diffstat (limited to 'crates/hir_ty/src/diagnostics/pattern.rs')
-rw-r--r-- | crates/hir_ty/src/diagnostics/pattern.rs | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/crates/hir_ty/src/diagnostics/pattern.rs b/crates/hir_ty/src/diagnostics/pattern.rs index 044506d66..6646826b3 100644 --- a/crates/hir_ty/src/diagnostics/pattern.rs +++ b/crates/hir_ty/src/diagnostics/pattern.rs | |||
@@ -2,8 +2,199 @@ | |||
2 | #![allow(unused)] // todo remove | 2 | #![allow(unused)] // todo remove |
3 | 3 | ||
4 | mod deconstruct_pat; | 4 | mod deconstruct_pat; |
5 | // TODO: find a better place for this? | ||
6 | mod pat_util; | ||
5 | pub mod usefulness; | 7 | pub mod usefulness; |
6 | 8 | ||
9 | use hir_def::{body::Body, EnumVariantId, LocalFieldId, VariantId}; | ||
10 | use la_arena::Idx; | ||
11 | |||
12 | use crate::{db::HirDatabase, AdtId, InferenceResult, Interner, Substitution, Ty, TyKind}; | ||
13 | |||
14 | use self::{deconstruct_pat::ToDo, pat_util::EnumerateAndAdjustIterator}; | ||
15 | |||
16 | pub type PatId = Idx<Pat>; | ||
17 | |||
18 | #[derive(Clone, Debug)] | ||
19 | pub(crate) enum PatternError { | ||
20 | Unimplemented, | ||
21 | } | ||
22 | |||
23 | #[derive(Clone, Debug, PartialEq)] | ||
24 | pub struct FieldPat { | ||
25 | pub field: LocalFieldId, | ||
26 | pub pattern: Pat, | ||
27 | } | ||
28 | |||
29 | #[derive(Clone, Debug, PartialEq)] | ||
30 | pub struct Pat { | ||
31 | pub ty: Ty, | ||
32 | pub kind: Box<PatKind>, | ||
33 | } | ||
34 | |||
35 | impl Pat { | ||
36 | pub(crate) fn wildcard_from_ty(ty: &Ty) -> Self { | ||
37 | Pat { ty: ty.clone(), kind: Box::new(PatKind::Wild) } | ||
38 | } | ||
39 | } | ||
40 | |||
41 | #[derive(Clone, Debug, PartialEq)] | ||
42 | pub enum PatKind { | ||
43 | Wild, | ||
44 | |||
45 | /// `x`, `ref x`, `x @ P`, etc. | ||
46 | Binding { | ||
47 | subpattern: Option<Pat>, | ||
48 | // todo: ToDo, | ||
49 | }, | ||
50 | |||
51 | /// `Foo(...)` or `Foo{...}` or `Foo`, where `Foo` is a variant name from an ADT with | ||
52 | /// multiple variants. | ||
53 | Variant { | ||
54 | substs: Substitution, | ||
55 | enum_variant: EnumVariantId, | ||
56 | subpatterns: Vec<FieldPat>, | ||
57 | }, | ||
58 | |||
59 | /// `(...)`, `Foo(...)`, `Foo{...}`, or `Foo`, where `Foo` is a variant name from an ADT with | ||
60 | /// a single variant. | ||
61 | Leaf { | ||
62 | subpatterns: Vec<FieldPat>, | ||
63 | }, | ||
64 | |||
65 | /// `box P`, `&P`, `&mut P`, etc. | ||
66 | Deref { | ||
67 | subpattern: Pat, | ||
68 | }, | ||
69 | |||
70 | /// An or-pattern, e.g. `p | q`. | ||
71 | /// Invariant: `pats.len() >= 2`. | ||
72 | Or { | ||
73 | pats: Vec<Pat>, | ||
74 | }, | ||
75 | } | ||
76 | |||
77 | pub(crate) struct PatCtxt<'a> { | ||
78 | db: &'a dyn HirDatabase, | ||
79 | infer: &'a InferenceResult, | ||
80 | body: &'a Body, | ||
81 | pub(crate) errors: Vec<PatternError>, | ||
82 | } | ||
83 | |||
84 | impl<'a> PatCtxt<'a> { | ||
85 | pub(crate) fn new(db: &'a dyn HirDatabase, infer: &'a InferenceResult, body: &'a Body) -> Self { | ||
86 | Self { db, infer, body, errors: Vec::new() } | ||
87 | } | ||
88 | |||
89 | pub(crate) fn lower_pattern(&mut self, pat: hir_def::expr::PatId) -> Pat { | ||
90 | // TODO: pattern adjustments (implicit dereference) | ||
91 | // More info https://github.com/rust-lang/rust/issues/42640#issuecomment-313535089 | ||
92 | let unadjusted_pat = self.lower_pattern_unadjusted(pat); | ||
93 | unadjusted_pat | ||
94 | } | ||
95 | |||
96 | fn lower_pattern_unadjusted(&mut self, pat: hir_def::expr::PatId) -> Pat { | ||
97 | let ty = &self.infer[pat]; | ||
98 | |||
99 | let kind = match self.body[pat] { | ||
100 | hir_def::expr::Pat::Wild => PatKind::Wild, | ||
101 | |||
102 | hir_def::expr::Pat::Tuple { ref args, ellipsis } => { | ||
103 | let arity = match *ty.kind(&Interner) { | ||
104 | TyKind::Tuple(arity, _) => arity, | ||
105 | _ => panic!("unexpected type for tuple pattern: {:?}", ty), | ||
106 | }; | ||
107 | let subpatterns = self.lower_tuple_subpats(args, arity, ellipsis); | ||
108 | PatKind::Leaf { subpatterns } | ||
109 | } | ||
110 | |||
111 | hir_def::expr::Pat::TupleStruct { ref args, ellipsis, .. } => { | ||
112 | let variant_data = match self.infer.variant_resolution_for_pat(pat) { | ||
113 | Some(variant_id) => variant_id.variant_data(self.db.upcast()), | ||
114 | None => panic!("tuple struct pattern not applied to an ADT {:?}", ty), | ||
115 | }; | ||
116 | let subpatterns = | ||
117 | self.lower_tuple_subpats(args, variant_data.fields().len(), ellipsis); | ||
118 | self.lower_variant_or_leaf(pat, ty, subpatterns) | ||
119 | } | ||
120 | |||
121 | hir_def::expr::Pat::Record { ref args, .. } => { | ||
122 | let variant_data = match self.infer.variant_resolution_for_pat(pat) { | ||
123 | Some(variant_id) => variant_id.variant_data(self.db.upcast()), | ||
124 | None => panic!("record pattern not applied to an ADT {:?}", ty), | ||
125 | }; | ||
126 | let subpatterns = args | ||
127 | .iter() | ||
128 | .map(|field| FieldPat { | ||
129 | // XXX(iDawer): field lookup is inefficient | ||
130 | field: variant_data.field(&field.name).unwrap_or_else(|| todo!()), | ||
131 | pattern: self.lower_pattern(field.pat), | ||
132 | }) | ||
133 | .collect(); | ||
134 | self.lower_variant_or_leaf(pat, ty, subpatterns) | ||
135 | } | ||
136 | |||
137 | hir_def::expr::Pat::Or(ref pats) => PatKind::Or { pats: self.lower_patterns(pats) }, | ||
138 | |||
139 | _ => { | ||
140 | self.errors.push(PatternError::Unimplemented); | ||
141 | PatKind::Wild | ||
142 | } | ||
143 | }; | ||
144 | |||
145 | Pat { ty: ty.clone(), kind: Box::new(kind) } | ||
146 | } | ||
147 | |||
148 | fn lower_tuple_subpats( | ||
149 | &mut self, | ||
150 | pats: &[hir_def::expr::PatId], | ||
151 | expected_len: usize, | ||
152 | ellipsis: Option<usize>, | ||
153 | ) -> Vec<FieldPat> { | ||
154 | pats.iter() | ||
155 | .enumerate_and_adjust(expected_len, ellipsis) | ||
156 | .map(|(i, &subpattern)| FieldPat { | ||
157 | field: LocalFieldId::from_raw((i as u32).into()), | ||
158 | pattern: self.lower_pattern(subpattern), | ||
159 | }) | ||
160 | .collect() | ||
161 | } | ||
162 | |||
163 | fn lower_patterns(&mut self, pats: &[hir_def::expr::PatId]) -> Vec<Pat> { | ||
164 | pats.iter().map(|&p| self.lower_pattern(p)).collect() | ||
165 | } | ||
166 | |||
167 | fn lower_variant_or_leaf( | ||
168 | &mut self, | ||
169 | pat: hir_def::expr::PatId, | ||
170 | ty: &Ty, | ||
171 | subpatterns: Vec<FieldPat>, | ||
172 | ) -> PatKind { | ||
173 | let kind = match self.infer.variant_resolution_for_pat(pat) { | ||
174 | Some(variant_id) => { | ||
175 | if let VariantId::EnumVariantId(enum_variant) = variant_id { | ||
176 | let substs = match ty.kind(&Interner) { | ||
177 | TyKind::Adt(_, substs) | TyKind::FnDef(_, substs) => substs.clone(), | ||
178 | TyKind::Error => { | ||
179 | return PatKind::Wild; | ||
180 | } | ||
181 | _ => panic!("inappropriate type for def: {:?}", ty), | ||
182 | }; | ||
183 | PatKind::Variant { substs, enum_variant, subpatterns } | ||
184 | } else { | ||
185 | PatKind::Leaf { subpatterns } | ||
186 | } | ||
187 | } | ||
188 | None => { | ||
189 | self.errors.push(PatternError::Unimplemented); | ||
190 | PatKind::Wild | ||
191 | } | ||
192 | }; | ||
193 | // TODO: do we need PatKind::AscribeUserType ? | ||
194 | kind | ||
195 | } | ||
196 | } | ||
197 | |||
7 | #[cfg(test)] | 198 | #[cfg(test)] |
8 | mod tests { | 199 | mod tests { |
9 | use crate::diagnostics::tests::check_diagnostics; | 200 | use crate::diagnostics::tests::check_diagnostics; |