Skip to content

Commit 3f4a23e

Browse files
committed
Allow lifetime repeats in macros: $($x)'a*
This works in rustc. This change isn't motivated by any real code. I just learned about it and decided to see why it doesn't work with rust-analyzer.
1 parent 0a74d46 commit 3f4a23e

File tree

6 files changed

+56
-8
lines changed

6 files changed

+56
-8
lines changed

src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2029,3 +2029,25 @@ fn f() {
20292029
"#]],
20302030
);
20312031
}
2032+
2033+
#[test]
2034+
fn lifetime_repeat() {
2035+
check(
2036+
r#"
2037+
macro_rules! m {
2038+
($($x:expr)'a*) => (stringify!($($x)'b*));
2039+
}
2040+
fn f() {
2041+
let _ = m!(0 'a 1 'a 2);
2042+
}
2043+
"#,
2044+
expect![[r#"
2045+
macro_rules! m {
2046+
($($x:expr)'a*) => (stringify!($($x)'b*));
2047+
}
2048+
fn f() {
2049+
let _ = stringify!(0 'b1 'b2);
2050+
}
2051+
"#]],
2052+
);
2053+
}

src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/meta_syntax.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ macro_rules! m {
1313
($(x),*) => ();
1414
($(x)_*) => ();
1515
($(x)i*) => ();
16+
($(x)'a*) => ();
17+
($(x)'_*) => ();
1618
($($i:ident)*) => ($_);
1719
($($true:ident)*) => ($true);
1820
($($false:ident)*) => ($false);
@@ -28,6 +30,8 @@ macro_rules! m {
2830
($(x),*) => ();
2931
($(x)_*) => ();
3032
($(x)i*) => ();
33+
($(x)'a*) => ();
34+
($(x)'_*) => ();
3135
($($i:ident)*) => ($_);
3236
($($true:ident)*) => ($true);
3337
($($false:ident)*) => ($false);

src/tools/rust-analyzer/crates/mbe/src/benchmark.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,10 @@ fn invocation_fixtures(
197197
builder.push(tt::Leaf::Punct(*it))
198198
}
199199
}
200+
Separator::Lifetime(punct, ident) => {
201+
builder.push(tt::Leaf::Punct(*punct));
202+
builder.push(tt::Leaf::Ident(ident.clone()));
203+
}
200204
};
201205
}
202206
}

src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -823,7 +823,7 @@ fn match_meta_var<'t>(
823823
"expected token tree",
824824
)
825825
}),
826-
MetaVarKind::Lifetime => expect_lifetime(input).map_err(|()| {
826+
MetaVarKind::Lifetime => expect_lifetime(input).map(drop).map_err(|()| {
827827
ExpandError::binding_error(
828828
span.unwrap_or(delim_span.close),
829829
"expected lifetime",
@@ -963,6 +963,10 @@ fn expect_separator<S: Copy>(iter: &mut TtIter<'_, S>, separator: &Separator) ->
963963
}
964964
Err(_) => false,
965965
},
966+
Separator::Lifetime(_punct, ident) => match expect_lifetime(&mut fork) {
967+
Ok(lifetime) => lifetime.sym == ident.sym,
968+
Err(_) => false,
969+
},
966970
};
967971
if ok {
968972
*iter = fork;
@@ -983,13 +987,12 @@ fn expect_tt<S: Copy>(iter: &mut TtIter<'_, S>) -> Result<(), ()> {
983987
Ok(())
984988
}
985989

986-
fn expect_lifetime<S: Copy>(iter: &mut TtIter<'_, S>) -> Result<(), ()> {
990+
fn expect_lifetime<'a, S: Copy>(iter: &mut TtIter<'a, S>) -> Result<&'a tt::Ident<S>, ()> {
987991
let punct = iter.expect_single_punct()?;
988992
if punct.char != '\'' {
989993
return Err(());
990994
}
991-
iter.expect_ident_or_underscore()?;
992-
Ok(())
995+
iter.expect_ident_or_underscore()
993996
}
994997

995998
fn eat_char<S: Copy>(iter: &mut TtIter<'_, S>, c: char) {

src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,10 @@ fn expand_repeat(
497497
builder.push(tt::Leaf::from(punct));
498498
}
499499
}
500+
Separator::Lifetime(punct, ident) => {
501+
builder.push(tt::Leaf::from(*punct));
502+
builder.push(tt::Leaf::from(ident.clone()));
503+
}
500504
};
501505
}
502506

src/tools/rust-analyzer/crates/mbe/src/parser.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ pub(crate) enum Separator {
155155
Literal(tt::Literal<Span>),
156156
Ident(tt::Ident<Span>),
157157
Puncts(ArrayVec<tt::Punct<Span>, MAX_GLUED_PUNCT_LEN>),
158+
Lifetime(tt::Punct<Span>, tt::Ident<Span>),
158159
}
159160

160161
// Note that when we compare a Separator, we just care about its textual value.
@@ -170,6 +171,7 @@ impl PartialEq for Separator {
170171
let b_iter = b.iter().map(|b| b.char);
171172
a_iter.eq(b_iter)
172173
}
174+
(Lifetime(_, a), Lifetime(_, b)) => a.sym == b.sym,
173175
_ => false,
174176
}
175177
}
@@ -350,10 +352,19 @@ fn parse_repeat(src: &mut TtIter<'_, Span>) -> Result<(Option<Separator>, Repeat
350352
_ => true,
351353
};
352354
match tt {
353-
tt::Leaf::Ident(_) | tt::Leaf::Literal(_) if has_sep => {
354-
return Err(ParseError::InvalidRepeat);
355-
}
356-
tt::Leaf::Ident(ident) => separator = Separator::Ident(ident.clone()),
355+
tt::Leaf::Ident(ident) => match separator {
356+
Separator::Puncts(puncts) if puncts.is_empty() => {
357+
separator = Separator::Ident(ident.clone());
358+
}
359+
Separator::Puncts(puncts) => match puncts.as_slice() {
360+
[tt::Punct { char: '\'', .. }] => {
361+
separator = Separator::Lifetime(puncts[0], ident.clone());
362+
}
363+
_ => return Err(ParseError::InvalidRepeat),
364+
},
365+
_ => return Err(ParseError::InvalidRepeat),
366+
},
367+
tt::Leaf::Literal(_) if has_sep => return Err(ParseError::InvalidRepeat),
357368
tt::Leaf::Literal(lit) => separator = Separator::Literal(lit.clone()),
358369
tt::Leaf::Punct(punct) => {
359370
let repeat_kind = match punct.char {

0 commit comments

Comments
 (0)