@@ -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 , |tok| tok. kind == 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 . kind == token:: Question
381
406
|| ( self . token . kind == token:: OpenDelim ( Delimiter :: Bracket )
382
- && self . look_ahead ( 1 , |tok| tok. kind != token:: CloseDelim ( Delimiter :: Bracket ) ) ) ;
407
+ && self . look_ahead ( 1 , |tok| tok. kind != 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
- // Assignements, 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 . kind == 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
+ // Assignements, 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 . kind == 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
@@ -540,7 +563,7 @@ impl<'a> Parser<'a> {
540
563
self . parse_pat_tuple_struct ( qself, path) ?
541
564
} else {
542
565
match self . maybe_recover_trailing_expr ( span, false ) {
543
- Some ( guar) => PatKind :: Err ( guar) ,
566
+ Some ( ( guar, _ ) ) => PatKind :: Err ( guar) ,
544
567
None => PatKind :: Path ( qself, path) ,
545
568
}
546
569
}
@@ -573,10 +596,10 @@ impl<'a> Parser<'a> {
573
596
// Try to parse everything else as literal with optional minus
574
597
match self . parse_literal_maybe_minus ( ) {
575
598
Ok ( begin) => {
576
- let begin = match self . maybe_recover_trailing_expr ( begin . span , false ) {
577
- Some ( guar ) => self . mk_expr_err ( begin. span , guar ) ,
578
- None => begin ,
579
- } ;
599
+ let begin = self
600
+ . maybe_recover_trailing_expr ( begin. span , false )
601
+ . map ( | ( guar , sp ) | self . mk_expr_err ( sp , guar ) )
602
+ . unwrap_or ( begin ) ;
580
603
581
604
match self . parse_range_end ( ) {
582
605
Some ( form) => self . parse_pat_range_begin_with ( begin, form) ?,
@@ -717,7 +740,8 @@ impl<'a> Parser<'a> {
717
740
// For backward compatibility, `(..)` is a tuple pattern as well.
718
741
let paren_pattern =
719
742
fields. len ( ) == 1 && !( matches ! ( trailing_comma, Trailing :: Yes ) || fields[ 0 ] . is_rest ( ) ) ;
720
- if paren_pattern {
743
+
744
+ let pat = if paren_pattern {
721
745
let pat = fields. into_iter ( ) . next ( ) . unwrap ( ) ;
722
746
let close_paren = self . prev_token . span ;
723
747
@@ -735,7 +759,7 @@ impl<'a> Parser<'a> {
735
759
} ,
736
760
} ) ;
737
761
738
- self . parse_pat_range_begin_with ( begin. clone ( ) , form)
762
+ self . parse_pat_range_begin_with ( begin. clone ( ) , form) ?
739
763
}
740
764
// recover ranges with parentheses around the `(start)..`
741
765
PatKind :: Err ( guar)
@@ -750,15 +774,20 @@ impl<'a> Parser<'a> {
750
774
} ,
751
775
} ) ;
752
776
753
- self . parse_pat_range_begin_with ( self . mk_expr_err ( pat. span , * guar) , form)
777
+ self . parse_pat_range_begin_with ( self . mk_expr_err ( pat. span , * guar) , form) ?
754
778
}
755
779
756
780
// (pat) with optional parentheses
757
- _ => Ok ( PatKind :: Paren ( pat) ) ,
781
+ _ => PatKind :: Paren ( pat) ,
758
782
}
759
783
} else {
760
- Ok ( PatKind :: Tuple ( fields) )
761
- }
784
+ PatKind :: Tuple ( fields)
785
+ } ;
786
+
787
+ Ok ( match self . maybe_recover_trailing_expr ( open_paren. to ( self . prev_token . span ) , false ) {
788
+ None => pat,
789
+ Some ( ( guar, _) ) => PatKind :: Err ( guar) ,
790
+ } )
762
791
}
763
792
764
793
/// Parse a mutable binding with the `mut` token already eaten.
@@ -1011,7 +1040,7 @@ impl<'a> Parser<'a> {
1011
1040
}
1012
1041
1013
1042
Ok ( match recovered {
1014
- Some ( guar) => self . mk_expr_err ( bound . span , guar) ,
1043
+ Some ( ( guar, sp ) ) => self . mk_expr_err ( sp , guar) ,
1015
1044
None => bound,
1016
1045
} )
1017
1046
}
@@ -1080,7 +1109,7 @@ impl<'a> Parser<'a> {
1080
1109
// but not `ident @ subpat` as `subpat` was already checked and `ident` continues with `@`.
1081
1110
1082
1111
let pat = if sub. is_none ( )
1083
- && let Some ( guar) = self . maybe_recover_trailing_expr ( ident. span , false )
1112
+ && let Some ( ( guar, _ ) ) = self . maybe_recover_trailing_expr ( ident. span , false )
1084
1113
{
1085
1114
PatKind :: Err ( guar)
1086
1115
} else {
0 commit comments