@@ -10,7 +10,7 @@ use crate::errors::{
1010 UnexpectedParenInRangePatSugg , UnexpectedVertVertBeforeFunctionParam ,
1111 UnexpectedVertVertInPattern ,
1212} ;
13- use crate :: parser:: expr:: could_be_unclosed_char_literal;
13+ use crate :: parser:: expr:: { could_be_unclosed_char_literal, DestructuredFloat } ;
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 if let Ok ( expr) =
@@ -411,14 +423,11 @@ impl<'a> Parser<'a> {
411423 || self . token . kind == token:: CloseDelim ( Delimiter :: Parenthesis )
412424 && self . look_ahead ( 1 , Token :: is_range_separator) ;
413425
414- // Check that `parse_expr_assoc_with` didn't eat a rhs.
415- let is_method_call = has_trailing_method && non_assoc_span == expr. span ;
416-
417- return Some ( self . dcx ( ) . emit_err ( UnexpectedExpressionInPattern {
418- span : expr. span ,
419- is_bound,
420- is_method_call,
421- } ) ) ;
426+ return Some ( (
427+ self . dcx ( )
428+ . emit_err ( UnexpectedExpressionInPattern { span : expr. span , is_bound } ) ,
429+ expr. span ,
430+ ) ) ;
422431 }
423432 }
424433
@@ -535,7 +544,7 @@ impl<'a> Parser<'a> {
535544 self . parse_pat_tuple_struct ( qself, path) ?
536545 } else {
537546 match self . maybe_recover_trailing_expr ( span, false ) {
538- Some ( guar) => PatKind :: Err ( guar) ,
547+ Some ( ( guar, _ ) ) => PatKind :: Err ( guar) ,
539548 None => PatKind :: Path ( qself, path) ,
540549 }
541550 }
@@ -568,10 +577,10 @@ impl<'a> Parser<'a> {
568577 // Try to parse everything else as literal with optional minus
569578 match self . parse_literal_maybe_minus ( ) {
570579 Ok ( begin) => {
571- let begin = match self . maybe_recover_trailing_expr ( begin . span , false ) {
572- Some ( guar ) => self . mk_expr_err ( begin. span , guar ) ,
573- None => begin ,
574- } ;
580+ let begin = self
581+ . maybe_recover_trailing_expr ( begin. span , false )
582+ . map ( | ( guar , sp ) | self . mk_expr_err ( sp , guar ) )
583+ . unwrap_or ( begin ) ;
575584
576585 match self . parse_range_end ( ) {
577586 Some ( form) => self . parse_pat_range_begin_with ( begin, form) ?,
@@ -701,7 +710,8 @@ impl<'a> Parser<'a> {
701710 // For backward compatibility, `(..)` is a tuple pattern as well.
702711 let paren_pattern =
703712 fields. len ( ) == 1 && !( matches ! ( trailing_comma, Trailing :: Yes ) || fields[ 0 ] . is_rest ( ) ) ;
704- if paren_pattern {
713+
714+ let pat = if paren_pattern {
705715 let pat = fields. into_iter ( ) . next ( ) . unwrap ( ) ;
706716 let close_paren = self . prev_token . span ;
707717
@@ -719,7 +729,7 @@ impl<'a> Parser<'a> {
719729 } ,
720730 } ) ;
721731
722- self . parse_pat_range_begin_with ( begin. clone ( ) , form)
732+ self . parse_pat_range_begin_with ( begin. clone ( ) , form) ?
723733 }
724734 // recover ranges with parentheses around the `(start)..`
725735 PatKind :: Err ( guar)
@@ -734,15 +744,20 @@ impl<'a> Parser<'a> {
734744 } ,
735745 } ) ;
736746
737- self . parse_pat_range_begin_with ( self . mk_expr_err ( pat. span , * guar) , form)
747+ self . parse_pat_range_begin_with ( self . mk_expr_err ( pat. span , * guar) , form) ?
738748 }
739749
740750 // (pat) with optional parentheses
741- _ => Ok ( PatKind :: Paren ( pat) ) ,
751+ _ => PatKind :: Paren ( pat) ,
742752 }
743753 } else {
744- Ok ( PatKind :: Tuple ( fields) )
745- }
754+ PatKind :: Tuple ( fields)
755+ } ;
756+
757+ Ok ( match self . maybe_recover_trailing_expr ( open_paren. to ( self . prev_token . span ) , false ) {
758+ None => pat,
759+ Some ( ( guar, _) ) => PatKind :: Err ( guar) ,
760+ } )
746761 }
747762
748763 /// 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