aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_ty/src/diagnostics/pattern.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_ty/src/diagnostics/pattern.rs')
-rw-r--r--crates/hir_ty/src/diagnostics/pattern.rs191
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
4mod deconstruct_pat; 4mod deconstruct_pat;
5// TODO: find a better place for this?
6mod pat_util;
5pub mod usefulness; 7pub mod usefulness;
6 8
9use hir_def::{body::Body, EnumVariantId, LocalFieldId, VariantId};
10use la_arena::Idx;
11
12use crate::{db::HirDatabase, AdtId, InferenceResult, Interner, Substitution, Ty, TyKind};
13
14use self::{deconstruct_pat::ToDo, pat_util::EnumerateAndAdjustIterator};
15
16pub type PatId = Idx<Pat>;
17
18#[derive(Clone, Debug)]
19pub(crate) enum PatternError {
20 Unimplemented,
21}
22
23#[derive(Clone, Debug, PartialEq)]
24pub struct FieldPat {
25 pub field: LocalFieldId,
26 pub pattern: Pat,
27}
28
29#[derive(Clone, Debug, PartialEq)]
30pub struct Pat {
31 pub ty: Ty,
32 pub kind: Box<PatKind>,
33}
34
35impl 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)]
42pub 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
77pub(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
84impl<'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)]
8mod tests { 199mod tests {
9 use crate::diagnostics::tests::check_diagnostics; 200 use crate::diagnostics::tests::check_diagnostics;