@@ -10,7 +10,7 @@ use crate::errors::{
1010 UnexpectedParenInRangePat , UnexpectedParenInRangePatSugg ,
1111 UnexpectedVertVertBeforeFunctionParam , UnexpectedVertVertInPattern , WrapInParens ,
1212} ;
13- use crate :: parser:: expr:: { could_be_unclosed_char_literal, LhsExpr } ;
13+ use crate :: parser:: expr:: { could_be_unclosed_char_literal, DestructuredFloat , LhsExpr } ;
1414use crate :: { maybe_recover_from_interpolated_ty_qpath, maybe_whole} ;
1515use rustc_ast:: mut_visit:: { noop_visit_pat, MutVisitor } ;
1616use rustc_ast:: ptr:: P ;
@@ -341,46 +341,72 @@ impl<'a> Parser<'a> {
341341 }
342342 }
343343
344- /// Ensures that the last parsed pattern (or pattern range bound) is not followed by a method call or an operator .
344+ /// Ensures that the last parsed pattern (or pattern range bound) is not followed by an expression .
345345 ///
346346 /// `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))
347347 /// in order to say "expected a pattern range bound" instead of "expected a pattern";
348348 /// ```text
349349 /// 0..=1 + 2
350350 /// ^^^^^
351351 /// ```
352- /// Only the end bound is spanned, and this function have no idea if there were a `..=` before `pat_span`, hence the parameter.
352+ /// 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.
353+ ///
354+ /// This function returns `Some` if a trailing expression was recovered, and said expression's span.
353355 #[ must_use = "the pattern must be discarded as `PatKind::Err` if this function returns Some" ]
354356 fn maybe_recover_trailing_expr (
355357 & mut self ,
356358 pat_span : Span ,
357359 is_end_bound : bool ,
358- ) -> Option < ErrorGuaranteed > {
360+ ) -> Option < ( ErrorGuaranteed , Span ) > {
359361 if self . prev_token . is_keyword ( kw:: Underscore ) || !self . may_recover ( ) {
360362 // Don't recover anything after an `_` or if recovery is disabled.
361363 return None ;
362364 }
363365
364- // Check for `.hello()`, but allow `.Hello()` to be recovered as `, Hello()` in `parse_seq_to_before_tokens()`.
365- let has_trailing_method = self . check_noexpect ( & token:: Dot )
366+ // Returns `true` iff `token` is an unsuffixed integer.
367+ let is_one_tuple_index = |_: & Self , token : & Token | -> bool {
368+ use token:: { Lit , LitKind } ;
369+
370+ matches ! (
371+ token. kind,
372+ token:: Literal ( Lit { kind: LitKind :: Integer , symbol: _, suffix: None } )
373+ )
374+ } ;
375+
376+ // Returns `true` iff `token` is an unsuffixed `x.y` float.
377+ let is_two_tuple_indexes = |this : & Self , token : & Token | -> bool {
378+ use token:: { Lit , LitKind } ;
379+
380+ if let token:: Literal ( Lit { kind : LitKind :: Float , symbol, suffix : None } ) = token. kind
381+ && let DestructuredFloat :: MiddleDot ( ..) = this. break_up_float ( symbol, token. span )
382+ {
383+ true
384+ } else {
385+ false
386+ }
387+ } ;
388+
389+ // Check for `.hello` or `.0`.
390+ let has_dot_expr = self . check_noexpect ( & token:: Dot ) // `.`
366391 && self . look_ahead ( 1 , |tok| {
367- tok. ident ( )
368- . and_then ( |( ident, _) | ident. name . as_str ( ) . chars ( ) . next ( ) )
369- . is_some_and ( char:: is_lowercase)
370- } )
371- && self . look_ahead ( 2 , |tok| tok. kind == token:: OpenDelim ( Delimiter :: Parenthesis ) ) ;
392+ tok. is_ident ( ) // `hello`
393+ || is_one_tuple_index ( & self , & tok) // `0`
394+ || is_two_tuple_indexes ( & self , & tok) // `0.0`
395+ } ) ;
372396
373397 // Check for operators.
374398 // `|` is excluded as it is used in pattern alternatives and lambdas,
375399 // `?` is included for error propagation,
376400 // `[` is included for indexing operations,
377- // `[]` is excluded as `a[]` isn't an expression and should be recovered as `a, []` (cf. `tests/ui/parser/pat-lt-bracket-7.rs`)
401+ // `[]` is excluded as `a[]` isn't an expression and should be recovered as `a, []` (cf. `tests/ui/parser/pat-lt-bracket-7.rs`),
402+ // `as` is included for type casts
378403 let has_trailing_operator = matches ! ( self . token. kind, token:: BinOp ( op) if op != BinOpToken :: Or )
379404 || self . token . kind == token:: Question
380405 || ( self . token . kind == token:: OpenDelim ( Delimiter :: Bracket )
381- && self . look_ahead ( 1 , |tok| tok. kind != token:: CloseDelim ( Delimiter :: Bracket ) ) ) ;
406+ && self . look_ahead ( 1 , |tok| tok. kind != token:: CloseDelim ( Delimiter :: Bracket ) ) ) // excludes `[]`
407+ || self . token . is_keyword ( kw:: As ) ;
382408
383- if !has_trailing_method && !has_trailing_operator {
409+ if !has_dot_expr && !has_trailing_operator {
384410 // Nothing to recover here.
385411 return None ;
386412 }
@@ -390,43 +416,40 @@ impl<'a> Parser<'a> {
390416 snapshot. restrictions . insert ( Restrictions :: IS_PAT ) ;
391417
392418 // Parse `?`, `.f`, `(arg0, arg1, ...)` or `[expr]` until they've all been eaten.
393- if let Ok ( expr) = snapshot
419+ let Ok ( expr) = snapshot
394420 . parse_expr_dot_or_call_with (
395421 AttrVec :: new ( ) ,
396422 self . mk_expr ( pat_span, ExprKind :: Dummy ) , // equivalent to transforming the parsed pattern into an `Expr`
397423 pat_span,
398424 )
399425 . map_err ( |err| err. cancel ( ) )
400- {
401- let non_assoc_span = expr. span ;
402-
403- // Parse an associative expression such as `+ expr`, `% expr`, ...
404- // Assignements, ranges and `|` are disabled by [`Restrictions::IS_PAT`].
405- let lhs = LhsExpr :: Parsed { expr, starts_statement : false } ;
406- if let Ok ( expr) = snapshot. parse_expr_assoc_with ( 0 , lhs) . map_err ( |err| err. cancel ( ) ) {
407- // We got a valid expression.
408- self . restore_snapshot ( snapshot) ;
409- self . restrictions . remove ( Restrictions :: IS_PAT ) ;
410-
411- let is_bound = is_end_bound
412- // is_start_bound: either `..` or `)..`
413- || self . token . is_range_separator ( )
414- || self . token . kind == token:: CloseDelim ( Delimiter :: Parenthesis )
415- && self . look_ahead ( 1 , Token :: is_range_separator) ;
416-
417- // Check that `parse_expr_assoc_with` didn't eat a rhs.
418- let is_method_call = has_trailing_method && non_assoc_span == expr. span ;
419-
420- return Some ( self . dcx ( ) . emit_err ( UnexpectedExpressionInPattern {
421- span : expr. span ,
422- is_bound,
423- is_method_call,
424- } ) ) ;
425- }
426- }
426+ else {
427+ // We got a trailing method/operator, but that wasn't an expression.
428+ return None ;
429+ } ;
430+
431+ // Parse an associative expression such as `+ expr`, `% expr`, ...
432+ // Assignements, ranges and `|` are disabled by [`Restrictions::IS_PAT`].
433+ let lhs = LhsExpr :: Parsed { expr, starts_statement : false } ;
434+ let Ok ( expr) = snapshot. parse_expr_assoc_with ( 0 , lhs) . map_err ( |err| err. cancel ( ) ) else {
435+ // We got a trailing method/operator, but that wasn't an expression.
436+ return None ;
437+ } ;
427438
428- // We got a trailing method/operator, but we couldn't parse an expression.
429- None
439+ // We got a valid expression.
440+ self . restore_snapshot ( snapshot) ;
441+ self . restrictions . remove ( Restrictions :: IS_PAT ) ;
442+
443+ let is_bound = is_end_bound
444+ // is_start_bound: either `..` or `)..`
445+ || self . token . is_range_separator ( )
446+ || self . token . kind == token:: CloseDelim ( Delimiter :: Parenthesis )
447+ && self . look_ahead ( 1 , Token :: is_range_separator) ;
448+
449+ Some ( (
450+ self . dcx ( ) . emit_err ( UnexpectedExpressionInPattern { span : expr. span , is_bound } ) ,
451+ expr. span ,
452+ ) )
430453 }
431454
432455 /// Parses a pattern, with a setting whether modern range patterns (e.g., `a..=b`, `a..b` are
@@ -538,7 +561,7 @@ impl<'a> Parser<'a> {
538561 self . parse_pat_tuple_struct ( qself, path) ?
539562 } else {
540563 match self . maybe_recover_trailing_expr ( span, false ) {
541- Some ( guar) => PatKind :: Err ( guar) ,
564+ Some ( ( guar, _ ) ) => PatKind :: Err ( guar) ,
542565 None => PatKind :: Path ( qself, path) ,
543566 }
544567 }
@@ -571,10 +594,10 @@ impl<'a> Parser<'a> {
571594 // Try to parse everything else as literal with optional minus
572595 match self . parse_literal_maybe_minus ( ) {
573596 Ok ( begin) => {
574- let begin = match self . maybe_recover_trailing_expr ( begin . span , false ) {
575- Some ( guar ) => self . mk_expr_err ( begin. span , guar ) ,
576- None => begin ,
577- } ;
597+ let begin = self
598+ . maybe_recover_trailing_expr ( begin. span , false )
599+ . map ( | ( guar , sp ) | self . mk_expr_err ( sp , guar ) )
600+ . unwrap_or ( begin ) ;
578601
579602 match self . parse_range_end ( ) {
580603 Some ( form) => self . parse_pat_range_begin_with ( begin, form) ?,
@@ -715,7 +738,8 @@ impl<'a> Parser<'a> {
715738 // For backward compatibility, `(..)` is a tuple pattern as well.
716739 let paren_pattern =
717740 fields. len ( ) == 1 && !( matches ! ( trailing_comma, Trailing :: Yes ) || fields[ 0 ] . is_rest ( ) ) ;
718- if paren_pattern {
741+
742+ let pat = if paren_pattern {
719743 let pat = fields. into_iter ( ) . next ( ) . unwrap ( ) ;
720744 let close_paren = self . prev_token . span ;
721745
@@ -733,7 +757,7 @@ impl<'a> Parser<'a> {
733757 } ,
734758 } ) ;
735759
736- self . parse_pat_range_begin_with ( begin. clone ( ) , form)
760+ self . parse_pat_range_begin_with ( begin. clone ( ) , form) ?
737761 }
738762 // recover ranges with parentheses around the `(start)..`
739763 PatKind :: Err ( guar)
@@ -748,15 +772,20 @@ impl<'a> Parser<'a> {
748772 } ,
749773 } ) ;
750774
751- self . parse_pat_range_begin_with ( self . mk_expr_err ( pat. span , * guar) , form)
775+ self . parse_pat_range_begin_with ( self . mk_expr_err ( pat. span , * guar) , form) ?
752776 }
753777
754778 // (pat) with optional parentheses
755- _ => Ok ( PatKind :: Paren ( pat) ) ,
779+ _ => PatKind :: Paren ( pat) ,
756780 }
757781 } else {
758- Ok ( PatKind :: Tuple ( fields) )
759- }
782+ PatKind :: Tuple ( fields)
783+ } ;
784+
785+ Ok ( match self . maybe_recover_trailing_expr ( open_paren. to ( self . prev_token . span ) , false ) {
786+ None => pat,
787+ Some ( ( guar, _) ) => PatKind :: Err ( guar) ,
788+ } )
760789 }
761790
762791 /// Parse a mutable binding with the `mut` token already eaten.
@@ -1009,7 +1038,7 @@ impl<'a> Parser<'a> {
10091038 }
10101039
10111040 Ok ( match recovered {
1012- Some ( guar) => self . mk_expr_err ( bound . span , guar) ,
1041+ Some ( ( guar, sp ) ) => self . mk_expr_err ( sp , guar) ,
10131042 None => bound,
10141043 } )
10151044 }
@@ -1078,7 +1107,7 @@ impl<'a> Parser<'a> {
10781107 // but not `ident @ subpat` as `subpat` was already checked and `ident` continues with `@`.
10791108
10801109 let pat = if sub. is_none ( )
1081- && let Some ( guar) = self . maybe_recover_trailing_expr ( ident. span , false )
1110+ && let Some ( ( guar, _ ) ) = self . maybe_recover_trailing_expr ( ident. span , false )
10821111 {
10831112 PatKind :: Err ( guar)
10841113 } else {
0 commit comments