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