1- use  super :: { ForceCollect ,  Parser ,  PathStyle ,  TrailingToken } ; 
1+ use  super :: { ForceCollect ,  Parser ,  PathStyle ,  Restrictions ,   TrailingToken } ; 
22use  crate :: errors:: { 
33 self ,  AmbiguousRangePattern ,  DotDotDotForRemainingFields ,  DotDotDotRangeToPatternNotAllowed , 
44 DotDotDotRestPattern ,  EnumPatternInsteadOfIdentifier ,  ExpectedBindingLeftOfAt , 
55 ExpectedCommaAfterPatternField ,  GenericArgsInPatRequireTurbofishSyntax , 
66 InclusiveRangeExtraEquals ,  InclusiveRangeMatchArrow ,  InclusiveRangeNoEnd ,  InvalidMutInPattern , 
77 PatternOnWrongSideOfAt ,  RefMutOrderIncorrect ,  RemoveLet ,  RepeatedMutInPattern , 
88 SwitchRefBoxOrder ,  TopLevelOrPatternNotAllowed ,  TopLevelOrPatternNotAllowedSugg , 
9-  TrailingVertNotAllowed ,  UnexpectedLifetimeInPattern ,   UnexpectedParenInRangePat , 
10-  UnexpectedParenInRangePatSugg ,   UnexpectedVertVertBeforeFunctionParam , 
11-  UnexpectedVertVertInPattern , 
9+  TrailingVertNotAllowed ,  UnexpectedExpressionInPattern ,   UnexpectedLifetimeInPattern , 
10+  UnexpectedParenInRangePat ,   UnexpectedParenInRangePatSugg , 
11+  UnexpectedVertVertBeforeFunctionParam ,   UnexpectedVertVertInPattern , 
1212} ; 
1313use  crate :: { maybe_recover_from_interpolated_ty_qpath,  maybe_whole} ; 
1414use  rustc_ast:: mut_visit:: { noop_visit_pat,  MutVisitor } ; 
1515use  rustc_ast:: ptr:: P ; 
16- use  rustc_ast:: token:: { self ,  Delimiter } ; 
16+ use  rustc_ast:: token:: { self ,  BinOpToken ,   Delimiter ,   Token } ; 
1717use  rustc_ast:: { 
1818 self  as  ast,  AttrVec ,  BindingAnnotation ,  ByRef ,  Expr ,  ExprKind ,  MacCall ,  Mutability ,  Pat , 
1919 PatField ,  PatFieldsRest ,  PatKind ,  Path ,  QSelf ,  RangeEnd ,  RangeSyntax , 
@@ -23,7 +23,7 @@ use rustc_errors::{Applicability, DiagnosticBuilder, PResult};
2323use  rustc_session:: errors:: ExprParenthesesNeeded ; 
2424use  rustc_span:: source_map:: { respan,  Spanned } ; 
2525use  rustc_span:: symbol:: { kw,  sym,  Ident } ; 
26- use  rustc_span:: Span ; 
26+ use  rustc_span:: { ErrorGuaranteed ,   Span } ; 
2727use  thin_vec:: { thin_vec,  ThinVec } ; 
2828
2929#[ derive( PartialEq ,  Copy ,  Clone ) ]  
@@ -336,6 +336,95 @@ impl<'a> Parser<'a> {
336336 } 
337337 } 
338338
339+  /// Ensures that the last parsed pattern (or pattern range bound) is not followed by a method call or an operator. 
340+ /// 
341+ /// `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)) 
342+ /// in order to say "expected a pattern range bound" instead of "expected a pattern"; 
343+ /// ```text 
344+ /// 0..=1 + 2 
345+ /// ^^^^^ 
346+ /// ``` 
347+ /// Only the end bound is spanned, and this function have no idea if there were a `..=` before `pat_span`, hence the parameter. 
348+ #[ must_use = "the pattern must be discarded as `PatKind::Err` if this function returns Some" ]  
349+  fn  maybe_recover_trailing_expr ( 
350+  & mut  self , 
351+  pat_span :  Span , 
352+  is_end_bound :  bool , 
353+  )  -> Option < ErrorGuaranteed >  { 
354+  if  self . prev_token . is_keyword ( kw:: Underscore )  || !self . may_recover ( )  { 
355+  // Don't recover anything after an `_` or if recovery is disabled. 
356+  return  None ; 
357+  } 
358+ 
359+  // Check for `.hello()`, but allow `.Hello()` to be recovered as `, Hello()` in `parse_seq_to_before_tokens()`. 
360+  let  has_trailing_method = self . check_noexpect ( & token:: Dot ) 
361+  && self . look_ahead ( 1 ,  |tok| { 
362+  tok. ident ( ) 
363+  . and_then ( |( ident,  _) | ident. name . as_str ( ) . chars ( ) . next ( ) ) 
364+  . is_some_and ( char:: is_lowercase) 
365+  } ) 
366+  && self . look_ahead ( 2 ,  |tok| tok. kind  == token:: OpenDelim ( Delimiter :: Parenthesis ) ) ; 
367+ 
368+  // Check for operators. 
369+  // `|` is excluded as it is used in pattern alternatives and lambdas, 
370+  // `?` is included for error propagation, 
371+  // `[` is included for indexing operations, 
372+  // `[]` is excluded as `a[]` isn't an expression and should be recovered as `a, []` (cf. `tests/ui/parser/pat-lt-bracket-7.rs`) 
373+  let  has_trailing_operator = matches ! ( self . token. kind,  token:: BinOp ( op)  if  op != BinOpToken :: Or ) 
374+  || self . token . kind  == token:: Question 
375+  || ( self . token . kind  == token:: OpenDelim ( Delimiter :: Bracket ) 
376+  && self . look_ahead ( 1 ,  |tok| tok. kind  != token:: CloseDelim ( Delimiter :: Bracket ) ) ) ; 
377+ 
378+  if  !has_trailing_method && !has_trailing_operator { 
379+  // Nothing to recover here. 
380+  return  None ; 
381+  } 
382+ 
383+  // Let's try to parse an expression to emit a better diagnostic. 
384+  let  mut  snapshot = self . create_snapshot_for_diagnostic ( ) ; 
385+  snapshot. restrictions . insert ( Restrictions :: IS_PAT ) ; 
386+ 
387+  // Parse `?`, `.f`, `(arg0, arg1, ...)` or `[expr]` until they've all been eaten. 
388+  if  let  Ok ( expr)  = snapshot
389+  . parse_expr_dot_or_call_with ( 
390+  self . mk_expr_err ( pat_span) ,  // equivalent to transforming the parsed pattern into an `Expr` 
391+  pat_span, 
392+  AttrVec :: new ( ) , 
393+  ) 
394+  . map_err ( |err| err. cancel ( ) ) 
395+  { 
396+  let  non_assoc_span = expr. span ; 
397+ 
398+  // Parse an associative expression such as `+ expr`, `% expr`, ... 
399+  // Assignements, ranges and `|` are disabled by [`Restrictions::IS_PAT`]. 
400+  if  let  Ok ( expr)  =
401+  snapshot. parse_expr_assoc_with ( 0 ,  expr. into ( ) ) . map_err ( |err| err. cancel ( ) ) 
402+  { 
403+  // We got a valid expression. 
404+  self . restore_snapshot ( snapshot) ; 
405+  self . restrictions . remove ( Restrictions :: IS_PAT ) ; 
406+ 
407+  let  is_bound = is_end_bound
408+  // is_start_bound: either `..` or `)..` 
409+  || self . token . is_range_separator ( ) 
410+  || self . token . kind  == token:: CloseDelim ( Delimiter :: Parenthesis ) 
411+  && self . look_ahead ( 1 ,  Token :: is_range_separator) ; 
412+ 
413+  // Check that `parse_expr_assoc_with` didn't eat a rhs. 
414+  let  is_method_call = has_trailing_method && non_assoc_span == expr. span ; 
415+ 
416+  return  Some ( self . dcx ( ) . emit_err ( UnexpectedExpressionInPattern  { 
417+  span :  expr. span , 
418+  is_bound, 
419+  is_method_call, 
420+  } ) ) ; 
421+  } 
422+  } 
423+ 
424+  // We got a trailing method/operator, but we couldn't parse an expression. 
425+  None 
426+  } 
427+ 
339428 /// Parses a pattern, with a setting whether modern range patterns (e.g., `a..=b`, `a..b` are 
340429/// allowed). 
341430fn  parse_pat_with_range_pat ( 
@@ -441,7 +530,10 @@ impl<'a> Parser<'a> {
441530 }  else  if  self . check ( & token:: OpenDelim ( Delimiter :: Parenthesis ) )  { 
442531 self . parse_pat_tuple_struct ( qself,  path) ?
443532 }  else  { 
444-  PatKind :: Path ( qself,  path) 
533+  match  self . maybe_recover_trailing_expr ( span,  false )  { 
534+  Some ( guar)  => PatKind :: Err ( guar) , 
535+  None  => PatKind :: Path ( qself,  path) , 
536+  } 
445537 } 
446538 }  else  if  matches ! ( self . token. kind,  token:: Lifetime ( _) ) 
447539 // In pattern position, we're totally fine with using "next token isn't colon" 
@@ -470,10 +562,17 @@ impl<'a> Parser<'a> {
470562 }  else  { 
471563 // Try to parse everything else as literal with optional minus 
472564 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-  } , 
565+  Ok ( begin)  => { 
566+  let  begin = match  self . maybe_recover_trailing_expr ( begin. span ,  false )  { 
567+  Some ( _)  => self . mk_expr_err ( begin. span ) , 
568+  None  => begin, 
569+  } ; 
570+ 
571+  match  self . parse_range_end ( )  { 
572+  Some ( form)  => self . parse_pat_range_begin_with ( begin,  form) ?, 
573+  None  => PatKind :: Lit ( begin) , 
574+  } 
575+  } 
477576 Err ( err)  => return  self . fatal_unexpected_non_pat ( err,  expected) , 
478577 } 
479578 } ; 
@@ -615,6 +714,21 @@ impl<'a> Parser<'a> {
615714
616715 self . parse_pat_range_begin_with ( begin. clone ( ) ,  form) ?
617716 } 
717+  // recover ranges with parentheses around the `(start)..` 
718+  PatKind :: Err ( _) 
719+  if  self . may_recover ( ) 
720+  && let  Some ( form)  = self . parse_range_end ( )  =>
721+  { 
722+  self . dcx ( ) . emit_err ( UnexpectedParenInRangePat  { 
723+  span :  vec ! [ open_paren,  close_paren] , 
724+  sugg :  UnexpectedParenInRangePatSugg  { 
725+  start_span :  open_paren, 
726+  end_span :  close_paren, 
727+  } , 
728+  } ) ; 
729+ 
730+  self . parse_pat_range_begin_with ( self . mk_expr ( pat. span ,  ExprKind :: Err ) ,  form) ?
731+  } 
618732
619733 // (pat) with optional parentheses 
620734 _ => PatKind :: Paren ( pat) , 
@@ -853,6 +967,8 @@ impl<'a> Parser<'a> {
853967 self . parse_literal_maybe_minus ( ) 
854968 } ?; 
855969
970+  let  recovered = self . maybe_recover_trailing_expr ( bound. span ,  true ) ; 
971+ 
856972 // recover trailing `)` 
857973 if  let  Some ( open_paren)  = open_paren { 
858974 self . expect ( & token:: CloseDelim ( Delimiter :: Parenthesis ) ) ?; 
@@ -866,7 +982,10 @@ impl<'a> Parser<'a> {
866982 } ) ; 
867983 } 
868984
869-  Ok ( bound) 
985+  Ok ( match  recovered { 
986+  Some ( _)  => self . mk_expr_err ( bound. span ) , 
987+  None  => bound, 
988+  } ) 
870989 } 
871990
872991 /// Is this the start of a pattern beginning with a path? 
@@ -929,7 +1048,17 @@ impl<'a> Parser<'a> {
9291048 . create_err ( EnumPatternInsteadOfIdentifier  {  span :  self . prev_token . span  } ) ) ; 
9301049 } 
9311050
932-  Ok ( PatKind :: Ident ( binding_annotation,  ident,  sub) ) 
1051+  // Check for method calls after the `ident`, 
1052+  // but not `ident @ subpat` as `subpat` was already checked and `ident` continues with `@`. 
1053+ 
1054+  let  pat = if  sub. is_none ( ) 
1055+  && let  Some ( guar)  = self . maybe_recover_trailing_expr ( ident. span ,  false ) 
1056+  { 
1057+  PatKind :: Err ( guar) 
1058+  }  else  { 
1059+  PatKind :: Ident ( binding_annotation,  ident,  sub) 
1060+  } ; 
1061+  Ok ( pat) 
9331062 } 
9341063
9351064 /// Parse a struct ("record") pattern (e.g. `Foo { ... }` or `Foo::Bar { ... }`). 
0 commit comments