about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crates/hir-def/src/macro_expansion_tests/mbe/meta_syntax.rs4
-rw-r--r--crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs78
-rw-r--r--crates/mbe/src/benchmark.rs2
-rw-r--r--crates/mbe/src/expander/matcher.rs6
-rw-r--r--crates/mbe/src/expander/transcriber.rs41
-rw-r--r--crates/mbe/src/parser.rs10
-rw-r--r--crates/mbe/src/tt_iter.rs7
7 files changed, 83 insertions, 65 deletions
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe/meta_syntax.rs b/crates/hir-def/src/macro_expansion_tests/mbe/meta_syntax.rs
index 7e7b4004421..e875950e4e5 100644
--- a/crates/hir-def/src/macro_expansion_tests/mbe/meta_syntax.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mbe/meta_syntax.rs
@@ -18,7 +18,7 @@ macro_rules! m {
     ($($false:ident)*) => ($false);
     (double_dollar) => ($$);
     ($) => (m!($););
-    ($($t:tt)*) => ($( ${ignore(t)} ${index()} )-*);
+    ($($t:tt)*) => ($( ${ignore($t)} ${index()} )-*);
 }
 m!($);
 "#,
@@ -33,7 +33,7 @@ macro_rules! m {
     ($($false:ident)*) => ($false);
     (double_dollar) => ($$);
     ($) => (m!($););
-    ($($t:tt)*) => ($( ${ignore(t)} ${index()} )-*);
+    ($($t:tt)*) => ($( ${ignore($t)} ${index()} )-*);
 }
 m!($);
 "#]],
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs b/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs
index 967b5ad36ba..6560d0ec466 100644
--- a/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs
@@ -77,13 +77,13 @@ fn test_metavar_exprs() {
     check(
         r#"
 macro_rules! m {
-    ( $( $t:tt )* ) => ( $( ${ignore(t)} -${index()} )-* );
+    ( $( $t:tt )* ) => ( $( ${ignore($t)} -${index()} )-* );
 }
 const _: i32 = m!(a b c);
     "#,
         expect![[r#"
 macro_rules! m {
-    ( $( $t:tt )* ) => ( $( ${ignore(t)} -${index()} )-* );
+    ( $( $t:tt )* ) => ( $( ${ignore($t)} -${index()} )-* );
 }
 const _: i32 = -0--1--2;
     "#]],
@@ -96,7 +96,7 @@ fn count_basic() {
         r#"
 macro_rules! m {
     ($($t:ident),*) => {
-        ${count(t)}
+        ${count($t)}
     }
 }
 
@@ -109,7 +109,7 @@ fn test() {
         expect![[r#"
 macro_rules! m {
     ($($t:ident),*) => {
-        ${count(t)}
+        ${count($t)}
     }
 }
 
@@ -130,9 +130,9 @@ macro_rules! foo {
     ($( $( $($t:ident)* ),* );*) => {
         $(
             {
-                let depth_none = ${count(t)};
-                let depth_zero = ${count(t, 0)};
-                let depth_one = ${count(t, 1)};
+                let depth_none = ${count($t)};
+                let depth_zero = ${count($t, 0)};
+                let depth_one = ${count($t, 1)};
             }
         )*
     }
@@ -150,9 +150,9 @@ macro_rules! foo {
     ($( $( $($t:ident)* ),* );*) => {
         $(
             {
-                let depth_none = ${count(t)};
-                let depth_zero = ${count(t, 0)};
-                let depth_one = ${count(t, 1)};
+                let depth_none = ${count($t)};
+                let depth_zero = ${count($t, 0)};
+                let depth_one = ${count($t, 1)};
             }
         )*
     }
@@ -160,11 +160,11 @@ macro_rules! foo {
 
 fn bar() {
      {
-        let depth_none = 6;
+        let depth_none = 3;
         let depth_zero = 3;
         let depth_one = 6;
     } {
-        let depth_none = 3;
+        let depth_none = 1;
         let depth_zero = 1;
         let depth_one = 3;
     }
@@ -178,12 +178,12 @@ fn count_depth_out_of_bounds() {
     check(
         r#"
 macro_rules! foo {
-    ($($t:ident)*) => { ${count(t, 1)} };
-    ($( $( $l:literal )* );*) => { $(${count(l, 1)};)* }
+    ($($t:ident)*) => { ${count($t, 1)} };
+    ($( $( $l:literal )* );*) => { $(${count($l, 1)};)* }
 }
 macro_rules! bar {
-    ($($t:ident)*) => { ${count(t, 1024)} };
-    ($( $( $l:literal )* );*) => { $(${count(l, 8192)};)* }
+    ($($t:ident)*) => { ${count($t, 1024)} };
+    ($( $( $l:literal )* );*) => { $(${count($l, 8192)};)* }
 }
 
 fn test() {
@@ -195,19 +195,21 @@ fn test() {
 "#,
         expect![[r#"
 macro_rules! foo {
-    ($($t:ident)*) => { ${count(t, 1)} };
-    ($( $( $l:literal )* );*) => { $(${count(l, 1)};)* }
+    ($($t:ident)*) => { ${count($t, 1)} };
+    ($( $( $l:literal )* );*) => { $(${count($l, 1)};)* }
 }
 macro_rules! bar {
-    ($($t:ident)*) => { ${count(t, 1024)} };
-    ($( $( $l:literal )* );*) => { $(${count(l, 8192)};)* }
+    ($($t:ident)*) => { ${count($t, 1024)} };
+    ($( $( $l:literal )* );*) => { $(${count($l, 8192)};)* }
 }
 
 fn test() {
-    /* error: ${count} out of bounds */;
-    /* error: ${count} out of bounds */;
-    /* error: ${count} out of bounds */;
-    /* error: ${count} out of bounds */;
+    2;
+    2;
+    1;;
+    2;
+    2;
+    1;;
 }
 "#]],
     );
@@ -218,8 +220,8 @@ fn misplaced_count() {
     check(
         r#"
 macro_rules! foo {
-    ($($t:ident)*) => { $(${count(t)})* };
-    ($l:literal) => { ${count(l)} }
+    ($($t:ident)*) => { $(${count($t)})* };
+    ($l:literal) => { ${count($l)} }
 }
 
 fn test() {
@@ -229,13 +231,13 @@ fn test() {
 "#,
         expect![[r#"
 macro_rules! foo {
-    ($($t:ident)*) => { $(${count(t)})* };
-    ($l:literal) => { ${count(l)} }
+    ($($t:ident)*) => { $(${count($t)})* };
+    ($l:literal) => { ${count($l)} }
 }
 
 fn test() {
-    /* error: ${count} misplaced */;
-    /* error: ${count} misplaced */;
+    1 1 1;
+    1;
 }
 "#]],
     );
@@ -246,13 +248,13 @@ fn malformed_count() {
     check(
         r#"
 macro_rules! too_many_args {
-    ($($t:ident)*) => { ${count(t, 1, leftover)} }
+    ($($t:ident)*) => { ${count($t, 1, leftover)} }
 }
 macro_rules! depth_suffixed {
-    ($($t:ident)*) => { ${count(t, 0usize)} }
+    ($($t:ident)*) => { ${count($t, 0usize)} }
 }
 macro_rules! depth_too_large {
-    ($($t:ident)*) => { ${count(t, 18446744073709551616)} }
+    ($($t:ident)*) => { ${count($t, 18446744073709551616)} }
 }
 
 fn test() {
@@ -263,13 +265,13 @@ fn test() {
 "#,
         expect![[r#"
 macro_rules! too_many_args {
-    ($($t:ident)*) => { ${count(t, 1, leftover)} }
+    ($($t:ident)*) => { ${count($t, 1, leftover)} }
 }
 macro_rules! depth_suffixed {
-    ($($t:ident)*) => { ${count(t, 0usize)} }
+    ($($t:ident)*) => { ${count($t, 0usize)} }
 }
 macro_rules! depth_too_large {
-    ($($t:ident)*) => { ${count(t, 18446744073709551616)} }
+    ($($t:ident)*) => { ${count($t, 18446744073709551616)} }
 }
 
 fn test() {
@@ -288,7 +290,7 @@ fn count_interaction_with_empty_binding() {
         r#"
 macro_rules! m {
     ($($t:ident),*) => {
-        ${count(t, 100)}
+        ${count($t, 100)}
     }
 }
 
@@ -299,7 +301,7 @@ fn test() {
         expect![[r#"
 macro_rules! m {
     ($($t:ident),*) => {
-        ${count(t, 100)}
+        ${count($t, 100)}
     }
 }
 
diff --git a/crates/mbe/src/benchmark.rs b/crates/mbe/src/benchmark.rs
index f503aecce2c..1d8c94848f7 100644
--- a/crates/mbe/src/benchmark.rs
+++ b/crates/mbe/src/benchmark.rs
@@ -199,7 +199,7 @@ fn invocation_fixtures(
                 });
                 parent.token_trees.push(subtree.into());
             }
-            Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. } => {}
+            Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. } | Op::Length { .. } => {}
         };
 
         // Simple linear congruential generator for deterministic result
diff --git a/crates/mbe/src/expander/matcher.rs b/crates/mbe/src/expander/matcher.rs
index 012b02a3f87..b7e865a68e5 100644
--- a/crates/mbe/src/expander/matcher.rs
+++ b/crates/mbe/src/expander/matcher.rs
@@ -588,7 +588,9 @@ fn match_loop_inner<'t, S: Span>(
                 item.is_error = true;
                 error_items.push(item);
             }
-            OpDelimited::Op(Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. }) => {
+            OpDelimited::Op(
+                Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. } | Op::Length { .. },
+            ) => {
                 stdx::never!("metavariable expression in lhs found");
             }
             OpDelimited::Open => {
@@ -851,7 +853,7 @@ fn collect_vars<S: Span>(collector_fun: &mut impl FnMut(SmolStr), pattern: &Meta
             Op::Subtree { tokens, .. } => collect_vars(collector_fun, tokens),
             Op::Repeat { tokens, .. } => collect_vars(collector_fun, tokens),
             Op::Literal(_) | Op::Ident(_) | Op::Punct(_) => {}
-            Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. } => {
+            Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. } | Op::Length { .. } => {
                 stdx::never!("metavariable expression in lhs found");
             }
         }
diff --git a/crates/mbe/src/expander/transcriber.rs b/crates/mbe/src/expander/transcriber.rs
index 9fccf5f424d..a4dbd907a37 100644
--- a/crates/mbe/src/expander/transcriber.rs
+++ b/crates/mbe/src/expander/transcriber.rs
@@ -232,6 +232,21 @@ fn expand_subtree<S: Span>(
                     .into(),
                 );
             }
+            Op::Length { depth } => {
+                let length = ctx.nesting.get(ctx.nesting.len() - 1 - depth).map_or(0, |_nest| {
+                    // FIXME: to be implemented
+                    0
+                });
+                arena.push(
+                    tt::Leaf::Literal(tt::Literal {
+                        text: length.to_string().into(),
+                        // FIXME
+                        #[allow(deprecated)]
+                        span: S::DUMMY,
+                    })
+                    .into(),
+                );
+            }
             Op::Count { name, depth } => {
                 let mut binding = match ctx.bindings.get(name.as_str()) {
                     Ok(b) => b,
@@ -518,28 +533,18 @@ fn fix_up_and_push_path_tt<S: Span>(buf: &mut Vec<tt::TokenTree<S>>, subtree: tt
 fn count<S>(
     ctx: &ExpandCtx<'_, S>,
     binding: &Binding<S>,
-    our_depth: usize,
-    count_depth: Option<usize>,
+    depth_curr: usize,
+    depth_max: usize,
 ) -> Result<usize, CountError> {
     match binding {
-        Binding::Nested(bs) => match count_depth {
-            None => bs.iter().map(|b| count(ctx, b, our_depth + 1, None)).sum(),
-            Some(0) => Ok(bs.len()),
-            Some(d) => bs.iter().map(|b| count(ctx, b, our_depth + 1, Some(d - 1))).sum(),
-        },
-        Binding::Empty => Ok(0),
-        Binding::Fragment(_) | Binding::Missing(_) => {
-            if our_depth == 0 {
-                // `${count(t)}` is placed inside the innermost repetition. This includes cases
-                // where `t` is not a repeated fragment.
-                Err(CountError::Misplaced)
-            } else if count_depth.is_none() {
-                Ok(1)
+        Binding::Nested(bs) => {
+            if depth_curr == depth_max {
+                Ok(bs.len())
             } else {
-                // We've reached at the innermost repeated fragment, but the user wants us to go
-                // further!
-                Err(CountError::OutOfBounds)
+                bs.iter().map(|b| count(ctx, b, depth_curr + 1, depth_max)).sum()
             }
         }
+        Binding::Empty => Ok(0),
+        Binding::Fragment(_) | Binding::Missing(_) => Ok(1),
     }
 }
diff --git a/crates/mbe/src/parser.rs b/crates/mbe/src/parser.rs
index 00ba35377a4..5f1815304ed 100644
--- a/crates/mbe/src/parser.rs
+++ b/crates/mbe/src/parser.rs
@@ -54,7 +54,8 @@ pub(crate) enum Op<S> {
     Var { name: SmolStr, kind: Option<MetaVarKind>, id: S },
     Ignore { name: SmolStr, id: S },
     Index { depth: usize },
-    Count { name: SmolStr, depth: Option<usize> },
+    Length { depth: usize },
+    Count { name: SmolStr, depth: usize },
     Repeat { tokens: MetaTemplate<S>, kind: RepeatKind, separator: Option<Separator<S>> },
     Subtree { tokens: MetaTemplate<S>, delimiter: tt::Delimiter<S> },
     Literal(tt::Literal<S>),
@@ -299,15 +300,16 @@ fn parse_metavar_expr<S: Span>(src: &mut TtIter<'_, S>) -> Result<Op<S>, ()> {
 
     let op = match &*func.text {
         "ignore" => {
+            args.expect_dollar()?;
             let ident = args.expect_ident()?;
             Op::Ignore { name: ident.text.clone(), id: ident.span }
         }
         "index" => Op::Index { depth: parse_depth(&mut args)? },
+        "length" => Op::Length { depth: parse_depth(&mut args)? },
         "count" => {
+            args.expect_dollar()?;
             let ident = args.expect_ident()?;
-            // `${count(t)}` and `${count(t,)}` have different meanings. Not sure if this is a bug
-            // but that's how it's implemented in rustc as of this writing. See rust-lang/rust#111904.
-            let depth = if try_eat_comma(&mut args) { Some(parse_depth(&mut args)?) } else { None };
+            let depth = if try_eat_comma(&mut args) { parse_depth(&mut args)? } else { 0 };
             Op::Count { name: ident.text.clone(), depth }
         }
         _ => return Err(()),
diff --git a/crates/mbe/src/tt_iter.rs b/crates/mbe/src/tt_iter.rs
index 40e8a2385f4..97b37b24add 100644
--- a/crates/mbe/src/tt_iter.rs
+++ b/crates/mbe/src/tt_iter.rs
@@ -51,6 +51,13 @@ impl<'a, S: Span> TtIter<'a, S> {
         }
     }
 
+    pub(crate) fn expect_dollar(&mut self) -> Result<(), ()> {
+        match self.expect_leaf()? {
+            tt::Leaf::Punct(tt::Punct { char: '$', .. }) => Ok(()),
+            _ => Err(()),
+        }
+    }
+
     pub(crate) fn expect_ident(&mut self) -> Result<&'a tt::Ident<S>, ()> {
         match self.expect_leaf()? {
             tt::Leaf::Ident(it) if it.text != "_" => Ok(it),