1
- use super :: { ForceCollect , Parser , PathStyle , TrailingToken } ;
1
+ use super :: { ForceCollect , Parser , PathStyle , Restrictions , TrailingToken } ;
2
2
use crate :: errors:: {
3
3
self , AmbiguousRangePattern , DotDotDotForRemainingFields , DotDotDotRangeToPatternNotAllowed ,
4
4
DotDotDotRestPattern , EnumPatternInsteadOfIdentifier , ExpectedBindingLeftOfAt ,
5
5
ExpectedCommaAfterPatternField , GenericArgsInPatRequireTurbofishSyntax ,
6
6
InclusiveRangeExtraEquals , InclusiveRangeMatchArrow , InclusiveRangeNoEnd , InvalidMutInPattern ,
7
7
PatternOnWrongSideOfAt , RefMutOrderIncorrect , RemoveLet , RepeatedMutInPattern ,
8
8
SwitchRefBoxOrder , TopLevelOrPatternNotAllowed , TopLevelOrPatternNotAllowedSugg ,
9
- TrailingVertNotAllowed , UnexpectedLifetimeInPattern , UnexpectedParenInRangePat ,
10
- UnexpectedParenInRangePatSugg , UnexpectedVertVertBeforeFunctionParam ,
11
- UnexpectedVertVertInPattern ,
9
+ TrailingVertNotAllowed , UnexpectedExpressionInPattern , UnexpectedLifetimeInPattern ,
10
+ UnexpectedParenInRangePat , UnexpectedParenInRangePatSugg ,
11
+ UnexpectedVertVertBeforeFunctionParam , UnexpectedVertVertInPattern ,
12
12
} ;
13
13
use crate :: { maybe_recover_from_interpolated_ty_qpath, maybe_whole} ;
14
14
use rustc_ast:: mut_visit:: { noop_visit_pat, MutVisitor } ;
15
15
use rustc_ast:: ptr:: P ;
16
- use rustc_ast:: token:: { self , Delimiter } ;
16
+ use rustc_ast:: token:: { self , BinOpToken , Delimiter , Token } ;
17
17
use rustc_ast:: {
18
18
self as ast, AttrVec , BindingAnnotation , ByRef , Expr , ExprKind , MacCall , Mutability , Pat ,
19
19
PatField , PatFieldsRest , PatKind , Path , QSelf , RangeEnd , RangeSyntax ,
@@ -23,7 +23,8 @@ use rustc_errors::{Applicability, DiagnosticBuilder, PResult};
23
23
use rustc_session:: errors:: ExprParenthesesNeeded ;
24
24
use rustc_span:: source_map:: { respan, Spanned } ;
25
25
use rustc_span:: symbol:: { kw, sym, Ident } ;
26
- use rustc_span:: Span ;
26
+ use rustc_span:: { ErrorGuaranteed , Span } ;
27
+ use std:: borrow:: Cow ;
27
28
use thin_vec:: { thin_vec, ThinVec } ;
28
29
29
30
#[ derive( PartialEq , Copy , Clone ) ]
@@ -336,6 +337,88 @@ impl<'a> Parser<'a> {
336
337
}
337
338
}
338
339
340
+ /// Ensures that the last parsed pattern is not followed by a method call or an operator.
341
+ #[ must_use = "the pattern must be discarded as `PatKind::Err` if this function returns Some" ]
342
+ fn maybe_recover_trailing_expr < ' b > (
343
+ & ' b mut self ,
344
+ pat : Cow < ' b , P < Expr > > ,
345
+ is_end_bound : bool ,
346
+ ) -> Option < ErrorGuaranteed > {
347
+ if self . prev_token . is_keyword ( kw:: Underscore ) || !self . may_recover ( ) {
348
+ // Don't recover anything after an `_` or if recovery is disabled.
349
+ return None ;
350
+ }
351
+
352
+ // Check for `.hello()`, but allow `.Hello()` to be recovered as `, Hello()` in `parse_seq_to_before_tokens()`.
353
+ let has_trailing_method = self . check_noexpect ( & token:: Dot )
354
+ && self . look_ahead ( 1 , |tok| {
355
+ tok. ident ( )
356
+ . and_then ( |( ident, _) | ident. name . to_string ( ) . chars ( ) . next ( ) )
357
+ . is_some_and ( char:: is_lowercase)
358
+ } )
359
+ && self . look_ahead ( 2 , |tok| tok. kind == token:: OpenDelim ( Delimiter :: Parenthesis ) ) ;
360
+
361
+ // Check for operators.
362
+ // `|` is excluded as it is used in pattern alternatives and lambdas,
363
+ // `?` is included for error propagation,
364
+ // `[` is included for indexing operations,
365
+ // `[]` is excluded as `a[]` isn't an expression and should be recovered as `a, []` (cf. `tests/ui/parser/pat-lt-bracket-7.rs`)
366
+ let has_trailing_operator = matches ! ( self . token. kind, token:: BinOp ( op) if op != BinOpToken :: Or )
367
+ || self . token . kind == token:: Question
368
+ || ( self . token . kind == token:: OpenDelim ( Delimiter :: Bracket )
369
+ && self . look_ahead ( 1 , |tok| tok. kind != token:: CloseDelim ( Delimiter :: Bracket ) ) ) ;
370
+
371
+ if !has_trailing_method && !has_trailing_operator {
372
+ // Nothing to recover here.
373
+ return None ;
374
+ }
375
+
376
+ // Let's try to parse an expression to emit a better diagnostic.
377
+ let pat = pat. into_owned ( ) ;
378
+ let pat_span = pat. span ;
379
+
380
+ let mut snapshot = self . create_snapshot_for_diagnostic ( ) ;
381
+ snapshot. restrictions . insert ( Restrictions :: IS_PAT ) ;
382
+
383
+ // Parse `?`, `.f`, `(arg0, arg1, ...)` or `[expr]` until they've all been eaten.
384
+ if let Ok ( expr) = snapshot
385
+ . parse_expr_dot_or_call_with ( pat, pat_span, AttrVec :: new ( ) )
386
+ . map_err ( |err| err. cancel ( ) )
387
+ {
388
+ let non_assoc_span = expr. span ;
389
+
390
+ // Parse an associative expression such as `+ expr`, `% expr`, ...
391
+ // Assignements, ranges and `|` are disabled by [`Restrictions::IS_PAT`].
392
+ if let Ok ( expr) =
393
+ snapshot. parse_expr_assoc_with ( 0 , expr. into ( ) ) . map_err ( |err| err. cancel ( ) )
394
+ {
395
+ // We got a valid expression.
396
+ self . restore_snapshot ( snapshot) ;
397
+ self . restrictions . remove ( Restrictions :: IS_PAT ) ;
398
+
399
+ let span = expr. span ;
400
+
401
+ let is_bound = is_end_bound
402
+ // is_start_bound: either `..` or `)..`
403
+ || self . token . is_range_separator ( )
404
+ || self . token . kind == token:: CloseDelim ( Delimiter :: Parenthesis )
405
+ && self . look_ahead ( 1 , Token :: is_range_separator) ;
406
+
407
+ // Check that `parse_expr_assoc_with` didn't eat a rhs.
408
+ let is_method_call = has_trailing_method && non_assoc_span == span;
409
+
410
+ return Some ( self . dcx ( ) . emit_err ( UnexpectedExpressionInPattern {
411
+ span,
412
+ is_bound,
413
+ is_method_call,
414
+ } ) ) ;
415
+ }
416
+ }
417
+
418
+ // We got a trailing method/operator, but we couldn't parse an expression.
419
+ None
420
+ }
421
+
339
422
/// Parses a pattern, with a setting whether modern range patterns (e.g., `a..=b`, `a..b` are
340
423
/// allowed).
341
424
fn parse_pat_with_range_pat (
@@ -441,7 +524,11 @@ impl<'a> Parser<'a> {
441
524
} else if self . check ( & token:: OpenDelim ( Delimiter :: Parenthesis ) ) {
442
525
self . parse_pat_tuple_struct ( qself, path) ?
443
526
} else {
444
- PatKind :: Path ( qself, path)
527
+ let pat = self . mk_expr ( span, ExprKind :: Path ( qself. clone ( ) , path. clone ( ) ) ) ;
528
+ match self . maybe_recover_trailing_expr ( Cow :: Owned ( pat) , false ) {
529
+ Some ( guar) => PatKind :: Err ( guar) ,
530
+ None => PatKind :: Path ( qself, path) ,
531
+ }
445
532
}
446
533
} else if matches ! ( self . token. kind, token:: Lifetime ( _) )
447
534
// In pattern position, we're totally fine with using "next token isn't colon"
@@ -470,10 +557,18 @@ impl<'a> Parser<'a> {
470
557
} else {
471
558
// Try to parse everything else as literal with optional minus
472
559
match self . parse_literal_maybe_minus ( ) {
473
- Ok ( begin) => match self . parse_range_end ( ) {
474
- Some ( form) => self . parse_pat_range_begin_with ( begin, form) ?,
475
- None => PatKind :: Lit ( begin) ,
476
- } ,
560
+ Ok ( begin) => {
561
+ let begin = match self . maybe_recover_trailing_expr ( Cow :: Borrowed ( & begin) , false )
562
+ {
563
+ Some ( _) => self . mk_expr_err ( begin. span ) ,
564
+ None => begin,
565
+ } ;
566
+
567
+ match self . parse_range_end ( ) {
568
+ Some ( form) => self . parse_pat_range_begin_with ( begin, form) ?,
569
+ None => PatKind :: Lit ( begin) ,
570
+ }
571
+ }
477
572
Err ( err) => return self . fatal_unexpected_non_pat ( err, expected) ,
478
573
}
479
574
} ;
@@ -615,6 +710,21 @@ impl<'a> Parser<'a> {
615
710
616
711
self . parse_pat_range_begin_with ( begin. clone ( ) , form) ?
617
712
}
713
+ // recover ranges with parentheses around the `(start)..`
714
+ PatKind :: Err ( _)
715
+ if self . may_recover ( )
716
+ && let Some ( form) = self . parse_range_end ( ) =>
717
+ {
718
+ self . dcx ( ) . emit_err ( UnexpectedParenInRangePat {
719
+ span : vec ! [ open_paren, close_paren] ,
720
+ sugg : UnexpectedParenInRangePatSugg {
721
+ start_span : open_paren,
722
+ end_span : close_paren,
723
+ } ,
724
+ } ) ;
725
+
726
+ self . parse_pat_range_begin_with ( self . mk_expr ( pat. span , ExprKind :: Err ) , form) ?
727
+ }
618
728
619
729
// (pat) with optional parentheses
620
730
_ => PatKind :: Paren ( pat) ,
@@ -853,6 +963,8 @@ impl<'a> Parser<'a> {
853
963
self . parse_literal_maybe_minus ( )
854
964
} ?;
855
965
966
+ let recovered = self . maybe_recover_trailing_expr ( Cow :: Borrowed ( & bound) , true ) ;
967
+
856
968
// recover trailing `)`
857
969
if let Some ( open_paren) = open_paren {
858
970
self . expect ( & token:: CloseDelim ( Delimiter :: Parenthesis ) ) ?;
@@ -866,7 +978,10 @@ impl<'a> Parser<'a> {
866
978
} ) ;
867
979
}
868
980
869
- Ok ( bound)
981
+ Ok ( match recovered {
982
+ Some ( _) => self . mk_expr_err ( bound. span ) ,
983
+ None => bound,
984
+ } )
870
985
}
871
986
872
987
/// Is this the start of a pattern beginning with a path?
@@ -929,7 +1044,18 @@ impl<'a> Parser<'a> {
929
1044
. create_err ( EnumPatternInsteadOfIdentifier { span : self . prev_token . span } ) ) ;
930
1045
}
931
1046
932
- Ok ( PatKind :: Ident ( binding_annotation, ident, sub) )
1047
+ // Check for method calls after the `ident`,
1048
+ // but not `ident @ subpat` as `subpat` was already checked and `ident` continues with `@`.
1049
+
1050
+ let expr = self . mk_expr ( ident. span , ExprKind :: Path ( None , Path :: from_ident ( ident) ) ) ;
1051
+ let pat = if sub. is_none ( )
1052
+ && let Some ( guar) = self . maybe_recover_trailing_expr ( Cow :: Owned ( expr) , false )
1053
+ {
1054
+ PatKind :: Err ( guar)
1055
+ } else {
1056
+ PatKind :: Ident ( binding_annotation, ident, sub)
1057
+ } ;
1058
+ Ok ( pat)
933
1059
}
934
1060
935
1061
/// Parse a struct ("record") pattern (e.g. `Foo { ... }` or `Foo::Bar { ... }`).
0 commit comments