@@ -9,10 +9,11 @@ use crate::errors::{
99 TrailingVertNotAllowed , UnexpectedLifetimeInPattern , UnexpectedVertVertBeforeFunctionParam ,
1010 UnexpectedVertVertInPattern ,
1111} ;
12+ use crate :: parser:: diagnostics:: SnapshotParser ;
1213use crate :: { maybe_recover_from_interpolated_ty_qpath, maybe_whole} ;
1314use rustc_ast:: mut_visit:: { noop_visit_pat, MutVisitor } ;
1415use rustc_ast:: ptr:: P ;
15- use rustc_ast:: token:: { self , Delimiter } ;
16+ use rustc_ast:: token:: { self , BinOpToken , Delimiter , TokenKind } ;
1617use rustc_ast:: {
1718 self as ast, AttrVec , BindingAnnotation , ByRef , Expr , ExprKind , MacCall , Mutability , Pat ,
1819 PatField , PatKind , Path , QSelf , RangeEnd , RangeSyntax ,
@@ -338,6 +339,59 @@ impl<'a> Parser<'a> {
338339 }
339340 }
340341
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 . look_ahead ( 0 , |tok| matches ! ( tok. kind, token:: BinOp ( op) if op != BinOpToken :: Or ) ) {
384+ if let Ok ( expr) = snapshot. parse_expr ( ) . map_err ( |err| err. cancel ( ) ) {
385+ self . sess . emit_err ( errors:: ExpressionInPattern { span : expr. span } ) ;
386+
387+ self . restore_snapshot ( snapshot) ;
388+ return PatKind :: Wild ;
389+ }
390+ }
391+
392+ pat
393+ }
394+
341395 /// Parses a pattern, with a setting whether modern range patterns (e.g., `a..=b`, `a..b` are
342396 /// allowed).
343397 fn parse_pat_with_range_pat (
@@ -422,6 +476,8 @@ impl<'a> Parser<'a> {
422476 // they are dealt with later in resolve.
423477 self . parse_pat_ident ( BindingAnnotation :: NONE , syntax_loc) ?
424478 } else if self . is_start_of_pat_with_path ( ) {
479+ let snapshot = self . create_snapshot_for_diagnostic ( ) ;
480+
425481 // Parse pattern starting with a path
426482 let ( qself, path) = if self . eat_lt ( ) {
427483 // Parse a qualified path
@@ -443,7 +499,7 @@ impl<'a> Parser<'a> {
443499 } else if self . check ( & token:: OpenDelim ( Delimiter :: Parenthesis ) ) {
444500 self . parse_pat_tuple_struct ( qself, path) ?
445501 } else {
446- PatKind :: Path ( qself, path)
502+ self . maybe_recover_methodcall_or_operator ( snapshot , PatKind :: Path ( qself, path) )
447503 }
448504 } else if matches ! ( self . token. kind, token:: Lifetime ( _) )
449505 // In pattern position, we're totally fine with using "next token isn't colon"
@@ -469,14 +525,18 @@ impl<'a> Parser<'a> {
469525 } ) ;
470526 PatKind :: Lit ( self . mk_expr ( lo, ExprKind :: Lit ( lit) ) )
471527 } else {
528+ let snapshot = self . create_snapshot_for_diagnostic ( ) ;
529+
472530 // Try to parse everything else as literal with optional minus
473- match self . parse_literal_maybe_minus ( ) {
531+ let lit = match self . parse_literal_maybe_minus ( ) {
474532 Ok ( begin) => match self . parse_range_end ( ) {
475533 Some ( form) => self . parse_pat_range_begin_with ( begin, form) ?,
476534 None => PatKind :: Lit ( begin) ,
477535 } ,
478536 Err ( err) => return self . fatal_unexpected_non_pat ( err, expected) ,
479- }
537+ } ;
538+
539+ self . maybe_recover_methodcall_or_operator ( snapshot, lit)
480540 } ;
481541
482542 let pat = self . mk_pat ( lo. to ( self . prev_token . span ) , pat) ;
@@ -851,6 +911,7 @@ impl<'a> Parser<'a> {
851911 binding_annotation : BindingAnnotation ,
852912 syntax_loc : Option < PatternLocation > ,
853913 ) -> PResult < ' a , PatKind > {
914+ let snapshot = self . create_snapshot_for_diagnostic ( ) ;
854915 let ident = self . parse_ident_common ( false ) ?;
855916
856917 if self . may_recover ( )
@@ -880,7 +941,11 @@ impl<'a> Parser<'a> {
880941 . into_diagnostic ( self . diagnostic ( ) ) ) ;
881942 }
882943
883- Ok ( PatKind :: Ident ( binding_annotation, ident, sub) )
944+ let has_subpat = sub. is_some ( ) ;
945+ let pat = PatKind :: Ident ( binding_annotation, ident, sub) ;
946+
947+ // check for methodcall after the `ident`, but not `ident @ pat` as `pat` was already checked
948+ Ok ( if !has_subpat { self . maybe_recover_methodcall_or_operator ( snapshot, pat) } else { pat } )
884949 }
885950
886951 /// Parse a struct ("record") pattern (e.g. `Foo { ... }` or `Foo::Bar { ... }`).
0 commit comments