@@ -25,7 +25,7 @@ use crate::errors::{
2525 UnexpectedParenInRangePat , UnexpectedParenInRangePatSugg ,
2626 UnexpectedVertVertBeforeFunctionParam , UnexpectedVertVertInPattern , WrapInParens ,
2727} ;
28- use crate :: parser:: expr:: could_be_unclosed_char_literal;
28+ use crate :: parser:: expr:: { could_be_unclosed_char_literal, DestructuredFloat } ;
2929use crate :: { maybe_recover_from_interpolated_ty_qpath, maybe_whole} ;
3030
3131#[ derive( PartialEq , Copy , Clone ) ]
@@ -342,46 +342,72 @@ impl<'a> Parser<'a> {
342342 }
343343 }
344344
345- /// Ensures that the last parsed pattern (or pattern range bound) is not followed by a method call or an operator .
345+ /// Ensures that the last parsed pattern (or pattern range bound) is not followed by an expression .
346346 ///
347347 /// `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))
348348 /// in order to say "expected a pattern range bound" instead of "expected a pattern";
349349 /// ```text
350350 /// 0..=1 + 2
351351 /// ^^^^^
352352 /// ```
353- /// Only the end bound is spanned, and this function have no idea if there were a `..=` before `pat_span`, hence the parameter.
353+ /// 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.
354+ ///
355+ /// This function returns `Some` if a trailing expression was recovered, and said expression's span.
354356 #[ must_use = "the pattern must be discarded as `PatKind::Err` if this function returns Some" ]
355357 fn maybe_recover_trailing_expr (
356358 & mut self ,
357359 pat_span : Span ,
358360 is_end_bound : bool ,
359- ) -> Option < ErrorGuaranteed > {
361+ ) -> Option < ( ErrorGuaranteed , Span ) > {
360362 if self . prev_token . is_keyword ( kw:: Underscore ) || !self . may_recover ( ) {
361363 // Don't recover anything after an `_` or if recovery is disabled.
362364 return None ;
363365 }
364366
365- // Check for `.hello()`, but allow `.Hello()` to be recovered as `, Hello()` in `parse_seq_to_before_tokens()`.
366- let has_trailing_method = self . check_noexpect ( & token:: Dot )
367+ // Returns `true` iff `token` is an unsuffixed integer.
368+ let is_one_tuple_index = |_: & Self , token : & Token | -> bool {
369+ use token:: { Lit , LitKind } ;
370+
371+ matches ! (
372+ token. kind,
373+ token:: Literal ( Lit { kind: LitKind :: Integer , symbol: _, suffix: None } )
374+ )
375+ } ;
376+
377+ // Returns `true` iff `token` is an unsuffixed `x.y` float.
378+ let is_two_tuple_indexes = |this : & Self , token : & Token | -> bool {
379+ use token:: { Lit , LitKind } ;
380+
381+ if let token:: Literal ( Lit { kind : LitKind :: Float , symbol, suffix : None } ) = token. kind
382+ && let DestructuredFloat :: MiddleDot ( ..) = this. break_up_float ( symbol, token. span )
383+ {
384+ true
385+ } else {
386+ false
387+ }
388+ } ;
389+
390+ // Check for `.hello` or `.0`.
391+ let has_dot_expr = self . check_noexpect ( & token:: Dot ) // `.`
367392 && self . look_ahead ( 1 , |tok| {
368- tok. ident ( )
369- . and_then ( |( ident, _) | ident. name . as_str ( ) . chars ( ) . next ( ) )
370- . is_some_and ( char:: is_lowercase)
371- } )
372- && self . look_ahead ( 2 , |t| * t == token:: OpenDelim ( Delimiter :: Parenthesis ) ) ;
393+ tok. is_ident ( ) // `hello`
394+ || is_one_tuple_index ( & self , & tok) // `0`
395+ || is_two_tuple_indexes ( & self , & tok) // `0.0`
396+ } ) ;
373397
374398 // Check for operators.
375399 // `|` is excluded as it is used in pattern alternatives and lambdas,
376400 // `?` is included for error propagation,
377401 // `[` is included for indexing operations,
378- // `[]` is excluded as `a[]` isn't an expression and should be recovered as `a, []` (cf. `tests/ui/parser/pat-lt-bracket-7.rs`)
402+ // `[]` is excluded as `a[]` isn't an expression and should be recovered as `a, []` (cf. `tests/ui/parser/pat-lt-bracket-7.rs`),
403+ // `as` is included for type casts
379404 let has_trailing_operator = matches ! ( self . token. kind, token:: BinOp ( op) if op != BinOpToken :: Or )
380405 || self . token == token:: Question
381406 || ( self . token == token:: OpenDelim ( Delimiter :: Bracket )
382- && self . look_ahead ( 1 , |t| * t != token:: CloseDelim ( Delimiter :: Bracket ) ) ) ;
407+ && self . look_ahead ( 1 , |t| * t != token:: CloseDelim ( Delimiter :: Bracket ) ) ) // excludes `[]`
408+ || self . token . is_keyword ( kw:: As ) ;
383409
384- if !has_trailing_method && !has_trailing_operator {
410+ if !has_dot_expr && !has_trailing_operator {
385411 // Nothing to recover here.
386412 return None ;
387413 }
@@ -391,44 +417,41 @@ impl<'a> Parser<'a> {
391417 snapshot. restrictions . insert ( Restrictions :: IS_PAT ) ;
392418
393419 // Parse `?`, `.f`, `(arg0, arg1, ...)` or `[expr]` until they've all been eaten.
394- if let Ok ( expr) = snapshot
420+ let Ok ( expr) = snapshot
395421 . parse_expr_dot_or_call_with (
396422 AttrVec :: new ( ) ,
397423 self . mk_expr ( pat_span, ExprKind :: Dummy ) , // equivalent to transforming the parsed pattern into an `Expr`
398424 pat_span,
399425 )
400426 . map_err ( |err| err. cancel ( ) )
401- {
402- let non_assoc_span = expr. span ;
427+ else {
428+ // We got a trailing method/operator, but that wasn't an expression.
429+ return None ;
430+ } ;
403431
404- // Parse an associative expression such as `+ expr`, `% expr`, ...
405- // Assignments, ranges and `|` are disabled by [`Restrictions::IS_PAT`].
406- if let Ok ( ( expr, _) ) =
407- snapshot. parse_expr_assoc_rest_with ( 0 , false , expr) . map_err ( |err| err. cancel ( ) )
408- {
409- // We got a valid expression.
410- self . restore_snapshot ( snapshot) ;
411- self . restrictions . remove ( Restrictions :: IS_PAT ) ;
412-
413- let is_bound = is_end_bound
414- // is_start_bound: either `..` or `)..`
415- || self . token . is_range_separator ( )
416- || self . token == token:: CloseDelim ( Delimiter :: Parenthesis )
417- && self . look_ahead ( 1 , Token :: is_range_separator) ;
418-
419- // Check that `parse_expr_assoc_with` didn't eat a rhs.
420- let is_method_call = has_trailing_method && non_assoc_span == expr. span ;
421-
422- return Some ( self . dcx ( ) . emit_err ( UnexpectedExpressionInPattern {
423- span : expr. span ,
424- is_bound,
425- is_method_call,
426- } ) ) ;
427- }
428- }
432+ // Parse an associative expression such as `+ expr`, `% expr`, ...
433+ // Assignments, ranges and `|` are disabled by [`Restrictions::IS_PAT`].
434+ let Ok ( ( expr, _) ) =
435+ snapshot. parse_expr_assoc_rest_with ( 0 , false , expr) . map_err ( |err| err. cancel ( ) )
436+ else {
437+ // We got a trailing method/operator, but that wasn't an expression.
438+ return None ;
439+ } ;
429440
430- // We got a trailing method/operator, but we couldn't parse an expression.
431- None
441+ // We got a valid expression.
442+ self . restore_snapshot ( snapshot) ;
443+ self . restrictions . remove ( Restrictions :: IS_PAT ) ;
444+
445+ let is_bound = is_end_bound
446+ // is_start_bound: either `..` or `)..`
447+ || self . token . is_range_separator ( )
448+ || self . token == token:: CloseDelim ( Delimiter :: Parenthesis )
449+ && self . look_ahead ( 1 , Token :: is_range_separator) ;
450+
451+ Some ( (
452+ self . dcx ( ) . emit_err ( UnexpectedExpressionInPattern { span : expr. span , is_bound } ) ,
453+ expr. span ,
454+ ) )
432455 }
433456
434457 /// Parses a pattern, with a setting whether modern range patterns (e.g., `a..=b`, `a..b` are
@@ -544,7 +567,7 @@ impl<'a> Parser<'a> {
544567 self . parse_pat_tuple_struct ( qself, path) ?
545568 } else {
546569 match self . maybe_recover_trailing_expr ( span, false ) {
547- Some ( guar) => PatKind :: Err ( guar) ,
570+ Some ( ( guar, _ ) ) => PatKind :: Err ( guar) ,
548571 None => PatKind :: Path ( qself, path) ,
549572 }
550573 }
@@ -577,10 +600,10 @@ impl<'a> Parser<'a> {
577600 // Try to parse everything else as literal with optional minus
578601 match self . parse_literal_maybe_minus ( ) {
579602 Ok ( begin) => {
580- let begin = match self . maybe_recover_trailing_expr ( begin . span , false ) {
581- Some ( guar ) => self . mk_expr_err ( begin. span , guar ) ,
582- None => begin ,
583- } ;
603+ let begin = self
604+ . maybe_recover_trailing_expr ( begin. span , false )
605+ . map ( | ( guar , sp ) | self . mk_expr_err ( sp , guar ) )
606+ . unwrap_or ( begin ) ;
584607
585608 match self . parse_range_end ( ) {
586609 Some ( form) => self . parse_pat_range_begin_with ( begin, form) ?,
@@ -721,7 +744,8 @@ impl<'a> Parser<'a> {
721744 // For backward compatibility, `(..)` is a tuple pattern as well.
722745 let paren_pattern =
723746 fields. len ( ) == 1 && !( matches ! ( trailing_comma, Trailing :: Yes ) || fields[ 0 ] . is_rest ( ) ) ;
724- if paren_pattern {
747+
748+ let pat = if paren_pattern {
725749 let pat = fields. into_iter ( ) . next ( ) . unwrap ( ) ;
726750 let close_paren = self . prev_token . span ;
727751
@@ -739,7 +763,7 @@ impl<'a> Parser<'a> {
739763 } ,
740764 } ) ;
741765
742- self . parse_pat_range_begin_with ( begin. clone ( ) , form)
766+ self . parse_pat_range_begin_with ( begin. clone ( ) , form) ?
743767 }
744768 // recover ranges with parentheses around the `(start)..`
745769 PatKind :: Err ( guar)
@@ -754,15 +778,20 @@ impl<'a> Parser<'a> {
754778 } ,
755779 } ) ;
756780
757- self . parse_pat_range_begin_with ( self . mk_expr_err ( pat. span , * guar) , form)
781+ self . parse_pat_range_begin_with ( self . mk_expr_err ( pat. span , * guar) , form) ?
758782 }
759783
760784 // (pat) with optional parentheses
761- _ => Ok ( PatKind :: Paren ( pat) ) ,
785+ _ => PatKind :: Paren ( pat) ,
762786 }
763787 } else {
764- Ok ( PatKind :: Tuple ( fields) )
765- }
788+ PatKind :: Tuple ( fields)
789+ } ;
790+
791+ Ok ( match self . maybe_recover_trailing_expr ( open_paren. to ( self . prev_token . span ) , false ) {
792+ None => pat,
793+ Some ( ( guar, _) ) => PatKind :: Err ( guar) ,
794+ } )
766795 }
767796
768797 /// Parse a mutable binding with the `mut` token already eaten.
@@ -1015,7 +1044,7 @@ impl<'a> Parser<'a> {
10151044 }
10161045
10171046 Ok ( match recovered {
1018- Some ( guar) => self . mk_expr_err ( bound . span , guar) ,
1047+ Some ( ( guar, sp ) ) => self . mk_expr_err ( sp , guar) ,
10191048 None => bound,
10201049 } )
10211050 }
@@ -1084,7 +1113,7 @@ impl<'a> Parser<'a> {
10841113 // but not `ident @ subpat` as `subpat` was already checked and `ident` continues with `@`.
10851114
10861115 let pat = if sub. is_none ( )
1087- && let Some ( guar) = self . maybe_recover_trailing_expr ( ident. span , false )
1116+ && let Some ( ( guar, _ ) ) = self . maybe_recover_trailing_expr ( ident. span , false )
10881117 {
10891118 PatKind :: Err ( guar)
10901119 } else {
0 commit comments