@@ -6,14 +6,14 @@ use super::{
6
6
use crate :: errors:: {
7
7
AmbiguousPlus , AttributeOnParamType , BadQPathStage2 , BadTypePlus , BadTypePlusSub ,
8
8
ComparisonOperatorsCannotBeChained , ComparisonOperatorsCannotBeChainedSugg ,
9
- ConstGenericWithoutBraces , ConstGenericWithoutBracesSugg , DocCommentOnParamType ,
10
- DoubleColonInBound , ExpectedIdentifier , ExpectedSemi , ExpectedSemiSugg ,
9
+ ConstGenericWithoutBraces , ConstGenericWithoutBracesSugg , DocCommentDoesNotDocumentAnything ,
10
+ DocCommentOnParamType , DoubleColonInBound , ExpectedIdentifier , ExpectedSemi , ExpectedSemiSugg ,
11
11
GenericParamsWithoutAngleBrackets , GenericParamsWithoutAngleBracketsSugg ,
12
12
HelpIdentifierStartsWithNumber , InInTypo , IncorrectAwait , IncorrectSemicolon ,
13
13
IncorrectUseOfAwait , ParenthesesInForHead , ParenthesesInForHeadSugg ,
14
14
PatternMethodParamWithoutBody , QuestionMarkInType , QuestionMarkInTypeSugg , SelfParamNotFirst ,
15
15
StructLiteralBodyWithoutPath , StructLiteralBodyWithoutPathSugg , StructLiteralNeedingParens ,
16
- StructLiteralNeedingParensSugg , SuggEscapeToUseAsIdentifier , SuggRemoveComma ,
16
+ StructLiteralNeedingParensSugg , SuggEscapeIdentifier , SuggRemoveComma ,
17
17
UnexpectedConstInGenericParam , UnexpectedConstParamDeclaration ,
18
18
UnexpectedConstParamDeclarationSugg , UnmatchedAngleBrackets , UseEqInstead ,
19
19
} ;
@@ -38,7 +38,7 @@ use rustc_errors::{
38
38
use rustc_session:: errors:: ExprParenthesesNeeded ;
39
39
use rustc_span:: source_map:: Spanned ;
40
40
use rustc_span:: symbol:: { kw, sym, Ident } ;
41
- use rustc_span:: { Span , SpanSnippetError , DUMMY_SP } ;
41
+ use rustc_span:: { Span , SpanSnippetError , Symbol , DUMMY_SP } ;
42
42
use std:: mem:: take;
43
43
use std:: ops:: { Deref , DerefMut } ;
44
44
use thin_vec:: { thin_vec, ThinVec } ;
@@ -268,7 +268,21 @@ impl<'a> Parser<'a> {
268
268
self . sess . source_map ( ) . span_to_snippet ( span)
269
269
}
270
270
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
+
272
286
let valid_follow = & [
273
287
TokenKind :: Eq ,
274
288
TokenKind :: Colon ,
@@ -281,31 +295,51 @@ impl<'a> Parser<'a> {
281
295
TokenKind :: CloseDelim ( Delimiter :: Parenthesis ) ,
282
296
] ;
283
297
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
+ } ;
298
326
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
+ } ;
302
331
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
+ } ) ;
305
339
306
340
let err = ExpectedIdentifier {
307
- span : self . token . span ,
308
- token : self . token . clone ( ) ,
341
+ span : bad_token . span ,
342
+ token : bad_token ,
309
343
suggest_raw,
310
344
suggest_remove_comma,
311
345
help_cannot_start_number,
@@ -314,6 +348,7 @@ impl<'a> Parser<'a> {
314
348
315
349
// if the token we have is a `<`
316
350
// it *might* be a misplaced generic
351
+ // FIXME: could we recover with this?
317
352
if self . token == token:: Lt {
318
353
// all keywords that could have generic applied
319
354
let valid_prev_keywords =
@@ -364,18 +399,38 @@ impl<'a> Parser<'a> {
364
399
}
365
400
}
366
401
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 ( )
368
412
}
369
413
370
414
/// Checks if the current token is a integer or float literal and looks like
371
415
/// 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
+ }
379
434
}
380
435
381
436
pub ( super ) fn expected_one_of_not_found (
0 commit comments