@@ -6,7 +6,8 @@ use rustc_ast::ast::{AnonConst, AssocTyConstraint, AssocTyConstraintKind, BlockC
6
6
use rustc_ast:: ast:: { Ident , Path , PathSegment , QSelf } ;
7
7
use rustc_ast:: ptr:: P ;
8
8
use 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 } ;
10
11
use rustc_span:: source_map:: { BytePos , Span } ;
11
12
use rustc_span:: symbol:: { kw, sym} ;
12
13
@@ -392,12 +393,110 @@ impl<'a> Parser<'a> {
392
393
while let Some ( arg) = self . parse_angle_arg ( ) ? {
393
394
args. push ( arg) ;
394
395
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
+ }
395
428
break ;
396
429
}
397
430
}
398
431
Ok ( args)
399
432
}
400
433
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
+
401
500
/// Parses a single argument in the angle arguments `<...>` of a path segment.
402
501
fn parse_angle_arg ( & mut self ) -> PResult < ' a , Option < AngleBracketedArg > > {
403
502
if self . check_ident ( ) && self . look_ahead ( 1 , |t| matches ! ( t. kind, token:: Eq | token:: Colon ) )
@@ -474,6 +573,7 @@ impl<'a> Parser<'a> {
474
573
/// Parse a generic argument in a path segment.
475
574
/// This does not include constraints, e.g., `Item = u8`, which is handled in `parse_angle_arg`.
476
575
fn parse_generic_arg ( & mut self ) -> PResult < ' a , Option < GenericArg > > {
576
+ let start = self . token . span ;
477
577
let arg = if self . check_lifetime ( ) && self . look_ahead ( 1 , |t| !t. is_like_plus ( ) ) {
478
578
// Parse lifetime argument.
479
579
GenericArg :: Lifetime ( self . expect_lifetime ( ) )
@@ -502,7 +602,13 @@ impl<'a> Parser<'a> {
502
602
GenericArg :: Const ( AnonConst { id : ast:: DUMMY_NODE_ID , value } )
503
603
} else if self . check_type ( ) {
504
604
// 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
+ }
506
612
} else {
507
613
return Ok ( None ) ;
508
614
} ;
0 commit comments