@@ -42,8 +42,10 @@ use rustc_ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty
4242use rustc_ast:: { Arm , Async , BlockCheckMode , Expr , ExprKind , Label , Movability , RangeLimits } ;
4343use rustc_ast:: { ClosureBinder , StmtKind } ;
4444use 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+ } ;
4749use rustc_session:: errors:: ExprParenthesesNeeded ;
4850use rustc_session:: lint:: builtin:: BREAK_WITH_LABEL_AND_LOOP ;
4951use rustc_session:: lint:: BuiltinLintDiagnostics ;
@@ -1513,11 +1515,11 @@ impl<'a> Parser<'a> {
15131515 /// Parse `'label: $expr`. The label is already parsed.
15141516 fn parse_labeled_expr (
15151517 & mut self ,
1516- label : Label ,
1518+ label_ : Label ,
15171519 mut consume_colon : bool ,
15181520 ) -> 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_ ) ;
15211523 let ate_colon = self . eat ( & token:: Colon ) ;
15221524 let expr = if self . eat_keyword ( kw:: While ) {
15231525 self . parse_while_expr ( label, lo)
@@ -1529,6 +1531,19 @@ impl<'a> Parser<'a> {
15291531 || self . token . is_whole_block ( )
15301532 {
15311533 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) ) )
15321547 } else if !ate_colon
15331548 && ( self . check_noexpect ( & TokenKind :: Comma ) || self . check_noexpect ( & TokenKind :: Gt ) )
15341549 {
@@ -1603,6 +1618,39 @@ impl<'a> Parser<'a> {
16031618 Ok ( expr)
16041619 }
16051620
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+
16061654 /// Recover on the syntax `do catch { ... }` suggesting `try { ... }` instead.
16071655 fn recover_do_catch ( & mut self ) -> PResult < ' a , P < Expr > > {
16081656 let lo = self . token . span ;
@@ -1728,7 +1776,7 @@ impl<'a> Parser<'a> {
17281776 }
17291777
17301778 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 ( | ( ) | {
17321780 if let token:: Interpolated ( inner) = & self . token . kind {
17331781 let expr = match inner. as_ref ( ) {
17341782 token:: NtExpr ( expr) => Some ( expr) ,
@@ -1740,12 +1788,22 @@ impl<'a> Parser<'a> {
17401788 let mut err = InvalidInterpolatedExpression { span : self . token . span }
17411789 . into_diagnostic ( & self . sess . span_diagnostic ) ;
17421790 err. downgrade_to_delayed_bug ( ) ;
1743- return err;
1791+ return Err ( err) ;
17441792 }
17451793 }
17461794 }
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+ }
17491807 } )
17501808 }
17511809
0 commit comments