@@ -10,7 +10,7 @@ use crate::errors::{
10
10
UnexpectedParenInRangePatSugg , UnexpectedVertVertBeforeFunctionParam ,
11
11
UnexpectedVertVertInPattern ,
12
12
} ;
13
- use crate :: parser:: expr:: { could_be_unclosed_char_literal, LhsExpr } ;
13
+ use crate :: parser:: expr:: { could_be_unclosed_char_literal, DestructuredFloat , LhsExpr } ;
14
14
use crate :: { maybe_recover_from_interpolated_ty_qpath, maybe_whole} ;
15
15
use rustc_ast:: mut_visit:: { noop_visit_pat, MutVisitor } ;
16
16
use rustc_ast:: ptr:: P ;
@@ -337,46 +337,61 @@ impl<'a> Parser<'a> {
337
337
}
338
338
}
339
339
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 .
341
341
///
342
342
/// `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))
343
343
/// in order to say "expected a pattern range bound" instead of "expected a pattern";
344
344
/// ```text
345
345
/// 0..=1 + 2
346
346
/// ^^^^^
347
347
/// ```
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.
349
351
#[ must_use = "the pattern must be discarded as `PatKind::Err` if this function returns Some" ]
350
352
fn maybe_recover_trailing_expr (
351
353
& mut self ,
352
354
pat_span : Span ,
353
355
is_end_bound : bool ,
354
- ) -> Option < ErrorGuaranteed > {
356
+ ) -> Option < ( ErrorGuaranteed , Span ) > {
355
357
if self . prev_token . is_keyword ( kw:: Underscore ) || !self . may_recover ( ) {
356
358
// Don't recover anything after an `_` or if recovery is disabled.
357
359
return None ;
358
360
}
359
361
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 ) // `.`
362
376
&& 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
+ } ) ;
368
381
369
382
// Check for operators.
370
383
// `|` is excluded as it is used in pattern alternatives and lambdas,
371
384
// `?` is included for error propagation,
372
385
// `[` 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
374
388
let has_trailing_operator = matches ! ( self . token. kind, token:: BinOp ( op) if op != BinOpToken :: Or )
375
389
|| self . token . kind == token:: Question
376
390
|| ( 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 ) ;
378
393
379
- if !has_trailing_method && !has_trailing_operator {
394
+ if !has_dot_expr && !has_trailing_operator {
380
395
// Nothing to recover here.
381
396
return None ;
382
397
}
@@ -386,43 +401,40 @@ impl<'a> Parser<'a> {
386
401
snapshot. restrictions . insert ( Restrictions :: IS_PAT ) ;
387
402
388
403
// Parse `?`, `.f`, `(arg0, arg1, ...)` or `[expr]` until they've all been eaten.
389
- if let Ok ( expr) = snapshot
404
+ let Ok ( expr) = snapshot
390
405
. parse_expr_dot_or_call_with (
391
406
self . mk_expr ( pat_span, ExprKind :: Dummy ) , // equivalent to transforming the parsed pattern into an `Expr`
392
407
pat_span,
393
408
AttrVec :: new ( ) ,
394
409
)
395
410
. 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) ;
423
433
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
+ ) )
426
438
}
427
439
428
440
/// 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> {
534
546
self . parse_pat_tuple_struct ( qself, path) ?
535
547
} else {
536
548
match self . maybe_recover_trailing_expr ( span, false ) {
537
- Some ( guar) => PatKind :: Err ( guar) ,
549
+ Some ( ( guar, _ ) ) => PatKind :: Err ( guar) ,
538
550
None => PatKind :: Path ( qself, path) ,
539
551
}
540
552
}
@@ -567,10 +579,10 @@ impl<'a> Parser<'a> {
567
579
// Try to parse everything else as literal with optional minus
568
580
match self . parse_literal_maybe_minus ( ) {
569
581
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 ) ;
574
586
575
587
match self . parse_range_end ( ) {
576
588
Some ( form) => self . parse_pat_range_begin_with ( begin, form) ?,
@@ -700,7 +712,8 @@ impl<'a> Parser<'a> {
700
712
// For backward compatibility, `(..)` is a tuple pattern as well.
701
713
let paren_pattern =
702
714
fields. len ( ) == 1 && !( matches ! ( trailing_comma, Trailing :: Yes ) || fields[ 0 ] . is_rest ( ) ) ;
703
- if paren_pattern {
715
+
716
+ let pat = if paren_pattern {
704
717
let pat = fields. into_iter ( ) . next ( ) . unwrap ( ) ;
705
718
let close_paren = self . prev_token . span ;
706
719
@@ -718,7 +731,7 @@ impl<'a> Parser<'a> {
718
731
} ,
719
732
} ) ;
720
733
721
- self . parse_pat_range_begin_with ( begin. clone ( ) , form)
734
+ self . parse_pat_range_begin_with ( begin. clone ( ) , form) ?
722
735
}
723
736
// recover ranges with parentheses around the `(start)..`
724
737
PatKind :: Err ( guar)
@@ -733,15 +746,20 @@ impl<'a> Parser<'a> {
733
746
} ,
734
747
} ) ;
735
748
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) ?
737
750
}
738
751
739
752
// (pat) with optional parentheses
740
- _ => Ok ( PatKind :: Paren ( pat) ) ,
753
+ _ => PatKind :: Paren ( pat) ,
741
754
}
742
755
} 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
+ } )
745
763
}
746
764
747
765
/// Parse a mutable binding with the `mut` token already eaten.
@@ -991,7 +1009,7 @@ impl<'a> Parser<'a> {
991
1009
}
992
1010
993
1011
Ok ( match recovered {
994
- Some ( guar) => self . mk_expr_err ( bound . span , guar) ,
1012
+ Some ( ( guar, sp ) ) => self . mk_expr_err ( sp , guar) ,
995
1013
None => bound,
996
1014
} )
997
1015
}
@@ -1060,7 +1078,7 @@ impl<'a> Parser<'a> {
1060
1078
// but not `ident @ subpat` as `subpat` was already checked and `ident` continues with `@`.
1061
1079
1062
1080
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 )
1064
1082
{
1065
1083
PatKind :: Err ( guar)
1066
1084
} else {
0 commit comments