@@ -25,7 +25,7 @@ use crate::errors::{
25
25
UnexpectedParenInRangePat , UnexpectedParenInRangePatSugg ,
26
26
UnexpectedVertVertBeforeFunctionParam , UnexpectedVertVertInPattern , WrapInParens ,
27
27
} ;
28
- use crate :: parser:: expr:: could_be_unclosed_char_literal;
28
+ use crate :: parser:: expr:: { could_be_unclosed_char_literal, DestructuredFloat } ;
29
29
use crate :: { maybe_recover_from_interpolated_ty_qpath, maybe_whole} ;
30
30
31
31
#[ derive( PartialEq , Copy , Clone ) ]
@@ -342,46 +342,72 @@ impl<'a> Parser<'a> {
342
342
}
343
343
}
344
344
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 .
346
346
///
347
347
/// `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))
348
348
/// in order to say "expected a pattern range bound" instead of "expected a pattern";
349
349
/// ```text
350
350
/// 0..=1 + 2
351
351
/// ^^^^^
352
352
/// ```
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.
354
356
#[ must_use = "the pattern must be discarded as `PatKind::Err` if this function returns Some" ]
355
357
fn maybe_recover_trailing_expr (
356
358
& mut self ,
357
359
pat_span : Span ,
358
360
is_end_bound : bool ,
359
- ) -> Option < ErrorGuaranteed > {
361
+ ) -> Option < ( ErrorGuaranteed , Span ) > {
360
362
if self . prev_token . is_keyword ( kw:: Underscore ) || !self . may_recover ( ) {
361
363
// Don't recover anything after an `_` or if recovery is disabled.
362
364
return None ;
363
365
}
364
366
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 ) // `.`
367
392
&& 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
+ } ) ;
373
397
374
398
// Check for operators.
375
399
// `|` is excluded as it is used in pattern alternatives and lambdas,
376
400
// `?` is included for error propagation,
377
401
// `[` 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
379
404
let has_trailing_operator = matches ! ( self . token. kind, token:: BinOp ( op) if op != BinOpToken :: Or )
380
405
|| self . token == token:: Question
381
406
|| ( 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 ) ;
383
409
384
- if !has_trailing_method && !has_trailing_operator {
410
+ if !has_dot_expr && !has_trailing_operator {
385
411
// Nothing to recover here.
386
412
return None ;
387
413
}
@@ -391,44 +417,41 @@ impl<'a> Parser<'a> {
391
417
snapshot. restrictions . insert ( Restrictions :: IS_PAT ) ;
392
418
393
419
// Parse `?`, `.f`, `(arg0, arg1, ...)` or `[expr]` until they've all been eaten.
394
- if let Ok ( expr) = snapshot
420
+ let Ok ( expr) = snapshot
395
421
. parse_expr_dot_or_call_with (
396
422
AttrVec :: new ( ) ,
397
423
self . mk_expr ( pat_span, ExprKind :: Dummy ) , // equivalent to transforming the parsed pattern into an `Expr`
398
424
pat_span,
399
425
)
400
426
. 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
+ } ;
403
431
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
+ } ;
429
440
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
+ ) )
432
455
}
433
456
434
457
/// 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> {
544
567
self . parse_pat_tuple_struct ( qself, path) ?
545
568
} else {
546
569
match self . maybe_recover_trailing_expr ( span, false ) {
547
- Some ( guar) => PatKind :: Err ( guar) ,
570
+ Some ( ( guar, _ ) ) => PatKind :: Err ( guar) ,
548
571
None => PatKind :: Path ( qself, path) ,
549
572
}
550
573
}
@@ -577,10 +600,10 @@ impl<'a> Parser<'a> {
577
600
// Try to parse everything else as literal with optional minus
578
601
match self . parse_literal_maybe_minus ( ) {
579
602
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 ) ;
584
607
585
608
match self . parse_range_end ( ) {
586
609
Some ( form) => self . parse_pat_range_begin_with ( begin, form) ?,
@@ -721,7 +744,8 @@ impl<'a> Parser<'a> {
721
744
// For backward compatibility, `(..)` is a tuple pattern as well.
722
745
let paren_pattern =
723
746
fields. len ( ) == 1 && !( matches ! ( trailing_comma, Trailing :: Yes ) || fields[ 0 ] . is_rest ( ) ) ;
724
- if paren_pattern {
747
+
748
+ let pat = if paren_pattern {
725
749
let pat = fields. into_iter ( ) . next ( ) . unwrap ( ) ;
726
750
let close_paren = self . prev_token . span ;
727
751
@@ -739,7 +763,7 @@ impl<'a> Parser<'a> {
739
763
} ,
740
764
} ) ;
741
765
742
- self . parse_pat_range_begin_with ( begin. clone ( ) , form)
766
+ self . parse_pat_range_begin_with ( begin. clone ( ) , form) ?
743
767
}
744
768
// recover ranges with parentheses around the `(start)..`
745
769
PatKind :: Err ( guar)
@@ -754,15 +778,20 @@ impl<'a> Parser<'a> {
754
778
} ,
755
779
} ) ;
756
780
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) ?
758
782
}
759
783
760
784
// (pat) with optional parentheses
761
- _ => Ok ( PatKind :: Paren ( pat) ) ,
785
+ _ => PatKind :: Paren ( pat) ,
762
786
}
763
787
} 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
+ } )
766
795
}
767
796
768
797
/// Parse a mutable binding with the `mut` token already eaten.
@@ -1015,7 +1044,7 @@ impl<'a> Parser<'a> {
1015
1044
}
1016
1045
1017
1046
Ok ( match recovered {
1018
- Some ( guar) => self . mk_expr_err ( bound . span , guar) ,
1047
+ Some ( ( guar, sp ) ) => self . mk_expr_err ( sp , guar) ,
1019
1048
None => bound,
1020
1049
} )
1021
1050
}
@@ -1084,7 +1113,7 @@ impl<'a> Parser<'a> {
1084
1113
// but not `ident @ subpat` as `subpat` was already checked and `ident` continues with `@`.
1085
1114
1086
1115
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 )
1088
1117
{
1089
1118
PatKind :: Err ( guar)
1090
1119
} else {
0 commit comments