@@ -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
@@ -193,74 +192,32 @@ fn parse_concat<'psess>(
193192 } ;
194193
195194 let make_err = |reason| {
196- let err = errors:: MveConcatInvalid {
195+ let err = errors:: MveConcatInvalidTy {
197196 span : tt. span ( ) ,
198- ident_span : expr_ident_span ,
197+ metavar_span : None ,
199198 reason,
200199 valid : VALID_EXPR_CONCAT_TYPES ,
201200 } ;
202201 Err ( dcx. create_err ( err) )
203202 } ;
204203
205204 let token = match tt {
206- TokenTree :: Token ( token, _) => token,
205+ TokenTree :: Token ( token, _) => * token,
207206 TokenTree :: Delimited ( ..) => {
208- return make_err ( MveConcatInvalidReason :: UnexpectedGroup ) ;
207+ return make_err ( MveConcatInvalidTyReason :: UnsupportedInput ) ;
209208 }
210209 } ;
211210
212211 let element = if let Some ( dollar) = dollar {
213212 // Expecting a metavar
214213 let Some ( ( ident, _) ) = token. ident ( ) else {
215- return make_err ( MveConcatInvalidReason :: ExpectedMetavarIdent {
216- found : pprust:: token_to_string ( token) . into_owned ( ) ,
217- dollar,
218- } ) ;
214+ return make_err ( MveConcatInvalidTyReason :: ExpectedMetavarIdent { dollar } ) ;
219215 } ;
220216
221217 // Variables get passed untouched
222218 MetaVarExprConcatElem :: Var ( ident)
223- } else if let TokenKind :: Literal ( lit) = token. kind {
224- // Preprocess with `from_token_lit` to handle unescaping, float / int literal suffix
225- // stripping.
226- //
227- // For consistent user experience, please keep this in sync with the handling of
228- // literals in `rustc_builtin_macros::concat`!
229- let s = match ast:: LitKind :: from_token_lit ( lit. clone ( ) ) {
230- Ok ( ast:: LitKind :: Str ( s, _) ) => s. to_string ( ) ,
231- Ok ( ast:: LitKind :: Float ( ..) ) => {
232- return make_err ( MveConcatInvalidReason :: FloatLit ) ;
233- }
234- Ok ( ast:: LitKind :: Char ( c) ) => c. to_string ( ) ,
235- Ok ( ast:: LitKind :: Int ( i, _) ) => i. to_string ( ) ,
236- Ok ( ast:: LitKind :: Bool ( b) ) => b. to_string ( ) ,
237- Ok ( ast:: LitKind :: CStr ( ..) ) => return make_err ( MveConcatInvalidReason :: CStrLit ) ,
238- Ok ( ast:: LitKind :: Byte ( ..) | ast:: LitKind :: ByteStr ( ..) ) => {
239- return make_err ( MveConcatInvalidReason :: ByteStrLit ) ;
240- }
241- Ok ( ast:: LitKind :: Err ( _guarantee) ) => {
242- // REVIEW: a diagnostic was already emitted, should we just break?
243- return make_err ( MveConcatInvalidReason :: InvalidLiteral ) ;
244- }
245- Err ( err) => return Err ( create_lit_error ( psess, err, lit, token. span ) ) ,
246- } ;
247-
248- if !s. chars ( ) . all ( |ch| is_id_continue ( ch) ) {
249- // Check that all characters are valid in the middle of an identifier. This doesn't
250- // guarantee that the final identifier is valid (we still need to check it later),
251- // but it allows us to catch errors with specific arguments before expansion time;
252- // for example, string literal "foo.bar" gets flagged before the macro is invoked.
253- return make_err ( MveConcatInvalidReason :: InvalidIdent ) ;
254- }
255-
256- MetaVarExprConcatElem :: Ident ( s)
257- } else if let Some ( ( elem, is_raw) ) = token. ident ( ) {
258- if is_raw == IdentIsRaw :: Yes {
259- return make_err ( MveConcatInvalidReason :: RawIdentifier ) ;
260- }
261- MetaVarExprConcatElem :: Ident ( elem. as_str ( ) . to_string ( ) )
262219 } else {
263- return make_err ( MveConcatInvalidReason :: UnsupportedInput ) ;
220+ MetaVarExprConcatElem :: Ident ( parse_tok_for_concat ( psess , token ) ? )
264221 } ;
265222
266223 result. push ( element) ;
@@ -327,6 +284,68 @@ fn parse_depth<'psess>(
327284 }
328285}
329286
287+ /// Validate that a token can be concatenated as an identifier, then stringify it.
288+ pub ( super ) fn parse_tok_for_concat < ' psess > (
289+ psess : & ' psess ParseSess ,
290+ token : Token ,
291+ ) -> PResult < ' psess , String > {
292+ let dcx = psess. dcx ( ) ;
293+ let make_err = |reason| {
294+ let err = errors:: MveConcatInvalidTy {
295+ span : token. span ,
296+ metavar_span : None ,
297+ reason,
298+ valid : VALID_EXPR_CONCAT_TYPES ,
299+ } ;
300+ Err ( dcx. create_err ( err) )
301+ } ;
302+
303+ let elem = if let TokenKind :: Literal ( lit) = token. kind {
304+ // Preprocess with `from_token_lit` to handle unescaping, float / int literal suffix
305+ // stripping.
306+ //
307+ // For consistent user experience, please keep this in sync with the handling of
308+ // literals in `rustc_builtin_macros::concat`!
309+ let s = match ast:: LitKind :: from_token_lit ( lit. clone ( ) ) {
310+ Ok ( ast:: LitKind :: Str ( s, _) ) => s. to_string ( ) ,
311+ Ok ( ast:: LitKind :: Float ( ..) ) => {
312+ return make_err ( MveConcatInvalidTyReason :: FloatLit ) ;
313+ }
314+ Ok ( ast:: LitKind :: Char ( c) ) => c. to_string ( ) ,
315+ Ok ( ast:: LitKind :: Int ( i, _) ) => i. to_string ( ) ,
316+ Ok ( ast:: LitKind :: Bool ( b) ) => b. to_string ( ) ,
317+ Ok ( ast:: LitKind :: CStr ( ..) ) => return make_err ( MveConcatInvalidTyReason :: CStrLit ) ,
318+ Ok ( ast:: LitKind :: Byte ( ..) | ast:: LitKind :: ByteStr ( ..) ) => {
319+ return make_err ( MveConcatInvalidTyReason :: ByteStrLit ) ;
320+ }
321+ Ok ( ast:: LitKind :: Err ( _guarantee) ) => {
322+ // REVIEW: a diagnostic was already emitted, should we just break?
323+ return make_err ( MveConcatInvalidTyReason :: InvalidLiteral ) ;
324+ }
325+ Err ( err) => return Err ( create_lit_error ( psess, err, lit, token. span ) ) ,
326+ } ;
327+
328+ if !s. chars ( ) . all ( |ch| is_id_continue ( ch) ) {
329+ // Check that all characters are valid in the middle of an identifier. This doesn't
330+ // guarantee that the final identifier is valid (we still need to check it later),
331+ // but it allows us to catch errors with specific arguments before expansion time;
332+ // for example, string literal "foo.bar" gets flagged before the macro is invoked.
333+ return make_err ( MveConcatInvalidTyReason :: InvalidIdent ) ;
334+ }
335+
336+ s
337+ } else if let Some ( ( elem, is_raw) ) = token. ident ( ) {
338+ if is_raw == IdentIsRaw :: Yes {
339+ return make_err ( MveConcatInvalidTyReason :: RawIdentifier ) ;
340+ }
341+ elem. as_str ( ) . to_string ( )
342+ } else {
343+ return make_err ( MveConcatInvalidTyReason :: UnsupportedInput ) ;
344+ } ;
345+
346+ Ok ( elem)
347+ }
348+
330349/// Tries to parse a generic ident. If this fails, create a missing identifier diagnostic with
331350/// `context` explanation.
332351fn parse_ident < ' psess > (
0 commit comments