@@ -6,14 +6,14 @@ use super::{
66use crate :: errors:: {
77 AmbiguousPlus , AttributeOnParamType , BadQPathStage2 , BadTypePlus , BadTypePlusSub ,
88 ComparisonOperatorsCannotBeChained , ComparisonOperatorsCannotBeChainedSugg ,
9- ConstGenericWithoutBraces , ConstGenericWithoutBracesSugg , DocCommentOnParamType ,
10- DoubleColonInBound , ExpectedIdentifier , ExpectedSemi , ExpectedSemiSugg ,
9+ ConstGenericWithoutBraces , ConstGenericWithoutBracesSugg , DocCommentDoesNotDocumentAnything ,
10+ DocCommentOnParamType , DoubleColonInBound , ExpectedIdentifier , ExpectedSemi , ExpectedSemiSugg ,
1111 GenericParamsWithoutAngleBrackets , GenericParamsWithoutAngleBracketsSugg ,
1212 HelpIdentifierStartsWithNumber , InInTypo , IncorrectAwait , IncorrectSemicolon ,
1313 IncorrectUseOfAwait , ParenthesesInForHead , ParenthesesInForHeadSugg ,
1414 PatternMethodParamWithoutBody , QuestionMarkInType , QuestionMarkInTypeSugg , SelfParamNotFirst ,
1515 StructLiteralBodyWithoutPath , StructLiteralBodyWithoutPathSugg , StructLiteralNeedingParens ,
16- StructLiteralNeedingParensSugg , SuggEscapeToUseAsIdentifier , SuggRemoveComma ,
16+ StructLiteralNeedingParensSugg , SuggEscapeIdentifier , SuggRemoveComma ,
1717 UnexpectedConstInGenericParam , UnexpectedConstParamDeclaration ,
1818 UnexpectedConstParamDeclarationSugg , UnmatchedAngleBrackets , UseEqInstead ,
1919} ;
@@ -38,7 +38,7 @@ use rustc_errors::{
3838use rustc_session:: errors:: ExprParenthesesNeeded ;
3939use rustc_span:: source_map:: Spanned ;
4040use rustc_span:: symbol:: { kw, sym, Ident } ;
41- use rustc_span:: { Span , SpanSnippetError , DUMMY_SP } ;
41+ use rustc_span:: { Span , SpanSnippetError , Symbol , DUMMY_SP } ;
4242use std:: mem:: take;
4343use std:: ops:: { Deref , DerefMut } ;
4444use thin_vec:: { thin_vec, ThinVec } ;
@@ -268,7 +268,21 @@ impl<'a> Parser<'a> {
268268 self . sess . source_map ( ) . span_to_snippet ( span)
269269 }
270270
271- pub ( super ) fn expected_ident_found ( & mut self ) -> DiagnosticBuilder < ' a , ErrorGuaranteed > {
271+ /// Emits an error with suggestions if an identifier was expected but not found.
272+ ///
273+ /// Returns a possibly recovered identifier.
274+ pub ( super ) fn expected_ident_found (
275+ & mut self ,
276+ recover : bool ,
277+ ) -> PResult < ' a , ( Ident , /* is_raw */ bool ) > {
278+ if let TokenKind :: DocComment ( ..) = self . prev_token . kind {
279+ return Err ( DocCommentDoesNotDocumentAnything {
280+ span : self . prev_token . span ,
281+ missing_comma : None ,
282+ }
283+ . into_diagnostic ( & self . sess . span_diagnostic ) ) ;
284+ }
285+
272286 let valid_follow = & [
273287 TokenKind :: Eq ,
274288 TokenKind :: Colon ,
@@ -281,31 +295,51 @@ impl<'a> Parser<'a> {
281295 TokenKind :: CloseDelim ( Delimiter :: Parenthesis ) ,
282296 ] ;
283297
284- let suggest_raw = match self . token . ident ( ) {
285- Some ( ( ident, false ) )
286- if ident. is_raw_guess ( )
287- && self . look_ahead ( 1 , |t| valid_follow. contains ( & t. kind ) ) =>
288- {
289- Some ( SuggEscapeToUseAsIdentifier {
290- span : ident. span . shrink_to_lo ( ) ,
291- // `Symbol::to_string()` is different from `Symbol::into_diagnostic_arg()`,
292- // which uses `Symbol::to_ident_string()` and "helpfully" adds an implicit `r#`
293- ident_name : ident. name . to_string ( ) ,
294- } )
295- }
296- _ => None ,
297- } ;
298+ let mut recovered_ident = None ;
299+ // we take this here so that the correct original token is retained in
300+ // the diagnostic, regardless of eager recovery.
301+ let bad_token = self . token . clone ( ) ;
302+
303+ // suggest prepending a keyword in identifier position with `r#`
304+ let suggest_raw = if let Some ( ( ident, false ) ) = self . token . ident ( )
305+ && ident. is_raw_guess ( )
306+ && self . look_ahead ( 1 , |t| valid_follow. contains ( & t. kind ) )
307+ {
308+ recovered_ident = Some ( ( ident, true ) ) ;
309+
310+ // `Symbol::to_string()` is different from `Symbol::into_diagnostic_arg()`,
311+ // which uses `Symbol::to_ident_string()` and "helpfully" adds an implicit `r#`
312+ let ident_name = ident. name . to_string ( ) ;
313+
314+ Some ( SuggEscapeIdentifier {
315+ span : ident. span . shrink_to_lo ( ) ,
316+ ident_name
317+ } )
318+ } else { None } ;
319+
320+ let suggest_remove_comma =
321+ if self . token == token:: Comma && self . look_ahead ( 1 , |t| t. is_ident ( ) ) {
322+ if recover {
323+ self . bump ( ) ;
324+ recovered_ident = self . ident_or_err ( false ) . ok ( ) ;
325+ } ;
298326
299- let suggest_remove_comma = ( self . token == token:: Comma
300- && self . look_ahead ( 1 , |t| t. is_ident ( ) ) )
301- . then_some ( SuggRemoveComma { span : self . token . span } ) ;
327+ Some ( SuggRemoveComma { span : bad_token. span } )
328+ } else {
329+ None
330+ } ;
302331
303- let help_cannot_start_number =
304- self . is_lit_bad_ident ( ) . then_some ( HelpIdentifierStartsWithNumber ) ;
332+ let help_cannot_start_number = self . is_lit_bad_ident ( ) . map ( |( len, valid_portion) | {
333+ let ( invalid, valid) = self . token . span . split_at ( len as u32 ) ;
334+
335+ recovered_ident = Some ( ( Ident :: new ( valid_portion, valid) , false ) ) ;
336+
337+ HelpIdentifierStartsWithNumber { num_span : invalid }
338+ } ) ;
305339
306340 let err = ExpectedIdentifier {
307- span : self . token . span ,
308- token : self . token . clone ( ) ,
341+ span : bad_token . span ,
342+ token : bad_token ,
309343 suggest_raw,
310344 suggest_remove_comma,
311345 help_cannot_start_number,
@@ -314,6 +348,7 @@ impl<'a> Parser<'a> {
314348
315349 // if the token we have is a `<`
316350 // it *might* be a misplaced generic
351+ // FIXME: could we recover with this?
317352 if self . token == token:: Lt {
318353 // all keywords that could have generic applied
319354 let valid_prev_keywords =
@@ -364,18 +399,38 @@ impl<'a> Parser<'a> {
364399 }
365400 }
366401
367- err
402+ if let Some ( recovered_ident) = recovered_ident && recover {
403+ err. emit ( ) ;
404+ Ok ( recovered_ident)
405+ } else {
406+ Err ( err)
407+ }
408+ }
409+
410+ pub ( super ) fn expected_ident_found_err ( & mut self ) -> DiagnosticBuilder < ' a , ErrorGuaranteed > {
411+ self . expected_ident_found ( false ) . unwrap_err ( )
368412 }
369413
370414 /// Checks if the current token is a integer or float literal and looks like
371415 /// it could be a invalid identifier with digits at the start.
372- pub ( super ) fn is_lit_bad_ident ( & mut self ) -> bool {
373- matches ! ( self . token. uninterpolate( ) . kind, token:: Literal ( Lit { kind: token:: LitKind :: Integer | token:: LitKind :: Float , .. } )
374- // ensure that the integer literal is followed by a *invalid*
375- // suffix: this is how we know that it is a identifier with an
376- // invalid beginning.
377- if rustc_ast:: MetaItemLit :: from_token( & self . token) . is_none( )
378- )
416+ ///
417+ /// Returns the number of characters (bytes) composing the invalid portion
418+ /// of the identifier and the valid portion of the identifier.
419+ pub ( super ) fn is_lit_bad_ident ( & mut self ) -> Option < ( usize , Symbol ) > {
420+ // ensure that the integer literal is followed by a *invalid*
421+ // suffix: this is how we know that it is a identifier with an
422+ // invalid beginning.
423+ if let token:: Literal ( Lit {
424+ kind : token:: LitKind :: Integer | token:: LitKind :: Float ,
425+ symbol,
426+ suffix,
427+ } ) = self . token . kind
428+ && rustc_ast:: MetaItemLit :: from_token ( & self . token ) . is_none ( )
429+ {
430+ Some ( ( symbol. as_str ( ) . len ( ) , suffix. unwrap ( ) ) )
431+ } else {
432+ None
433+ }
379434 }
380435
381436 pub ( super ) fn expected_one_of_not_found (
0 commit comments