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