]> git.lizzy.rs Git - rust.git/blob - src/librustc_ast_lowering/pat.rs
Fix missed same-sized member clash in ClashingExternDeclarations.
[rust.git] / src / librustc_ast_lowering / pat.rs
1 use super::{ImplTraitContext, LoweringContext, ParamMode};
2
3 use rustc_ast::ast::*;
4 use rustc_ast::ptr::P;
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                     rest = Some((idx, pat.span));
116                     let sp = pat.span;
117                     self.diagnostic()
118                         .struct_span_err(
119                             sp,
120                             &format!("`{} @` is not allowed in a {}", ident.name, ctx),
121                         )
122                         .span_label(sp, "this is only allowed in slice patterns")
123                         .help("remove this and bind each tuple field independently")
124                         .span_suggestion_verbose(
125                             sp,
126                             &format!("if you don't need to use the contents of {}, discard the tuple's remaining fields", ident),
127                             "..".to_string(),
128                             Applicability::MaybeIncorrect,
129                         )
130                         .emit();
131                     break;
132                 }
133                 _ => {}
134             }
135
136             // It was not a sub-tuple pattern so lower it normally.
137             elems.push(self.lower_pat(pat));
138         }
139
140         for (_, pat) in iter {
141             // There was a previous sub-tuple pattern; make sure we don't allow more...
142             if pat.is_rest() {
143                 // ...but there was one again, so error.
144                 self.ban_extra_rest_pat(pat.span, rest.unwrap().1, ctx);
145             } else {
146                 elems.push(self.lower_pat(pat));
147             }
148         }
149
150         (self.arena.alloc_from_iter(elems), rest.map(|(ddpos, _)| ddpos))
151     }
152
153     /// Lower a slice pattern of form `[pat_0, ..., pat_n]` into
154     /// `hir::PatKind::Slice(before, slice, after)`.
155     ///
156     /// When encountering `($binding_mode $ident @)? ..` (`slice`),
157     /// this is interpreted as a sub-slice pattern semantically.
158     /// Patterns that follow, which are not like `slice` -- or an error occurs, are in `after`.
159     fn lower_pat_slice(&mut self, pats: &[P<Pat>]) -> hir::PatKind<'hir> {
160         let mut before = Vec::new();
161         let mut after = Vec::new();
162         let mut slice = None;
163         let mut prev_rest_span = None;
164
165         // Lowers `$bm $ident @ ..` to `$bm $ident @ _`.
166         let lower_rest_sub = |this: &mut Self, pat, bm, ident, sub| {
167             let lower_sub = |this: &mut Self| Some(this.pat_wild_with_node_id_of(sub));
168             let node = this.lower_pat_ident(pat, bm, ident, lower_sub);
169             this.pat_with_node_id_of(pat, node)
170         };
171
172         let mut iter = pats.iter();
173         // Lower all the patterns until the first occurrence of a sub-slice pattern.
174         for pat in iter.by_ref() {
175             match pat.kind {
176                 // Found a sub-slice pattern `..`. Record, lower it to `_`, and stop here.
177                 PatKind::Rest => {
178                     prev_rest_span = Some(pat.span);
179                     slice = Some(self.pat_wild_with_node_id_of(pat));
180                     break;
181                 }
182                 // Found a sub-slice pattern `$binding_mode $ident @ ..`.
183                 // Record, lower it to `$binding_mode $ident @ _`, and stop here.
184                 PatKind::Ident(ref bm, ident, Some(ref sub)) if sub.is_rest() => {
185                     prev_rest_span = Some(sub.span);
186                     slice = Some(lower_rest_sub(self, pat, bm, ident, sub));
187                     break;
188                 }
189                 // It was not a subslice pattern so lower it normally.
190                 _ => before.push(self.lower_pat(pat)),
191             }
192         }
193
194         // Lower all the patterns after the first sub-slice pattern.
195         for pat in iter {
196             // There was a previous subslice pattern; make sure we don't allow more.
197             let rest_span = match pat.kind {
198                 PatKind::Rest => Some(pat.span),
199                 PatKind::Ident(ref bm, ident, Some(ref sub)) if sub.is_rest() => {
200                     // #69103: Lower into `binding @ _` as above to avoid ICEs.
201                     after.push(lower_rest_sub(self, pat, bm, ident, sub));
202                     Some(sub.span)
203                 }
204                 _ => None,
205             };
206             if let Some(rest_span) = rest_span {
207                 // We have e.g., `[a, .., b, ..]`. That's no good, error!
208                 self.ban_extra_rest_pat(rest_span, prev_rest_span.unwrap(), "slice");
209             } else {
210                 // Lower the pattern normally.
211                 after.push(self.lower_pat(pat));
212             }
213         }
214
215         hir::PatKind::Slice(
216             self.arena.alloc_from_iter(before),
217             slice,
218             self.arena.alloc_from_iter(after),
219         )
220     }
221
222     fn lower_pat_ident(
223         &mut self,
224         p: &Pat,
225         binding_mode: &BindingMode,
226         ident: Ident,
227         lower_sub: impl FnOnce(&mut Self) -> Option<&'hir hir::Pat<'hir>>,
228     ) -> hir::PatKind<'hir> {
229         match self.resolver.get_partial_res(p.id).map(|d| d.base_res()) {
230             // `None` can occur in body-less function signatures
231             res @ (None | Some(Res::Local(_))) => {
232                 let canonical_id = match res {
233                     Some(Res::Local(id)) => id,
234                     _ => p.id,
235                 };
236
237                 hir::PatKind::Binding(
238                     self.lower_binding_mode(binding_mode),
239                     self.lower_node_id(canonical_id),
240                     ident,
241                     lower_sub(self),
242                 )
243             }
244             Some(res) => hir::PatKind::Path(hir::QPath::Resolved(
245                 None,
246                 self.arena.alloc(hir::Path {
247                     span: ident.span,
248                     res: self.lower_res(res),
249                     segments: arena_vec![self; hir::PathSegment::from_ident(ident)],
250                 }),
251             )),
252         }
253     }
254
255     fn lower_binding_mode(&mut self, b: &BindingMode) -> hir::BindingAnnotation {
256         match *b {
257             BindingMode::ByValue(Mutability::Not) => hir::BindingAnnotation::Unannotated,
258             BindingMode::ByRef(Mutability::Not) => hir::BindingAnnotation::Ref,
259             BindingMode::ByValue(Mutability::Mut) => hir::BindingAnnotation::Mutable,
260             BindingMode::ByRef(Mutability::Mut) => hir::BindingAnnotation::RefMut,
261         }
262     }
263
264     fn pat_wild_with_node_id_of(&mut self, p: &Pat) -> &'hir hir::Pat<'hir> {
265         self.pat_with_node_id_of(p, hir::PatKind::Wild)
266     }
267
268     /// Construct a `Pat` with the `HirId` of `p.id` lowered.
269     fn pat_with_node_id_of(&mut self, p: &Pat, kind: hir::PatKind<'hir>) -> &'hir hir::Pat<'hir> {
270         self.arena.alloc(hir::Pat { hir_id: self.lower_node_id(p.id), kind, span: p.span })
271     }
272
273     /// Emit a friendly error for extra `..` patterns in a tuple/tuple struct/slice pattern.
274     fn ban_extra_rest_pat(&self, sp: Span, prev_sp: Span, ctx: &str) {
275         self.diagnostic()
276             .struct_span_err(sp, &format!("`..` can only be used once per {} pattern", ctx))
277             .span_label(sp, &format!("can only be used once per {} pattern", ctx))
278             .span_label(prev_sp, "previously used here")
279             .emit();
280     }
281
282     /// Used to ban the `..` pattern in places it shouldn't be semantically.
283     fn ban_illegal_rest_pat(&self, sp: Span) -> hir::PatKind<'hir> {
284         self.diagnostic()
285             .struct_span_err(sp, "`..` patterns are not allowed here")
286             .note("only allowed in tuple, tuple struct, and slice patterns")
287             .emit();
288
289         // We're not in a list context so `..` can be reasonably treated
290         // as `_` because it should always be valid and roughly matches the
291         // intent of `..` (notice that the rest of a single slot is that slot).
292         hir::PatKind::Wild
293     }
294
295     fn lower_range_end(&mut self, e: &RangeEnd, has_end: bool) -> hir::RangeEnd {
296         match *e {
297             RangeEnd::Excluded if has_end => hir::RangeEnd::Excluded,
298             // No end; so `X..` behaves like `RangeFrom`.
299             RangeEnd::Excluded | RangeEnd::Included(_) => hir::RangeEnd::Included,
300         }
301     }
302 }