Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/mbe/src/parser.rs')
-rw-r--r--crates/mbe/src/parser.rs82
1 files changed, 76 insertions, 6 deletions
diff --git a/crates/mbe/src/parser.rs b/crates/mbe/src/parser.rs
index 218c04640f..b55edf4a5e 100644
--- a/crates/mbe/src/parser.rs
+++ b/crates/mbe/src/parser.rs
@@ -84,6 +84,10 @@ pub(crate) enum Op {
// FIXME: `usize`` once we drop support for 1.76
depth: Option<usize>,
},
+ Concat {
+ elements: Box<[ConcatMetaVarExprElem]>,
+ span: Span,
+ },
Repeat {
tokens: MetaTemplate,
kind: RepeatKind,
@@ -98,6 +102,18 @@ pub(crate) enum Op {
Ident(tt::Ident<Span>),
}
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub(crate) enum ConcatMetaVarExprElem {
+ /// There is NO preceding dollar sign, which means that this identifier should be interpreted
+ /// as a literal.
+ Ident(tt::Ident<Span>),
+ /// There is a preceding dollar sign, which means that this identifier should be expanded
+ /// and interpreted as a variable.
+ Var(tt::Ident<Span>),
+ /// For example, a number or a string.
+ Literal(tt::Literal<Span>),
+}
+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub(crate) enum RepeatKind {
ZeroOrMore,
@@ -106,6 +122,16 @@ pub(crate) enum RepeatKind {
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub(crate) enum ExprKind {
+ // Matches expressions using the post-edition 2024. Was written using
+ // `expr` in edition 2024 or later.
+ Expr,
+ // Matches expressions using the pre-edition 2024 rules.
+ // Either written using `expr` in edition 2021 or earlier or.was written using `expr_2021`.
+ Expr2021,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub(crate) enum MetaVarKind {
Path,
Ty,
@@ -116,7 +142,7 @@ pub(crate) enum MetaVarKind {
Meta,
Item,
Vis,
- Expr,
+ Expr(ExprKind),
Ident,
Tt,
Lifetime,
@@ -277,17 +303,27 @@ fn eat_fragment_kind(
let kind = match ident.sym.as_str() {
"path" => MetaVarKind::Path,
"ty" => MetaVarKind::Ty,
- "pat" => match edition(ident.span.ctx) {
- Edition::Edition2015 | Edition::Edition2018 => MetaVarKind::PatParam,
- Edition::Edition2021 | Edition::Edition2024 => MetaVarKind::Pat,
- },
+ "pat" => {
+ if edition(ident.span.ctx).at_least_2021() {
+ MetaVarKind::Pat
+ } else {
+ MetaVarKind::PatParam
+ }
+ }
"pat_param" => MetaVarKind::PatParam,
"stmt" => MetaVarKind::Stmt,
"block" => MetaVarKind::Block,
"meta" => MetaVarKind::Meta,
"item" => MetaVarKind::Item,
"vis" => MetaVarKind::Vis,
- "expr" => MetaVarKind::Expr,
+ "expr" => {
+ if edition(ident.span.ctx).at_least_2024() {
+ MetaVarKind::Expr(ExprKind::Expr)
+ } else {
+ MetaVarKind::Expr(ExprKind::Expr2021)
+ }
+ }
+ "expr_2021" => MetaVarKind::Expr(ExprKind::Expr2021),
"ident" => MetaVarKind::Ident,
"tt" => MetaVarKind::Tt,
"lifetime" => MetaVarKind::Lifetime,
@@ -364,6 +400,32 @@ fn parse_metavar_expr(src: &mut TtIter<'_, Span>) -> Result<Op, ()> {
let depth = if try_eat_comma(&mut args) { Some(parse_depth(&mut args)?) } else { None };
Op::Count { name: ident.sym.clone(), depth }
}
+ s if sym::concat == *s => {
+ let mut elements = Vec::new();
+ while let Some(next) = args.peek_n(0) {
+ let element = if let tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) = next {
+ args.next().expect("already peeked");
+ ConcatMetaVarExprElem::Literal(lit.clone())
+ } else {
+ let is_var = try_eat_dollar(&mut args);
+ let ident = args.expect_ident_or_underscore()?.clone();
+
+ if is_var {
+ ConcatMetaVarExprElem::Var(ident)
+ } else {
+ ConcatMetaVarExprElem::Ident(ident)
+ }
+ };
+ elements.push(element);
+ if args.peek_n(0).is_some() {
+ args.expect_comma()?;
+ }
+ }
+ if elements.len() < 2 {
+ return Err(());
+ }
+ Op::Concat { elements: elements.into_boxed_slice(), span: func.span }
+ }
_ => return Err(()),
};
@@ -394,3 +456,11 @@ fn try_eat_comma(src: &mut TtIter<'_, Span>) -> bool {
}
false
}
+
+fn try_eat_dollar(src: &mut TtIter<'_, Span>) -> bool {
+ if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '$', .. }))) = src.peek_n(0) {
+ let _ = src.next();
+ return true;
+ }
+ false
+}