@@ -9,10 +9,11 @@ use crate::errors::{
9
9
TrailingVertNotAllowed , UnexpectedLifetimeInPattern , UnexpectedVertVertBeforeFunctionParam ,
10
10
UnexpectedVertVertInPattern ,
11
11
} ;
12
+ use crate :: parser:: diagnostics:: SnapshotParser ;
12
13
use crate :: { maybe_recover_from_interpolated_ty_qpath, maybe_whole} ;
13
14
use rustc_ast:: mut_visit:: { noop_visit_pat, MutVisitor } ;
14
15
use rustc_ast:: ptr:: P ;
15
- use rustc_ast:: token:: { self , Delimiter } ;
16
+ use rustc_ast:: token:: { self , BinOpToken , Delimiter , TokenKind } ;
16
17
use rustc_ast:: {
17
18
self as ast, AttrVec , BindingAnnotation , ByRef , Expr , ExprKind , MacCall , Mutability , Pat ,
18
19
PatField , PatKind , Path , QSelf , RangeEnd , RangeSyntax ,
@@ -338,6 +339,61 @@ impl<'a> Parser<'a> {
338
339
}
339
340
}
340
341
342
+ /// Ensures that the last parsed pattern is not followed by a method call or an binary operator.
343
+ /// Returns `pat` if so, else emit an error, consume the `.methodCall()` or the expression and returns [`PatKind::Wild`]
344
+ /// (thus discarding the pattern).
345
+ fn maybe_recover_methodcall_or_operator (
346
+ & mut self ,
347
+ mut snapshot : SnapshotParser < ' a > ,
348
+ pat : PatKind ,
349
+ ) -> PatKind {
350
+ // check for `.hello()`, but allow `.Hello()` to be recovered as `, Hello()` in `parse_seq_to_before_tokens()`.
351
+ if self . check_noexpect ( & token:: Dot )
352
+ && self . look_ahead ( 1 , |tok| {
353
+ tok. ident ( )
354
+ . and_then ( |( ident, _) | ident. name . to_string ( ) . chars ( ) . next ( ) )
355
+ . is_some_and ( char:: is_lowercase)
356
+ } )
357
+ && self . look_ahead ( 2 , |tok| tok. kind == TokenKind :: OpenDelim ( Delimiter :: Parenthesis ) )
358
+ {
359
+ let span = snapshot. token . span ;
360
+
361
+ if let Ok ( expr) = snapshot. parse_expr ( ) . map_err ( |err| err. cancel ( ) ) {
362
+ // we could have `.hello() + something`, so let's parse only the methodcall
363
+ self . bump ( ) ; // .
364
+ self . bump ( ) ; // hello
365
+ self . parse_paren_comma_seq ( |f| f. parse_expr ( ) ) . unwrap ( ) ; // (arg0, arg1, ...)
366
+
367
+ let span = span. to ( self . prev_token . span ) ;
368
+
369
+ if span != expr. span {
370
+ // we got something after the methodcall
371
+ self . sess . emit_err ( errors:: ExpressionInPattern { span : expr. span } ) ;
372
+ } else {
373
+ // we only have a methodcall
374
+ self . sess . emit_err ( errors:: MethodCallInPattern { span } ) ;
375
+ }
376
+
377
+ self . restore_snapshot ( snapshot) ;
378
+ return PatKind :: Wild ;
379
+ }
380
+ }
381
+
382
+ // `|` may be used in pattern alternatives and lambdas
383
+ if self
384
+ . look_ahead ( 0 , |tok| matches ! ( tok. kind, token:: BinOp ( ref op) if op != & BinOpToken :: Or ) )
385
+ {
386
+ if let Ok ( expr) = snapshot. parse_expr ( ) . map_err ( |err| err. cancel ( ) ) {
387
+ self . sess . emit_err ( errors:: ExpressionInPattern { span : expr. span } ) ;
388
+
389
+ self . restore_snapshot ( snapshot) ;
390
+ return PatKind :: Wild ;
391
+ }
392
+ }
393
+
394
+ pat
395
+ }
396
+
341
397
/// Parses a pattern, with a setting whether modern range patterns (e.g., `a..=b`, `a..b` are
342
398
/// allowed).
343
399
fn parse_pat_with_range_pat (
@@ -422,6 +478,8 @@ impl<'a> Parser<'a> {
422
478
// they are dealt with later in resolve.
423
479
self . parse_pat_ident ( BindingAnnotation :: NONE , syntax_loc) ?
424
480
} else if self . is_start_of_pat_with_path ( ) {
481
+ let snapshot = self . create_snapshot_for_diagnostic ( ) ;
482
+
425
483
// Parse pattern starting with a path
426
484
let ( qself, path) = if self . eat_lt ( ) {
427
485
// Parse a qualified path
@@ -443,7 +501,7 @@ impl<'a> Parser<'a> {
443
501
} else if self . check ( & token:: OpenDelim ( Delimiter :: Parenthesis ) ) {
444
502
self . parse_pat_tuple_struct ( qself, path) ?
445
503
} else {
446
- PatKind :: Path ( qself, path)
504
+ self . maybe_recover_methodcall_or_operator ( snapshot , PatKind :: Path ( qself, path) )
447
505
}
448
506
} else if matches ! ( self . token. kind, token:: Lifetime ( _) )
449
507
// In pattern position, we're totally fine with using "next token isn't colon"
@@ -469,14 +527,18 @@ impl<'a> Parser<'a> {
469
527
} ) ;
470
528
PatKind :: Lit ( self . mk_expr ( lo, ExprKind :: Lit ( lit) ) )
471
529
} else {
530
+ let snapshot = self . create_snapshot_for_diagnostic ( ) ;
531
+
472
532
// Try to parse everything else as literal with optional minus
473
- match self . parse_literal_maybe_minus ( ) {
533
+ let lit = match self . parse_literal_maybe_minus ( ) {
474
534
Ok ( begin) => match self . parse_range_end ( ) {
475
535
Some ( form) => self . parse_pat_range_begin_with ( begin, form) ?,
476
536
None => PatKind :: Lit ( begin) ,
477
537
} ,
478
538
Err ( err) => return self . fatal_unexpected_non_pat ( err, expected) ,
479
- }
539
+ } ;
540
+
541
+ self . maybe_recover_methodcall_or_operator ( snapshot, lit)
480
542
} ;
481
543
482
544
let pat = self . mk_pat ( lo. to ( self . prev_token . span ) , pat) ;
@@ -851,6 +913,7 @@ impl<'a> Parser<'a> {
851
913
binding_annotation : BindingAnnotation ,
852
914
syntax_loc : Option < PatternLocation > ,
853
915
) -> PResult < ' a , PatKind > {
916
+ let snapshot = self . create_snapshot_for_diagnostic ( ) ;
854
917
let ident = self . parse_ident_common ( false ) ?;
855
918
856
919
if self . may_recover ( )
@@ -880,7 +943,11 @@ impl<'a> Parser<'a> {
880
943
. into_diagnostic ( self . diagnostic ( ) ) ) ;
881
944
}
882
945
883
- Ok ( PatKind :: Ident ( binding_annotation, ident, sub) )
946
+ let has_subpat = sub. is_some ( ) ;
947
+ let pat = PatKind :: Ident ( binding_annotation, ident, sub) ;
948
+
949
+ // check for methodcall after the `ident`, but not `ident @ pat` as `pat` was already checked
950
+ Ok ( if !has_subpat { self . maybe_recover_methodcall_or_operator ( snapshot, pat) } else { pat } )
884
951
}
885
952
886
953
/// Parse a struct ("record") pattern (e.g. `Foo { ... }` or `Foo::Bar { ... }`).
0 commit comments