Unnamed repository; edit this file 'description' to name the repository.
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.
| -rw-r--r-- | crates/hir-def/src/macro_expansion_tests/mbe.rs | 22 | ||||
| -rw-r--r-- | crates/hir-def/src/macro_expansion_tests/mbe/meta_syntax.rs | 4 | ||||
| -rw-r--r-- | crates/mbe/src/benchmark.rs | 4 | ||||
| -rw-r--r-- | crates/mbe/src/expander/matcher.rs | 11 | ||||
| -rw-r--r-- | crates/mbe/src/expander/transcriber.rs | 4 | ||||
| -rw-r--r-- | crates/mbe/src/parser.rs | 19 |
6 files changed, 56 insertions, 8 deletions
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe.rs b/crates/hir-def/src/macro_expansion_tests/mbe.rs index eea50d16f5..ef93a2a790 100644 --- a/crates/hir-def/src/macro_expansion_tests/mbe.rs +++ b/crates/hir-def/src/macro_expansion_tests/mbe.rs @@ -2029,3 +2029,25 @@ fn f() { "#]], ); } + +#[test] +fn lifetime_repeat() { + check( + r#" +macro_rules! m { + ($($x:expr)'a*) => (stringify!($($x)'b*)); +} +fn f() { + let _ = m!(0 'a 1 'a 2); +} + "#, + expect![[r#" +macro_rules! m { + ($($x:expr)'a*) => (stringify!($($x)'b*)); +} +fn f() { + let _ = stringify!(0 'b1 'b2); +} + "#]], + ); +} 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 2d289b7683..2c94f0e8d0 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 @@ -13,6 +13,8 @@ macro_rules! m { ($(x),*) => (); ($(x)_*) => (); ($(x)i*) => (); + ($(x)'a*) => (); + ($(x)'_*) => (); ($($i:ident)*) => ($_); ($($true:ident)*) => ($true); ($($false:ident)*) => ($false); @@ -28,6 +30,8 @@ macro_rules! m { ($(x),*) => (); ($(x)_*) => (); ($(x)i*) => (); + ($(x)'a*) => (); + ($(x)'_*) => (); ($($i:ident)*) => ($_); ($($true:ident)*) => ($true); ($($false:ident)*) => ($false); diff --git a/crates/mbe/src/benchmark.rs b/crates/mbe/src/benchmark.rs index db75dceae1..04ac85ad43 100644 --- a/crates/mbe/src/benchmark.rs +++ b/crates/mbe/src/benchmark.rs @@ -197,6 +197,10 @@ fn invocation_fixtures( builder.push(tt::Leaf::Punct(*it)) } } + Separator::Lifetime(punct, ident) => { + builder.push(tt::Leaf::Punct(*punct)); + builder.push(tt::Leaf::Ident(ident.clone())); + } }; } } diff --git a/crates/mbe/src/expander/matcher.rs b/crates/mbe/src/expander/matcher.rs index 940aaacb02..a8d5965d48 100644 --- a/crates/mbe/src/expander/matcher.rs +++ b/crates/mbe/src/expander/matcher.rs @@ -823,7 +823,7 @@ fn match_meta_var<'t>( "expected token tree", ) }), - MetaVarKind::Lifetime => expect_lifetime(input).map_err(|()| { + MetaVarKind::Lifetime => expect_lifetime(input).map(drop).map_err(|()| { ExpandError::binding_error( span.unwrap_or(delim_span.close), "expected lifetime", @@ -963,6 +963,10 @@ fn expect_separator<S: Copy>(iter: &mut TtIter<'_, S>, separator: &Separator) -> } Err(_) => false, }, + Separator::Lifetime(_punct, ident) => match expect_lifetime(&mut fork) { + Ok(lifetime) => lifetime.sym == ident.sym, + Err(_) => false, + }, }; if ok { *iter = fork; @@ -983,13 +987,12 @@ fn expect_tt<S: Copy>(iter: &mut TtIter<'_, S>) -> Result<(), ()> { Ok(()) } -fn expect_lifetime<S: Copy>(iter: &mut TtIter<'_, S>) -> Result<(), ()> { +fn expect_lifetime<'a, S: Copy>(iter: &mut TtIter<'a, S>) -> Result<&'a tt::Ident<S>, ()> { let punct = iter.expect_single_punct()?; if punct.char != '\'' { return Err(()); } - iter.expect_ident_or_underscore()?; - Ok(()) + iter.expect_ident_or_underscore() } fn eat_char<S: Copy>(iter: &mut TtIter<'_, S>, c: char) { diff --git a/crates/mbe/src/expander/transcriber.rs b/crates/mbe/src/expander/transcriber.rs index ec277ba72e..2c046df10f 100644 --- a/crates/mbe/src/expander/transcriber.rs +++ b/crates/mbe/src/expander/transcriber.rs @@ -497,6 +497,10 @@ fn expand_repeat( builder.push(tt::Leaf::from(punct)); } } + Separator::Lifetime(punct, ident) => { + builder.push(tt::Leaf::from(*punct)); + builder.push(tt::Leaf::from(ident.clone())); + } }; } diff --git a/crates/mbe/src/parser.rs b/crates/mbe/src/parser.rs index fbc353d610..711101260a 100644 --- a/crates/mbe/src/parser.rs +++ b/crates/mbe/src/parser.rs @@ -155,6 +155,7 @@ pub(crate) enum Separator { Literal(tt::Literal<Span>), Ident(tt::Ident<Span>), Puncts(ArrayVec<tt::Punct<Span>, MAX_GLUED_PUNCT_LEN>), + Lifetime(tt::Punct<Span>, tt::Ident<Span>), } // Note that when we compare a Separator, we just care about its textual value. @@ -170,6 +171,7 @@ impl PartialEq for Separator { let b_iter = b.iter().map(|b| b.char); a_iter.eq(b_iter) } + (Lifetime(_, a), Lifetime(_, b)) => a.sym == b.sym, _ => false, } } @@ -350,10 +352,19 @@ fn parse_repeat(src: &mut TtIter<'_, Span>) -> Result<(Option<Separator>, Repeat _ => true, }; match tt { - tt::Leaf::Ident(_) | tt::Leaf::Literal(_) if has_sep => { - return Err(ParseError::InvalidRepeat); - } - tt::Leaf::Ident(ident) => separator = Separator::Ident(ident.clone()), + tt::Leaf::Ident(ident) => match separator { + Separator::Puncts(puncts) if puncts.is_empty() => { + separator = Separator::Ident(ident.clone()); + } + Separator::Puncts(puncts) => match puncts.as_slice() { + [tt::Punct { char: '\'', .. }] => { + separator = Separator::Lifetime(puncts[0], ident.clone()); + } + _ => return Err(ParseError::InvalidRepeat), + }, + _ => return Err(ParseError::InvalidRepeat), + }, + tt::Leaf::Literal(_) if has_sep => return Err(ParseError::InvalidRepeat), tt::Leaf::Literal(lit) => separator = Separator::Literal(lit.clone()), tt::Leaf::Punct(punct) => { let repeat_kind = match punct.char { |