|
2 | 2 | //! `$ident => foo`, interpolates variables in the template, to get `fn foo() {}`
|
3 | 3 |
|
4 | 4 | use intern::{sym, Symbol};
|
5 |
| -use span::Span; |
| 5 | +use span::{Edition, Span}; |
6 | 6 | use tt::Delimiter;
|
7 | 7 |
|
8 | 8 | use crate::{
|
9 | 9 | expander::{Binding, Bindings, Fragment},
|
10 |
| - parser::{MetaVarKind, Op, RepeatKind, Separator}, |
| 10 | + parser::{ConcatMetaVarExprElem, MetaVarKind, Op, RepeatKind, Separator}, |
11 | 11 | ExpandError, ExpandErrorKind, ExpandResult, MetaTemplate,
|
12 | 12 | };
|
13 | 13 |
|
@@ -312,6 +312,82 @@ fn expand_subtree(
|
312 | 312 | .into(),
|
313 | 313 | );
|
314 | 314 | }
|
| 315 | + Op::Concat { elements, span: concat_span } => { |
| 316 | + let mut concatenated = String::new(); |
| 317 | + for element in elements { |
| 318 | + match element { |
| 319 | + ConcatMetaVarExprElem::Ident(ident) => { |
| 320 | + concatenated.push_str(ident.sym.as_str()) |
| 321 | + } |
| 322 | + ConcatMetaVarExprElem::Literal(lit) => { |
| 323 | + // FIXME: This isn't really correct wrt. escaping, but that's what rustc does and anyway |
| 324 | + // escaping is used most of the times for characters that are invalid in identifiers. |
| 325 | + concatenated.push_str(lit.symbol.as_str()) |
| 326 | + } |
| 327 | + ConcatMetaVarExprElem::Var(var) => { |
| 328 | + // Handling of repetitions in `${concat}` isn't fleshed out in rustc, so we currently |
| 329 | + // err at it. |
| 330 | + // FIXME: Do what rustc does for repetitions. |
| 331 | + let var_value = match ctx.bindings.get_fragment( |
| 332 | + &var.sym, |
| 333 | + var.span, |
| 334 | + &mut ctx.nesting, |
| 335 | + marker, |
| 336 | + ) { |
| 337 | + Ok(var) => var, |
| 338 | + Err(e) => { |
| 339 | + if err.is_none() { |
| 340 | + err = Some(e); |
| 341 | + }; |
| 342 | + continue; |
| 343 | + } |
| 344 | + }; |
| 345 | + let value = match &var_value { |
| 346 | + Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) => { |
| 347 | + ident.sym.as_str() |
| 348 | + } |
| 349 | + Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Literal(lit))) => { |
| 350 | + lit.symbol.as_str() |
| 351 | + } |
| 352 | + _ => { |
| 353 | + if err.is_none() { |
| 354 | + err = Some(ExpandError::binding_error(var.span, "metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt`")) |
| 355 | + } |
| 356 | + continue; |
| 357 | + } |
| 358 | + }; |
| 359 | + concatenated.push_str(value); |
| 360 | + } |
| 361 | + } |
| 362 | + } |
| 363 | + |
| 364 | + // `${concat}` span comes from the macro (at least for now). |
| 365 | + // See https://github.com/rust-lang/rust/blob/b0af276da341/compiler/rustc_expand/src/mbe/transcribe.rs#L724-L726. |
| 366 | + let mut result_span = *concat_span; |
| 367 | + marker(&mut result_span); |
| 368 | + |
| 369 | + // FIXME: NFC normalize the result. |
| 370 | + if !rustc_lexer::is_ident(&concatenated) { |
| 371 | + if err.is_none() { |
| 372 | + err = Some(ExpandError::binding_error( |
| 373 | + *concat_span, |
| 374 | + "`${concat(..)}` is not generating a valid identifier", |
| 375 | + )); |
| 376 | + } |
| 377 | + // Insert a dummy identifier for better parsing. |
| 378 | + concatenated.clear(); |
| 379 | + concatenated.push_str("__ra_concat_dummy"); |
| 380 | + } |
| 381 | + |
| 382 | + let needs_raw = |
| 383 | + parser::SyntaxKind::from_keyword(&concatenated, Edition::LATEST).is_some(); |
| 384 | + let is_raw = if needs_raw { tt::IdentIsRaw::Yes } else { tt::IdentIsRaw::No }; |
| 385 | + arena.push(tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { |
| 386 | + is_raw, |
| 387 | + span: result_span, |
| 388 | + sym: Symbol::intern(&concatenated), |
| 389 | + }))); |
| 390 | + } |
315 | 391 | }
|
316 | 392 | }
|
317 | 393 | // drain the elements added in this instance of expand_subtree
|
|
0 commit comments