rustc_parse/parser/
pat.rs

1use std::ops::Bound;
2
3use rustc_ast::mut_visit::{self, MutVisitor};
4use rustc_ast::ptr::P;
5use rustc_ast::token::NtPatKind::*;
6use rustc_ast::token::{self, IdentIsRaw, MetaVarKind, Token};
7use rustc_ast::util::parser::ExprPrecedence;
8use rustc_ast::visit::{self, Visitor};
9use rustc_ast::{
10    self as ast, Arm, AttrVec, BindingMode, ByRef, Expr, ExprKind, LocalKind, MacCall, Mutability,
11    Pat, PatField, PatFieldsRest, PatKind, Path, QSelf, RangeEnd, RangeSyntax, Stmt, StmtKind,
12};
13use rustc_ast_pretty::pprust;
14use rustc_errors::{Applicability, Diag, DiagArgValue, PResult, StashKey};
15use rustc_session::errors::ExprParenthesesNeeded;
16use rustc_span::source_map::{Spanned, respan};
17use rustc_span::{BytePos, ErrorGuaranteed, Ident, Span, kw, sym};
18use thin_vec::{ThinVec, thin_vec};
19
20use super::{ForceCollect, Parser, PathStyle, Restrictions, Trailing, UsePreAttrPos};
21use crate::errors::{
22    self, AmbiguousRangePattern, AtDotDotInStructPattern, AtInStructPattern,
23    DotDotDotForRemainingFields, DotDotDotRangeToPatternNotAllowed, DotDotDotRestPattern,
24    EnumPatternInsteadOfIdentifier, ExpectedBindingLeftOfAt, ExpectedCommaAfterPatternField,
25    GenericArgsInPatRequireTurbofishSyntax, InclusiveRangeExtraEquals, InclusiveRangeMatchArrow,
26    InclusiveRangeNoEnd, InvalidMutInPattern, ParenRangeSuggestion, PatternOnWrongSideOfAt,
27    RemoveLet, RepeatedMutInPattern, SwitchRefBoxOrder, TopLevelOrPatternNotAllowed,
28    TopLevelOrPatternNotAllowedSugg, TrailingVertNotAllowed, UnexpectedExpressionInPattern,
29    UnexpectedExpressionInPatternSugg, UnexpectedLifetimeInPattern, UnexpectedParenInRangePat,
30    UnexpectedParenInRangePatSugg, UnexpectedVertVertBeforeFunctionParam,
31    UnexpectedVertVertInPattern, WrapInParens,
32};
33use crate::parser::expr::{DestructuredFloat, could_be_unclosed_char_literal};
34use crate::{exp, maybe_recover_from_interpolated_ty_qpath};
35
36#[derive(PartialEq, Copy, Clone)]
37pub enum Expected {
38    ParameterName,
39    ArgumentName,
40    Identifier,
41    BindingPattern,
42}
43
44impl Expected {
45    // FIXME(#100717): migrate users of this to proper localization
46    fn to_string_or_fallback(expected: Option<Expected>) -> &'static str {
47        match expected {
48            Some(Expected::ParameterName) => "parameter name",
49            Some(Expected::ArgumentName) => "argument name",
50            Some(Expected::Identifier) => "identifier",
51            Some(Expected::BindingPattern) => "binding pattern",
52            None => "pattern",
53        }
54    }
55}
56
57const WHILE_PARSING_OR_MSG: &str = "while parsing this or-pattern starting here";
58
59/// Whether or not to recover a `,` when parsing or-patterns.
60#[derive(PartialEq, Copy, Clone)]
61pub enum RecoverComma {
62    Yes,
63    No,
64}
65
66/// Whether or not to recover a `:` when parsing patterns that were meant to be paths.
67#[derive(PartialEq, Copy, Clone)]
68pub enum RecoverColon {
69    Yes,
70    No,
71}
72
73/// Whether or not to recover a `a, b` when parsing patterns as `(a, b)` or that *and* `a | b`.
74#[derive(PartialEq, Copy, Clone)]
75pub enum CommaRecoveryMode {
76    LikelyTuple,
77    EitherTupleOrPipe,
78}
79
80/// The result of `eat_or_separator`. We want to distinguish which case we are in to avoid
81/// emitting duplicate diagnostics.
82#[derive(Debug, Clone, Copy)]
83enum EatOrResult {
84    /// We recovered from a trailing vert.
85    TrailingVert,
86    /// We ate an `|` (or `||` and recovered).
87    AteOr,
88    /// We did not eat anything (i.e. the current token is not `|` or `||`).
89    None,
90}
91
92/// The syntax location of a given pattern. Used for diagnostics.
93#[derive(Clone, Copy)]
94pub enum PatternLocation {
95    LetBinding,
96    FunctionParameter,
97}
98
99impl<'a> Parser<'a> {
100    /// Parses a pattern.
101    ///
102    /// Corresponds to `Pattern` in RFC 3637 and admits guard patterns at the top level.
103    /// Used when parsing patterns in all cases where neither `PatternNoTopGuard` nor
104    /// `PatternNoTopAlt` (see below) are used.
105    pub fn parse_pat_allow_top_guard(
106        &mut self,
107        expected: Option<Expected>,
108        rc: RecoverComma,
109        ra: RecoverColon,
110        rt: CommaRecoveryMode,
111    ) -> PResult<'a, P<Pat>> {
112        let pat = self.parse_pat_no_top_guard(expected, rc, ra, rt)?;
113
114        if self.eat_keyword(exp!(If)) {
115            let cond = self.parse_expr()?;
116            // Feature-gate guard patterns
117            self.psess.gated_spans.gate(sym::guard_patterns, cond.span);
118            let span = pat.span.to(cond.span);
119            Ok(self.mk_pat(span, PatKind::Guard(pat, cond)))
120        } else {
121            Ok(pat)
122        }
123    }
124
125    /// Parses a pattern.
126    ///
127    /// Corresponds to `PatternNoTopAlt` in RFC 3637 and does not admit or-patterns
128    /// or guard patterns at the top level. Used when parsing the parameters of lambda
129    /// expressions, functions, function pointers, and `pat_param` macro fragments.
130    pub fn parse_pat_no_top_alt(
131        &mut self,
132        expected: Option<Expected>,
133        syntax_loc: Option<PatternLocation>,
134    ) -> PResult<'a, P<Pat>> {
135        self.parse_pat_with_range_pat(true, expected, syntax_loc)
136    }
137
138    /// Parses a pattern.
139    ///
140    /// Corresponds to `PatternNoTopGuard` in RFC 3637 and allows or-patterns, but not
141    /// guard patterns, at the top level. Used for parsing patterns in `pat` fragments (until
142    /// the next edition) and `let`, `if let`, and `while let` expressions.
143    ///
144    /// Note that after the FCP in <https://github.com/rust-lang/rust/issues/81415>,
145    /// a leading vert is allowed in nested or-patterns, too. This allows us to
146    /// simplify the grammar somewhat.
147    pub fn parse_pat_no_top_guard(
148        &mut self,
149        expected: Option<Expected>,
150        rc: RecoverComma,
151        ra: RecoverColon,
152        rt: CommaRecoveryMode,
153    ) -> PResult<'a, P<Pat>> {
154        self.parse_pat_no_top_guard_inner(expected, rc, ra, rt, None).map(|(pat, _)| pat)
155    }
156
157    /// Returns the pattern and a bool indicating whether we recovered from a trailing vert (true =
158    /// recovered).
159    fn parse_pat_no_top_guard_inner(
160        &mut self,
161        expected: Option<Expected>,
162        rc: RecoverComma,
163        ra: RecoverColon,
164        rt: CommaRecoveryMode,
165        syntax_loc: Option<PatternLocation>,
166    ) -> PResult<'a, (P<Pat>, bool)> {
167        // Keep track of whether we recovered from a trailing vert so that we can avoid duplicated
168        // suggestions (which bothers rustfix).
169        //
170        // Allow a '|' before the pats (RFCs 1925, 2530, and 2535).
171        let (leading_vert_span, mut trailing_vert) = match self.eat_or_separator(None) {
172            EatOrResult::AteOr => (Some(self.prev_token.span), false),
173            EatOrResult::TrailingVert => (None, true),
174            EatOrResult::None => (None, false),
175        };
176
177        // Parse the first pattern (`p_0`).
178        let mut first_pat = match self.parse_pat_no_top_alt(expected, syntax_loc) {
179            Ok(pat) => pat,
180            Err(err)
181                if self.token.is_reserved_ident()
182                    && !self.token.is_keyword(kw::In)
183                    && !self.token.is_keyword(kw::If) =>
184            {
185                err.emit();
186                self.bump();
187                self.mk_pat(self.token.span, PatKind::Wild)
188            }
189            Err(err) => return Err(err),
190        };
191        if rc == RecoverComma::Yes && !first_pat.could_be_never_pattern() {
192            self.maybe_recover_unexpected_comma(first_pat.span, rt)?;
193        }
194
195        // If the next token is not a `|`,
196        // this is not an or-pattern and we should exit here.
197        if !self.check(exp!(Or)) && self.token != token::OrOr {
198            // If we parsed a leading `|` which should be gated,
199            // then we should really gate the leading `|`.
200            // This complicated procedure is done purely for diagnostics UX.
201
202            // Check if the user wrote `foo:bar` instead of `foo::bar`.
203            if ra == RecoverColon::Yes {
204                first_pat = self.maybe_recover_colon_colon_in_pat_typo(first_pat, expected);
205            }
206
207            if let Some(leading_vert_span) = leading_vert_span {
208                // If there was a leading vert, treat this as an or-pattern. This improves
209                // diagnostics.
210                let span = leading_vert_span.to(self.prev_token.span);
211                return Ok((self.mk_pat(span, PatKind::Or(thin_vec![first_pat])), trailing_vert));
212            }
213
214            return Ok((first_pat, trailing_vert));
215        }
216
217        // Parse the patterns `p_1 | ... | p_n` where `n > 0`.
218        let lo = leading_vert_span.unwrap_or(first_pat.span);
219        let mut pats = thin_vec![first_pat];
220        loop {
221            match self.eat_or_separator(Some(lo)) {
222                EatOrResult::AteOr => {}
223                EatOrResult::None => break,
224                EatOrResult::TrailingVert => {
225                    trailing_vert = true;
226                    break;
227                }
228            }
229            let pat = self.parse_pat_no_top_alt(expected, syntax_loc).map_err(|mut err| {
230                err.span_label(lo, WHILE_PARSING_OR_MSG);
231                err
232            })?;
233            if rc == RecoverComma::Yes && !pat.could_be_never_pattern() {
234                self.maybe_recover_unexpected_comma(pat.span, rt)?;
235            }
236            pats.push(pat);
237        }
238        let or_pattern_span = lo.to(self.prev_token.span);
239
240        Ok((self.mk_pat(or_pattern_span, PatKind::Or(pats)), trailing_vert))
241    }
242
243    /// Parse a pattern and (maybe) a `Colon` in positions where a pattern may be followed by a
244    /// type annotation (e.g. for `let` bindings or `fn` params).
245    ///
246    /// Generally, this corresponds to `pat_no_top_alt` followed by an optional `Colon`. It will
247    /// eat the `Colon` token if one is present.
248    ///
249    /// The return value represents the parsed pattern and `true` if a `Colon` was parsed (`false`
250    /// otherwise).
251    pub(super) fn parse_pat_before_ty(
252        &mut self,
253        expected: Option<Expected>,
254        rc: RecoverComma,
255        syntax_loc: PatternLocation,
256    ) -> PResult<'a, (P<Pat>, bool)> {
257        // We use `parse_pat_allow_top_alt` regardless of whether we actually want top-level
258        // or-patterns so that we can detect when a user tries to use it. This allows us to print a
259        // better error message.
260        let (pat, trailing_vert) = self.parse_pat_no_top_guard_inner(
261            expected,
262            rc,
263            RecoverColon::No,
264            CommaRecoveryMode::LikelyTuple,
265            Some(syntax_loc),
266        )?;
267        let colon = self.eat(exp!(Colon));
268
269        if let PatKind::Or(pats) = &pat.kind {
270            let span = pat.span;
271            let sub = if pats.len() == 1 {
272                Some(TopLevelOrPatternNotAllowedSugg::RemoveLeadingVert {
273                    span: span.with_hi(span.lo() + BytePos(1)),
274                })
275            } else {
276                Some(TopLevelOrPatternNotAllowedSugg::WrapInParens {
277                    span,
278                    suggestion: WrapInParens { lo: span.shrink_to_lo(), hi: span.shrink_to_hi() },
279                })
280            };
281
282            let err = self.dcx().create_err(match syntax_loc {
283                PatternLocation::LetBinding => {
284                    TopLevelOrPatternNotAllowed::LetBinding { span, sub }
285                }
286                PatternLocation::FunctionParameter => {
287                    TopLevelOrPatternNotAllowed::FunctionParameter { span, sub }
288                }
289            });
290            if trailing_vert {
291                err.delay_as_bug();
292            } else {
293                err.emit();
294            }
295        }
296
297        Ok((pat, colon))
298    }
299
300    /// Parse the pattern for a function or function pointer parameter, followed by a colon.
301    ///
302    /// The return value represents the parsed pattern and `true` if a `Colon` was parsed (`false`
303    /// otherwise).
304    pub(super) fn parse_fn_param_pat_colon(&mut self) -> PResult<'a, (P<Pat>, bool)> {
305        // In order to get good UX, we first recover in the case of a leading vert for an illegal
306        // top-level or-pat. Normally, this means recovering both `|` and `||`, but in this case,
307        // a leading `||` probably doesn't indicate an or-pattern attempt, so we handle that
308        // separately.
309        if let token::OrOr = self.token.kind {
310            self.dcx().emit_err(UnexpectedVertVertBeforeFunctionParam { span: self.token.span });
311            self.bump();
312        }
313
314        self.parse_pat_before_ty(
315            Some(Expected::ParameterName),
316            RecoverComma::No,
317            PatternLocation::FunctionParameter,
318        )
319    }
320
321    /// Eat the or-pattern `|` separator.
322    /// If instead a `||` token is encountered, recover and pretend we parsed `|`.
323    fn eat_or_separator(&mut self, lo: Option<Span>) -> EatOrResult {
324        if self.recover_trailing_vert(lo) {
325            EatOrResult::TrailingVert
326        } else if self.token.kind == token::OrOr {
327            // Found `||`; Recover and pretend we parsed `|`.
328            self.dcx().emit_err(UnexpectedVertVertInPattern { span: self.token.span, start: lo });
329            self.bump();
330            EatOrResult::AteOr
331        } else if self.eat(exp!(Or)) {
332            EatOrResult::AteOr
333        } else {
334            EatOrResult::None
335        }
336    }
337
338    /// Recover if `|` or `||` is the current token and we have one of the
339    /// tokens `=>`, `if`, `=`, `:`, `;`, `,`, `]`, `)`, or `}` ahead of us.
340    ///
341    /// These tokens all indicate that we reached the end of the or-pattern
342    /// list and can now reliably say that the `|` was an illegal trailing vert.
343    /// Note that there are more tokens such as `@` for which we know that the `|`
344    /// is an illegal parse. However, the user's intent is less clear in that case.
345    fn recover_trailing_vert(&mut self, lo: Option<Span>) -> bool {
346        let is_end_ahead = self.look_ahead(1, |token| {
347            matches!(
348                &token.uninterpolate().kind,
349                token::FatArrow // e.g. `a | => 0,`.
350                | token::Ident(kw::If, token::IdentIsRaw::No) // e.g. `a | if expr`.
351                | token::Eq // e.g. `let a | = 0`.
352                | token::Semi // e.g. `let a |;`.
353                | token::Colon // e.g. `let a | :`.
354                | token::Comma // e.g. `let (a |,)`.
355                | token::CloseBracket // e.g. `let [a | ]`.
356                | token::CloseParen // e.g. `let (a | )`.
357                | token::CloseBrace // e.g. `let A { f: a | }`.
358            )
359        });
360        match (is_end_ahead, &self.token.kind) {
361            (true, token::Or | token::OrOr) => {
362                // A `|` or possibly `||` token shouldn't be here. Ban it.
363                self.dcx().emit_err(TrailingVertNotAllowed {
364                    span: self.token.span,
365                    start: lo,
366                    token: self.token,
367                    note_double_vert: self.token.kind == token::OrOr,
368                });
369                self.bump();
370                true
371            }
372            _ => false,
373        }
374    }
375
376    /// Ensures that the last parsed pattern (or pattern range bound) is not followed by an expression.
377    ///
378    /// `is_end_bound` indicates whether the last parsed thing was the end bound of a range pattern (see [`parse_pat_range_end`](Self::parse_pat_range_end))
379    /// in order to say "expected a pattern range bound" instead of "expected a pattern";
380    /// ```text
381    /// 0..=1 + 2
382    ///     ^^^^^
383    /// ```
384    /// Only the end bound is spanned in this case, and this function has no idea if there was a `..=` before `pat_span`, hence the parameter.
385    ///
386    /// This function returns `Some` if a trailing expression was recovered, and said expression's span.
387    #[must_use = "the pattern must be discarded as `PatKind::Err` if this function returns Some"]
388    fn maybe_recover_trailing_expr(
389        &mut self,
390        pat_span: Span,
391        is_end_bound: bool,
392    ) -> Option<(ErrorGuaranteed, Span)> {
393        if self.prev_token.is_keyword(kw::Underscore) || !self.may_recover() {
394            // Don't recover anything after an `_` or if recovery is disabled.
395            return None;
396        }
397
398        // Returns `true` iff `token` is an unsuffixed integer.
399        let is_one_tuple_index = |_: &Self, token: &Token| -> bool {
400            use token::{Lit, LitKind};
401
402            matches!(
403                token.kind,
404                token::Literal(Lit { kind: LitKind::Integer, symbol: _, suffix: None })
405            )
406        };
407
408        // Returns `true` iff `token` is an unsuffixed `x.y` float.
409        let is_two_tuple_indexes = |this: &Self, token: &Token| -> bool {
410            use token::{Lit, LitKind};
411
412            if let token::Literal(Lit { kind: LitKind::Float, symbol, suffix: None }) = token.kind
413                && let DestructuredFloat::MiddleDot(..) = this.break_up_float(symbol, token.span)
414            {
415                true
416            } else {
417                false
418            }
419        };
420
421        // Check for `.hello` or `.0`.
422        let has_dot_expr = self.check_noexpect(&token::Dot) // `.`
423            && self.look_ahead(1, |tok| {
424                tok.is_ident() // `hello`
425                || is_one_tuple_index(&self, &tok) // `0`
426                || is_two_tuple_indexes(&self, &tok) // `0.0`
427            });
428
429        // Check for operators.
430        // `|` is excluded as it is used in pattern alternatives and lambdas,
431        // `?` is included for error propagation,
432        // `[` is included for indexing operations,
433        // `[]` is excluded as `a[]` isn't an expression and should be recovered as `a, []` (cf. `tests/ui/parser/pat-lt-bracket-7.rs`),
434        // `as` is included for type casts
435        let has_trailing_operator = matches!(
436                self.token.kind,
437                token::Plus | token::Minus | token::Star | token::Slash | token::Percent
438                | token::Caret | token::And | token::Shl | token::Shr // excludes `Or`
439            )
440            || self.token == token::Question
441            || (self.token == token::OpenBracket
442                && self.look_ahead(1, |t| *t != token::CloseBracket)) // excludes `[]`
443            || self.token.is_keyword(kw::As);
444
445        if !has_dot_expr && !has_trailing_operator {
446            // Nothing to recover here.
447            return None;
448        }
449
450        // Let's try to parse an expression to emit a better diagnostic.
451        let mut snapshot = self.create_snapshot_for_diagnostic();
452        snapshot.restrictions.insert(Restrictions::IS_PAT);
453
454        // Parse `?`, `.f`, `(arg0, arg1, ...)` or `[expr]` until they've all been eaten.
455        let Ok(expr) = snapshot
456            .parse_expr_dot_or_call_with(
457                AttrVec::new(),
458                self.mk_expr(pat_span, ExprKind::Dummy), // equivalent to transforming the parsed pattern into an `Expr`
459                pat_span,
460            )
461            .map_err(|err| err.cancel())
462        else {
463            // We got a trailing method/operator, but that wasn't an expression.
464            return None;
465        };
466
467        // Parse an associative expression such as `+ expr`, `% expr`, ...
468        // Assignments, ranges and `|` are disabled by [`Restrictions::IS_PAT`].
469        let Ok((expr, _)) = snapshot
470            .parse_expr_assoc_rest_with(Bound::Unbounded, false, expr)
471            .map_err(|err| err.cancel())
472        else {
473            // We got a trailing method/operator, but that wasn't an expression.
474            return None;
475        };
476
477        // We got a valid expression.
478        self.restore_snapshot(snapshot);
479        self.restrictions.remove(Restrictions::IS_PAT);
480
481        let is_bound = is_end_bound
482            // is_start_bound: either `..` or `)..`
483            || self.token.is_range_separator()
484            || self.token == token::CloseParen
485                && self.look_ahead(1, Token::is_range_separator);
486
487        let span = expr.span;
488
489        Some((
490            self.dcx()
491                .create_err(UnexpectedExpressionInPattern {
492                    span,
493                    is_bound,
494                    expr_precedence: expr.precedence(),
495                })
496                .stash(span, StashKey::ExprInPat)
497                .unwrap(),
498            span,
499        ))
500    }
501
502    /// Called by [`Parser::parse_stmt_without_recovery`], used to add statement-aware subdiagnostics to the errors stashed
503    /// by [`Parser::maybe_recover_trailing_expr`].
504    pub(super) fn maybe_augment_stashed_expr_in_pats_with_suggestions(&mut self, stmt: &Stmt) {
505        if self.dcx().has_errors().is_none() {
506            // No need to walk the statement if there's no stashed errors.
507            return;
508        }
509
510        struct PatVisitor<'a> {
511            /// `self`
512            parser: &'a Parser<'a>,
513            /// The freshly-parsed statement.
514            stmt: &'a Stmt,
515            /// The current match arm (for arm guard suggestions).
516            arm: Option<&'a Arm>,
517            /// The current struct field (for variable name suggestions).
518            field: Option<&'a PatField>,
519        }
520
521        impl<'a> PatVisitor<'a> {
522            /// Looks for stashed [`StashKey::ExprInPat`] errors in `stash_span`, and emit them with suggestions.
523            /// `stash_span` is contained in `expr_span`, the latter being larger in borrow patterns;
524            /// ```txt
525            /// &mut x.y
526            /// -----^^^ `stash_span`
527            /// |
528            /// `expr_span`
529            /// ```
530            /// `is_range_bound` is used to exclude arm guard suggestions in range pattern bounds.
531            fn maybe_add_suggestions_then_emit(
532                &self,
533                stash_span: Span,
534                expr_span: Span,
535                is_range_bound: bool,
536            ) {
537                self.parser.dcx().try_steal_modify_and_emit_err(
538                    stash_span,
539                    StashKey::ExprInPat,
540                    |err| {
541                        // Includes pre-pats (e.g. `&mut <err>`) in the diagnostic.
542                        err.span.replace(stash_span, expr_span);
543
544                        let sm = self.parser.psess.source_map();
545                        let stmt = self.stmt;
546                        let line_lo = sm.span_extend_to_line(stmt.span).shrink_to_lo();
547                        let indentation = sm.indentation_before(stmt.span).unwrap_or_default();
548                        let Ok(expr) = self.parser.span_to_snippet(expr_span) else {
549                            // FIXME: some suggestions don't actually need the snippet; see PR #123877's unresolved conversations.
550                            return;
551                        };
552
553                        if let StmtKind::Let(local) = &stmt.kind {
554                            match &local.kind {
555                                LocalKind::Decl | LocalKind::Init(_) => {
556                                    // It's kinda hard to guess what the user intended, so don't make suggestions.
557                                    return;
558                                }
559
560                                LocalKind::InitElse(_, _) => {}
561                            }
562                        }
563
564                        // help: use an arm guard `if val == expr`
565                        // FIXME(guard_patterns): suggest this regardless of a match arm.
566                        if let Some(arm) = &self.arm
567                            && !is_range_bound
568                        {
569                            let (ident, ident_span) = match self.field {
570                                Some(field) => {
571                                    (field.ident.to_string(), field.ident.span.to(expr_span))
572                                }
573                                None => ("val".to_owned(), expr_span),
574                            };
575
576                            // Are parentheses required around `expr`?
577                            // HACK: a neater way would be preferable.
578                            let expr = match &err.args["expr_precedence"] {
579                                DiagArgValue::Number(expr_precedence) => {
580                                    if *expr_precedence <= ExprPrecedence::Compare as i32 {
581                                        format!("({expr})")
582                                    } else {
583                                        format!("{expr}")
584                                    }
585                                }
586                                _ => unreachable!(),
587                            };
588
589                            match &arm.guard {
590                                None => {
591                                    err.subdiagnostic(
592                                        UnexpectedExpressionInPatternSugg::CreateGuard {
593                                            ident_span,
594                                            pat_hi: arm.pat.span.shrink_to_hi(),
595                                            ident,
596                                            expr,
597                                        },
598                                    );
599                                }
600                                Some(guard) => {
601                                    // Are parentheses required around the old guard?
602                                    let wrap_guard = guard.precedence() <= ExprPrecedence::LAnd;
603
604                                    err.subdiagnostic(
605                                        UnexpectedExpressionInPatternSugg::UpdateGuard {
606                                            ident_span,
607                                            guard_lo: if wrap_guard {
608                                                Some(guard.span.shrink_to_lo())
609                                            } else {
610                                                None
611                                            },
612                                            guard_hi: guard.span.shrink_to_hi(),
613                                            guard_hi_paren: if wrap_guard { ")" } else { "" },
614                                            ident,
615                                            expr,
616                                        },
617                                    );
618                                }
619                            }
620                        }
621
622                        // help: extract the expr into a `const VAL: _ = expr`
623                        let ident = match self.field {
624                            Some(field) => field.ident.as_str().to_uppercase(),
625                            None => "VAL".to_owned(),
626                        };
627                        err.subdiagnostic(UnexpectedExpressionInPatternSugg::Const {
628                            stmt_lo: line_lo,
629                            ident_span: expr_span,
630                            expr,
631                            ident,
632                            indentation,
633                        });
634                    },
635                );
636            }
637        }
638
639        impl<'a> Visitor<'a> for PatVisitor<'a> {
640            fn visit_arm(&mut self, a: &'a Arm) -> Self::Result {
641                self.arm = Some(a);
642                visit::walk_arm(self, a);
643                self.arm = None;
644            }
645
646            fn visit_pat_field(&mut self, fp: &'a PatField) -> Self::Result {
647                self.field = Some(fp);
648                visit::walk_pat_field(self, fp);
649                self.field = None;
650            }
651
652            fn visit_pat(&mut self, p: &'a Pat) -> Self::Result {
653                match &p.kind {
654                    // Base expression
655                    PatKind::Err(_) | PatKind::Expr(_) => {
656                        self.maybe_add_suggestions_then_emit(p.span, p.span, false)
657                    }
658
659                    // Sub-patterns
660                    // FIXME: this doesn't work with recursive subpats (`&mut &mut <err>`)
661                    PatKind::Box(subpat) | PatKind::Ref(subpat, _)
662                        if matches!(subpat.kind, PatKind::Err(_) | PatKind::Expr(_)) =>
663                    {
664                        self.maybe_add_suggestions_then_emit(subpat.span, p.span, false)
665                    }
666
667                    // Sub-expressions
668                    PatKind::Range(start, end, _) => {
669                        if let Some(start) = start {
670                            self.maybe_add_suggestions_then_emit(start.span, start.span, true);
671                        }
672
673                        if let Some(end) = end {
674                            self.maybe_add_suggestions_then_emit(end.span, end.span, true);
675                        }
676                    }
677
678                    // Walk continuation
679                    _ => visit::walk_pat(self, p),
680                }
681            }
682        }
683
684        // Starts the visit.
685        PatVisitor { parser: self, stmt, arm: None, field: None }.visit_stmt(stmt);
686    }
687
688    fn eat_metavar_pat(&mut self) -> Option<P<Pat>> {
689        // Must try both kinds of pattern nonterminals.
690        if let Some(pat) = self.eat_metavar_seq_with_matcher(
691            |mv_kind| matches!(mv_kind, MetaVarKind::Pat(PatParam { .. })),
692            |this| this.parse_pat_no_top_alt(None, None),
693        ) {
694            Some(pat)
695        } else if let Some(pat) = self.eat_metavar_seq(MetaVarKind::Pat(PatWithOr), |this| {
696            this.parse_pat_no_top_guard(
697                None,
698                RecoverComma::No,
699                RecoverColon::No,
700                CommaRecoveryMode::EitherTupleOrPipe,
701            )
702        }) {
703            Some(pat)
704        } else {
705            None
706        }
707    }
708
709    /// Parses a pattern, with a setting whether modern range patterns (e.g., `a..=b`, `a..b` are
710    /// allowed).
711    fn parse_pat_with_range_pat(
712        &mut self,
713        allow_range_pat: bool,
714        expected: Option<Expected>,
715        syntax_loc: Option<PatternLocation>,
716    ) -> PResult<'a, P<Pat>> {
717        maybe_recover_from_interpolated_ty_qpath!(self, true);
718
719        if let Some(pat) = self.eat_metavar_pat() {
720            return Ok(pat);
721        }
722
723        let mut lo = self.token.span;
724
725        if self.token.is_keyword(kw::Let)
726            && self.look_ahead(1, |tok| {
727                tok.can_begin_pattern(token::NtPatKind::PatParam { inferred: false })
728            })
729        {
730            self.bump();
731            // Trim extra space after the `let`
732            let span = lo.with_hi(self.token.span.lo());
733            self.dcx().emit_err(RemoveLet { span: lo, suggestion: span });
734            lo = self.token.span;
735        }
736
737        let pat = if self.check(exp!(And)) || self.token == token::AndAnd {
738            self.parse_pat_deref(expected)?
739        } else if self.check(exp!(OpenParen)) {
740            self.parse_pat_tuple_or_parens()?
741        } else if self.check(exp!(OpenBracket)) {
742            // Parse `[pat, pat,...]` as a slice pattern.
743            let (pats, _) =
744                self.parse_delim_comma_seq(exp!(OpenBracket), exp!(CloseBracket), |p| {
745                    p.parse_pat_allow_top_guard(
746                        None,
747                        RecoverComma::No,
748                        RecoverColon::No,
749                        CommaRecoveryMode::EitherTupleOrPipe,
750                    )
751                })?;
752            PatKind::Slice(pats)
753        } else if self.check(exp!(DotDot)) && !self.is_pat_range_end_start(1) {
754            // A rest pattern `..`.
755            self.bump(); // `..`
756            PatKind::Rest
757        } else if self.check(exp!(DotDotDot)) && !self.is_pat_range_end_start(1) {
758            self.recover_dotdotdot_rest_pat(lo)
759        } else if let Some(form) = self.parse_range_end() {
760            self.parse_pat_range_to(form)? // `..=X`, `...X`, or `..X`.
761        } else if self.eat(exp!(Bang)) {
762            // Parse `!`
763            self.psess.gated_spans.gate(sym::never_patterns, self.prev_token.span);
764            PatKind::Never
765        } else if self.eat_keyword(exp!(Underscore)) {
766            // Parse `_`
767            PatKind::Wild
768        } else if self.eat_keyword(exp!(Mut)) {
769            self.parse_pat_ident_mut()?
770        } else if self.eat_keyword(exp!(Ref)) {
771            if self.check_keyword(exp!(Box)) {
772                // Suggest `box ref`.
773                let span = self.prev_token.span.to(self.token.span);
774                self.bump();
775                self.dcx().emit_err(SwitchRefBoxOrder { span });
776            }
777            // Parse ref ident @ pat / ref mut ident @ pat
778            let mutbl = self.parse_mutability();
779            self.parse_pat_ident(BindingMode(ByRef::Yes(mutbl), Mutability::Not), syntax_loc)?
780        } else if self.eat_keyword(exp!(Box)) {
781            self.parse_pat_box()?
782        } else if self.check_inline_const(0) {
783            // Parse `const pat`
784            let const_expr = self.parse_const_block(lo.to(self.token.span), true)?;
785
786            if let Some(re) = self.parse_range_end() {
787                self.parse_pat_range_begin_with(const_expr, re)?
788            } else {
789                PatKind::Expr(const_expr)
790            }
791        } else if self.is_builtin() {
792            self.parse_pat_builtin()?
793        }
794        // Don't eagerly error on semantically invalid tokens when matching
795        // declarative macros, as the input to those doesn't have to be
796        // semantically valid. For attribute/derive proc macros this is not the
797        // case, so doing the recovery for them is fine.
798        else if self.can_be_ident_pat()
799            || (self.is_lit_bad_ident().is_some() && self.may_recover())
800        {
801            // Parse `ident @ pat`
802            // This can give false positives and parse nullary enums,
803            // they are dealt with later in resolve.
804            self.parse_pat_ident(BindingMode::NONE, syntax_loc)?
805        } else if self.is_start_of_pat_with_path() {
806            // Parse pattern starting with a path
807            let (qself, path) = if self.eat_lt() {
808                // Parse a qualified path
809                let (qself, path) = self.parse_qpath(PathStyle::Pat)?;
810                (Some(qself), path)
811            } else {
812                // Parse an unqualified path
813                (None, self.parse_path(PathStyle::Pat)?)
814            };
815            let span = lo.to(self.prev_token.span);
816
817            if qself.is_none() && self.check(exp!(Bang)) {
818                self.parse_pat_mac_invoc(path)?
819            } else if let Some(form) = self.parse_range_end() {
820                let begin = self.mk_expr(span, ExprKind::Path(qself, path));
821                self.parse_pat_range_begin_with(begin, form)?
822            } else if self.check(exp!(OpenBrace)) {
823                self.parse_pat_struct(qself, path)?
824            } else if self.check(exp!(OpenParen)) {
825                self.parse_pat_tuple_struct(qself, path)?
826            } else {
827                match self.maybe_recover_trailing_expr(span, false) {
828                    Some((guar, _)) => PatKind::Err(guar),
829                    None => PatKind::Path(qself, path),
830                }
831            }
832        } else if let Some((lt, IdentIsRaw::No)) = self.token.lifetime()
833            // In pattern position, we're totally fine with using "next token isn't colon"
834            // as a heuristic. We could probably just always try to recover if it's a lifetime,
835            // because we never have `'a: label {}` in a pattern position anyways, but it does
836            // keep us from suggesting something like `let 'a: Ty = ..` => `let 'a': Ty = ..`
837            && could_be_unclosed_char_literal(lt)
838            && !self.look_ahead(1, |token| token.kind == token::Colon)
839        {
840            // Recover a `'a` as a `'a'` literal
841            let lt = self.expect_lifetime();
842            let (lit, _) =
843                self.recover_unclosed_char(lt.ident, Parser::mk_token_lit_char, |self_| {
844                    let expected = Expected::to_string_or_fallback(expected);
845                    let msg = format!(
846                        "expected {}, found {}",
847                        expected,
848                        super::token_descr(&self_.token)
849                    );
850
851                    self_
852                        .dcx()
853                        .struct_span_err(self_.token.span, msg)
854                        .with_span_label(self_.token.span, format!("expected {expected}"))
855                });
856            PatKind::Expr(self.mk_expr(lo, ExprKind::Lit(lit)))
857        } else {
858            // Try to parse everything else as literal with optional minus
859            match self.parse_literal_maybe_minus() {
860                Ok(begin) => {
861                    let begin = self
862                        .maybe_recover_trailing_expr(begin.span, false)
863                        .map(|(guar, sp)| self.mk_expr_err(sp, guar))
864                        .unwrap_or(begin);
865
866                    match self.parse_range_end() {
867                        Some(form) => self.parse_pat_range_begin_with(begin, form)?,
868                        None => PatKind::Expr(begin),
869                    }
870                }
871                Err(err) => return self.fatal_unexpected_non_pat(err, expected),
872            }
873        };
874
875        let pat = self.mk_pat(lo.to(self.prev_token.span), pat);
876        let pat = self.maybe_recover_from_bad_qpath(pat)?;
877        let pat = self.recover_intersection_pat(pat)?;
878
879        if !allow_range_pat {
880            self.ban_pat_range_if_ambiguous(&pat)
881        }
882
883        Ok(pat)
884    }
885
886    /// Recover from a typoed `...` pattern that was encountered
887    /// Ref: Issue #70388
888    fn recover_dotdotdot_rest_pat(&mut self, lo: Span) -> PatKind {
889        // A typoed rest pattern `...`.
890        self.bump(); // `...`
891
892        // The user probably mistook `...` for a rest pattern `..`.
893        self.dcx().emit_err(DotDotDotRestPattern {
894            span: lo,
895            suggestion: lo.with_lo(lo.hi() - BytePos(1)),
896        });
897        PatKind::Rest
898    }
899
900    /// Try to recover the more general form `intersect ::= $pat_lhs @ $pat_rhs`.
901    ///
902    /// Allowed binding patterns generated by `binding ::= ref? mut? $ident @ $pat_rhs`
903    /// should already have been parsed by now at this point,
904    /// if the next token is `@` then we can try to parse the more general form.
905    ///
906    /// Consult `parse_pat_ident` for the `binding` grammar.
907    ///
908    /// The notion of intersection patterns are found in
909    /// e.g. [F#][and] where they are called AND-patterns.
910    ///
911    /// [and]: https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/pattern-matching
912    fn recover_intersection_pat(&mut self, lhs: P<Pat>) -> PResult<'a, P<Pat>> {
913        if self.token != token::At {
914            // Next token is not `@` so it's not going to be an intersection pattern.
915            return Ok(lhs);
916        }
917
918        // At this point we attempt to parse `@ $pat_rhs` and emit an error.
919        self.bump(); // `@`
920        let mut rhs = self.parse_pat_no_top_alt(None, None)?;
921        let whole_span = lhs.span.to(rhs.span);
922
923        if let PatKind::Ident(_, _, sub @ None) = &mut rhs.kind {
924            // The user inverted the order, so help them fix that.
925            let lhs_span = lhs.span;
926            // Move the LHS into the RHS as a subpattern.
927            // The RHS is now the full pattern.
928            *sub = Some(lhs);
929
930            self.dcx().emit_err(PatternOnWrongSideOfAt {
931                whole_span,
932                whole_pat: pprust::pat_to_string(&rhs),
933                pattern: lhs_span,
934                binding: rhs.span,
935            });
936        } else {
937            // The special case above doesn't apply so we may have e.g. `A(x) @ B(y)`.
938            rhs.kind = PatKind::Wild;
939            self.dcx().emit_err(ExpectedBindingLeftOfAt {
940                whole_span,
941                lhs: lhs.span,
942                rhs: rhs.span,
943            });
944        }
945
946        rhs.span = whole_span;
947        Ok(rhs)
948    }
949
950    /// Ban a range pattern if it has an ambiguous interpretation.
951    fn ban_pat_range_if_ambiguous(&self, pat: &Pat) {
952        match pat.kind {
953            PatKind::Range(
954                ..,
955                Spanned { node: RangeEnd::Included(RangeSyntax::DotDotDot), .. },
956            ) => return,
957            PatKind::Range(..) => {}
958            _ => return,
959        }
960
961        self.dcx().emit_err(AmbiguousRangePattern {
962            span: pat.span,
963            suggestion: ParenRangeSuggestion {
964                lo: pat.span.shrink_to_lo(),
965                hi: pat.span.shrink_to_hi(),
966            },
967        });
968    }
969
970    /// Parse `&pat` / `&mut pat`.
971    fn parse_pat_deref(&mut self, expected: Option<Expected>) -> PResult<'a, PatKind> {
972        self.expect_and()?;
973        if let Some((lifetime, _)) = self.token.lifetime() {
974            self.bump(); // `'a`
975
976            self.dcx().emit_err(UnexpectedLifetimeInPattern {
977                span: self.prev_token.span,
978                symbol: lifetime.name,
979                suggestion: self.prev_token.span.until(self.token.span),
980            });
981        }
982
983        let mutbl = self.parse_mutability();
984        let subpat = self.parse_pat_with_range_pat(false, expected, None)?;
985        Ok(PatKind::Ref(subpat, mutbl))
986    }
987
988    /// Parse a tuple or parenthesis pattern.
989    fn parse_pat_tuple_or_parens(&mut self) -> PResult<'a, PatKind> {
990        let open_paren = self.token.span;
991
992        let (fields, trailing_comma) = self.parse_paren_comma_seq(|p| {
993            p.parse_pat_allow_top_guard(
994                None,
995                RecoverComma::No,
996                RecoverColon::No,
997                CommaRecoveryMode::LikelyTuple,
998            )
999        })?;
1000
1001        // Here, `(pat,)` is a tuple pattern.
1002        // For backward compatibility, `(..)` is a tuple pattern as well.
1003        let paren_pattern =
1004            fields.len() == 1 && !(matches!(trailing_comma, Trailing::Yes) || fields[0].is_rest());
1005
1006        let pat = if paren_pattern {
1007            let pat = fields.into_iter().next().unwrap();
1008            let close_paren = self.prev_token.span;
1009
1010            match &pat.kind {
1011                // recover ranges with parentheses around the `(start)..`
1012                PatKind::Expr(begin)
1013                    if self.may_recover()
1014                        && let Some(form) = self.parse_range_end() =>
1015                {
1016                    self.dcx().emit_err(UnexpectedParenInRangePat {
1017                        span: vec![open_paren, close_paren],
1018                        sugg: UnexpectedParenInRangePatSugg {
1019                            start_span: open_paren,
1020                            end_span: close_paren,
1021                        },
1022                    });
1023
1024                    self.parse_pat_range_begin_with(begin.clone(), form)?
1025                }
1026                // recover ranges with parentheses around the `(start)..`
1027                PatKind::Err(guar)
1028                    if self.may_recover()
1029                        && let Some(form) = self.parse_range_end() =>
1030                {
1031                    self.dcx().emit_err(UnexpectedParenInRangePat {
1032                        span: vec![open_paren, close_paren],
1033                        sugg: UnexpectedParenInRangePatSugg {
1034                            start_span: open_paren,
1035                            end_span: close_paren,
1036                        },
1037                    });
1038
1039                    self.parse_pat_range_begin_with(self.mk_expr_err(pat.span, *guar), form)?
1040                }
1041
1042                // (pat) with optional parentheses
1043                _ => PatKind::Paren(pat),
1044            }
1045        } else {
1046            PatKind::Tuple(fields)
1047        };
1048
1049        Ok(match self.maybe_recover_trailing_expr(open_paren.to(self.prev_token.span), false) {
1050            None => pat,
1051            Some((guar, _)) => PatKind::Err(guar),
1052        })
1053    }
1054
1055    /// Parse a mutable binding with the `mut` token already eaten.
1056    fn parse_pat_ident_mut(&mut self) -> PResult<'a, PatKind> {
1057        let mut_span = self.prev_token.span;
1058
1059        self.recover_additional_muts();
1060
1061        let byref = self.parse_byref();
1062
1063        self.recover_additional_muts();
1064
1065        // Make sure we don't allow e.g. `let mut $p;` where `$p:pat`.
1066        if let Some(MetaVarKind::Pat(_)) = self.token.is_metavar_seq() {
1067            self.expected_ident_found_err().emit();
1068        }
1069
1070        // Parse the pattern we hope to be an identifier.
1071        let mut pat = self.parse_pat_no_top_alt(Some(Expected::Identifier), None)?;
1072
1073        // If we don't have `mut $ident (@ pat)?`, error.
1074        if let PatKind::Ident(BindingMode(br @ ByRef::No, m @ Mutability::Not), ..) = &mut pat.kind
1075        {
1076            // Don't recurse into the subpattern.
1077            // `mut` on the outer binding doesn't affect the inner bindings.
1078            *br = byref;
1079            *m = Mutability::Mut;
1080        } else {
1081            // Add `mut` to any binding in the parsed pattern.
1082            let changed_any_binding = Self::make_all_value_bindings_mutable(&mut pat);
1083            self.ban_mut_general_pat(mut_span, &pat, changed_any_binding);
1084        }
1085
1086        if matches!(pat.kind, PatKind::Ident(BindingMode(ByRef::Yes(_), Mutability::Mut), ..)) {
1087            self.psess.gated_spans.gate(sym::mut_ref, pat.span);
1088        }
1089        Ok(pat.kind)
1090    }
1091
1092    /// Turn all by-value immutable bindings in a pattern into mutable bindings.
1093    /// Returns `true` if any change was made.
1094    fn make_all_value_bindings_mutable(pat: &mut P<Pat>) -> bool {
1095        struct AddMut(bool);
1096        impl MutVisitor for AddMut {
1097            fn visit_pat(&mut self, pat: &mut Pat) {
1098                if let PatKind::Ident(BindingMode(ByRef::No, m @ Mutability::Not), ..) =
1099                    &mut pat.kind
1100                {
1101                    self.0 = true;
1102                    *m = Mutability::Mut;
1103                }
1104                mut_visit::walk_pat(self, pat);
1105            }
1106        }
1107
1108        let mut add_mut = AddMut(false);
1109        add_mut.visit_pat(pat);
1110        add_mut.0
1111    }
1112
1113    /// Error on `mut $pat` where `$pat` is not an ident.
1114    fn ban_mut_general_pat(&self, lo: Span, pat: &Pat, changed_any_binding: bool) {
1115        self.dcx().emit_err(if changed_any_binding {
1116            InvalidMutInPattern::NestedIdent {
1117                span: lo.to(pat.span),
1118                pat: pprust::pat_to_string(pat),
1119            }
1120        } else {
1121            InvalidMutInPattern::NonIdent { span: lo.until(pat.span) }
1122        });
1123    }
1124
1125    /// Eat any extraneous `mut`s and error + recover if we ate any.
1126    fn recover_additional_muts(&mut self) {
1127        let lo = self.token.span;
1128        while self.eat_keyword(exp!(Mut)) {}
1129        if lo == self.token.span {
1130            return;
1131        }
1132
1133        let span = lo.to(self.prev_token.span);
1134        let suggestion = span.with_hi(self.token.span.lo());
1135        self.dcx().emit_err(RepeatedMutInPattern { span, suggestion });
1136    }
1137
1138    /// Parse macro invocation
1139    fn parse_pat_mac_invoc(&mut self, path: Path) -> PResult<'a, PatKind> {
1140        self.bump();
1141        let args = self.parse_delim_args()?;
1142        let mac = P(MacCall { path, args });
1143        Ok(PatKind::MacCall(mac))
1144    }
1145
1146    fn fatal_unexpected_non_pat(
1147        &mut self,
1148        err: Diag<'a>,
1149        expected: Option<Expected>,
1150    ) -> PResult<'a, P<Pat>> {
1151        err.cancel();
1152
1153        let expected = Expected::to_string_or_fallback(expected);
1154        let msg = format!("expected {}, found {}", expected, super::token_descr(&self.token));
1155
1156        let mut err = self.dcx().struct_span_err(self.token.span, msg);
1157        err.span_label(self.token.span, format!("expected {expected}"));
1158
1159        let sp = self.psess.source_map().start_point(self.token.span);
1160        if let Some(sp) = self.psess.ambiguous_block_expr_parse.borrow().get(&sp) {
1161            err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
1162        }
1163
1164        Err(err)
1165    }
1166
1167    /// Parses the range pattern end form `".." | "..." | "..=" ;`.
1168    fn parse_range_end(&mut self) -> Option<Spanned<RangeEnd>> {
1169        let re = if self.eat(exp!(DotDotDot)) {
1170            RangeEnd::Included(RangeSyntax::DotDotDot)
1171        } else if self.eat(exp!(DotDotEq)) {
1172            RangeEnd::Included(RangeSyntax::DotDotEq)
1173        } else if self.eat(exp!(DotDot)) {
1174            RangeEnd::Excluded
1175        } else {
1176            return None;
1177        };
1178        Some(respan(self.prev_token.span, re))
1179    }
1180
1181    /// Parse a range pattern `$begin $form $end?` where `$form = ".." | "..." | "..=" ;`.
1182    /// `$begin $form` has already been parsed.
1183    fn parse_pat_range_begin_with(
1184        &mut self,
1185        begin: P<Expr>,
1186        re: Spanned<RangeEnd>,
1187    ) -> PResult<'a, PatKind> {
1188        let end = if self.is_pat_range_end_start(0) {
1189            // Parsing e.g. `X..=Y`.
1190            Some(self.parse_pat_range_end()?)
1191        } else {
1192            // Parsing e.g. `X..`.
1193            if let RangeEnd::Included(_) = re.node {
1194                // FIXME(Centril): Consider semantic errors instead in `ast_validation`.
1195                self.inclusive_range_with_incorrect_end();
1196            }
1197            None
1198        };
1199        Ok(PatKind::Range(Some(begin), end, re))
1200    }
1201
1202    pub(super) fn inclusive_range_with_incorrect_end(&mut self) -> ErrorGuaranteed {
1203        let tok = &self.token;
1204        let span = self.prev_token.span;
1205        // If the user typed "..==" instead of "..=", we want to give them
1206        // a specific error message telling them to use "..=".
1207        // If they typed "..=>", suggest they use ".. =>".
1208        // Otherwise, we assume that they meant to type a half open exclusive
1209        // range and give them an error telling them to do that instead.
1210        let no_space = tok.span.lo() == span.hi();
1211        match tok.kind {
1212            token::Eq if no_space => {
1213                let span_with_eq = span.to(tok.span);
1214
1215                // Ensure the user doesn't receive unhelpful unexpected token errors
1216                self.bump();
1217                if self.is_pat_range_end_start(0) {
1218                    let _ = self.parse_pat_range_end().map_err(|e| e.cancel());
1219                }
1220
1221                self.dcx().emit_err(InclusiveRangeExtraEquals { span: span_with_eq })
1222            }
1223            token::Gt if no_space => {
1224                let after_pat = span.with_hi(span.hi() - BytePos(1)).shrink_to_hi();
1225                self.dcx().emit_err(InclusiveRangeMatchArrow { span, arrow: tok.span, after_pat })
1226            }
1227            _ => self.dcx().emit_err(InclusiveRangeNoEnd {
1228                span,
1229                suggestion: span.with_lo(span.hi() - BytePos(1)),
1230            }),
1231        }
1232    }
1233
1234    /// Parse a range-to pattern, `..X` or `..=X` where `X` remains to be parsed.
1235    ///
1236    /// The form `...X` is prohibited to reduce confusion with the potential
1237    /// expression syntax `...expr` for splatting in expressions.
1238    fn parse_pat_range_to(&mut self, mut re: Spanned<RangeEnd>) -> PResult<'a, PatKind> {
1239        let end = self.parse_pat_range_end()?;
1240        if let RangeEnd::Included(syn @ RangeSyntax::DotDotDot) = &mut re.node {
1241            *syn = RangeSyntax::DotDotEq;
1242            self.dcx().emit_err(DotDotDotRangeToPatternNotAllowed { span: re.span });
1243        }
1244        Ok(PatKind::Range(None, Some(end), re))
1245    }
1246
1247    /// Is the token `dist` away from the current suitable as the start of a range patterns end?
1248    fn is_pat_range_end_start(&self, dist: usize) -> bool {
1249        self.check_inline_const(dist)
1250            || self.look_ahead(dist, |t| {
1251                t.is_path_start() // e.g. `MY_CONST`;
1252                || *t == token::Dot // e.g. `.5` for recovery;
1253                || matches!(t.kind, token::Literal(..) | token::Minus)
1254                || t.is_bool_lit()
1255                || t.is_metavar_expr()
1256                || t.is_lifetime() // recover `'a` instead of `'a'`
1257                || (self.may_recover() // recover leading `(`
1258                    && *t == token::OpenParen
1259                    && self.look_ahead(dist + 1, |t| *t != token::OpenParen)
1260                    && self.is_pat_range_end_start(dist + 1))
1261            })
1262    }
1263
1264    /// Parse a range pattern end bound
1265    fn parse_pat_range_end(&mut self) -> PResult<'a, P<Expr>> {
1266        // recover leading `(`
1267        let open_paren = (self.may_recover() && self.eat_noexpect(&token::OpenParen))
1268            .then_some(self.prev_token.span);
1269
1270        let bound = if self.check_inline_const(0) {
1271            self.parse_const_block(self.token.span, true)
1272        } else if self.check_path() {
1273            let lo = self.token.span;
1274            let (qself, path) = if self.eat_lt() {
1275                // Parse a qualified path
1276                let (qself, path) = self.parse_qpath(PathStyle::Pat)?;
1277                (Some(qself), path)
1278            } else {
1279                // Parse an unqualified path
1280                (None, self.parse_path(PathStyle::Pat)?)
1281            };
1282            let hi = self.prev_token.span;
1283            Ok(self.mk_expr(lo.to(hi), ExprKind::Path(qself, path)))
1284        } else {
1285            self.parse_literal_maybe_minus()
1286        }?;
1287
1288        let recovered = self.maybe_recover_trailing_expr(bound.span, true);
1289
1290        // recover trailing `)`
1291        if let Some(open_paren) = open_paren {
1292            self.expect(exp!(CloseParen))?;
1293
1294            self.dcx().emit_err(UnexpectedParenInRangePat {
1295                span: vec![open_paren, self.prev_token.span],
1296                sugg: UnexpectedParenInRangePatSugg {
1297                    start_span: open_paren,
1298                    end_span: self.prev_token.span,
1299                },
1300            });
1301        }
1302
1303        Ok(match recovered {
1304            Some((guar, sp)) => self.mk_expr_err(sp, guar),
1305            None => bound,
1306        })
1307    }
1308
1309    /// Is this the start of a pattern beginning with a path?
1310    fn is_start_of_pat_with_path(&mut self) -> bool {
1311        self.check_path()
1312        // Just for recovery (see `can_be_ident`).
1313        || self.token.is_ident() && !self.token.is_bool_lit() && !self.token.is_keyword(kw::In)
1314    }
1315
1316    /// Would `parse_pat_ident` be appropriate here?
1317    fn can_be_ident_pat(&mut self) -> bool {
1318        self.check_ident()
1319        && !self.token.is_bool_lit() // Avoid `true` or `false` as a binding as it is a literal.
1320        && !self.token.is_path_segment_keyword() // Avoid e.g. `Self` as it is a path.
1321        // Avoid `in`. Due to recovery in the list parser this messes with `for ( $pat in $expr )`.
1322        && !self.token.is_keyword(kw::In)
1323        // Try to do something more complex?
1324        && self.look_ahead(1, |t| !matches!(t.kind, token::OpenParen // A tuple struct pattern.
1325            | token::OpenBrace // A struct pattern.
1326            | token::DotDotDot | token::DotDotEq | token::DotDot // A range pattern.
1327            | token::PathSep // A tuple / struct variant pattern.
1328            | token::Bang)) // A macro expanding to a pattern.
1329    }
1330
1331    /// Parses `ident` or `ident @ pat`.
1332    /// Used by the copy foo and ref foo patterns to give a good
1333    /// error message when parsing mistakes like `ref foo(a, b)`.
1334    fn parse_pat_ident(
1335        &mut self,
1336        binding_annotation: BindingMode,
1337        syntax_loc: Option<PatternLocation>,
1338    ) -> PResult<'a, PatKind> {
1339        let ident = self.parse_ident_common(false)?;
1340
1341        if self.may_recover()
1342            && !matches!(syntax_loc, Some(PatternLocation::FunctionParameter))
1343            && self.check_noexpect(&token::Lt)
1344            && self.look_ahead(1, |t| t.can_begin_type())
1345        {
1346            return Err(self.dcx().create_err(GenericArgsInPatRequireTurbofishSyntax {
1347                span: self.token.span,
1348                suggest_turbofish: self.token.span.shrink_to_lo(),
1349            }));
1350        }
1351
1352        let sub = if self.eat(exp!(At)) {
1353            Some(self.parse_pat_no_top_alt(Some(Expected::BindingPattern), None)?)
1354        } else {
1355            None
1356        };
1357
1358        // Just to be friendly, if they write something like `ref Some(i)`,
1359        // we end up here with `(` as the current token.
1360        // This shortly leads to a parse error. Note that if there is no explicit
1361        // binding mode then we do not end up here, because the lookahead
1362        // will direct us over to `parse_enum_variant()`.
1363        if self.token == token::OpenParen {
1364            return Err(self
1365                .dcx()
1366                .create_err(EnumPatternInsteadOfIdentifier { span: self.prev_token.span }));
1367        }
1368
1369        // Check for method calls after the `ident`,
1370        // but not `ident @ subpat` as `subpat` was already checked and `ident` continues with `@`.
1371
1372        let pat = if sub.is_none()
1373            && let Some((guar, _)) = self.maybe_recover_trailing_expr(ident.span, false)
1374        {
1375            PatKind::Err(guar)
1376        } else {
1377            PatKind::Ident(binding_annotation, ident, sub)
1378        };
1379        Ok(pat)
1380    }
1381
1382    /// Parse a struct ("record") pattern (e.g. `Foo { ... }` or `Foo::Bar { ... }`).
1383    fn parse_pat_struct(&mut self, qself: Option<P<QSelf>>, path: Path) -> PResult<'a, PatKind> {
1384        if qself.is_some() {
1385            // Feature gate the use of qualified paths in patterns
1386            self.psess.gated_spans.gate(sym::more_qualified_paths, path.span);
1387        }
1388        self.bump();
1389        let (fields, etc) = self.parse_pat_fields().unwrap_or_else(|mut e| {
1390            e.span_label(path.span, "while parsing the fields for this pattern");
1391            let guar = e.emit();
1392            self.recover_stmt();
1393            // When recovering, pretend we had `Foo { .. }`, to avoid cascading errors.
1394            (ThinVec::new(), PatFieldsRest::Recovered(guar))
1395        });
1396        self.bump();
1397        Ok(PatKind::Struct(qself, path, fields, etc))
1398    }
1399
1400    /// Parse tuple struct or tuple variant pattern (e.g. `Foo(...)` or `Foo::Bar(...)`).
1401    fn parse_pat_tuple_struct(
1402        &mut self,
1403        qself: Option<P<QSelf>>,
1404        path: Path,
1405    ) -> PResult<'a, PatKind> {
1406        let (fields, _) = self.parse_paren_comma_seq(|p| {
1407            p.parse_pat_allow_top_guard(
1408                None,
1409                RecoverComma::No,
1410                RecoverColon::No,
1411                CommaRecoveryMode::EitherTupleOrPipe,
1412            )
1413        })?;
1414        if qself.is_some() {
1415            self.psess.gated_spans.gate(sym::more_qualified_paths, path.span);
1416        }
1417        Ok(PatKind::TupleStruct(qself, path, fields))
1418    }
1419
1420    /// Are we sure this could not possibly be the start of a pattern?
1421    ///
1422    /// Currently, this only accounts for tokens that can follow identifiers
1423    /// in patterns, but this can be extended as necessary.
1424    fn isnt_pattern_start(&self) -> bool {
1425        [
1426            token::Eq,
1427            token::Colon,
1428            token::Comma,
1429            token::Semi,
1430            token::At,
1431            token::OpenBrace,
1432            token::CloseBrace,
1433            token::CloseParen,
1434        ]
1435        .contains(&self.token.kind)
1436    }
1437
1438    fn parse_pat_builtin(&mut self) -> PResult<'a, PatKind> {
1439        self.parse_builtin(|self_, _lo, ident| {
1440            Ok(match ident.name {
1441                // builtin#deref(PAT)
1442                sym::deref => Some(ast::PatKind::Deref(self_.parse_pat_allow_top_guard(
1443                    None,
1444                    RecoverComma::Yes,
1445                    RecoverColon::Yes,
1446                    CommaRecoveryMode::LikelyTuple,
1447                )?)),
1448                _ => None,
1449            })
1450        })
1451    }
1452
1453    /// Parses `box pat`
1454    fn parse_pat_box(&mut self) -> PResult<'a, PatKind> {
1455        let box_span = self.prev_token.span;
1456
1457        if self.isnt_pattern_start() {
1458            let descr = super::token_descr(&self.token);
1459            self.dcx().emit_err(errors::BoxNotPat {
1460                span: self.token.span,
1461                kw: box_span,
1462                lo: box_span.shrink_to_lo(),
1463                descr,
1464            });
1465
1466            // We cannot use `parse_pat_ident()` since it will complain `box`
1467            // is not an identifier.
1468            let sub = if self.eat(exp!(At)) {
1469                Some(self.parse_pat_no_top_alt(Some(Expected::BindingPattern), None)?)
1470            } else {
1471                None
1472            };
1473
1474            Ok(PatKind::Ident(BindingMode::NONE, Ident::new(kw::Box, box_span), sub))
1475        } else {
1476            let pat = self.parse_pat_with_range_pat(false, None, None)?;
1477            self.psess.gated_spans.gate(sym::box_patterns, box_span.to(self.prev_token.span));
1478            Ok(PatKind::Box(pat))
1479        }
1480    }
1481
1482    /// Parses the fields of a struct-like pattern.
1483    fn parse_pat_fields(&mut self) -> PResult<'a, (ThinVec<PatField>, PatFieldsRest)> {
1484        let mut fields: ThinVec<PatField> = ThinVec::new();
1485        let mut etc = PatFieldsRest::None;
1486        let mut ate_comma = true;
1487        let mut delayed_err: Option<Diag<'a>> = None;
1488        let mut first_etc_and_maybe_comma_span = None;
1489        let mut last_non_comma_dotdot_span = None;
1490
1491        while self.token != token::CloseBrace {
1492            // check that a comma comes after every field
1493            if !ate_comma {
1494                let err = if self.token == token::At {
1495                    let prev_field = fields
1496                        .last()
1497                        .expect("Unreachable on first iteration, not empty otherwise")
1498                        .ident;
1499                    self.report_misplaced_at_in_struct_pat(prev_field)
1500                } else {
1501                    let mut err = self
1502                        .dcx()
1503                        .create_err(ExpectedCommaAfterPatternField { span: self.token.span });
1504                    self.recover_misplaced_pattern_modifiers(&fields, &mut err);
1505                    err
1506                };
1507                if let Some(delayed) = delayed_err {
1508                    delayed.emit();
1509                }
1510                return Err(err);
1511            }
1512            ate_comma = false;
1513
1514            if self.check(exp!(DotDot))
1515                || self.check_noexpect(&token::DotDotDot)
1516                || self.check_keyword(exp!(Underscore))
1517            {
1518                etc = PatFieldsRest::Rest;
1519                let mut etc_sp = self.token.span;
1520                if first_etc_and_maybe_comma_span.is_none() {
1521                    if let Some(comma_tok) =
1522                        self.look_ahead(1, |&t| if t == token::Comma { Some(t) } else { None })
1523                    {
1524                        let nw_span = self
1525                            .psess
1526                            .source_map()
1527                            .span_extend_to_line(comma_tok.span)
1528                            .trim_start(comma_tok.span.shrink_to_lo())
1529                            .map(|s| self.psess.source_map().span_until_non_whitespace(s));
1530                        first_etc_and_maybe_comma_span = nw_span.map(|s| etc_sp.to(s));
1531                    } else {
1532                        first_etc_and_maybe_comma_span =
1533                            Some(self.psess.source_map().span_until_non_whitespace(etc_sp));
1534                    }
1535                }
1536
1537                self.recover_bad_dot_dot();
1538                self.bump(); // `..` || `...` || `_`
1539
1540                if self.token == token::CloseBrace {
1541                    break;
1542                }
1543                let token_str = super::token_descr(&self.token);
1544                let msg = format!("expected `}}`, found {token_str}");
1545                let mut err = self.dcx().struct_span_err(self.token.span, msg);
1546
1547                err.span_label(self.token.span, "expected `}`");
1548                let mut comma_sp = None;
1549                if self.token == token::Comma {
1550                    // Issue #49257
1551                    let nw_span =
1552                        self.psess.source_map().span_until_non_whitespace(self.token.span);
1553                    etc_sp = etc_sp.to(nw_span);
1554                    err.span_label(
1555                        etc_sp,
1556                        "`..` must be at the end and cannot have a trailing comma",
1557                    );
1558                    comma_sp = Some(self.token.span);
1559                    self.bump();
1560                    ate_comma = true;
1561                }
1562
1563                if self.token == token::CloseBrace {
1564                    // If the struct looks otherwise well formed, recover and continue.
1565                    if let Some(sp) = comma_sp {
1566                        err.span_suggestion_short(
1567                            sp,
1568                            "remove this comma",
1569                            "",
1570                            Applicability::MachineApplicable,
1571                        );
1572                    }
1573                    err.emit();
1574                    break;
1575                } else if self.token.is_ident() && ate_comma {
1576                    // Accept fields coming after `..,`.
1577                    // This way we avoid "pattern missing fields" errors afterwards.
1578                    // We delay this error until the end in order to have a span for a
1579                    // suggested fix.
1580                    if let Some(delayed_err) = delayed_err {
1581                        delayed_err.emit();
1582                        return Err(err);
1583                    } else {
1584                        delayed_err = Some(err);
1585                    }
1586                } else {
1587                    if let Some(err) = delayed_err {
1588                        err.emit();
1589                    }
1590                    return Err(err);
1591                }
1592            }
1593
1594            let attrs = match self.parse_outer_attributes() {
1595                Ok(attrs) => attrs,
1596                Err(err) => {
1597                    if let Some(delayed) = delayed_err {
1598                        delayed.emit();
1599                    }
1600                    return Err(err);
1601                }
1602            };
1603            let lo = self.token.span;
1604
1605            let field = self.collect_tokens(None, attrs, ForceCollect::No, |this, attrs| {
1606                let field = match this.parse_pat_field(lo, attrs) {
1607                    Ok(field) => Ok(field),
1608                    Err(err) => {
1609                        if let Some(delayed_err) = delayed_err.take() {
1610                            delayed_err.emit();
1611                        }
1612                        return Err(err);
1613                    }
1614                }?;
1615                ate_comma = this.eat(exp!(Comma));
1616
1617                last_non_comma_dotdot_span = Some(this.prev_token.span);
1618
1619                // We just ate a comma, so there's no need to capture a trailing token.
1620                Ok((field, Trailing::No, UsePreAttrPos::No))
1621            })?;
1622
1623            fields.push(field)
1624        }
1625
1626        if let Some(mut err) = delayed_err {
1627            if let Some(first_etc_span) = first_etc_and_maybe_comma_span {
1628                if self.prev_token == token::DotDot {
1629                    // We have `.., x, ..`.
1630                    err.multipart_suggestion(
1631                        "remove the starting `..`",
1632                        vec![(first_etc_span, String::new())],
1633                        Applicability::MachineApplicable,
1634                    );
1635                } else if let Some(last_non_comma_dotdot_span) = last_non_comma_dotdot_span {
1636                    // We have `.., x`.
1637                    err.multipart_suggestion(
1638                        "move the `..` to the end of the field list",
1639                        vec![
1640                            (first_etc_span, String::new()),
1641                            (
1642                                self.token.span.to(last_non_comma_dotdot_span.shrink_to_hi()),
1643                                format!("{} .. }}", if ate_comma { "" } else { "," }),
1644                            ),
1645                        ],
1646                        Applicability::MachineApplicable,
1647                    );
1648                }
1649            }
1650            err.emit();
1651        }
1652        Ok((fields, etc))
1653    }
1654
1655    #[deny(rustc::untranslatable_diagnostic)]
1656    fn report_misplaced_at_in_struct_pat(&self, prev_field: Ident) -> Diag<'a> {
1657        debug_assert_eq!(self.token, token::At);
1658        let span = prev_field.span.to(self.token.span);
1659        if let Some(dot_dot_span) =
1660            self.look_ahead(1, |t| if t == &token::DotDot { Some(t.span) } else { None })
1661        {
1662            self.dcx().create_err(AtDotDotInStructPattern {
1663                span: span.to(dot_dot_span),
1664                remove: span.until(dot_dot_span),
1665                ident: prev_field,
1666            })
1667        } else {
1668            self.dcx().create_err(AtInStructPattern { span })
1669        }
1670    }
1671
1672    /// If the user writes `S { ref field: name }` instead of `S { field: ref name }`, we suggest
1673    /// the correct code.
1674    fn recover_misplaced_pattern_modifiers(&self, fields: &ThinVec<PatField>, err: &mut Diag<'a>) {
1675        if let Some(last) = fields.iter().last()
1676            && last.is_shorthand
1677            && let PatKind::Ident(binding, ident, None) = last.pat.kind
1678            && binding != BindingMode::NONE
1679            && self.token == token::Colon
1680            // We found `ref mut? ident:`, try to parse a `name,` or `name }`.
1681            && let Some(name_span) = self.look_ahead(1, |t| t.is_ident().then(|| t.span))
1682            && self.look_ahead(2, |t| {
1683                t == &token::Comma || t == &token::CloseBrace
1684            })
1685        {
1686            let span = last.pat.span.with_hi(ident.span.lo());
1687            // We have `S { ref field: name }` instead of `S { field: ref name }`
1688            err.multipart_suggestion(
1689                "the pattern modifiers belong after the `:`",
1690                vec![
1691                    (span, String::new()),
1692                    (name_span.shrink_to_lo(), binding.prefix_str().to_string()),
1693                ],
1694                Applicability::MachineApplicable,
1695            );
1696        }
1697    }
1698
1699    /// Recover on `...` or `_` as if it were `..` to avoid further errors.
1700    /// See issue #46718.
1701    fn recover_bad_dot_dot(&self) {
1702        if self.token == token::DotDot {
1703            return;
1704        }
1705
1706        let token_str = pprust::token_to_string(&self.token);
1707        self.dcx().emit_err(DotDotDotForRemainingFields { span: self.token.span, token_str });
1708    }
1709
1710    fn parse_pat_field(&mut self, lo: Span, attrs: AttrVec) -> PResult<'a, PatField> {
1711        // Check if a colon exists one ahead. This means we're parsing a fieldname.
1712        let hi;
1713        let (subpat, fieldname, is_shorthand) = if self.look_ahead(1, |t| t == &token::Colon) {
1714            // Parsing a pattern of the form `fieldname: pat`.
1715            let fieldname = self.parse_field_name()?;
1716            self.bump();
1717            let pat = self.parse_pat_allow_top_guard(
1718                None,
1719                RecoverComma::No,
1720                RecoverColon::No,
1721                CommaRecoveryMode::EitherTupleOrPipe,
1722            )?;
1723            hi = pat.span;
1724            (pat, fieldname, false)
1725        } else {
1726            // Parsing a pattern of the form `(box) (ref) (mut) fieldname`.
1727            let is_box = self.eat_keyword(exp!(Box));
1728            let boxed_span = self.token.span;
1729            let mutability = self.parse_mutability();
1730            let by_ref = self.parse_byref();
1731
1732            let fieldname = self.parse_field_name()?;
1733            hi = self.prev_token.span;
1734            let ann = BindingMode(by_ref, mutability);
1735            let fieldpat = self.mk_pat_ident(boxed_span.to(hi), ann, fieldname);
1736            let subpat =
1737                if is_box { self.mk_pat(lo.to(hi), PatKind::Box(fieldpat)) } else { fieldpat };
1738            (subpat, fieldname, true)
1739        };
1740
1741        Ok(PatField {
1742            ident: fieldname,
1743            pat: subpat,
1744            is_shorthand,
1745            attrs,
1746            id: ast::DUMMY_NODE_ID,
1747            span: lo.to(hi),
1748            is_placeholder: false,
1749        })
1750    }
1751
1752    pub(super) fn mk_pat_ident(&self, span: Span, ann: BindingMode, ident: Ident) -> P<Pat> {
1753        self.mk_pat(span, PatKind::Ident(ann, ident, None))
1754    }
1755
1756    pub(super) fn mk_pat(&self, span: Span, kind: PatKind) -> P<Pat> {
1757        P(Pat { kind, span, id: ast::DUMMY_NODE_ID, tokens: None })
1758    }
1759}
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy