@@ -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
@@ -193,74 +192,32 @@ fn parse_concat<'psess>(
193
192
} ;
194
193
195
194
let make_err = |reason| {
196
- let err = errors:: MveConcatInvalid {
195
+ let err = errors:: MveConcatInvalidTy {
197
196
span : tt. span ( ) ,
198
- ident_span : expr_ident_span ,
197
+ metavar_span : None ,
199
198
reason,
200
199
valid : VALID_EXPR_CONCAT_TYPES ,
201
200
} ;
202
201
Err ( dcx. create_err ( err) )
203
202
} ;
204
203
205
204
let token = match tt {
206
- TokenTree :: Token ( token, _) => token,
205
+ TokenTree :: Token ( token, _) => * token,
207
206
TokenTree :: Delimited ( ..) => {
208
- return make_err ( MveConcatInvalidReason :: UnexpectedGroup ) ;
207
+ return make_err ( MveConcatInvalidTyReason :: UnsupportedInput ) ;
209
208
}
210
209
} ;
211
210
212
211
let element = if let Some ( dollar) = dollar {
213
212
// Expecting a metavar
214
213
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 } ) ;
219
215
} ;
220
216
221
217
// Variables get passed untouched
222
218
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 ( ) )
262
219
} else {
263
- return make_err ( MveConcatInvalidReason :: UnsupportedInput ) ;
220
+ MetaVarExprConcatElem :: Ident ( parse_tok_for_concat ( psess , token ) ? )
264
221
} ;
265
222
266
223
result. push ( element) ;
@@ -327,6 +284,68 @@ fn parse_depth<'psess>(
327
284
}
328
285
}
329
286
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
+
330
349
/// Tries to parse a generic ident. If this fails, create a missing identifier diagnostic with
331
350
/// `context` explanation.
332
351
fn parse_ident < ' psess > (
0 commit comments