]>
Commit | Line | Data |
---|---|---|
f20569fa XL |
1 | use super::{Parser, PathStyle}; |
2 | use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; | |
3 | use rustc_ast::mut_visit::{noop_visit_pat, MutVisitor}; | |
4 | use rustc_ast::ptr::P; | |
5 | use rustc_ast::token; | |
6 | use rustc_ast::{self as ast, AttrVec, Attribute, FieldPat, MacCall, Pat, PatKind, RangeEnd}; | |
7 | use rustc_ast::{BindingMode, Expr, ExprKind, Mutability, Path, QSelf, RangeSyntax}; | |
8 | use rustc_ast_pretty::pprust; | |
9 | use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, PResult}; | |
10 | use rustc_span::source_map::{respan, Span, Spanned}; | |
11 | use rustc_span::symbol::{kw, sym, Ident}; | |
12 | ||
13 | type Expected = Option<&'static str>; | |
14 | ||
15 | /// `Expected` for function and lambda parameter patterns. | |
16 | pub(super) const PARAM_EXPECTED: Expected = Some("parameter name"); | |
17 | ||
18 | const WHILE_PARSING_OR_MSG: &str = "while parsing this or-pattern starting here"; | |
19 | ||
20 | /// Whether or not an or-pattern should be gated when occurring in the current context. | |
21 | #[derive(PartialEq, Clone, Copy)] | |
22 | pub(super) enum GateOr { | |
23 | Yes, | |
24 | No, | |
25 | } | |
26 | ||
27 | /// Whether or not to recover a `,` when parsing or-patterns. | |
28 | #[derive(PartialEq, Copy, Clone)] | |
29 | pub(super) enum RecoverComma { | |
30 | Yes, | |
31 | No, | |
32 | } | |
33 | ||
34 | impl<'a> Parser<'a> { | |
35 | /// Parses a pattern. | |
36 | /// | |
37 | /// Corresponds to `pat<no_top_alt>` in RFC 2535 and does not admit or-patterns | |
38 | /// at the top level. Used when parsing the parameters of lambda expressions, | |
39 | /// functions, function pointers, and `pat` macro fragments. | |
40 | pub fn parse_pat(&mut self, expected: Expected) -> PResult<'a, P<Pat>> { | |
41 | self.parse_pat_with_range_pat(true, expected) | |
42 | } | |
43 | ||
44 | /// Entry point to the main pattern parser. | |
45 | /// Corresponds to `top_pat` in RFC 2535 and allows or-pattern at the top level. | |
46 | pub(super) fn parse_top_pat( | |
47 | &mut self, | |
48 | gate_or: GateOr, | |
49 | rc: RecoverComma, | |
50 | ) -> PResult<'a, P<Pat>> { | |
51 | // Allow a '|' before the pats (RFCs 1925, 2530, and 2535). | |
52 | let gated_leading_vert = self.eat_or_separator(None) && gate_or == GateOr::Yes; | |
53 | let leading_vert_span = self.prev_token.span; | |
54 | ||
55 | // Parse the possibly-or-pattern. | |
56 | let pat = self.parse_pat_with_or(None, gate_or, rc)?; | |
57 | ||
58 | // If we parsed a leading `|` which should be gated, | |
59 | // and no other gated or-pattern has been parsed thus far, | |
60 | // then we should really gate the leading `|`. | |
61 | // This complicated procedure is done purely for diagnostics UX. | |
62 | if gated_leading_vert && self.sess.gated_spans.is_ungated(sym::or_patterns) { | |
63 | self.sess.gated_spans.gate(sym::or_patterns, leading_vert_span); | |
64 | } | |
65 | ||
66 | Ok(pat) | |
67 | } | |
68 | ||
69 | /// Parse the pattern for a function or function pointer parameter. | |
70 | /// Special recovery is provided for or-patterns and leading `|`. | |
71 | pub(super) fn parse_fn_param_pat(&mut self) -> PResult<'a, P<Pat>> { | |
72 | self.recover_leading_vert(None, "not allowed in a parameter pattern"); | |
73 | let pat = self.parse_pat_with_or(PARAM_EXPECTED, GateOr::No, RecoverComma::No)?; | |
74 | ||
75 | if let PatKind::Or(..) = &pat.kind { | |
76 | self.ban_illegal_fn_param_or_pat(&pat); | |
77 | } | |
78 | ||
79 | Ok(pat) | |
80 | } | |
81 | ||
82 | /// Ban `A | B` immediately in a parameter pattern and suggest wrapping in parens. | |
83 | fn ban_illegal_fn_param_or_pat(&self, pat: &Pat) { | |
84 | let msg = "wrap the pattern in parenthesis"; | |
85 | let fix = format!("({})", pprust::pat_to_string(pat)); | |
86 | self.struct_span_err(pat.span, "an or-pattern parameter must be wrapped in parenthesis") | |
87 | .span_suggestion(pat.span, msg, fix, Applicability::MachineApplicable) | |
88 | .emit(); | |
89 | } | |
90 | ||
91 | /// Parses a pattern, that may be a or-pattern (e.g. `Foo | Bar` in `Some(Foo | Bar)`). | |
92 | /// Corresponds to `pat<allow_top_alt>` in RFC 2535. | |
93 | fn parse_pat_with_or( | |
94 | &mut self, | |
95 | expected: Expected, | |
96 | gate_or: GateOr, | |
97 | rc: RecoverComma, | |
98 | ) -> PResult<'a, P<Pat>> { | |
99 | // Parse the first pattern (`p_0`). | |
100 | let first_pat = self.parse_pat(expected)?; | |
101 | self.maybe_recover_unexpected_comma(first_pat.span, rc, gate_or)?; | |
102 | ||
103 | // If the next token is not a `|`, | |
104 | // this is not an or-pattern and we should exit here. | |
105 | if !self.check(&token::BinOp(token::Or)) && self.token != token::OrOr { | |
106 | return Ok(first_pat); | |
107 | } | |
108 | ||
109 | // Parse the patterns `p_1 | ... | p_n` where `n > 0`. | |
110 | let lo = first_pat.span; | |
111 | let mut pats = vec![first_pat]; | |
112 | while self.eat_or_separator(Some(lo)) { | |
113 | let pat = self.parse_pat(expected).map_err(|mut err| { | |
114 | err.span_label(lo, WHILE_PARSING_OR_MSG); | |
115 | err | |
116 | })?; | |
117 | self.maybe_recover_unexpected_comma(pat.span, rc, gate_or)?; | |
118 | pats.push(pat); | |
119 | } | |
120 | let or_pattern_span = lo.to(self.prev_token.span); | |
121 | ||
122 | // Feature gate the or-pattern if instructed: | |
123 | if gate_or == GateOr::Yes { | |
124 | self.sess.gated_spans.gate(sym::or_patterns, or_pattern_span); | |
125 | } | |
126 | ||
127 | Ok(self.mk_pat(or_pattern_span, PatKind::Or(pats))) | |
128 | } | |
129 | ||
130 | /// Eat the or-pattern `|` separator. | |
131 | /// If instead a `||` token is encountered, recover and pretend we parsed `|`. | |
132 | fn eat_or_separator(&mut self, lo: Option<Span>) -> bool { | |
133 | if self.recover_trailing_vert(lo) { | |
134 | return false; | |
135 | } | |
136 | ||
137 | match self.token.kind { | |
138 | token::OrOr => { | |
139 | // Found `||`; Recover and pretend we parsed `|`. | |
140 | self.ban_unexpected_or_or(lo); | |
141 | self.bump(); | |
142 | true | |
143 | } | |
144 | _ => self.eat(&token::BinOp(token::Or)), | |
145 | } | |
146 | } | |
147 | ||
148 | /// Recover if `|` or `||` is the current token and we have one of the | |
149 | /// tokens `=>`, `if`, `=`, `:`, `;`, `,`, `]`, `)`, or `}` ahead of us. | |
150 | /// | |
151 | /// These tokens all indicate that we reached the end of the or-pattern | |
152 | /// list and can now reliably say that the `|` was an illegal trailing vert. | |
153 | /// Note that there are more tokens such as `@` for which we know that the `|` | |
154 | /// is an illegal parse. However, the user's intent is less clear in that case. | |
155 | fn recover_trailing_vert(&mut self, lo: Option<Span>) -> bool { | |
156 | let is_end_ahead = self.look_ahead(1, |token| { | |
157 | matches!( | |
158 | &token.uninterpolate().kind, | |
159 | token::FatArrow // e.g. `a | => 0,`. | |
160 | | token::Ident(kw::If, false) // e.g. `a | if expr`. | |
161 | | token::Eq // e.g. `let a | = 0`. | |
162 | | token::Semi // e.g. `let a |;`. | |
163 | | token::Colon // e.g. `let a | :`. | |
164 | | token::Comma // e.g. `let (a |,)`. | |
165 | | token::CloseDelim(token::Bracket) // e.g. `let [a | ]`. | |
166 | | token::CloseDelim(token::Paren) // e.g. `let (a | )`. | |
167 | | token::CloseDelim(token::Brace) // e.g. `let A { f: a | }`. | |
168 | ) | |
169 | }); | |
170 | match (is_end_ahead, &self.token.kind) { | |
171 | (true, token::BinOp(token::Or) | token::OrOr) => { | |
172 | self.ban_illegal_vert(lo, "trailing", "not allowed in an or-pattern"); | |
173 | self.bump(); | |
174 | true | |
175 | } | |
176 | _ => false, | |
177 | } | |
178 | } | |
179 | ||
180 | /// We have parsed `||` instead of `|`. Error and suggest `|` instead. | |
181 | fn ban_unexpected_or_or(&mut self, lo: Option<Span>) { | |
182 | let mut err = self.struct_span_err(self.token.span, "unexpected token `||` after pattern"); | |
183 | err.span_suggestion( | |
184 | self.token.span, | |
185 | "use a single `|` to separate multiple alternative patterns", | |
186 | "|".to_owned(), | |
187 | Applicability::MachineApplicable, | |
188 | ); | |
189 | if let Some(lo) = lo { | |
190 | err.span_label(lo, WHILE_PARSING_OR_MSG); | |
191 | } | |
192 | err.emit(); | |
193 | } | |
194 | ||
195 | /// Some special error handling for the "top-level" patterns in a match arm, | |
196 | /// `for` loop, `let`, &c. (in contrast to subpatterns within such). | |
197 | fn maybe_recover_unexpected_comma( | |
198 | &mut self, | |
199 | lo: Span, | |
200 | rc: RecoverComma, | |
201 | gate_or: GateOr, | |
202 | ) -> PResult<'a, ()> { | |
203 | if rc == RecoverComma::No || self.token != token::Comma { | |
204 | return Ok(()); | |
205 | } | |
206 | ||
207 | // An unexpected comma after a top-level pattern is a clue that the | |
208 | // user (perhaps more accustomed to some other language) forgot the | |
209 | // parentheses in what should have been a tuple pattern; return a | |
210 | // suggestion-enhanced error here rather than choking on the comma later. | |
211 | let comma_span = self.token.span; | |
212 | self.bump(); | |
213 | if let Err(mut err) = self.skip_pat_list() { | |
214 | // We didn't expect this to work anyway; we just wanted to advance to the | |
215 | // end of the comma-sequence so we know the span to suggest parenthesizing. | |
216 | err.cancel(); | |
217 | } | |
218 | let seq_span = lo.to(self.prev_token.span); | |
219 | let mut err = self.struct_span_err(comma_span, "unexpected `,` in pattern"); | |
220 | if let Ok(seq_snippet) = self.span_to_snippet(seq_span) { | |
221 | const MSG: &str = "try adding parentheses to match on a tuple..."; | |
222 | ||
223 | let or_suggestion = | |
224 | gate_or == GateOr::No || !self.sess.gated_spans.is_ungated(sym::or_patterns); | |
225 | err.span_suggestion( | |
226 | seq_span, | |
227 | if or_suggestion { MSG } else { MSG.trim_end_matches('.') }, | |
228 | format!("({})", seq_snippet), | |
229 | Applicability::MachineApplicable, | |
230 | ); | |
231 | if or_suggestion { | |
232 | err.span_suggestion( | |
233 | seq_span, | |
234 | "...or a vertical bar to match on multiple alternatives", | |
235 | seq_snippet.replace(",", " |"), | |
236 | Applicability::MachineApplicable, | |
237 | ); | |
238 | } | |
239 | } | |
240 | Err(err) | |
241 | } | |
242 | ||
243 | /// Parse and throw away a parenthesized comma separated | |
244 | /// sequence of patterns until `)` is reached. | |
245 | fn skip_pat_list(&mut self) -> PResult<'a, ()> { | |
246 | while !self.check(&token::CloseDelim(token::Paren)) { | |
247 | self.parse_pat(None)?; | |
248 | if !self.eat(&token::Comma) { | |
249 | return Ok(()); | |
250 | } | |
251 | } | |
252 | Ok(()) | |
253 | } | |
254 | ||
255 | /// Recursive possibly-or-pattern parser with recovery for an erroneous leading `|`. | |
256 | /// See `parse_pat_with_or` for details on parsing or-patterns. | |
257 | fn parse_pat_with_or_inner(&mut self) -> PResult<'a, P<Pat>> { | |
258 | self.recover_leading_vert(None, "only allowed in a top-level pattern"); | |
259 | self.parse_pat_with_or(None, GateOr::Yes, RecoverComma::No) | |
260 | } | |
261 | ||
262 | /// Recover if `|` or `||` is here. | |
263 | /// The user is thinking that a leading `|` is allowed in this position. | |
264 | fn recover_leading_vert(&mut self, lo: Option<Span>, ctx: &str) { | |
265 | if let token::BinOp(token::Or) | token::OrOr = self.token.kind { | |
266 | self.ban_illegal_vert(lo, "leading", ctx); | |
267 | self.bump(); | |
268 | } | |
269 | } | |
270 | ||
271 | /// A `|` or possibly `||` token shouldn't be here. Ban it. | |
272 | fn ban_illegal_vert(&mut self, lo: Option<Span>, pos: &str, ctx: &str) { | |
273 | let span = self.token.span; | |
274 | let mut err = self.struct_span_err(span, &format!("a {} `|` is {}", pos, ctx)); | |
275 | err.span_suggestion( | |
276 | span, | |
277 | &format!("remove the `{}`", pprust::token_to_string(&self.token)), | |
278 | String::new(), | |
279 | Applicability::MachineApplicable, | |
280 | ); | |
281 | if let Some(lo) = lo { | |
282 | err.span_label(lo, WHILE_PARSING_OR_MSG); | |
283 | } | |
284 | if let token::OrOr = self.token.kind { | |
285 | err.note("alternatives in or-patterns are separated with `|`, not `||`"); | |
286 | } | |
287 | err.emit(); | |
288 | } | |
289 | ||
290 | /// Parses a pattern, with a setting whether modern range patterns (e.g., `a..=b`, `a..b` are | |
291 | /// allowed). | |
292 | fn parse_pat_with_range_pat( | |
293 | &mut self, | |
294 | allow_range_pat: bool, | |
295 | expected: Expected, | |
296 | ) -> PResult<'a, P<Pat>> { | |
297 | maybe_recover_from_interpolated_ty_qpath!(self, true); | |
298 | maybe_whole!(self, NtPat, |x| x); | |
299 | ||
300 | let lo = self.token.span; | |
301 | ||
302 | let pat = if self.check(&token::BinOp(token::And)) || self.token.kind == token::AndAnd { | |
303 | self.parse_pat_deref(expected)? | |
304 | } else if self.check(&token::OpenDelim(token::Paren)) { | |
305 | self.parse_pat_tuple_or_parens()? | |
306 | } else if self.check(&token::OpenDelim(token::Bracket)) { | |
307 | // Parse `[pat, pat,...]` as a slice pattern. | |
308 | let (pats, _) = | |
309 | self.parse_delim_comma_seq(token::Bracket, |p| p.parse_pat_with_or_inner())?; | |
310 | PatKind::Slice(pats) | |
311 | } else if self.check(&token::DotDot) && !self.is_pat_range_end_start(1) { | |
312 | // A rest pattern `..`. | |
313 | self.bump(); // `..` | |
314 | PatKind::Rest | |
315 | } else if self.check(&token::DotDotDot) && !self.is_pat_range_end_start(1) { | |
316 | self.recover_dotdotdot_rest_pat(lo) | |
317 | } else if let Some(form) = self.parse_range_end() { | |
318 | self.parse_pat_range_to(form)? // `..=X`, `...X`, or `..X`. | |
319 | } else if self.eat_keyword(kw::Underscore) { | |
320 | // Parse _ | |
321 | PatKind::Wild | |
322 | } else if self.eat_keyword(kw::Mut) { | |
323 | self.parse_pat_ident_mut()? | |
324 | } else if self.eat_keyword(kw::Ref) { | |
325 | // Parse ref ident @ pat / ref mut ident @ pat | |
326 | let mutbl = self.parse_mutability(); | |
327 | self.parse_pat_ident(BindingMode::ByRef(mutbl))? | |
328 | } else if self.eat_keyword(kw::Box) { | |
329 | // Parse `box pat` | |
330 | let pat = self.parse_pat_with_range_pat(false, None)?; | |
331 | self.sess.gated_spans.gate(sym::box_patterns, lo.to(self.prev_token.span)); | |
332 | PatKind::Box(pat) | |
333 | } else if self.check_inline_const(0) { | |
334 | // Parse `const pat` | |
335 | let const_expr = self.parse_const_block(lo.to(self.token.span))?; | |
336 | ||
337 | if let Some(re) = self.parse_range_end() { | |
338 | self.parse_pat_range_begin_with(const_expr, re)? | |
339 | } else { | |
340 | PatKind::Lit(const_expr) | |
341 | } | |
342 | } else if self.can_be_ident_pat() { | |
343 | // Parse `ident @ pat` | |
344 | // This can give false positives and parse nullary enums, | |
345 | // they are dealt with later in resolve. | |
346 | self.parse_pat_ident(BindingMode::ByValue(Mutability::Not))? | |
347 | } else if self.is_start_of_pat_with_path() { | |
348 | // Parse pattern starting with a path | |
349 | let (qself, path) = if self.eat_lt() { | |
350 | // Parse a qualified path | |
351 | let (qself, path) = self.parse_qpath(PathStyle::Expr)?; | |
352 | (Some(qself), path) | |
353 | } else { | |
354 | // Parse an unqualified path | |
355 | (None, self.parse_path(PathStyle::Expr)?) | |
356 | }; | |
357 | let span = lo.to(self.prev_token.span); | |
358 | ||
359 | if qself.is_none() && self.check(&token::Not) { | |
360 | self.parse_pat_mac_invoc(path)? | |
361 | } else if let Some(form) = self.parse_range_end() { | |
362 | let begin = self.mk_expr(span, ExprKind::Path(qself, path), AttrVec::new()); | |
363 | self.parse_pat_range_begin_with(begin, form)? | |
364 | } else if self.check(&token::OpenDelim(token::Brace)) { | |
365 | self.parse_pat_struct(qself, path)? | |
366 | } else if self.check(&token::OpenDelim(token::Paren)) { | |
367 | self.parse_pat_tuple_struct(qself, path)? | |
368 | } else { | |
369 | PatKind::Path(qself, path) | |
370 | } | |
371 | } else { | |
372 | // Try to parse everything else as literal with optional minus | |
373 | match self.parse_literal_maybe_minus() { | |
374 | Ok(begin) => match self.parse_range_end() { | |
375 | Some(form) => self.parse_pat_range_begin_with(begin, form)?, | |
376 | None => PatKind::Lit(begin), | |
377 | }, | |
378 | Err(err) => return self.fatal_unexpected_non_pat(err, expected), | |
379 | } | |
380 | }; | |
381 | ||
382 | let pat = self.mk_pat(lo.to(self.prev_token.span), pat); | |
383 | let pat = self.maybe_recover_from_bad_qpath(pat, true)?; | |
384 | let pat = self.recover_intersection_pat(pat)?; | |
385 | ||
386 | if !allow_range_pat { | |
387 | self.ban_pat_range_if_ambiguous(&pat) | |
388 | } | |
389 | ||
390 | Ok(pat) | |
391 | } | |
392 | ||
393 | /// Recover from a typoed `...` pattern that was encountered | |
394 | /// Ref: Issue #70388 | |
395 | fn recover_dotdotdot_rest_pat(&mut self, lo: Span) -> PatKind { | |
396 | // A typoed rest pattern `...`. | |
397 | self.bump(); // `...` | |
398 | ||
399 | // The user probably mistook `...` for a rest pattern `..`. | |
400 | self.struct_span_err(lo, "unexpected `...`") | |
401 | .span_label(lo, "not a valid pattern") | |
402 | .span_suggestion_short( | |
403 | lo, | |
404 | "for a rest pattern, use `..` instead of `...`", | |
405 | "..".to_owned(), | |
406 | Applicability::MachineApplicable, | |
407 | ) | |
408 | .emit(); | |
409 | PatKind::Rest | |
410 | } | |
411 | ||
412 | /// Try to recover the more general form `intersect ::= $pat_lhs @ $pat_rhs`. | |
413 | /// | |
414 | /// Allowed binding patterns generated by `binding ::= ref? mut? $ident @ $pat_rhs` | |
415 | /// should already have been parsed by now at this point, | |
416 | /// if the next token is `@` then we can try to parse the more general form. | |
417 | /// | |
418 | /// Consult `parse_pat_ident` for the `binding` grammar. | |
419 | /// | |
420 | /// The notion of intersection patterns are found in | |
421 | /// e.g. [F#][and] where they are called AND-patterns. | |
422 | /// | |
423 | /// [and]: https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/pattern-matching | |
424 | fn recover_intersection_pat(&mut self, lhs: P<Pat>) -> PResult<'a, P<Pat>> { | |
425 | if self.token.kind != token::At { | |
426 | // Next token is not `@` so it's not going to be an intersection pattern. | |
427 | return Ok(lhs); | |
428 | } | |
429 | ||
430 | // At this point we attempt to parse `@ $pat_rhs` and emit an error. | |
431 | self.bump(); // `@` | |
432 | let mut rhs = self.parse_pat(None)?; | |
433 | let sp = lhs.span.to(rhs.span); | |
434 | ||
435 | if let PatKind::Ident(_, _, ref mut sub @ None) = rhs.kind { | |
436 | // The user inverted the order, so help them fix that. | |
437 | let mut applicability = Applicability::MachineApplicable; | |
438 | // FIXME(bindings_after_at): Remove this code when stabilizing the feature. | |
439 | lhs.walk(&mut |p| match p.kind { | |
440 | // `check_match` is unhappy if the subpattern has a binding anywhere. | |
441 | PatKind::Ident(..) => { | |
442 | applicability = Applicability::MaybeIncorrect; | |
443 | false // Short-circuit. | |
444 | } | |
445 | _ => true, | |
446 | }); | |
447 | ||
448 | let lhs_span = lhs.span; | |
449 | // Move the LHS into the RHS as a subpattern. | |
450 | // The RHS is now the full pattern. | |
451 | *sub = Some(lhs); | |
452 | ||
453 | self.struct_span_err(sp, "pattern on wrong side of `@`") | |
454 | .span_label(lhs_span, "pattern on the left, should be on the right") | |
455 | .span_label(rhs.span, "binding on the right, should be on the left") | |
456 | .span_suggestion(sp, "switch the order", pprust::pat_to_string(&rhs), applicability) | |
457 | .emit(); | |
458 | } else { | |
459 | // The special case above doesn't apply so we may have e.g. `A(x) @ B(y)`. | |
460 | rhs.kind = PatKind::Wild; | |
461 | self.struct_span_err(sp, "left-hand side of `@` must be a binding") | |
462 | .span_label(lhs.span, "interpreted as a pattern, not a binding") | |
463 | .span_label(rhs.span, "also a pattern") | |
464 | .note("bindings are `x`, `mut x`, `ref x`, and `ref mut x`") | |
465 | .emit(); | |
466 | } | |
467 | ||
468 | rhs.span = sp; | |
469 | Ok(rhs) | |
470 | } | |
471 | ||
472 | /// Ban a range pattern if it has an ambiguous interpretation. | |
473 | fn ban_pat_range_if_ambiguous(&self, pat: &Pat) { | |
474 | match pat.kind { | |
475 | PatKind::Range( | |
476 | .., | |
477 | Spanned { node: RangeEnd::Included(RangeSyntax::DotDotDot), .. }, | |
478 | ) => return, | |
479 | PatKind::Range(..) => {} | |
480 | _ => return, | |
481 | } | |
482 | ||
483 | self.struct_span_err(pat.span, "the range pattern here has ambiguous interpretation") | |
484 | .span_suggestion( | |
485 | pat.span, | |
486 | "add parentheses to clarify the precedence", | |
487 | format!("({})", pprust::pat_to_string(&pat)), | |
488 | // "ambiguous interpretation" implies that we have to be guessing | |
489 | Applicability::MaybeIncorrect, | |
490 | ) | |
491 | .emit(); | |
492 | } | |
493 | ||
494 | /// Parse `&pat` / `&mut pat`. | |
495 | fn parse_pat_deref(&mut self, expected: Expected) -> PResult<'a, PatKind> { | |
496 | self.expect_and()?; | |
497 | self.recover_lifetime_in_deref_pat(); | |
498 | let mutbl = self.parse_mutability(); | |
499 | let subpat = self.parse_pat_with_range_pat(false, expected)?; | |
500 | Ok(PatKind::Ref(subpat, mutbl)) | |
501 | } | |
502 | ||
503 | fn recover_lifetime_in_deref_pat(&mut self) { | |
504 | if let token::Lifetime(name) = self.token.kind { | |
505 | self.bump(); // `'a` | |
506 | ||
507 | let span = self.prev_token.span; | |
508 | self.struct_span_err(span, &format!("unexpected lifetime `{}` in pattern", name)) | |
509 | .span_suggestion( | |
510 | span, | |
511 | "remove the lifetime", | |
512 | String::new(), | |
513 | Applicability::MachineApplicable, | |
514 | ) | |
515 | .emit(); | |
516 | } | |
517 | } | |
518 | ||
519 | /// Parse a tuple or parenthesis pattern. | |
520 | fn parse_pat_tuple_or_parens(&mut self) -> PResult<'a, PatKind> { | |
521 | let (fields, trailing_comma) = | |
522 | self.parse_paren_comma_seq(|p| p.parse_pat_with_or_inner())?; | |
523 | ||
524 | // Here, `(pat,)` is a tuple pattern. | |
525 | // For backward compatibility, `(..)` is a tuple pattern as well. | |
526 | Ok(if fields.len() == 1 && !(trailing_comma || fields[0].is_rest()) { | |
527 | PatKind::Paren(fields.into_iter().next().unwrap()) | |
528 | } else { | |
529 | PatKind::Tuple(fields) | |
530 | }) | |
531 | } | |
532 | ||
533 | /// Parse a mutable binding with the `mut` token already eaten. | |
534 | fn parse_pat_ident_mut(&mut self) -> PResult<'a, PatKind> { | |
535 | let mut_span = self.prev_token.span; | |
536 | ||
537 | if self.eat_keyword(kw::Ref) { | |
538 | return self.recover_mut_ref_ident(mut_span); | |
539 | } | |
540 | ||
541 | self.recover_additional_muts(); | |
542 | ||
543 | // Make sure we don't allow e.g. `let mut $p;` where `$p:pat`. | |
544 | if let token::Interpolated(ref nt) = self.token.kind { | |
545 | if let token::NtPat(_) = **nt { | |
546 | self.expected_ident_found().emit(); | |
547 | } | |
548 | } | |
549 | ||
550 | // Parse the pattern we hope to be an identifier. | |
551 | let mut pat = self.parse_pat(Some("identifier"))?; | |
552 | ||
553 | // If we don't have `mut $ident (@ pat)?`, error. | |
554 | if let PatKind::Ident(BindingMode::ByValue(m @ Mutability::Not), ..) = &mut pat.kind { | |
555 | // Don't recurse into the subpattern. | |
556 | // `mut` on the outer binding doesn't affect the inner bindings. | |
557 | *m = Mutability::Mut; | |
558 | } else { | |
559 | // Add `mut` to any binding in the parsed pattern. | |
560 | let changed_any_binding = Self::make_all_value_bindings_mutable(&mut pat); | |
561 | self.ban_mut_general_pat(mut_span, &pat, changed_any_binding); | |
562 | } | |
563 | ||
564 | Ok(pat.into_inner().kind) | |
565 | } | |
566 | ||
567 | /// Recover on `mut ref? ident @ pat` and suggest | |
568 | /// that the order of `mut` and `ref` is incorrect. | |
569 | fn recover_mut_ref_ident(&mut self, lo: Span) -> PResult<'a, PatKind> { | |
570 | let mutref_span = lo.to(self.prev_token.span); | |
571 | self.struct_span_err(mutref_span, "the order of `mut` and `ref` is incorrect") | |
572 | .span_suggestion( | |
573 | mutref_span, | |
574 | "try switching the order", | |
575 | "ref mut".into(), | |
576 | Applicability::MachineApplicable, | |
577 | ) | |
578 | .emit(); | |
579 | ||
580 | self.parse_pat_ident(BindingMode::ByRef(Mutability::Mut)) | |
581 | } | |
582 | ||
583 | /// Turn all by-value immutable bindings in a pattern into mutable bindings. | |
584 | /// Returns `true` if any change was made. | |
585 | fn make_all_value_bindings_mutable(pat: &mut P<Pat>) -> bool { | |
586 | struct AddMut(bool); | |
587 | impl MutVisitor for AddMut { | |
588 | fn visit_pat(&mut self, pat: &mut P<Pat>) { | |
589 | if let PatKind::Ident(BindingMode::ByValue(m @ Mutability::Not), ..) = &mut pat.kind | |
590 | { | |
591 | self.0 = true; | |
592 | *m = Mutability::Mut; | |
593 | } | |
594 | noop_visit_pat(pat, self); | |
595 | } | |
596 | } | |
597 | ||
598 | let mut add_mut = AddMut(false); | |
599 | add_mut.visit_pat(pat); | |
600 | add_mut.0 | |
601 | } | |
602 | ||
603 | /// Error on `mut $pat` where `$pat` is not an ident. | |
604 | fn ban_mut_general_pat(&self, lo: Span, pat: &Pat, changed_any_binding: bool) { | |
605 | let span = lo.to(pat.span); | |
606 | let fix = pprust::pat_to_string(&pat); | |
607 | let (problem, suggestion) = if changed_any_binding { | |
608 | ("`mut` must be attached to each individual binding", "add `mut` to each binding") | |
609 | } else { | |
610 | ("`mut` must be followed by a named binding", "remove the `mut` prefix") | |
611 | }; | |
612 | self.struct_span_err(span, problem) | |
613 | .span_suggestion(span, suggestion, fix, Applicability::MachineApplicable) | |
614 | .note("`mut` may be followed by `variable` and `variable @ pattern`") | |
615 | .emit(); | |
616 | } | |
617 | ||
618 | /// Eat any extraneous `mut`s and error + recover if we ate any. | |
619 | fn recover_additional_muts(&mut self) { | |
620 | let lo = self.token.span; | |
621 | while self.eat_keyword(kw::Mut) {} | |
622 | if lo == self.token.span { | |
623 | return; | |
624 | } | |
625 | ||
626 | let span = lo.to(self.prev_token.span); | |
627 | self.struct_span_err(span, "`mut` on a binding may not be repeated") | |
628 | .span_suggestion( | |
629 | span, | |
630 | "remove the additional `mut`s", | |
631 | String::new(), | |
632 | Applicability::MachineApplicable, | |
633 | ) | |
634 | .emit(); | |
635 | } | |
636 | ||
637 | /// Parse macro invocation | |
638 | fn parse_pat_mac_invoc(&mut self, path: Path) -> PResult<'a, PatKind> { | |
639 | self.bump(); | |
640 | let args = self.parse_mac_args()?; | |
641 | let mac = MacCall { path, args, prior_type_ascription: self.last_type_ascription }; | |
642 | Ok(PatKind::MacCall(mac)) | |
643 | } | |
644 | ||
645 | fn fatal_unexpected_non_pat( | |
646 | &mut self, | |
647 | mut err: DiagnosticBuilder<'a>, | |
648 | expected: Expected, | |
649 | ) -> PResult<'a, P<Pat>> { | |
650 | err.cancel(); | |
651 | ||
652 | let expected = expected.unwrap_or("pattern"); | |
653 | let msg = format!("expected {}, found {}", expected, super::token_descr(&self.token)); | |
654 | ||
655 | let mut err = self.struct_span_err(self.token.span, &msg); | |
656 | err.span_label(self.token.span, format!("expected {}", expected)); | |
657 | ||
658 | let sp = self.sess.source_map().start_point(self.token.span); | |
659 | if let Some(sp) = self.sess.ambiguous_block_expr_parse.borrow().get(&sp) { | |
660 | self.sess.expr_parentheses_needed(&mut err, *sp, None); | |
661 | } | |
662 | ||
663 | Err(err) | |
664 | } | |
665 | ||
666 | /// Parses the range pattern end form `".." | "..." | "..=" ;`. | |
667 | fn parse_range_end(&mut self) -> Option<Spanned<RangeEnd>> { | |
668 | let re = if self.eat(&token::DotDotDot) { | |
669 | RangeEnd::Included(RangeSyntax::DotDotDot) | |
670 | } else if self.eat(&token::DotDotEq) { | |
671 | RangeEnd::Included(RangeSyntax::DotDotEq) | |
672 | } else if self.eat(&token::DotDot) { | |
673 | self.sess.gated_spans.gate(sym::exclusive_range_pattern, self.prev_token.span); | |
674 | RangeEnd::Excluded | |
675 | } else { | |
676 | return None; | |
677 | }; | |
678 | Some(respan(self.prev_token.span, re)) | |
679 | } | |
680 | ||
681 | /// Parse a range pattern `$begin $form $end?` where `$form = ".." | "..." | "..=" ;`. | |
682 | /// `$begin $form` has already been parsed. | |
683 | fn parse_pat_range_begin_with( | |
684 | &mut self, | |
685 | begin: P<Expr>, | |
686 | re: Spanned<RangeEnd>, | |
687 | ) -> PResult<'a, PatKind> { | |
688 | let end = if self.is_pat_range_end_start(0) { | |
689 | // Parsing e.g. `X..=Y`. | |
690 | Some(self.parse_pat_range_end()?) | |
691 | } else { | |
692 | // Parsing e.g. `X..`. | |
693 | self.sess.gated_spans.gate(sym::half_open_range_patterns, begin.span.to(re.span)); | |
694 | if let RangeEnd::Included(_) = re.node { | |
695 | // FIXME(Centril): Consider semantic errors instead in `ast_validation`. | |
696 | // Possibly also do this for `X..=` in *expression* contexts. | |
697 | self.error_inclusive_range_with_no_end(re.span); | |
698 | } | |
699 | None | |
700 | }; | |
701 | Ok(PatKind::Range(Some(begin), end, re)) | |
702 | } | |
703 | ||
704 | pub(super) fn error_inclusive_range_with_no_end(&self, span: Span) { | |
705 | struct_span_err!(self.sess.span_diagnostic, span, E0586, "inclusive range with no end") | |
706 | .span_suggestion_short( | |
707 | span, | |
708 | "use `..` instead", | |
709 | "..".to_string(), | |
710 | Applicability::MachineApplicable, | |
711 | ) | |
712 | .note("inclusive ranges must be bounded at the end (`..=b` or `a..=b`)") | |
713 | .emit(); | |
714 | } | |
715 | ||
716 | /// Parse a range-to pattern, `..X` or `..=X` where `X` remains to be parsed. | |
717 | /// | |
718 | /// The form `...X` is prohibited to reduce confusion with the potential | |
719 | /// expression syntax `...expr` for splatting in expressions. | |
720 | fn parse_pat_range_to(&mut self, mut re: Spanned<RangeEnd>) -> PResult<'a, PatKind> { | |
721 | let end = self.parse_pat_range_end()?; | |
722 | self.sess.gated_spans.gate(sym::half_open_range_patterns, re.span.to(self.prev_token.span)); | |
723 | if let RangeEnd::Included(ref mut syn @ RangeSyntax::DotDotDot) = &mut re.node { | |
724 | *syn = RangeSyntax::DotDotEq; | |
725 | self.struct_span_err(re.span, "range-to patterns with `...` are not allowed") | |
726 | .span_suggestion_short( | |
727 | re.span, | |
728 | "use `..=` instead", | |
729 | "..=".to_string(), | |
730 | Applicability::MachineApplicable, | |
731 | ) | |
732 | .emit(); | |
733 | } | |
734 | Ok(PatKind::Range(None, Some(end), re)) | |
735 | } | |
736 | ||
737 | /// Is the token `dist` away from the current suitable as the start of a range patterns end? | |
738 | fn is_pat_range_end_start(&self, dist: usize) -> bool { | |
739 | self.check_inline_const(dist) | |
740 | || self.look_ahead(dist, |t| { | |
741 | t.is_path_start() // e.g. `MY_CONST`; | |
742 | || t.kind == token::Dot // e.g. `.5` for recovery; | |
743 | || t.can_begin_literal_maybe_minus() // e.g. `42`. | |
744 | || t.is_whole_expr() | |
745 | }) | |
746 | } | |
747 | ||
748 | fn parse_pat_range_end(&mut self) -> PResult<'a, P<Expr>> { | |
749 | if self.check_inline_const(0) { | |
750 | self.parse_const_block(self.token.span) | |
751 | } else if self.check_path() { | |
752 | let lo = self.token.span; | |
753 | let (qself, path) = if self.eat_lt() { | |
754 | // Parse a qualified path | |
755 | let (qself, path) = self.parse_qpath(PathStyle::Expr)?; | |
756 | (Some(qself), path) | |
757 | } else { | |
758 | // Parse an unqualified path | |
759 | (None, self.parse_path(PathStyle::Expr)?) | |
760 | }; | |
761 | let hi = self.prev_token.span; | |
762 | Ok(self.mk_expr(lo.to(hi), ExprKind::Path(qself, path), AttrVec::new())) | |
763 | } else { | |
764 | self.parse_literal_maybe_minus() | |
765 | } | |
766 | } | |
767 | ||
768 | /// Is this the start of a pattern beginning with a path? | |
769 | fn is_start_of_pat_with_path(&mut self) -> bool { | |
770 | self.check_path() | |
771 | // Just for recovery (see `can_be_ident`). | |
772 | || self.token.is_ident() && !self.token.is_bool_lit() && !self.token.is_keyword(kw::In) | |
773 | } | |
774 | ||
775 | /// Would `parse_pat_ident` be appropriate here? | |
776 | fn can_be_ident_pat(&mut self) -> bool { | |
777 | self.check_ident() | |
778 | && !self.token.is_bool_lit() // Avoid `true` or `false` as a binding as it is a literal. | |
779 | && !self.token.is_path_segment_keyword() // Avoid e.g. `Self` as it is a path. | |
780 | // Avoid `in`. Due to recovery in the list parser this messes with `for ( $pat in $expr )`. | |
781 | && !self.token.is_keyword(kw::In) | |
782 | // Try to do something more complex? | |
783 | && self.look_ahead(1, |t| !matches!(t.kind, token::OpenDelim(token::Paren) // A tuple struct pattern. | |
784 | | token::OpenDelim(token::Brace) // A struct pattern. | |
785 | | token::DotDotDot | token::DotDotEq | token::DotDot // A range pattern. | |
786 | | token::ModSep // A tuple / struct variant pattern. | |
787 | | token::Not)) // A macro expanding to a pattern. | |
788 | } | |
789 | ||
790 | /// Parses `ident` or `ident @ pat`. | |
791 | /// Used by the copy foo and ref foo patterns to give a good | |
792 | /// error message when parsing mistakes like `ref foo(a, b)`. | |
793 | fn parse_pat_ident(&mut self, binding_mode: BindingMode) -> PResult<'a, PatKind> { | |
794 | let ident = self.parse_ident()?; | |
795 | let sub = if self.eat(&token::At) { | |
796 | Some(self.parse_pat(Some("binding pattern"))?) | |
797 | } else { | |
798 | None | |
799 | }; | |
800 | ||
801 | // Just to be friendly, if they write something like `ref Some(i)`, | |
802 | // we end up here with `(` as the current token. | |
803 | // This shortly leads to a parse error. Note that if there is no explicit | |
804 | // binding mode then we do not end up here, because the lookahead | |
805 | // will direct us over to `parse_enum_variant()`. | |
806 | if self.token == token::OpenDelim(token::Paren) { | |
807 | return Err(self | |
808 | .struct_span_err(self.prev_token.span, "expected identifier, found enum pattern")); | |
809 | } | |
810 | ||
811 | Ok(PatKind::Ident(binding_mode, ident, sub)) | |
812 | } | |
813 | ||
814 | /// Parse a struct ("record") pattern (e.g. `Foo { ... }` or `Foo::Bar { ... }`). | |
815 | fn parse_pat_struct(&mut self, qself: Option<QSelf>, path: Path) -> PResult<'a, PatKind> { | |
816 | if qself.is_some() { | |
817 | return self.error_qpath_before_pat(&path, "{"); | |
818 | } | |
819 | self.bump(); | |
820 | let (fields, etc) = self.parse_pat_fields().unwrap_or_else(|mut e| { | |
821 | e.span_label(path.span, "while parsing the fields for this pattern"); | |
822 | e.emit(); | |
823 | self.recover_stmt(); | |
824 | (vec![], true) | |
825 | }); | |
826 | self.bump(); | |
827 | Ok(PatKind::Struct(path, fields, etc)) | |
828 | } | |
829 | ||
830 | /// Parse tuple struct or tuple variant pattern (e.g. `Foo(...)` or `Foo::Bar(...)`). | |
831 | fn parse_pat_tuple_struct(&mut self, qself: Option<QSelf>, path: Path) -> PResult<'a, PatKind> { | |
832 | if qself.is_some() { | |
833 | return self.error_qpath_before_pat(&path, "("); | |
834 | } | |
835 | let (fields, _) = self.parse_paren_comma_seq(|p| p.parse_pat_with_or_inner())?; | |
836 | Ok(PatKind::TupleStruct(path, fields)) | |
837 | } | |
838 | ||
839 | /// Error when there's a qualified path, e.g. `<Foo as Bar>::Baz` | |
840 | /// as the path of e.g., a tuple or record struct pattern. | |
841 | fn error_qpath_before_pat(&mut self, path: &Path, token: &str) -> PResult<'a, PatKind> { | |
842 | let msg = &format!("unexpected `{}` after qualified path", token); | |
843 | let mut err = self.struct_span_err(self.token.span, msg); | |
844 | err.span_label(self.token.span, msg); | |
845 | err.span_label(path.span, "the qualified path"); | |
846 | Err(err) | |
847 | } | |
848 | ||
849 | /// Parses the fields of a struct-like pattern. | |
850 | fn parse_pat_fields(&mut self) -> PResult<'a, (Vec<FieldPat>, bool)> { | |
851 | let mut fields = Vec::new(); | |
852 | let mut etc = false; | |
853 | let mut ate_comma = true; | |
854 | let mut delayed_err: Option<DiagnosticBuilder<'a>> = None; | |
855 | let mut etc_span = None; | |
856 | ||
857 | while self.token != token::CloseDelim(token::Brace) { | |
858 | let attrs = match self.parse_outer_attributes() { | |
859 | Ok(attrs) => attrs, | |
860 | Err(err) => { | |
861 | if let Some(mut delayed) = delayed_err { | |
862 | delayed.emit(); | |
863 | } | |
864 | return Err(err); | |
865 | } | |
866 | }; | |
867 | let lo = self.token.span; | |
868 | ||
869 | // check that a comma comes after every field | |
870 | if !ate_comma { | |
871 | let err = self.struct_span_err(self.token.span, "expected `,`"); | |
872 | if let Some(mut delayed) = delayed_err { | |
873 | delayed.emit(); | |
874 | } | |
875 | return Err(err); | |
876 | } | |
877 | ate_comma = false; | |
878 | ||
879 | if self.check(&token::DotDot) || self.token == token::DotDotDot { | |
880 | etc = true; | |
881 | let mut etc_sp = self.token.span; | |
882 | ||
883 | self.recover_one_fewer_dotdot(); | |
884 | self.bump(); // `..` || `...` | |
885 | ||
886 | if self.token == token::CloseDelim(token::Brace) { | |
887 | etc_span = Some(etc_sp); | |
888 | break; | |
889 | } | |
890 | let token_str = super::token_descr(&self.token); | |
891 | let msg = &format!("expected `}}`, found {}", token_str); | |
892 | let mut err = self.struct_span_err(self.token.span, msg); | |
893 | ||
894 | err.span_label(self.token.span, "expected `}`"); | |
895 | let mut comma_sp = None; | |
896 | if self.token == token::Comma { | |
897 | // Issue #49257 | |
898 | let nw_span = self.sess.source_map().span_until_non_whitespace(self.token.span); | |
899 | etc_sp = etc_sp.to(nw_span); | |
900 | err.span_label( | |
901 | etc_sp, | |
902 | "`..` must be at the end and cannot have a trailing comma", | |
903 | ); | |
904 | comma_sp = Some(self.token.span); | |
905 | self.bump(); | |
906 | ate_comma = true; | |
907 | } | |
908 | ||
909 | etc_span = Some(etc_sp.until(self.token.span)); | |
910 | if self.token == token::CloseDelim(token::Brace) { | |
911 | // If the struct looks otherwise well formed, recover and continue. | |
912 | if let Some(sp) = comma_sp { | |
913 | err.span_suggestion_short( | |
914 | sp, | |
915 | "remove this comma", | |
916 | String::new(), | |
917 | Applicability::MachineApplicable, | |
918 | ); | |
919 | } | |
920 | err.emit(); | |
921 | break; | |
922 | } else if self.token.is_ident() && ate_comma { | |
923 | // Accept fields coming after `..,`. | |
924 | // This way we avoid "pattern missing fields" errors afterwards. | |
925 | // We delay this error until the end in order to have a span for a | |
926 | // suggested fix. | |
927 | if let Some(mut delayed_err) = delayed_err { | |
928 | delayed_err.emit(); | |
929 | return Err(err); | |
930 | } else { | |
931 | delayed_err = Some(err); | |
932 | } | |
933 | } else { | |
934 | if let Some(mut err) = delayed_err { | |
935 | err.emit(); | |
936 | } | |
937 | return Err(err); | |
938 | } | |
939 | } | |
940 | ||
941 | fields.push(match self.parse_pat_field(lo, attrs) { | |
942 | Ok(field) => field, | |
943 | Err(err) => { | |
944 | if let Some(mut delayed_err) = delayed_err { | |
945 | delayed_err.emit(); | |
946 | } | |
947 | return Err(err); | |
948 | } | |
949 | }); | |
950 | ate_comma = self.eat(&token::Comma); | |
951 | } | |
952 | ||
953 | if let Some(mut err) = delayed_err { | |
954 | if let Some(etc_span) = etc_span { | |
955 | err.multipart_suggestion( | |
956 | "move the `..` to the end of the field list", | |
957 | vec![ | |
958 | (etc_span, String::new()), | |
959 | (self.token.span, format!("{}.. }}", if ate_comma { "" } else { ", " })), | |
960 | ], | |
961 | Applicability::MachineApplicable, | |
962 | ); | |
963 | } | |
964 | err.emit(); | |
965 | } | |
966 | Ok((fields, etc)) | |
967 | } | |
968 | ||
969 | /// Recover on `...` as if it were `..` to avoid further errors. | |
970 | /// See issue #46718. | |
971 | fn recover_one_fewer_dotdot(&self) { | |
972 | if self.token != token::DotDotDot { | |
973 | return; | |
974 | } | |
975 | ||
976 | self.struct_span_err(self.token.span, "expected field pattern, found `...`") | |
977 | .span_suggestion( | |
978 | self.token.span, | |
979 | "to omit remaining fields, use one fewer `.`", | |
980 | "..".to_owned(), | |
981 | Applicability::MachineApplicable, | |
982 | ) | |
983 | .emit(); | |
984 | } | |
985 | ||
986 | fn parse_pat_field(&mut self, lo: Span, attrs: Vec<Attribute>) -> PResult<'a, FieldPat> { | |
987 | // Check if a colon exists one ahead. This means we're parsing a fieldname. | |
988 | let hi; | |
989 | let (subpat, fieldname, is_shorthand) = if self.look_ahead(1, |t| t == &token::Colon) { | |
990 | // Parsing a pattern of the form `fieldname: pat`. | |
991 | let fieldname = self.parse_field_name()?; | |
992 | self.bump(); | |
993 | let pat = self.parse_pat_with_or_inner()?; | |
994 | hi = pat.span; | |
995 | (pat, fieldname, false) | |
996 | } else { | |
997 | // Parsing a pattern of the form `(box) (ref) (mut) fieldname`. | |
998 | let is_box = self.eat_keyword(kw::Box); | |
999 | let boxed_span = self.token.span; | |
1000 | let is_ref = self.eat_keyword(kw::Ref); | |
1001 | let is_mut = self.eat_keyword(kw::Mut); | |
1002 | let fieldname = self.parse_ident()?; | |
1003 | hi = self.prev_token.span; | |
1004 | ||
1005 | let bind_type = match (is_ref, is_mut) { | |
1006 | (true, true) => BindingMode::ByRef(Mutability::Mut), | |
1007 | (true, false) => BindingMode::ByRef(Mutability::Not), | |
1008 | (false, true) => BindingMode::ByValue(Mutability::Mut), | |
1009 | (false, false) => BindingMode::ByValue(Mutability::Not), | |
1010 | }; | |
1011 | ||
1012 | let fieldpat = self.mk_pat_ident(boxed_span.to(hi), bind_type, fieldname); | |
1013 | let subpat = | |
1014 | if is_box { self.mk_pat(lo.to(hi), PatKind::Box(fieldpat)) } else { fieldpat }; | |
1015 | (subpat, fieldname, true) | |
1016 | }; | |
1017 | ||
1018 | Ok(FieldPat { | |
1019 | ident: fieldname, | |
1020 | pat: subpat, | |
1021 | is_shorthand, | |
1022 | attrs: attrs.into(), | |
1023 | id: ast::DUMMY_NODE_ID, | |
1024 | span: lo.to(hi), | |
1025 | is_placeholder: false, | |
1026 | }) | |
1027 | } | |
1028 | ||
1029 | pub(super) fn mk_pat_ident(&self, span: Span, bm: BindingMode, ident: Ident) -> P<Pat> { | |
1030 | self.mk_pat(span, PatKind::Ident(bm, ident, None)) | |
1031 | } | |
1032 | ||
1033 | fn mk_pat(&self, span: Span, kind: PatKind) -> P<Pat> { | |
1034 | P(Pat { kind, span, id: ast::DUMMY_NODE_ID, tokens: None }) | |
1035 | } | |
1036 | } |