@@ -10,7 +10,7 @@ use crate::errors::{
10
10
UnexpectedParenInRangePat , UnexpectedParenInRangePatSugg ,
11
11
UnexpectedVertVertBeforeFunctionParam , UnexpectedVertVertInPattern , WrapInParens ,
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 ;
@@ -341,46 +341,72 @@ impl<'a> Parser<'a> {
341
341
}
342
342
}
343
343
344
- /// Ensures that the last parsed pattern (or pattern range bound) is not followed by a method call or an operator .
344
+ /// Ensures that the last parsed pattern (or pattern range bound) is not followed by an expression .
345
345
///
346
346
/// `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))
347
347
/// in order to say "expected a pattern range bound" instead of "expected a pattern";
348
348
/// ```text
349
349
/// 0..=1 + 2
350
350
/// ^^^^^
351
351
/// ```
352
- /// Only the end bound is spanned, and this function have no idea if there were a `..=` before `pat_span`, hence the parameter.
352
+ /// 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.
353
+ ///
354
+ /// This function returns `Some` if a trailing expression was recovered, and said expression's span.
353
355
#[ must_use = "the pattern must be discarded as `PatKind::Err` if this function returns Some" ]
354
356
fn maybe_recover_trailing_expr (
355
357
& mut self ,
356
358
pat_span : Span ,
357
359
is_end_bound : bool ,
358
- ) -> Option < ErrorGuaranteed > {
360
+ ) -> Option < ( ErrorGuaranteed , Span ) > {
359
361
if self . prev_token . is_keyword ( kw:: Underscore ) || !self . may_recover ( ) {
360
362
// Don't recover anything after an `_` or if recovery is disabled.
361
363
return None ;
362
364
}
363
365
364
- // Check for `.hello()`, but allow `.Hello()` to be recovered as `, Hello()` in `parse_seq_to_before_tokens()`.
365
- let has_trailing_method = self . check_noexpect ( & token:: Dot )
366
+ // Returns `true` iff `token` is an unsuffixed integer.
367
+ let is_one_tuple_index = |_: & Self , token : & Token | -> bool {
368
+ use token:: { Lit , LitKind } ;
369
+
370
+ matches ! (
371
+ token. kind,
372
+ token:: Literal ( Lit { kind: LitKind :: Integer , symbol: _, suffix: None } )
373
+ )
374
+ } ;
375
+
376
+ // Returns `true` iff `token` is an unsuffixed `x.y` float.
377
+ let is_two_tuple_indexes = |this : & Self , token : & Token | -> bool {
378
+ use token:: { Lit , LitKind } ;
379
+
380
+ if let token:: Literal ( Lit { kind : LitKind :: Float , symbol, suffix : None } ) = token. kind
381
+ && let DestructuredFloat :: MiddleDot ( ..) = this. break_up_float ( symbol, token. span )
382
+ {
383
+ true
384
+ } else {
385
+ false
386
+ }
387
+ } ;
388
+
389
+ // Check for `.hello` or `.0`.
390
+ let has_dot_expr = self . check_noexpect ( & token:: Dot ) // `.`
366
391
&& self . look_ahead ( 1 , |tok| {
367
- tok. ident ( )
368
- . and_then ( |( ident, _) | ident. name . as_str ( ) . chars ( ) . next ( ) )
369
- . is_some_and ( char:: is_lowercase)
370
- } )
371
- && self . look_ahead ( 2 , |tok| tok. kind == token:: OpenDelim ( Delimiter :: Parenthesis ) ) ;
392
+ tok. is_ident ( ) // `hello`
393
+ || is_one_tuple_index ( & self , & tok) // `0`
394
+ || is_two_tuple_indexes ( & self , & tok) // `0.0`
395
+ } ) ;
372
396
373
397
// Check for operators.
374
398
// `|` is excluded as it is used in pattern alternatives and lambdas,
375
399
// `?` is included for error propagation,
376
400
// `[` is included for indexing operations,
377
- // `[]` is excluded as `a[]` isn't an expression and should be recovered as `a, []` (cf. `tests/ui/parser/pat-lt-bracket-7.rs`)
401
+ // `[]` is excluded as `a[]` isn't an expression and should be recovered as `a, []` (cf. `tests/ui/parser/pat-lt-bracket-7.rs`),
402
+ // `as` is included for type casts
378
403
let has_trailing_operator = matches ! ( self . token. kind, token:: BinOp ( op) if op != BinOpToken :: Or )
379
404
|| self . token . kind == token:: Question
380
405
|| ( self . token . kind == token:: OpenDelim ( Delimiter :: Bracket )
381
- && self . look_ahead ( 1 , |tok| tok. kind != token:: CloseDelim ( Delimiter :: Bracket ) ) ) ;
406
+ && self . look_ahead ( 1 , |tok| tok. kind != token:: CloseDelim ( Delimiter :: Bracket ) ) ) // excludes `[]`
407
+ || self . token . is_keyword ( kw:: As ) ;
382
408
383
- if !has_trailing_method && !has_trailing_operator {
409
+ if !has_dot_expr && !has_trailing_operator {
384
410
// Nothing to recover here.
385
411
return None ;
386
412
}
@@ -390,43 +416,40 @@ impl<'a> Parser<'a> {
390
416
snapshot. restrictions . insert ( Restrictions :: IS_PAT ) ;
391
417
392
418
// Parse `?`, `.f`, `(arg0, arg1, ...)` or `[expr]` until they've all been eaten.
393
- if let Ok ( expr) = snapshot
419
+ let Ok ( expr) = snapshot
394
420
. parse_expr_dot_or_call_with (
395
421
AttrVec :: new ( ) ,
396
422
self . mk_expr ( pat_span, ExprKind :: Dummy ) , // equivalent to transforming the parsed pattern into an `Expr`
397
423
pat_span,
398
424
)
399
425
. map_err ( |err| err. cancel ( ) )
400
- {
401
- let non_assoc_span = expr. span ;
402
-
403
- // Parse an associative expression such as `+ expr`, `% expr`, ...
404
- // Assignements, ranges and `|` are disabled by [`Restrictions::IS_PAT`].
405
- let lhs = LhsExpr :: Parsed { expr, starts_statement : false } ;
406
- if let Ok ( expr) = snapshot. parse_expr_assoc_with ( 0 , lhs) . map_err ( |err| err. cancel ( ) ) {
407
- // We got a valid expression.
408
- self . restore_snapshot ( snapshot) ;
409
- self . restrictions . remove ( Restrictions :: IS_PAT ) ;
410
-
411
- let is_bound = is_end_bound
412
- // is_start_bound: either `..` or `)..`
413
- || self . token . is_range_separator ( )
414
- || self . token . kind == token:: CloseDelim ( Delimiter :: Parenthesis )
415
- && self . look_ahead ( 1 , Token :: is_range_separator) ;
416
-
417
- // Check that `parse_expr_assoc_with` didn't eat a rhs.
418
- let is_method_call = has_trailing_method && non_assoc_span == expr. span ;
419
-
420
- return Some ( self . dcx ( ) . emit_err ( UnexpectedExpressionInPattern {
421
- span : expr. span ,
422
- is_bound,
423
- is_method_call,
424
- } ) ) ;
425
- }
426
- }
426
+ else {
427
+ // We got a trailing method/operator, but that wasn't an expression.
428
+ return None ;
429
+ } ;
430
+
431
+ // Parse an associative expression such as `+ expr`, `% expr`, ...
432
+ // Assignements, ranges and `|` are disabled by [`Restrictions::IS_PAT`].
433
+ let lhs = LhsExpr :: Parsed { expr, starts_statement : false } ;
434
+ let Ok ( expr) = snapshot. parse_expr_assoc_with ( 0 , lhs) . map_err ( |err| err. cancel ( ) ) else {
435
+ // We got a trailing method/operator, but that wasn't an expression.
436
+ return None ;
437
+ } ;
427
438
428
- // We got a trailing method/operator, but we couldn't parse an expression.
429
- None
439
+ // We got a valid expression.
440
+ self . restore_snapshot ( snapshot) ;
441
+ self . restrictions . remove ( Restrictions :: IS_PAT ) ;
442
+
443
+ let is_bound = is_end_bound
444
+ // is_start_bound: either `..` or `)..`
445
+ || self . token . is_range_separator ( )
446
+ || self . token . kind == token:: CloseDelim ( Delimiter :: Parenthesis )
447
+ && self . look_ahead ( 1 , Token :: is_range_separator) ;
448
+
449
+ Some ( (
450
+ self . dcx ( ) . emit_err ( UnexpectedExpressionInPattern { span : expr. span , is_bound } ) ,
451
+ expr. span ,
452
+ ) )
430
453
}
431
454
432
455
/// Parses a pattern, with a setting whether modern range patterns (e.g., `a..=b`, `a..b` are
@@ -538,7 +561,7 @@ impl<'a> Parser<'a> {
538
561
self . parse_pat_tuple_struct ( qself, path) ?
539
562
} else {
540
563
match self . maybe_recover_trailing_expr ( span, false ) {
541
- Some ( guar) => PatKind :: Err ( guar) ,
564
+ Some ( ( guar, _ ) ) => PatKind :: Err ( guar) ,
542
565
None => PatKind :: Path ( qself, path) ,
543
566
}
544
567
}
@@ -571,10 +594,10 @@ impl<'a> Parser<'a> {
571
594
// Try to parse everything else as literal with optional minus
572
595
match self . parse_literal_maybe_minus ( ) {
573
596
Ok ( begin) => {
574
- let begin = match self . maybe_recover_trailing_expr ( begin . span , false ) {
575
- Some ( guar ) => self . mk_expr_err ( begin. span , guar ) ,
576
- None => begin ,
577
- } ;
597
+ let begin = self
598
+ . maybe_recover_trailing_expr ( begin. span , false )
599
+ . map ( | ( guar , sp ) | self . mk_expr_err ( sp , guar ) )
600
+ . unwrap_or ( begin ) ;
578
601
579
602
match self . parse_range_end ( ) {
580
603
Some ( form) => self . parse_pat_range_begin_with ( begin, form) ?,
@@ -715,7 +738,8 @@ impl<'a> Parser<'a> {
715
738
// For backward compatibility, `(..)` is a tuple pattern as well.
716
739
let paren_pattern =
717
740
fields. len ( ) == 1 && !( matches ! ( trailing_comma, Trailing :: Yes ) || fields[ 0 ] . is_rest ( ) ) ;
718
- if paren_pattern {
741
+
742
+ let pat = if paren_pattern {
719
743
let pat = fields. into_iter ( ) . next ( ) . unwrap ( ) ;
720
744
let close_paren = self . prev_token . span ;
721
745
@@ -733,7 +757,7 @@ impl<'a> Parser<'a> {
733
757
} ,
734
758
} ) ;
735
759
736
- self . parse_pat_range_begin_with ( begin. clone ( ) , form)
760
+ self . parse_pat_range_begin_with ( begin. clone ( ) , form) ?
737
761
}
738
762
// recover ranges with parentheses around the `(start)..`
739
763
PatKind :: Err ( guar)
@@ -748,15 +772,20 @@ impl<'a> Parser<'a> {
748
772
} ,
749
773
} ) ;
750
774
751
- self . parse_pat_range_begin_with ( self . mk_expr_err ( pat. span , * guar) , form)
775
+ self . parse_pat_range_begin_with ( self . mk_expr_err ( pat. span , * guar) , form) ?
752
776
}
753
777
754
778
// (pat) with optional parentheses
755
- _ => Ok ( PatKind :: Paren ( pat) ) ,
779
+ _ => PatKind :: Paren ( pat) ,
756
780
}
757
781
} else {
758
- Ok ( PatKind :: Tuple ( fields) )
759
- }
782
+ PatKind :: Tuple ( fields)
783
+ } ;
784
+
785
+ Ok ( match self . maybe_recover_trailing_expr ( open_paren. to ( self . prev_token . span ) , false ) {
786
+ None => pat,
787
+ Some ( ( guar, _) ) => PatKind :: Err ( guar) ,
788
+ } )
760
789
}
761
790
762
791
/// Parse a mutable binding with the `mut` token already eaten.
@@ -1009,7 +1038,7 @@ impl<'a> Parser<'a> {
1009
1038
}
1010
1039
1011
1040
Ok ( match recovered {
1012
- Some ( guar) => self . mk_expr_err ( bound . span , guar) ,
1041
+ Some ( ( guar, sp ) ) => self . mk_expr_err ( sp , guar) ,
1013
1042
None => bound,
1014
1043
} )
1015
1044
}
@@ -1078,7 +1107,7 @@ impl<'a> Parser<'a> {
1078
1107
// but not `ident @ subpat` as `subpat` was already checked and `ident` continues with `@`.
1079
1108
1080
1109
let pat = if sub. is_none ( )
1081
- && let Some ( guar) = self . maybe_recover_trailing_expr ( ident. span , false )
1110
+ && let Some ( ( guar, _ ) ) = self . maybe_recover_trailing_expr ( ident. span , false )
1082
1111
{
1083
1112
PatKind :: Err ( guar)
1084
1113
} else {
0 commit comments