@@ -10,7 +10,7 @@ use crate::errors::{
1010 UnexpectedParenInRangePatSugg , UnexpectedVertVertBeforeFunctionParam ,
1111 UnexpectedVertVertInPattern ,
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 ;
@@ -346,37 +346,51 @@ impl<'a> Parser<'a> {
346346 /// ^^^^^
347347 /// ```
348348 /// Only the end bound is spanned, and this function have no idea if there were a `..=` before `pat_span`, hence the parameter.
349+ /// This function returns `Some` if a trailing expression was recovered, and said expression's span.
349350 #[ must_use = "the pattern must be discarded as `PatKind::Err` if this function returns Some" ]
350351 fn maybe_recover_trailing_expr (
351352 & mut self ,
352353 pat_span : Span ,
353354 is_end_bound : bool ,
354- ) -> Option < ErrorGuaranteed > {
355+ ) -> Option < ( ErrorGuaranteed , Span ) > {
355356 if self . prev_token . is_keyword ( kw:: Underscore ) || !self . may_recover ( ) {
356357 // Don't recover anything after an `_` or if recovery is disabled.
357358 return None ;
358359 }
359360
360- // Check for `.hello()`, but allow `.Hello()` to be recovered as `, Hello()` in `parse_seq_to_before_tokens()`.
361- let has_trailing_method = self . check_noexpect ( & token:: Dot )
361+ // Returns `true` iff `token` is a `x.y` float
362+ let is_float_literal = |that : & Self , token : & Token | -> bool {
363+ use token:: { Lit , LitKind } ;
364+
365+ let token:: Literal ( Lit { kind : LitKind :: Float , symbol, suffix : None } ) = token. kind
366+ else {
367+ return false ;
368+ } ;
369+
370+ matches ! ( that. break_up_float( symbol, token. span) , DestructuredFloat :: MiddleDot ( ..) )
371+ } ;
372+
373+ // Check for `.hello` or `.0`.
374+ let has_dot_expr = self . check_noexpect ( & token:: Dot ) // `.`
362375 && self . look_ahead ( 1 , |tok| {
363- tok. ident ( )
364- . and_then ( |( ident, _) | ident. name . as_str ( ) . chars ( ) . next ( ) )
365- . is_some_and ( char:: is_lowercase)
366- } )
367- && self . look_ahead ( 2 , |tok| tok. kind == token:: OpenDelim ( Delimiter :: Parenthesis ) ) ;
376+ tok. is_ident ( ) // `hello`
377+ || tok. is_integer_lit ( ) // `0`
378+ || is_float_literal ( & self , & tok) // `0.0`
379+ } ) ;
368380
369381 // Check for operators.
370382 // `|` is excluded as it is used in pattern alternatives and lambdas,
371383 // `?` is included for error propagation,
372384 // `[` is included for indexing operations,
373- // `[]` is excluded as `a[]` isn't an expression and should be recovered as `a, []` (cf. `tests/ui/parser/pat-lt-bracket-7.rs`)
385+ // `[]` is excluded as `a[]` isn't an expression and should be recovered as `a, []` (cf. `tests/ui/parser/pat-lt-bracket-7.rs`),
386+ // `as` is included for type casts
374387 let has_trailing_operator = matches ! ( self . token. kind, token:: BinOp ( op) if op != BinOpToken :: Or )
375388 || self . token . kind == token:: Question
376389 || ( self . token . kind == token:: OpenDelim ( Delimiter :: Bracket )
377- && self . look_ahead ( 1 , |tok| tok. kind != token:: CloseDelim ( Delimiter :: Bracket ) ) ) ;
390+ && self . look_ahead ( 1 , |tok| tok. kind != token:: CloseDelim ( Delimiter :: Bracket ) ) ) // excludes `[]`
391+ || self . token . is_keyword ( kw:: As ) ;
378392
379- if !has_trailing_method && !has_trailing_operator {
393+ if !has_dot_expr && !has_trailing_operator {
380394 // Nothing to recover here.
381395 return None ;
382396 }
@@ -394,8 +408,6 @@ impl<'a> Parser<'a> {
394408 )
395409 . map_err ( |err| err. cancel ( ) )
396410 {
397- let non_assoc_span = expr. span ;
398-
399411 // Parse an associative expression such as `+ expr`, `% expr`, ...
400412 // Assignements, ranges and `|` are disabled by [`Restrictions::IS_PAT`].
401413 let lhs = LhsExpr :: Parsed { expr, starts_statement : false } ;
@@ -410,14 +422,11 @@ impl<'a> Parser<'a> {
410422 || self . token . kind == token:: CloseDelim ( Delimiter :: Parenthesis )
411423 && self . look_ahead ( 1 , Token :: is_range_separator) ;
412424
413- // Check that `parse_expr_assoc_with` didn't eat a rhs.
414- let is_method_call = has_trailing_method && non_assoc_span == expr. span ;
415-
416- return Some ( self . dcx ( ) . emit_err ( UnexpectedExpressionInPattern {
417- span : expr. span ,
418- is_bound,
419- is_method_call,
420- } ) ) ;
425+ return Some ( (
426+ self . dcx ( )
427+ . emit_err ( UnexpectedExpressionInPattern { span : expr. span , is_bound } ) ,
428+ expr. span ,
429+ ) ) ;
421430 }
422431 }
423432
@@ -534,7 +543,7 @@ impl<'a> Parser<'a> {
534543 self . parse_pat_tuple_struct ( qself, path) ?
535544 } else {
536545 match self . maybe_recover_trailing_expr ( span, false ) {
537- Some ( guar) => PatKind :: Err ( guar) ,
546+ Some ( ( guar, _ ) ) => PatKind :: Err ( guar) ,
538547 None => PatKind :: Path ( qself, path) ,
539548 }
540549 }
@@ -567,10 +576,10 @@ impl<'a> Parser<'a> {
567576 // Try to parse everything else as literal with optional minus
568577 match self . parse_literal_maybe_minus ( ) {
569578 Ok ( begin) => {
570- let begin = match self . maybe_recover_trailing_expr ( begin . span , false ) {
571- Some ( guar ) => self . mk_expr_err ( begin. span , guar ) ,
572- None => begin ,
573- } ;
579+ let begin = self
580+ . maybe_recover_trailing_expr ( begin. span , false )
581+ . map ( | ( guar , sp ) | self . mk_expr_err ( sp , guar ) )
582+ . unwrap_or ( begin ) ;
574583
575584 match self . parse_range_end ( ) {
576585 Some ( form) => self . parse_pat_range_begin_with ( begin, form) ?,
@@ -700,7 +709,8 @@ impl<'a> Parser<'a> {
700709 // For backward compatibility, `(..)` is a tuple pattern as well.
701710 let paren_pattern =
702711 fields. len ( ) == 1 && !( matches ! ( trailing_comma, Trailing :: Yes ) || fields[ 0 ] . is_rest ( ) ) ;
703- if paren_pattern {
712+
713+ let pat = if paren_pattern {
704714 let pat = fields. into_iter ( ) . next ( ) . unwrap ( ) ;
705715 let close_paren = self . prev_token . span ;
706716
@@ -718,7 +728,7 @@ impl<'a> Parser<'a> {
718728 } ,
719729 } ) ;
720730
721- self . parse_pat_range_begin_with ( begin. clone ( ) , form)
731+ self . parse_pat_range_begin_with ( begin. clone ( ) , form) ?
722732 }
723733 // recover ranges with parentheses around the `(start)..`
724734 PatKind :: Err ( guar)
@@ -733,15 +743,20 @@ impl<'a> Parser<'a> {
733743 } ,
734744 } ) ;
735745
736- self . parse_pat_range_begin_with ( self . mk_expr_err ( pat. span , * guar) , form)
746+ self . parse_pat_range_begin_with ( self . mk_expr_err ( pat. span , * guar) , form) ?
737747 }
738748
739749 // (pat) with optional parentheses
740- _ => Ok ( PatKind :: Paren ( pat) ) ,
750+ _ => PatKind :: Paren ( pat) ,
741751 }
742752 } else {
743- Ok ( PatKind :: Tuple ( fields) )
744- }
753+ PatKind :: Tuple ( fields)
754+ } ;
755+
756+ Ok ( match self . maybe_recover_trailing_expr ( open_paren. to ( self . prev_token . span ) , false ) {
757+ None => pat,
758+ Some ( ( guar, _) ) => PatKind :: Err ( guar) ,
759+ } )
745760 }
746761
747762 /// Parse a mutable binding with the `mut` token already eaten.
@@ -991,7 +1006,7 @@ impl<'a> Parser<'a> {
9911006 }
9921007
9931008 Ok ( match recovered {
994- Some ( guar) => self . mk_expr_err ( bound . span , guar) ,
1009+ Some ( ( guar, sp ) ) => self . mk_expr_err ( sp , guar) ,
9951010 None => bound,
9961011 } )
9971012 }
@@ -1060,7 +1075,7 @@ impl<'a> Parser<'a> {
10601075 // but not `ident @ subpat` as `subpat` was already checked and `ident` continues with `@`.
10611076
10621077 let pat = if sub. is_none ( )
1063- && let Some ( guar) = self . maybe_recover_trailing_expr ( ident. span , false )
1078+ && let Some ( ( guar, _ ) ) = self . maybe_recover_trailing_expr ( ident. span , false )
10641079 {
10651080 PatKind :: Err ( guar)
10661081 } else {
0 commit comments