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