@@ -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,61 @@ 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
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+
341397 /// Parses a pattern, with a setting whether modern range patterns (e.g., `a..=b`, `a..b` are
342398 /// allowed).
343399 fn parse_pat_with_range_pat (
@@ -422,6 +478,8 @@ impl<'a> Parser<'a> {
422478 // they are dealt with later in resolve.
423479 self . parse_pat_ident ( BindingAnnotation :: NONE , syntax_loc) ?
424480 } else if self . is_start_of_pat_with_path ( ) {
481+ let snapshot = self . create_snapshot_for_diagnostic ( ) ;
482+
425483 // Parse pattern starting with a path
426484 let ( qself, path) = if self . eat_lt ( ) {
427485 // Parse a qualified path
@@ -443,7 +501,7 @@ impl<'a> Parser<'a> {
443501 } else if self . check ( & token:: OpenDelim ( Delimiter :: Parenthesis ) ) {
444502 self . parse_pat_tuple_struct ( qself, path) ?
445503 } else {
446- PatKind :: Path ( qself, path)
504+ self . maybe_recover_methodcall_or_operator ( snapshot , PatKind :: Path ( qself, path) )
447505 }
448506 } else if matches ! ( self . token. kind, token:: Lifetime ( _) )
449507 // In pattern position, we're totally fine with using "next token isn't colon"
@@ -469,14 +527,18 @@ impl<'a> Parser<'a> {
469527 } ) ;
470528 PatKind :: Lit ( self . mk_expr ( lo, ExprKind :: Lit ( lit) ) )
471529 } else {
530+ let snapshot = self . create_snapshot_for_diagnostic ( ) ;
531+
472532 // 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 ( ) {
474534 Ok ( begin) => match self . parse_range_end ( ) {
475535 Some ( form) => self . parse_pat_range_begin_with ( begin, form) ?,
476536 None => PatKind :: Lit ( begin) ,
477537 } ,
478538 Err ( err) => return self . fatal_unexpected_non_pat ( err, expected) ,
479- }
539+ } ;
540+
541+ self . maybe_recover_methodcall_or_operator ( snapshot, lit)
480542 } ;
481543
482544 let pat = self . mk_pat ( lo. to ( self . prev_token . span ) , pat) ;
@@ -851,6 +913,7 @@ impl<'a> Parser<'a> {
851913 binding_annotation : BindingAnnotation ,
852914 syntax_loc : Option < PatternLocation > ,
853915 ) -> PResult < ' a , PatKind > {
916+ let snapshot = self . create_snapshot_for_diagnostic ( ) ;
854917 let ident = self . parse_ident_common ( false ) ?;
855918
856919 if self . may_recover ( )
@@ -880,7 +943,11 @@ impl<'a> Parser<'a> {
880943 . into_diagnostic ( self . diagnostic ( ) ) ) ;
881944 }
882945
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 } )
884951 }
885952
886953 /// Parse a struct ("record") pattern (e.g. `Foo { ... }` or `Foo::Bar { ... }`).
0 commit comments