@@ -6,7 +6,8 @@ use rustc_ast::ast::{AnonConst, AssocTyConstraint, AssocTyConstraintKind, BlockC
66use rustc_ast:: ast:: { Ident , Path , PathSegment , QSelf } ;
77use rustc_ast:: ptr:: P ;
88use rustc_ast:: token:: { self , Token } ;
9- use rustc_errors:: { pluralize, Applicability , PResult } ;
9+ use rustc_ast:: util:: parser:: AssocOp ;
10+ use rustc_errors:: { pluralize, Applicability , DiagnosticBuilder , PResult } ;
1011use rustc_span:: source_map:: { BytePos , Span } ;
1112use rustc_span:: symbol:: { kw, sym} ;
1213
@@ -392,12 +393,110 @@ impl<'a> Parser<'a> {
392393 while let Some ( arg) = self . parse_angle_arg ( ) ? {
393394 args. push ( arg) ;
394395 if !self . eat ( & token:: Comma ) {
396+ if self . token . kind . should_end_const_arg ( ) {
397+ // We will correctly parse a closing `>`, exit.
398+ } else {
399+ // Try to recover from possible `const` arg without braces.
400+ let arg = args. pop ( ) . unwrap ( ) ;
401+ // FIXME: for some reason using `unexpected` or `expected_one_of_not_found` has
402+ // adverse side-effects to subsequent errors and seems to advance the parser.
403+ // We are causing this error here exclusively in case that a `const` expression
404+ // could be recovered from the current parser state, even if followed by more
405+ // arguments after a comma.
406+ let mut err = self . struct_span_err (
407+ self . token . span ,
408+ & format ! (
409+ "expected one of `,` or `>`, found {}" ,
410+ super :: token_descr( & self . token)
411+ ) ,
412+ ) ;
413+ err. span_label ( self . token . span , "expected one of `,` or `>`" ) ;
414+ match self . recover_const_arg ( arg. span ( ) , err) {
415+ Ok ( arg) => {
416+ args. push ( AngleBracketedArg :: Arg ( arg) ) ;
417+ if self . eat ( & token:: Comma ) {
418+ continue ;
419+ }
420+ }
421+ Err ( mut err) => {
422+ args. push ( arg) ;
423+ // We will emit a more generic error later.
424+ err. delay_as_bug ( ) ;
425+ }
426+ }
427+ }
395428 break ;
396429 }
397430 }
398431 Ok ( args)
399432 }
400433
434+ /// Try to recover from possible `const` arg without braces.
435+ ///
436+ /// When encountering code like `foo::< bar + 3 >` or `foo::< bar - baz >` we suggest
437+ /// `foo::<{ bar + 3 }>` and `foo::<{ bar - baz }>` respectively. We only provide a suggestion
438+ /// when we have a high degree of certainty that the resulting expression would be well formed.
439+ pub fn recover_const_arg (
440+ & mut self ,
441+ start : Span ,
442+ mut err : DiagnosticBuilder < ' a > ,
443+ ) -> PResult < ' a , GenericArg > {
444+ let is_op = AssocOp :: from_token ( & self . token )
445+ . and_then ( |op| {
446+ if let AssocOp :: Greater
447+ | AssocOp :: Less
448+ | AssocOp :: ShiftRight
449+ | AssocOp :: GreaterEqual
450+ | AssocOp :: Assign // Don't recover from `foo::<bar = baz>`
451+ | AssocOp :: AssignOp ( _) = op
452+ {
453+ None
454+ } else {
455+ Some ( op)
456+ }
457+ } )
458+ . is_some ( ) ;
459+ // This will be true when a trait object type `Foo +` has been parsed.
460+ let was_op = self . prev_token . kind == token:: BinOp ( token:: Plus ) ;
461+ if !is_op && !was_op {
462+ // We perform these checks and early return to avoid taking a snapshot unnecessarily.
463+ return Err ( err) ;
464+ }
465+ let snapshot = self . clone ( ) ;
466+ if is_op {
467+ self . bump ( ) ;
468+ }
469+ match self . parse_expr_res ( Restrictions :: CONST_EXPR , None ) {
470+ Ok ( expr) => {
471+ if token:: Comma == self . token . kind || self . token . kind . should_end_const_arg ( ) {
472+ // Avoid the following output by checking that we consumed a full const arg:
473+ // help: to write a `const` expression, surround it with braces for it to
474+ // be unambiguous
475+ // |
476+ // LL | let sr: Vec<{ (u32, _, _) = vec![] };
477+ // | ^ ^
478+ err. multipart_suggestion (
479+ "to write a `const` expression, surround it with braces for it to be \
480+ unambiguous",
481+ vec ! [
482+ ( start. shrink_to_lo( ) , "{ " . to_string( ) ) ,
483+ ( expr. span. shrink_to_hi( ) , " }" . to_string( ) ) ,
484+ ] ,
485+ Applicability :: MaybeIncorrect ,
486+ ) ;
487+ let value = self . mk_expr_err ( start. to ( expr. span ) ) ;
488+ err. emit ( ) ;
489+ return Ok ( GenericArg :: Const ( AnonConst { id : ast:: DUMMY_NODE_ID , value } ) ) ;
490+ }
491+ }
492+ Err ( mut err) => {
493+ err. cancel ( ) ;
494+ }
495+ }
496+ * self = snapshot;
497+ Err ( err)
498+ }
499+
401500 /// Parses a single argument in the angle arguments `<...>` of a path segment.
402501 fn parse_angle_arg ( & mut self ) -> PResult < ' a , Option < AngleBracketedArg > > {
403502 if self . check_ident ( ) && self . look_ahead ( 1 , |t| matches ! ( t. kind, token:: Eq | token:: Colon ) )
@@ -474,6 +573,7 @@ impl<'a> Parser<'a> {
474573 /// Parse a generic argument in a path segment.
475574 /// This does not include constraints, e.g., `Item = u8`, which is handled in `parse_angle_arg`.
476575 fn parse_generic_arg ( & mut self ) -> PResult < ' a , Option < GenericArg > > {
576+ let start = self . token . span ;
477577 let arg = if self . check_lifetime ( ) && self . look_ahead ( 1 , |t| !t. is_like_plus ( ) ) {
478578 // Parse lifetime argument.
479579 GenericArg :: Lifetime ( self . expect_lifetime ( ) )
@@ -502,7 +602,13 @@ impl<'a> Parser<'a> {
502602 GenericArg :: Const ( AnonConst { id : ast:: DUMMY_NODE_ID , value } )
503603 } else if self . check_type ( ) {
504604 // Parse type argument.
505- GenericArg :: Type ( self . parse_ty ( ) ?)
605+ match self . parse_ty ( ) {
606+ Ok ( ty) => GenericArg :: Type ( ty) ,
607+ Err ( err) => {
608+ // Try to recover from possible `const` arg without braces.
609+ return self . recover_const_arg ( start, err) . map ( Some ) ;
610+ }
611+ }
506612 } else {
507613 return Ok ( None ) ;
508614 } ;
0 commit comments