@@ -42,8 +42,10 @@ use rustc_ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty
42
42
use rustc_ast:: { Arm , Async , BlockCheckMode , Expr , ExprKind , Label , Movability , RangeLimits } ;
43
43
use rustc_ast:: { ClosureBinder , StmtKind } ;
44
44
use rustc_ast_pretty:: pprust;
45
- use rustc_errors:: IntoDiagnostic ;
46
- use rustc_errors:: { Applicability , Diagnostic , PResult } ;
45
+ use rustc_errors:: {
46
+ Applicability , Diagnostic , DiagnosticBuilder , ErrorGuaranteed , IntoDiagnostic , PResult ,
47
+ StashKey ,
48
+ } ;
47
49
use rustc_session:: errors:: ExprParenthesesNeeded ;
48
50
use rustc_session:: lint:: builtin:: BREAK_WITH_LABEL_AND_LOOP ;
49
51
use rustc_session:: lint:: BuiltinLintDiagnostics ;
@@ -1513,11 +1515,11 @@ impl<'a> Parser<'a> {
1513
1515
/// Parse `'label: $expr`. The label is already parsed.
1514
1516
fn parse_labeled_expr (
1515
1517
& mut self ,
1516
- label : Label ,
1518
+ label_ : Label ,
1517
1519
mut consume_colon : bool ,
1518
1520
) -> PResult < ' a , P < Expr > > {
1519
- let lo = label . ident . span ;
1520
- let label = Some ( label ) ;
1521
+ let lo = label_ . ident . span ;
1522
+ let label = Some ( label_ ) ;
1521
1523
let ate_colon = self . eat ( & token:: Colon ) ;
1522
1524
let expr = if self . eat_keyword ( kw:: While ) {
1523
1525
self . parse_while_expr ( label, lo)
@@ -1529,6 +1531,19 @@ impl<'a> Parser<'a> {
1529
1531
|| self . token . is_whole_block ( )
1530
1532
{
1531
1533
self . parse_block_expr ( label, lo, BlockCheckMode :: Default )
1534
+ } else if !ate_colon
1535
+ && ( matches ! ( self . token. kind, token:: CloseDelim ( _) | token:: Comma )
1536
+ || self . token . is_op ( ) )
1537
+ {
1538
+ let lit = self . recover_unclosed_char ( label_. ident , |self_| {
1539
+ self_. sess . create_err ( UnexpectedTokenAfterLabel {
1540
+ span : self_. token . span ,
1541
+ remove_label : None ,
1542
+ enclose_in_block : None ,
1543
+ } )
1544
+ } ) ;
1545
+ consume_colon = false ;
1546
+ Ok ( self . mk_expr ( lo, ExprKind :: Lit ( lit) ) )
1532
1547
} else if !ate_colon
1533
1548
&& ( self . check_noexpect ( & TokenKind :: Comma ) || self . check_noexpect ( & TokenKind :: Gt ) )
1534
1549
{
@@ -1603,6 +1618,39 @@ impl<'a> Parser<'a> {
1603
1618
Ok ( expr)
1604
1619
}
1605
1620
1621
+ /// Emit an error when a char is parsed as a lifetime because of a missing quote
1622
+ pub ( super ) fn recover_unclosed_char (
1623
+ & mut self ,
1624
+ lifetime : Ident ,
1625
+ err : impl FnOnce ( & mut Self ) -> DiagnosticBuilder < ' a , ErrorGuaranteed > ,
1626
+ ) -> ast:: Lit {
1627
+ if let Some ( mut diag) =
1628
+ self . sess . span_diagnostic . steal_diagnostic ( lifetime. span , StashKey :: LifetimeIsChar )
1629
+ {
1630
+ diag. span_suggestion_verbose (
1631
+ lifetime. span . shrink_to_hi ( ) ,
1632
+ "add `'` to close the char literal" ,
1633
+ "'" ,
1634
+ Applicability :: MaybeIncorrect ,
1635
+ )
1636
+ . emit ( ) ;
1637
+ } else {
1638
+ err ( self )
1639
+ . span_suggestion_verbose (
1640
+ lifetime. span . shrink_to_hi ( ) ,
1641
+ "add `'` to close the char literal" ,
1642
+ "'" ,
1643
+ Applicability :: MaybeIncorrect ,
1644
+ )
1645
+ . emit ( ) ;
1646
+ }
1647
+ ast:: Lit {
1648
+ token_lit : token:: Lit :: new ( token:: LitKind :: Char , lifetime. name , None ) ,
1649
+ kind : ast:: LitKind :: Char ( lifetime. name . as_str ( ) . chars ( ) . next ( ) . unwrap_or ( '_' ) ) ,
1650
+ span : lifetime. span ,
1651
+ }
1652
+ }
1653
+
1606
1654
/// Recover on the syntax `do catch { ... }` suggesting `try { ... }` instead.
1607
1655
fn recover_do_catch ( & mut self ) -> PResult < ' a , P < Expr > > {
1608
1656
let lo = self . token . span ;
@@ -1728,7 +1776,7 @@ impl<'a> Parser<'a> {
1728
1776
}
1729
1777
1730
1778
pub ( super ) fn parse_lit ( & mut self ) -> PResult < ' a , Lit > {
1731
- self . parse_opt_lit ( ) . ok_or_else ( | | {
1779
+ self . parse_opt_lit ( ) . ok_or ( ( ) ) . or_else ( | ( ) | {
1732
1780
if let token:: Interpolated ( inner) = & self . token . kind {
1733
1781
let expr = match inner. as_ref ( ) {
1734
1782
token:: NtExpr ( expr) => Some ( expr) ,
@@ -1740,12 +1788,22 @@ impl<'a> Parser<'a> {
1740
1788
let mut err = InvalidInterpolatedExpression { span : self . token . span }
1741
1789
. into_diagnostic ( & self . sess . span_diagnostic ) ;
1742
1790
err. downgrade_to_delayed_bug ( ) ;
1743
- return err;
1791
+ return Err ( err) ;
1744
1792
}
1745
1793
}
1746
1794
}
1747
- let msg = format ! ( "unexpected token: {}" , super :: token_descr( & self . token) ) ;
1748
- self . struct_span_err ( self . token . span , & msg)
1795
+ let token = self . token . clone ( ) ;
1796
+ let err = |self_ : & mut Self | {
1797
+ let msg = format ! ( "unexpected token: {}" , super :: token_descr( & token) ) ;
1798
+ self_. struct_span_err ( token. span , & msg)
1799
+ } ;
1800
+ // On an error path, eagerly consider a lifetime to be an unclosed character lit
1801
+ if self . token . is_lifetime ( ) {
1802
+ let lt = self . expect_lifetime ( ) ;
1803
+ Ok ( self . recover_unclosed_char ( lt. ident , err) )
1804
+ } else {
1805
+ Err ( err ( self ) )
1806
+ }
1749
1807
} )
1750
1808
}
1751
1809
0 commit comments