@@ -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