Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/mbe/src/expander/transcriber.rs')
| -rw-r--r-- | crates/mbe/src/expander/transcriber.rs | 82 |
1 files changed, 79 insertions, 3 deletions
diff --git a/crates/mbe/src/expander/transcriber.rs b/crates/mbe/src/expander/transcriber.rs index 286bd748cb..1db2f35d26 100644 --- a/crates/mbe/src/expander/transcriber.rs +++ b/crates/mbe/src/expander/transcriber.rs @@ -2,12 +2,12 @@ //! `$ident => foo`, interpolates variables in the template, to get `fn foo() {}` use intern::{sym, Symbol}; -use span::Span; +use span::{Edition, Span}; use tt::Delimiter; use crate::{ expander::{Binding, Bindings, Fragment}, - parser::{MetaVarKind, Op, RepeatKind, Separator}, + parser::{ConcatMetaVarExprElem, MetaVarKind, Op, RepeatKind, Separator}, ExpandError, ExpandErrorKind, ExpandResult, MetaTemplate, }; @@ -97,7 +97,7 @@ impl Bindings { | MetaVarKind::Ty | MetaVarKind::Pat | MetaVarKind::PatParam - | MetaVarKind::Expr + | MetaVarKind::Expr(_) | MetaVarKind::Ident => { Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { sym: sym::missing.clone(), @@ -312,6 +312,82 @@ fn expand_subtree( .into(), ); } + Op::Concat { elements, span: concat_span } => { + let mut concatenated = String::new(); + for element in elements { + match element { + ConcatMetaVarExprElem::Ident(ident) => { + concatenated.push_str(ident.sym.as_str()) + } + ConcatMetaVarExprElem::Literal(lit) => { + // FIXME: This isn't really correct wrt. escaping, but that's what rustc does and anyway + // escaping is used most of the times for characters that are invalid in identifiers. + concatenated.push_str(lit.symbol.as_str()) + } + ConcatMetaVarExprElem::Var(var) => { + // Handling of repetitions in `${concat}` isn't fleshed out in rustc, so we currently + // err at it. + // FIXME: Do what rustc does for repetitions. + let var_value = match ctx.bindings.get_fragment( + &var.sym, + var.span, + &mut ctx.nesting, + marker, + ) { + Ok(var) => var, + Err(e) => { + if err.is_none() { + err = Some(e); + }; + continue; + } + }; + let value = match &var_value { + Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) => { + ident.sym.as_str() + } + Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Literal(lit))) => { + lit.symbol.as_str() + } + _ => { + if err.is_none() { + err = Some(ExpandError::binding_error(var.span, "metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt`")) + } + continue; + } + }; + concatenated.push_str(value); + } + } + } + + // `${concat}` span comes from the macro (at least for now). + // See https://github.com/rust-lang/rust/blob/b0af276da341/compiler/rustc_expand/src/mbe/transcribe.rs#L724-L726. + let mut result_span = *concat_span; + marker(&mut result_span); + + // FIXME: NFC normalize the result. + if !rustc_lexer::is_ident(&concatenated) { + if err.is_none() { + err = Some(ExpandError::binding_error( + *concat_span, + "`${concat(..)}` is not generating a valid identifier", + )); + } + // Insert a dummy identifier for better parsing. + concatenated.clear(); + concatenated.push_str("__ra_concat_dummy"); + } + + let needs_raw = + parser::SyntaxKind::from_keyword(&concatenated, Edition::LATEST).is_some(); + let is_raw = if needs_raw { tt::IdentIsRaw::Yes } else { tt::IdentIsRaw::No }; + arena.push(tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { + is_raw, + span: result_span, + sym: Symbol::intern(&concatenated), + }))); + } } } // drain the elements added in this instance of expand_subtree |