@@ -2,16 +2,15 @@ use rustc_ast::token::{self, Delimiter, IdentIsRaw, Token, TokenKind};
2
2
use rustc_ast:: tokenstream:: { TokenStream , TokenStreamIter , TokenTree } ;
3
3
use rustc_ast:: { self as ast, LitIntType , LitKind } ;
4
4
use rustc_ast_pretty:: pprust;
5
- use rustc_errors:: PResult ;
5
+ use rustc_errors:: { DiagCtxtHandle , PResult } ;
6
6
use rustc_lexer:: is_id_continue;
7
7
use rustc_macros:: { Decodable , Encodable } ;
8
8
use rustc_session:: errors:: create_lit_error;
9
9
use rustc_session:: parse:: ParseSess ;
10
10
use rustc_span:: { Ident , Span , Symbol } ;
11
11
12
- use crate :: errors:: { self , MveConcatInvalidReason , MveExpectedIdentContext } ;
12
+ use crate :: errors:: { self , MveConcatInvalidTyReason , MveExpectedIdentContext } ;
13
13
14
- pub ( crate ) const RAW_IDENT_ERR : & str = "`${concat(..)}` currently does not support raw identifiers" ;
15
14
pub ( crate ) const VALID_EXPR_CONCAT_TYPES : & str =
16
15
"metavariables, identifiers, string literals, and integer literals" ;
17
16
@@ -218,74 +217,32 @@ fn parse_concat<'psess>(
218
217
} ;
219
218
220
219
let make_err = |reason| {
221
- let err = errors:: MveConcatInvalid {
220
+ let err = errors:: MveConcatInvalidTy {
222
221
span : tt. span ( ) ,
223
- ident_span : expr_ident_span ,
222
+ metavar_span : None ,
224
223
reason,
225
224
valid : VALID_EXPR_CONCAT_TYPES ,
226
225
} ;
227
226
Err ( dcx. create_err ( err) )
228
227
} ;
229
228
230
229
let token = match tt {
231
- TokenTree :: Token ( token, _) => token,
230
+ TokenTree :: Token ( token, _) => * token,
232
231
TokenTree :: Delimited ( ..) => {
233
- return make_err ( MveConcatInvalidReason :: UnexpectedGroup ) ;
232
+ return make_err ( MveConcatInvalidTyReason :: UnsupportedInput ) ;
234
233
}
235
234
} ;
236
235
237
236
let element = if let Some ( dollar) = dollar {
238
237
// Expecting a metavar
239
238
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 } ) ;
244
240
} ;
245
241
246
242
// Variables get passed untouched
247
243
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 ( ) )
287
244
} else {
288
- return make_err ( MveConcatInvalidReason :: UnsupportedInput ) ;
245
+ MetaVarExprConcatElem :: Ident ( parse_tok_for_concat ( psess , token ) ? )
289
246
} ;
290
247
291
248
result. push ( element) ;
@@ -352,6 +309,68 @@ fn parse_depth<'psess>(
352
309
}
353
310
}
354
311
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
+
355
374
/// Tries to parse a generic ident. If this fails, create a missing identifier diagnostic with
356
375
/// `context` explanation.
357
376
fn parse_ident < ' psess > (
0 commit comments