@@ -10,13 +10,13 @@ use crate::errors::{
1010 ConstGenericWithoutBracesSugg , DocCommentDoesNotDocumentAnything , DocCommentOnParamType ,
1111 DoubleColonInBound , ExpectedIdentifier , ExpectedSemi , ExpectedSemiSugg ,
1212 GenericParamsWithoutAngleBrackets , GenericParamsWithoutAngleBracketsSugg ,
13- HelpIdentifierStartsWithNumber , InInTypo , IncorrectAwait , IncorrectSemicolon ,
14- IncorrectUseOfAwait , PatternMethodParamWithoutBody , QuestionMarkInType , QuestionMarkInTypeSugg ,
15- SelfParamNotFirst , StructLiteralBodyWithoutPath , StructLiteralBodyWithoutPathSugg ,
16- StructLiteralNeedingParens , StructLiteralNeedingParensSugg , SuggAddMissingLetStmt ,
17- SuggEscapeIdentifier , SuggRemoveComma , TernaryOperator , UnexpectedConstInGenericParam ,
18- UnexpectedConstParamDeclaration , UnexpectedConstParamDeclarationSugg , UnmatchedAngleBrackets ,
19- UseEqInstead , WrapType ,
13+ HelpIdentifierStartsWithNumber , HelpUseLatestEdition , InInTypo , IncorrectAwait ,
14+ IncorrectSemicolon , IncorrectUseOfAwait , PatternMethodParamWithoutBody , QuestionMarkInType ,
15+ QuestionMarkInTypeSugg , SelfParamNotFirst , StructLiteralBodyWithoutPath ,
16+ StructLiteralBodyWithoutPathSugg , StructLiteralNeedingParens , StructLiteralNeedingParensSugg ,
17+ SuggAddMissingLetStmt , SuggEscapeIdentifier , SuggRemoveComma , TernaryOperator ,
18+ UnexpectedConstInGenericParam , UnexpectedConstParamDeclaration ,
19+ UnexpectedConstParamDeclarationSugg , UnmatchedAngleBrackets , UseEqInstead , WrapType ,
2020} ;
2121use crate :: fluent_generated as fluent;
2222use crate :: parser;
@@ -640,6 +640,28 @@ impl<'a> Parser<'a> {
640640 }
641641 }
642642
643+ // Try to detect an intended c-string literal while using a pre-2021 edition. The heuristic
644+ // here is to identify a cooked, uninterpolated `c` id immediately followed by a string, or
645+ // a cooked, uninterpolated `cr` id immediately followed by a string or a `#`, in an edition
646+ // where c-string literals are not allowed. There is the very slight possibility of a false
647+ // positive for a `cr#` that wasn't intended to start a c-string literal, but identifying
648+ // that in the parser requires unbounded lookahead, so we only add a hint to the existing
649+ // error rather than replacing it entirely.
650+ if ( ( self . prev_token . kind == TokenKind :: Ident ( sym:: c, false )
651+ && matches ! ( & self . token. kind, TokenKind :: Literal ( token:: Lit { kind: token:: Str , .. } ) ) )
652+ || ( self . prev_token . kind == TokenKind :: Ident ( sym:: cr, false )
653+ && matches ! (
654+ & self . token. kind,
655+ TokenKind :: Literal ( token:: Lit { kind: token:: Str , .. } ) | token:: Pound
656+ ) ) )
657+ && self . prev_token . span . hi ( ) == self . token . span . lo ( )
658+ && !self . token . span . at_least_rust_2021 ( )
659+ {
660+ err. note ( "you may be trying to write a c-string literal" ) ;
661+ err. note ( "c-string literals require Rust 2021 or later" ) ;
662+ HelpUseLatestEdition :: new ( ) . add_to_diagnostic ( & mut err) ;
663+ }
664+
643665 // `pub` may be used for an item or `pub(crate)`
644666 if self . prev_token . is_ident_named ( sym:: public)
645667 && ( self . token . can_begin_item ( )
0 commit comments