Skip to content

Commit bc521f1

Browse files
committed
Implement ${count()} metavariable expression
1 parent 5102939 commit bc521f1

File tree

7 files changed

+382
-25
lines changed

7 files changed

+382
-25
lines changed

crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs

+220
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,223 @@ const _: i32 = -0--1--2;
8989
"#]],
9090
);
9191
}
92+
93+
#[test]
94+
fn count_basic() {
95+
check(
96+
r#"
97+
macro_rules! m {
98+
($($t:ident),*) => {
99+
${count(t)}
100+
}
101+
}
102+
103+
fn test() {
104+
m!();
105+
m!(a);
106+
m!(a, a);
107+
}
108+
"#,
109+
expect![[r#"
110+
macro_rules! m {
111+
($($t:ident),*) => {
112+
${count(t)}
113+
}
114+
}
115+
116+
fn test() {
117+
0;
118+
1;
119+
2;
120+
}
121+
"#]],
122+
);
123+
}
124+
125+
#[test]
126+
fn count_with_depth() {
127+
check(
128+
r#"
129+
macro_rules! foo {
130+
($( $( $($t:ident)* ),* );*) => {
131+
$(
132+
{
133+
let depth_none = ${count(t)};
134+
let depth_zero = ${count(t, 0)};
135+
let depth_one = ${count(t, 1)};
136+
}
137+
)*
138+
}
139+
}
140+
141+
fn bar() {
142+
foo!(
143+
a a a, a, a a;
144+
a a a
145+
)
146+
}
147+
"#,
148+
expect![[r#"
149+
macro_rules! foo {
150+
($( $( $($t:ident)* ),* );*) => {
151+
$(
152+
{
153+
let depth_none = ${count(t)};
154+
let depth_zero = ${count(t, 0)};
155+
let depth_one = ${count(t, 1)};
156+
}
157+
)*
158+
}
159+
}
160+
161+
fn bar() {
162+
{
163+
let depth_none = 6;
164+
let depth_zero = 3;
165+
let depth_one = 6;
166+
} {
167+
let depth_none = 3;
168+
let depth_zero = 1;
169+
let depth_one = 3;
170+
}
171+
}
172+
"#]],
173+
);
174+
}
175+
176+
#[test]
177+
fn count_depth_out_of_bounds() {
178+
check(
179+
r#"
180+
macro_rules! foo {
181+
($($t:ident)*) => { ${count(t, 1)} };
182+
($( $( $l:literal )* );*) => { $(${count(l, 1)};)* }
183+
}
184+
macro_rules! bar {
185+
($($t:ident)*) => { ${count(t, 1024)} };
186+
($( $( $l:literal )* );*) => { $(${count(l, 8192)};)* }
187+
}
188+
189+
fn test() {
190+
foo!(a b);
191+
foo!(1 2; 3);
192+
bar!(a b);
193+
bar!(1 2; 3);
194+
}
195+
"#,
196+
expect![[r#"
197+
macro_rules! foo {
198+
($($t:ident)*) => { ${count(t, 1)} };
199+
($( $( $l:literal )* );*) => { $(${count(l, 1)};)* }
200+
}
201+
macro_rules! bar {
202+
($($t:ident)*) => { ${count(t, 1024)} };
203+
($( $( $l:literal )* );*) => { $(${count(l, 8192)};)* }
204+
}
205+
206+
fn test() {
207+
/* error: ${count} out of bounds */;
208+
/* error: ${count} out of bounds */;
209+
/* error: ${count} out of bounds */;
210+
/* error: ${count} out of bounds */;
211+
}
212+
"#]],
213+
);
214+
}
215+
216+
#[test]
217+
fn misplaced_count() {
218+
check(
219+
r#"
220+
macro_rules! foo {
221+
($($t:ident)*) => { $(${count(t)})* };
222+
($l:literal) => { ${count(l)} }
223+
}
224+
225+
fn test() {
226+
foo!(a b c);
227+
foo!(1);
228+
}
229+
"#,
230+
expect![[r#"
231+
macro_rules! foo {
232+
($($t:ident)*) => { $(${count(t)})* };
233+
($l:literal) => { ${count(l)} }
234+
}
235+
236+
fn test() {
237+
/* error: ${count} misplaced */;
238+
/* error: ${count} misplaced */;
239+
}
240+
"#]],
241+
);
242+
}
243+
244+
#[test]
245+
fn malformed_count() {
246+
check(
247+
r#"
248+
macro_rules! too_many_args {
249+
($($t:ident)*) => { ${count(t, 1, leftover)} }
250+
}
251+
macro_rules! depth_suffixed {
252+
($($t:ident)*) => { ${count(t, 0usize)} }
253+
}
254+
macro_rules! depth_too_large {
255+
($($t:ident)*) => { ${count(t, 18446744073709551616)} }
256+
}
257+
258+
fn test() {
259+
too_many_args!();
260+
depth_suffixed!();
261+
depth_too_large!();
262+
}
263+
"#,
264+
expect![[r#"
265+
macro_rules! too_many_args {
266+
($($t:ident)*) => { ${count(t, 1, leftover)} }
267+
}
268+
macro_rules! depth_suffixed {
269+
($($t:ident)*) => { ${count(t, 0usize)} }
270+
}
271+
macro_rules! depth_too_large {
272+
($($t:ident)*) => { ${count(t, 18446744073709551616)} }
273+
}
274+
275+
fn test() {
276+
/* error: invalid macro definition: invalid metavariable expression */;
277+
/* error: invalid macro definition: invalid metavariable expression */;
278+
/* error: invalid macro definition: invalid metavariable expression */;
279+
}
280+
"#]],
281+
);
282+
}
283+
284+
#[test]
285+
fn count_interaction_with_empty_binding() {
286+
// FIXME: Should this error? rustc currently accepts it.
287+
check(
288+
r#"
289+
macro_rules! m {
290+
($($t:ident),*) => {
291+
${count(t, 100)}
292+
}
293+
}
294+
295+
fn test() {
296+
m!();
297+
}
298+
"#,
299+
expect![[r#"
300+
macro_rules! m {
301+
($($t:ident),*) => {
302+
${count(t, 100)}
303+
}
304+
}
305+
306+
fn test() {
307+
0;
308+
}
309+
"#]],
310+
);
311+
}

crates/mbe/src/benchmark.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ fn invocation_fixtures(rules: &FxHashMap<String, DeclarativeMacro>) -> Vec<(Stri
195195
});
196196
parent.token_trees.push(subtree.into());
197197
}
198-
Op::Ignore { .. } | Op::Index { .. } => {}
198+
Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. } => {}
199199
};
200200

201201
// Simple linear congruential generator for deterministic result

crates/mbe/src/expander/matcher.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -567,7 +567,9 @@ fn match_loop_inner<'t>(
567567
item.is_error = true;
568568
error_items.push(item);
569569
}
570-
OpDelimited::Op(Op::Ignore { .. } | Op::Index { .. }) => {}
570+
OpDelimited::Op(Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. }) => {
571+
stdx::never!("metavariable expression in lhs found");
572+
}
571573
OpDelimited::Open => {
572574
if matches!(src.peek_n(0), Some(tt::TokenTree::Subtree(..))) {
573575
item.dot.next();
@@ -811,7 +813,9 @@ fn collect_vars(collector_fun: &mut impl FnMut(SmolStr), pattern: &MetaTemplate)
811813
Op::Var { name, .. } => collector_fun(name.clone()),
812814
Op::Subtree { tokens, .. } => collect_vars(collector_fun, tokens),
813815
Op::Repeat { tokens, .. } => collect_vars(collector_fun, tokens),
814-
Op::Ignore { .. } | Op::Index { .. } | Op::Literal(_) | Op::Ident(_) | Op::Punct(_) => {
816+
Op::Literal(_) | Op::Ident(_) | Op::Punct(_) => {}
817+
Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. } => {
818+
stdx::never!("metavariable expression in lhs found");
815819
}
816820
}
817821
}

0 commit comments

Comments
 (0)