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