@@ -2,16 +2,15 @@ use rustc_ast::token::{self, Delimiter, IdentIsRaw, Token, TokenKind};
22use rustc_ast:: tokenstream:: { TokenStream , TokenStreamIter , TokenTree } ;
33use rustc_ast:: { self as ast, LitIntType , LitKind } ;
44use rustc_ast_pretty:: pprust;
5- use rustc_errors:: PResult ;
5+ use rustc_errors:: { DiagCtxtHandle , PResult } ;
66use rustc_lexer:: is_id_continue;
77use rustc_macros:: { Decodable , Encodable } ;
88use rustc_session:: errors:: create_lit_error;
99use rustc_session:: parse:: ParseSess ;
1010use rustc_span:: { Ident , Span , Symbol } ;
1111
12- use crate :: errors:: { self , MveConcatInvalidReason , MveExpectedIdentContext } ;
12+ use crate :: errors:: { self , MveConcatInvalidTyReason , MveExpectedIdentContext } ;
1313
14- pub ( crate ) const RAW_IDENT_ERR : & str = "`${concat(..)}` currently does not support raw identifiers" ;
1514pub ( crate ) const VALID_EXPR_CONCAT_TYPES : & str =
1615 "metavariables, identifiers, string literals, and integer literals" ;
1716
@@ -218,74 +217,32 @@ fn parse_concat<'psess>(
218217 } ;
219218
220219 let make_err = |reason| {
221- let err = errors:: MveConcatInvalid {
220+ let err = errors:: MveConcatInvalidTy {
222221 span : tt. span ( ) ,
223- ident_span : expr_ident_span ,
222+ metavar_span : None ,
224223 reason,
225224 valid : VALID_EXPR_CONCAT_TYPES ,
226225 } ;
227226 Err ( dcx. create_err ( err) )
228227 } ;
229228
230229 let token = match tt {
231- TokenTree :: Token ( token, _) => token,
230+ TokenTree :: Token ( token, _) => * token,
232231 TokenTree :: Delimited ( ..) => {
233- return make_err ( MveConcatInvalidReason :: UnexpectedGroup ) ;
232+ return make_err ( MveConcatInvalidTyReason :: UnsupportedInput ) ;
234233 }
235234 } ;
236235
237236 let element = if let Some ( dollar) = dollar {
238237 // Expecting a metavar
239238 let Some ( ( ident, _) ) = token. ident ( ) else {
240- return make_err ( MveConcatInvalidReason :: ExpectedMetavarIdent {
241- found : pprust:: token_to_string ( token) . into_owned ( ) ,
242- dollar,
243- } ) ;
239+ return make_err ( MveConcatInvalidTyReason :: ExpectedMetavarIdent { dollar } ) ;
244240 } ;
245241
246242 // Variables get passed untouched
247243 MetaVarExprConcatElem :: Var ( ident)
248- } else if let TokenKind :: Literal ( lit) = token. kind {
249- // Preprocess with `from_token_lit` to handle unescaping, float / int literal suffix
250- // stripping.
251- //
252- // For consistent user experience, please keep this in sync with the handling of
253- // literals in `rustc_builtin_macros::concat`!
254- let s = match ast:: LitKind :: from_token_lit ( lit. clone ( ) ) {
255- Ok ( ast:: LitKind :: Str ( s, _) ) => s. to_string ( ) ,
256- Ok ( ast:: LitKind :: Float ( ..) ) => {
257- return make_err ( MveConcatInvalidReason :: FloatLit ) ;
258- }
259- Ok ( ast:: LitKind :: Char ( c) ) => c. to_string ( ) ,
260- Ok ( ast:: LitKind :: Int ( i, _) ) => i. to_string ( ) ,
261- Ok ( ast:: LitKind :: Bool ( b) ) => b. to_string ( ) ,
262- Ok ( ast:: LitKind :: CStr ( ..) ) => return make_err ( MveConcatInvalidReason :: CStrLit ) ,
263- Ok ( ast:: LitKind :: Byte ( ..) | ast:: LitKind :: ByteStr ( ..) ) => {
264- return make_err ( MveConcatInvalidReason :: ByteStrLit ) ;
265- }
266- Ok ( ast:: LitKind :: Err ( _guarantee) ) => {
267- // REVIEW: a diagnostic was already emitted, should we just break?
268- return make_err ( MveConcatInvalidReason :: InvalidLiteral ) ;
269- }
270- Err ( err) => return Err ( create_lit_error ( psess, err, lit, token. span ) ) ,
271- } ;
272-
273- if !s. chars ( ) . all ( |ch| is_id_continue ( ch) ) {
274- // Check that all characters are valid in the middle of an identifier. This doesn't
275- // guarantee that the final identifier is valid (we still need to check it later),
276- // but it allows us to catch errors with specific arguments before expansion time;
277- // for example, string literal "foo.bar" gets flagged before the macro is invoked.
278- return make_err ( MveConcatInvalidReason :: InvalidIdent ) ;
279- }
280-
281- MetaVarExprConcatElem :: Ident ( s)
282- } else if let Some ( ( elem, is_raw) ) = token. ident ( ) {
283- if is_raw == IdentIsRaw :: Yes {
284- return make_err ( MveConcatInvalidReason :: RawIdentifier ) ;
285- }
286- MetaVarExprConcatElem :: Ident ( elem. as_str ( ) . to_string ( ) )
287244 } else {
288- return make_err ( MveConcatInvalidReason :: UnsupportedInput ) ;
245+ MetaVarExprConcatElem :: Ident ( parse_tok_for_concat ( psess , token ) ? )
289246 } ;
290247
291248 result. push ( element) ;
@@ -352,6 +309,68 @@ fn parse_depth<'psess>(
352309 }
353310}
354311
312+ /// Validate that a token can be concatenated as an identifier, then stringify it.
313+ pub ( super ) fn parse_tok_for_concat < ' psess > (
314+ psess : & ' psess ParseSess ,
315+ token : Token ,
316+ ) -> PResult < ' psess , String > {
317+ let dcx = psess. dcx ( ) ;
318+ let make_err = |reason| {
319+ let err = errors:: MveConcatInvalidTy {
320+ span : token. span ,
321+ metavar_span : None ,
322+ reason,
323+ valid : VALID_EXPR_CONCAT_TYPES ,
324+ } ;
325+ Err ( dcx. create_err ( err) )
326+ } ;
327+
328+ let elem = if let TokenKind :: Literal ( lit) = token. kind {
329+ // Preprocess with `from_token_lit` to handle unescaping, float / int literal suffix
330+ // stripping.
331+ //
332+ // For consistent user experience, please keep this in sync with the handling of
333+ // literals in `rustc_builtin_macros::concat`!
334+ let s = match ast:: LitKind :: from_token_lit ( lit. clone ( ) ) {
335+ Ok ( ast:: LitKind :: Str ( s, _) ) => s. to_string ( ) ,
336+ Ok ( ast:: LitKind :: Float ( ..) ) => {
337+ return make_err ( MveConcatInvalidTyReason :: FloatLit ) ;
338+ }
339+ Ok ( ast:: LitKind :: Char ( c) ) => c. to_string ( ) ,
340+ Ok ( ast:: LitKind :: Int ( i, _) ) => i. to_string ( ) ,
341+ Ok ( ast:: LitKind :: Bool ( b) ) => b. to_string ( ) ,
342+ Ok ( ast:: LitKind :: CStr ( ..) ) => return make_err ( MveConcatInvalidTyReason :: CStrLit ) ,
343+ Ok ( ast:: LitKind :: Byte ( ..) | ast:: LitKind :: ByteStr ( ..) ) => {
344+ return make_err ( MveConcatInvalidTyReason :: ByteStrLit ) ;
345+ }
346+ Ok ( ast:: LitKind :: Err ( _guarantee) ) => {
347+ // REVIEW: a diagnostic was already emitted, should we just break?
348+ return make_err ( MveConcatInvalidTyReason :: InvalidLiteral ) ;
349+ }
350+ Err ( err) => return Err ( create_lit_error ( psess, err, lit, token. span ) ) ,
351+ } ;
352+
353+ if !s. chars ( ) . all ( |ch| is_id_continue ( ch) ) {
354+ // Check that all characters are valid in the middle of an identifier. This doesn't
355+ // guarantee that the final identifier is valid (we still need to check it later),
356+ // but it allows us to catch errors with specific arguments before expansion time;
357+ // for example, string literal "foo.bar" gets flagged before the macro is invoked.
358+ return make_err ( MveConcatInvalidTyReason :: InvalidIdent ) ;
359+ }
360+
361+ s
362+ } else if let Some ( ( elem, is_raw) ) = token. ident ( ) {
363+ if is_raw == IdentIsRaw :: Yes {
364+ return make_err ( MveConcatInvalidTyReason :: RawIdentifier ) ;
365+ }
366+ elem. as_str ( ) . to_string ( )
367+ } else {
368+ return make_err ( MveConcatInvalidTyReason :: UnsupportedInput ) ;
369+ } ;
370+
371+ Ok ( elem)
372+ }
373+
355374/// Tries to parse a generic ident. If this fails, create a missing identifier diagnostic with
356375/// `context` explanation.
357376fn parse_ident < ' psess > (
0 commit comments