@@ -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 ;
@@ -337,46 +337,61 @@ impl<'a> Parser<'a> {
337337 }
338338 }
339339
340- /// Ensures that the last parsed pattern (or pattern range bound) is not followed by a method call or an operator .
340+ /// Ensures that the last parsed pattern (or pattern range bound) is not followed by an expression .
341341 ///
342342 /// `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))
343343 /// in order to say "expected a pattern range bound" instead of "expected a pattern";
344344 /// ```text
345345 /// 0..=1 + 2
346346 /// ^^^^^
347347 /// ```
348- /// Only the end bound is spanned, and this function have no idea if there were a `..=` before `pat_span`, hence the parameter.
348+ /// Only the end bound is spanned in this case, and this function have no idea if there were a `..=` before `pat_span`, hence the parameter.
349+ ///
350+ /// This function returns `Some` if a trailing expression was recovered, and said expression's span.
349351 #[ must_use = "the pattern must be discarded as `PatKind::Err` if this function returns Some" ]
350352 fn maybe_recover_trailing_expr (
351353 & mut self ,
352354 pat_span : Span ,
353355 is_end_bound : bool ,
354- ) -> Option < ErrorGuaranteed > {
356+ ) -> Option < ( ErrorGuaranteed , Span ) > {
355357 if self . prev_token . is_keyword ( kw:: Underscore ) || !self . may_recover ( ) {
356358 // Don't recover anything after an `_` or if recovery is disabled.
357359 return None ;
358360 }
359361
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 )
362+ // Returns `true` iff `token` is a `x.y` float.
363+ let is_two_tuple_indexes = |that : & Self , token : & Token | -> bool {
364+ use token:: { Lit , LitKind } ;
365+
366+ let token:: Literal ( Lit { kind : LitKind :: Float , symbol, suffix : None } ) = token. kind
367+ else {
368+ return false ;
369+ } ;
370+
371+ matches ! ( that. break_up_float( symbol, token. span) , DestructuredFloat :: MiddleDot ( ..) )
372+ } ;
373+
374+ // Check for `.hello` or `.0`.
375+ let has_dot_expr = self . check_noexpect ( & token:: Dot ) // `.`
362376 && 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 ) ) ;
377+ tok. is_ident ( ) // `hello`
378+ || tok. is_integer_lit ( ) // `0`
379+ || is_two_tuple_indexes ( & self , & tok) // `0.0`
380+ } ) ;
368381
369382 // Check for operators.
370383 // `|` is excluded as it is used in pattern alternatives and lambdas,
371384 // `?` is included for error propagation,
372385 // `[` 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`)
386+ // `[]` is excluded as `a[]` isn't an expression and should be recovered as `a, []` (cf. `tests/ui/parser/pat-lt-bracket-7.rs`),
387+ // `as` is included for type casts
374388 let has_trailing_operator = matches ! ( self . token. kind, token:: BinOp ( op) if op != BinOpToken :: Or )
375389 || self . token . kind == token:: Question
376390 || ( self . token . kind == token:: OpenDelim ( Delimiter :: Bracket )
377- && self . look_ahead ( 1 , |tok| tok. kind != token:: CloseDelim ( Delimiter :: Bracket ) ) ) ;
391+ && self . look_ahead ( 1 , |tok| tok. kind != token:: CloseDelim ( Delimiter :: Bracket ) ) ) // excludes `[]`
392+ || self . token . is_keyword ( kw:: As ) ;
378393
379- if !has_trailing_method && !has_trailing_operator {
394+ if !has_dot_expr && !has_trailing_operator {
380395 // Nothing to recover here.
381396 return None ;
382397 }
@@ -386,43 +401,40 @@ impl<'a> Parser<'a> {
386401 snapshot. restrictions . insert ( Restrictions :: IS_PAT ) ;
387402
388403 // Parse `?`, `.f`, `(arg0, arg1, ...)` or `[expr]` until they've all been eaten.
389- if let Ok ( expr) = snapshot
404+ let Ok ( expr) = snapshot
390405 . parse_expr_dot_or_call_with (
391406 self . mk_expr ( pat_span, ExprKind :: Dummy ) , // equivalent to transforming the parsed pattern into an `Expr`
392407 pat_span,
393408 AttrVec :: new ( ) ,
394409 )
395410 . map_err ( |err| err. cancel ( ) )
396- {
397- let non_assoc_span = expr. span ;
398-
399- // Parse an associative expression such as `+ expr`, `% expr`, ...
400- // Assignements, ranges and `|` are disabled by [`Restrictions::IS_PAT`].
401- let lhs = LhsExpr :: Parsed { expr, starts_statement : false } ;
402- if let Ok ( expr) = snapshot. parse_expr_assoc_with ( 0 , lhs) . map_err ( |err| err. cancel ( ) ) {
403- // We got a valid expression.
404- self . restore_snapshot ( snapshot) ;
405- self . restrictions . remove ( Restrictions :: IS_PAT ) ;
406-
407- let is_bound = is_end_bound
408- // is_start_bound: either `..` or `)..`
409- || self . token . is_range_separator ( )
410- || self . token . kind == token:: CloseDelim ( Delimiter :: Parenthesis )
411- && self . look_ahead ( 1 , Token :: is_range_separator) ;
412-
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- } ) ) ;
421- }
422- }
411+ else {
412+ // We got a trailing method/operator, but that wasn't an expression.
413+ return None ;
414+ } ;
415+
416+ // Parse an associative expression such as `+ expr`, `% expr`, ...
417+ // Assignements, ranges and `|` are disabled by [`Restrictions::IS_PAT`].
418+ let lhs = LhsExpr :: Parsed { expr, starts_statement : false } ;
419+ let Ok ( expr) = snapshot. parse_expr_assoc_with ( 0 , lhs) . map_err ( |err| err. cancel ( ) ) else {
420+ // We got a trailing method/operator, but that wasn't an expression.
421+ return None ;
422+ } ;
423+
424+ // We got a valid expression.
425+ self . restore_snapshot ( snapshot) ;
426+ self . restrictions . remove ( Restrictions :: IS_PAT ) ;
427+
428+ let is_bound = is_end_bound
429+ // is_start_bound: either `..` or `)..`
430+ || self . token . is_range_separator ( )
431+ || self . token . kind == token:: CloseDelim ( Delimiter :: Parenthesis )
432+ && self . look_ahead ( 1 , Token :: is_range_separator) ;
423433
424- // We got a trailing method/operator, but we couldn't parse an expression.
425- None
434+ Some ( (
435+ self . dcx ( ) . emit_err ( UnexpectedExpressionInPattern { span : expr. span , is_bound } ) ,
436+ expr. span ,
437+ ) )
426438 }
427439
428440 /// Parses a pattern, with a setting whether modern range patterns (e.g., `a..=b`, `a..b` are
@@ -534,7 +546,7 @@ impl<'a> Parser<'a> {
534546 self . parse_pat_tuple_struct ( qself, path) ?
535547 } else {
536548 match self . maybe_recover_trailing_expr ( span, false ) {
537- Some ( guar) => PatKind :: Err ( guar) ,
549+ Some ( ( guar, _ ) ) => PatKind :: Err ( guar) ,
538550 None => PatKind :: Path ( qself, path) ,
539551 }
540552 }
@@ -567,10 +579,10 @@ impl<'a> Parser<'a> {
567579 // Try to parse everything else as literal with optional minus
568580 match self . parse_literal_maybe_minus ( ) {
569581 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- } ;
582+ let begin = self
583+ . maybe_recover_trailing_expr ( begin. span , false )
584+ . map ( | ( guar , sp ) | self . mk_expr_err ( sp , guar ) )
585+ . unwrap_or ( begin ) ;
574586
575587 match self . parse_range_end ( ) {
576588 Some ( form) => self . parse_pat_range_begin_with ( begin, form) ?,
@@ -700,7 +712,8 @@ impl<'a> Parser<'a> {
700712 // For backward compatibility, `(..)` is a tuple pattern as well.
701713 let paren_pattern =
702714 fields. len ( ) == 1 && !( matches ! ( trailing_comma, Trailing :: Yes ) || fields[ 0 ] . is_rest ( ) ) ;
703- if paren_pattern {
715+
716+ let pat = if paren_pattern {
704717 let pat = fields. into_iter ( ) . next ( ) . unwrap ( ) ;
705718 let close_paren = self . prev_token . span ;
706719
@@ -718,7 +731,7 @@ impl<'a> Parser<'a> {
718731 } ,
719732 } ) ;
720733
721- self . parse_pat_range_begin_with ( begin. clone ( ) , form)
734+ self . parse_pat_range_begin_with ( begin. clone ( ) , form) ?
722735 }
723736 // recover ranges with parentheses around the `(start)..`
724737 PatKind :: Err ( guar)
@@ -733,15 +746,20 @@ impl<'a> Parser<'a> {
733746 } ,
734747 } ) ;
735748
736- self . parse_pat_range_begin_with ( self . mk_expr_err ( pat. span , * guar) , form)
749+ self . parse_pat_range_begin_with ( self . mk_expr_err ( pat. span , * guar) , form) ?
737750 }
738751
739752 // (pat) with optional parentheses
740- _ => Ok ( PatKind :: Paren ( pat) ) ,
753+ _ => PatKind :: Paren ( pat) ,
741754 }
742755 } else {
743- Ok ( PatKind :: Tuple ( fields) )
744- }
756+ PatKind :: Tuple ( fields)
757+ } ;
758+
759+ Ok ( match self . maybe_recover_trailing_expr ( open_paren. to ( self . prev_token . span ) , false ) {
760+ None => pat,
761+ Some ( ( guar, _) ) => PatKind :: Err ( guar) ,
762+ } )
745763 }
746764
747765 /// Parse a mutable binding with the `mut` token already eaten.
@@ -991,7 +1009,7 @@ impl<'a> Parser<'a> {
9911009 }
9921010
9931011 Ok ( match recovered {
994- Some ( guar) => self . mk_expr_err ( bound . span , guar) ,
1012+ Some ( ( guar, sp ) ) => self . mk_expr_err ( sp , guar) ,
9951013 None => bound,
9961014 } )
9971015 }
@@ -1060,7 +1078,7 @@ impl<'a> Parser<'a> {
10601078 // but not `ident @ subpat` as `subpat` was already checked and `ident` continues with `@`.
10611079
10621080 let pat = if sub. is_none ( )
1063- && let Some ( guar) = self . maybe_recover_trailing_expr ( ident. span , false )
1081+ && let Some ( ( guar, _ ) ) = self . maybe_recover_trailing_expr ( ident. span , false )
10641082 {
10651083 PatKind :: Err ( guar)
10661084 } else {
0 commit comments