]> git.lizzy.rs Git - rust.git/blob - src/librustc_ast_lowering/pat.rs
Rollup merge of #68340 - GuillaumeGomez:clean-up-e0200, r=Dylan-DPC
[rust.git] / src / librustc_ast_lowering / pat.rs
1 use super::{ImplTraitContext, LoweringContext, ParamMode};
2
3 use rustc_hir as hir;
4 use rustc_hir::def::Res;
5 use rustc_span::{source_map::Spanned, Span};
6 use syntax::ast::*;
7 use syntax::ptr::P;
8
9 impl<'a, 'hir> LoweringContext<'a, 'hir> {
10     crate fn lower_pat(&mut self, p: &Pat) -> &'hir hir::Pat<'hir> {
11         let node = match p.kind {
12             PatKind::Wild => hir::PatKind::Wild,
13             PatKind::Ident(ref binding_mode, ident, ref sub) => {
14                 let lower_sub = |this: &mut Self| sub.as_ref().map(|s| this.lower_pat(&*s));
15                 let node = self.lower_pat_ident(p, binding_mode, ident, lower_sub);
16                 node
17             }
18             PatKind::Lit(ref e) => hir::PatKind::Lit(self.lower_expr(e)),
19             PatKind::TupleStruct(ref path, ref pats) => {
20                 let qpath = self.lower_qpath(
21                     p.id,
22                     &None,
23                     path,
24                     ParamMode::Optional,
25                     ImplTraitContext::disallowed(),
26                 );
27                 let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple struct");
28                 hir::PatKind::TupleStruct(qpath, pats, ddpos)
29             }
30             PatKind::Or(ref pats) => {
31                 hir::PatKind::Or(self.arena.alloc_from_iter(pats.iter().map(|x| self.lower_pat(x))))
32             }
33             PatKind::Path(ref qself, ref path) => {
34                 let qpath = self.lower_qpath(
35                     p.id,
36                     qself,
37                     path,
38                     ParamMode::Optional,
39                     ImplTraitContext::disallowed(),
40                 );
41                 hir::PatKind::Path(qpath)
42             }
43             PatKind::Struct(ref path, ref fields, etc) => {
44                 let qpath = self.lower_qpath(
45                     p.id,
46                     &None,
47                     path,
48                     ParamMode::Optional,
49                     ImplTraitContext::disallowed(),
50                 );
51
52                 let fs = self.arena.alloc_from_iter(fields.iter().map(|f| hir::FieldPat {
53                     hir_id: self.next_id(),
54                     ident: f.ident,
55                     pat: self.lower_pat(&f.pat),
56                     is_shorthand: f.is_shorthand,
57                     span: f.span,
58                 }));
59                 hir::PatKind::Struct(qpath, fs, etc)
60             }
61             PatKind::Tuple(ref pats) => {
62                 let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple");
63                 hir::PatKind::Tuple(pats, ddpos)
64             }
65             PatKind::Box(ref inner) => hir::PatKind::Box(self.lower_pat(inner)),
66             PatKind::Ref(ref inner, mutbl) => hir::PatKind::Ref(self.lower_pat(inner), mutbl),
67             PatKind::Range(ref e1, ref e2, Spanned { node: ref end, .. }) => hir::PatKind::Range(
68                 e1.as_deref().map(|e| self.lower_expr(e)),
69                 e2.as_deref().map(|e| self.lower_expr(e)),
70                 self.lower_range_end(end, e2.is_some()),
71             ),
72             PatKind::Slice(ref pats) => self.lower_pat_slice(pats),
73             PatKind::Rest => {
74                 // If we reach here the `..` pattern is not semantically allowed.
75                 self.ban_illegal_rest_pat(p.span)
76             }
77             PatKind::Paren(ref inner) => return self.lower_pat(inner),
78             PatKind::Mac(_) => panic!("Shouldn't exist here"),
79         };
80
81         self.pat_with_node_id_of(p, node)
82     }
83
84     fn lower_pat_tuple(
85         &mut self,
86         pats: &[P<Pat>],
87         ctx: &str,
88     ) -> (&'hir [&'hir hir::Pat<'hir>], Option<usize>) {
89         let mut elems = Vec::with_capacity(pats.len());
90         let mut rest = None;
91
92         let mut iter = pats.iter().enumerate();
93         for (idx, pat) in iter.by_ref() {
94             // Interpret the first `..` pattern as a sub-tuple pattern.
95             // Note that unlike for slice patterns,
96             // where `xs @ ..` is a legal sub-slice pattern,
97             // it is not a legal sub-tuple pattern.
98             if pat.is_rest() {
99                 rest = Some((idx, pat.span));
100                 break;
101             }
102             // It was not a sub-tuple pattern so lower it normally.
103             elems.push(self.lower_pat(pat));
104         }
105
106         for (_, pat) in iter {
107             // There was a previous sub-tuple pattern; make sure we don't allow more...
108             if pat.is_rest() {
109                 // ...but there was one again, so error.
110                 self.ban_extra_rest_pat(pat.span, rest.unwrap().1, ctx);
111             } else {
112                 elems.push(self.lower_pat(pat));
113             }
114         }
115
116         (self.arena.alloc_from_iter(elems), rest.map(|(ddpos, _)| ddpos))
117     }
118
119     /// Lower a slice pattern of form `[pat_0, ..., pat_n]` into
120     /// `hir::PatKind::Slice(before, slice, after)`.
121     ///
122     /// When encountering `($binding_mode $ident @)? ..` (`slice`),
123     /// this is interpreted as a sub-slice pattern semantically.
124     /// Patterns that follow, which are not like `slice` -- or an error occurs, are in `after`.
125     fn lower_pat_slice(&mut self, pats: &[P<Pat>]) -> hir::PatKind<'hir> {
126         let mut before = Vec::new();
127         let mut after = Vec::new();
128         let mut slice = None;
129         let mut prev_rest_span = None;
130
131         let mut iter = pats.iter();
132         // Lower all the patterns until the first occurence of a sub-slice pattern.
133         for pat in iter.by_ref() {
134             match pat.kind {
135                 // Found a sub-slice pattern `..`. Record, lower it to `_`, and stop here.
136                 PatKind::Rest => {
137                     prev_rest_span = Some(pat.span);
138                     slice = Some(self.pat_wild_with_node_id_of(pat));
139                     break;
140                 }
141                 // Found a sub-slice pattern `$binding_mode $ident @ ..`.
142                 // Record, lower it to `$binding_mode $ident @ _`, and stop here.
143                 PatKind::Ident(ref bm, ident, Some(ref sub)) if sub.is_rest() => {
144                     prev_rest_span = Some(sub.span);
145                     let lower_sub = |this: &mut Self| Some(this.pat_wild_with_node_id_of(sub));
146                     let node = self.lower_pat_ident(pat, bm, ident, lower_sub);
147                     slice = Some(self.pat_with_node_id_of(pat, node));
148                     break;
149                 }
150                 // It was not a subslice pattern so lower it normally.
151                 _ => before.push(self.lower_pat(pat)),
152             }
153         }
154
155         // Lower all the patterns after the first sub-slice pattern.
156         for pat in iter {
157             // There was a previous subslice pattern; make sure we don't allow more.
158             let rest_span = match pat.kind {
159                 PatKind::Rest => Some(pat.span),
160                 PatKind::Ident(.., Some(ref sub)) if sub.is_rest() => {
161                     // The `HirValidator` is merciless; add a `_` pattern to avoid ICEs.
162                     after.push(self.pat_wild_with_node_id_of(pat));
163                     Some(sub.span)
164                 }
165                 _ => None,
166             };
167             if let Some(rest_span) = rest_span {
168                 // We have e.g., `[a, .., b, ..]`. That's no good, error!
169                 self.ban_extra_rest_pat(rest_span, prev_rest_span.unwrap(), "slice");
170             } else {
171                 // Lower the pattern normally.
172                 after.push(self.lower_pat(pat));
173             }
174         }
175
176         hir::PatKind::Slice(
177             self.arena.alloc_from_iter(before),
178             slice,
179             self.arena.alloc_from_iter(after),
180         )
181     }
182
183     fn lower_pat_ident(
184         &mut self,
185         p: &Pat,
186         binding_mode: &BindingMode,
187         ident: Ident,
188         lower_sub: impl FnOnce(&mut Self) -> Option<&'hir hir::Pat<'hir>>,
189     ) -> hir::PatKind<'hir> {
190         match self.resolver.get_partial_res(p.id).map(|d| d.base_res()) {
191             // `None` can occur in body-less function signatures
192             res @ None | res @ Some(Res::Local(_)) => {
193                 let canonical_id = match res {
194                     Some(Res::Local(id)) => id,
195                     _ => p.id,
196                 };
197
198                 hir::PatKind::Binding(
199                     self.lower_binding_mode(binding_mode),
200                     self.lower_node_id(canonical_id),
201                     ident,
202                     lower_sub(self),
203                 )
204             }
205             Some(res) => hir::PatKind::Path(hir::QPath::Resolved(
206                 None,
207                 self.arena.alloc(hir::Path {
208                     span: ident.span,
209                     res: self.lower_res(res),
210                     segments: arena_vec![self; hir::PathSegment::from_ident(ident)],
211                 }),
212             )),
213         }
214     }
215
216     fn lower_binding_mode(&mut self, b: &BindingMode) -> hir::BindingAnnotation {
217         match *b {
218             BindingMode::ByValue(Mutability::Not) => hir::BindingAnnotation::Unannotated,
219             BindingMode::ByRef(Mutability::Not) => hir::BindingAnnotation::Ref,
220             BindingMode::ByValue(Mutability::Mut) => hir::BindingAnnotation::Mutable,
221             BindingMode::ByRef(Mutability::Mut) => hir::BindingAnnotation::RefMut,
222         }
223     }
224
225     fn pat_wild_with_node_id_of(&mut self, p: &Pat) -> &'hir hir::Pat<'hir> {
226         self.pat_with_node_id_of(p, hir::PatKind::Wild)
227     }
228
229     /// Construct a `Pat` with the `HirId` of `p.id` lowered.
230     fn pat_with_node_id_of(&mut self, p: &Pat, kind: hir::PatKind<'hir>) -> &'hir hir::Pat<'hir> {
231         self.arena.alloc(hir::Pat { hir_id: self.lower_node_id(p.id), kind, span: p.span })
232     }
233
234     /// Emit a friendly error for extra `..` patterns in a tuple/tuple struct/slice pattern.
235     fn ban_extra_rest_pat(&self, sp: Span, prev_sp: Span, ctx: &str) {
236         self.diagnostic()
237             .struct_span_err(sp, &format!("`..` can only be used once per {} pattern", ctx))
238             .span_label(sp, &format!("can only be used once per {} pattern", ctx))
239             .span_label(prev_sp, "previously used here")
240             .emit();
241     }
242
243     /// Used to ban the `..` pattern in places it shouldn't be semantically.
244     fn ban_illegal_rest_pat(&self, sp: Span) -> hir::PatKind<'hir> {
245         self.diagnostic()
246             .struct_span_err(sp, "`..` patterns are not allowed here")
247             .note("only allowed in tuple, tuple struct, and slice patterns")
248             .emit();
249
250         // We're not in a list context so `..` can be reasonably treated
251         // as `_` because it should always be valid and roughly matches the
252         // intent of `..` (notice that the rest of a single slot is that slot).
253         hir::PatKind::Wild
254     }
255
256     fn lower_range_end(&mut self, e: &RangeEnd, has_end: bool) -> hir::RangeEnd {
257         match *e {
258             RangeEnd::Excluded if has_end => hir::RangeEnd::Excluded,
259             // No end; so `X..` behaves like `RangeFrom`.
260             RangeEnd::Excluded | RangeEnd::Included(_) => hir::RangeEnd::Included,
261         }
262     }
263 }