From a7c09532a08c1a82830cd3a2edc31e99b1c07990 Mon Sep 17 00:00:00 2001 From: Tadeo Kondrak Date: Fri, 13 Jun 2025 17:51:10 -0600 Subject: [PATCH 1/3] 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. --- .../hir-def/src/macro_expansion_tests/mbe.rs | 22 +++++++++++++++++++ .../macro_expansion_tests/mbe/meta_syntax.rs | 4 ++++ crates/mbe/src/benchmark.rs | 4 ++++ crates/mbe/src/expander/matcher.rs | 11 ++++++---- crates/mbe/src/expander/transcriber.rs | 4 ++++ 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(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(iter: &mut TtIter<'_, S>) -> Result<(), ()> { Ok(()) } -fn expect_lifetime(iter: &mut TtIter<'_, S>) -> Result<(), ()> { +fn expect_lifetime<'a, S: Copy>(iter: &mut TtIter<'a, S>) -> Result<&'a tt::Ident, ()> { 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(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), Ident(tt::Ident), Puncts(ArrayVec, MAX_GLUED_PUNCT_LEN>), + Lifetime(tt::Punct, tt::Ident), } // 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, 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 { From 12226b704a9a382ad59cec7188d1667f3acba04e Mon Sep 17 00:00:00 2001 From: Tadeo Kondrak Date: Sun, 15 Jun 2025 17:42:06 +0200 Subject: [PATCH 2/3] Fix spacing for LIFETIME_IDENT near keywords and literals in test output --- crates/hir-def/src/macro_expansion_tests/mbe.rs | 2 +- .../src/macro_expansion_tests/mbe/regression.rs | 2 +- crates/hir-def/src/macro_expansion_tests/mod.rs | 12 ++++++++++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/crates/hir-def/src/macro_expansion_tests/mbe.rs b/crates/hir-def/src/macro_expansion_tests/mbe.rs index ef93a2a790..c6d901ec93 100644 --- a/crates/hir-def/src/macro_expansion_tests/mbe.rs +++ b/crates/hir-def/src/macro_expansion_tests/mbe.rs @@ -2046,7 +2046,7 @@ macro_rules! m { ($($x:expr)'a*) => (stringify!($($x)'b*)); } fn f() { - let _ = stringify!(0 'b1 'b2); + let _ = stringify!(0 'b 1 'b 2); } "#]], ); diff --git a/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs b/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs index 2cc3ca8c75..e2022c7967 100644 --- a/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs +++ b/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs @@ -784,7 +784,7 @@ macro_rules! delegate_impl { } } } -impl <> Data for &'amut G where G: Data {} +impl <> Data for &'a mut G where G: Data {} "#]], ); } diff --git a/crates/hir-def/src/macro_expansion_tests/mod.rs b/crates/hir-def/src/macro_expansion_tests/mod.rs index b532156091..15b31c1e7e 100644 --- a/crates/hir-def/src/macro_expansion_tests/mod.rs +++ b/crates/hir-def/src/macro_expansion_tests/mod.rs @@ -308,8 +308,16 @@ fn pretty_print_macro_expansion( { " " } - (IDENT, _) if curr_kind.is_keyword(Edition::CURRENT) => " ", - (_, IDENT) if prev_kind.is_keyword(Edition::CURRENT) => " ", + (IDENT | LIFETIME_IDENT, _) + if curr_kind.is_keyword(Edition::CURRENT) || curr_kind.is_literal() => + { + " " + } + (_, IDENT | LIFETIME_IDENT) + if prev_kind.is_keyword(Edition::CURRENT) || prev_kind.is_literal() => + { + " " + } (T![>], IDENT) => " ", (T![>], _) if curr_kind.is_keyword(Edition::CURRENT) => " ", (T![->], _) | (_, T![->]) => " ", From 2070e9a8f393528b716ff6b7df52ecc0564b9d62 Mon Sep 17 00:00:00 2001 From: Tadeo Kondrak Date: Sun, 15 Jun 2025 18:23:18 +0200 Subject: [PATCH 3/3] Use is_any_identifier in pretty_print_macro_expansion --- .../hir-def/src/macro_expansion_tests/mod.rs | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/crates/hir-def/src/macro_expansion_tests/mod.rs b/crates/hir-def/src/macro_expansion_tests/mod.rs index 15b31c1e7e..1c69b37f16 100644 --- a/crates/hir-def/src/macro_expansion_tests/mod.rs +++ b/crates/hir-def/src/macro_expansion_tests/mod.rs @@ -302,19 +302,12 @@ fn pretty_print_macro_expansion( (_, T!['{']) => " ", (T![;] | T!['{'] | T!['}'], _) => "\n", (_, T!['}']) => "\n", - (IDENT | LIFETIME_IDENT, IDENT | LIFETIME_IDENT) => " ", - _ if prev_kind.is_keyword(Edition::CURRENT) - && curr_kind.is_keyword(Edition::CURRENT) => - { - " " - } - (IDENT | LIFETIME_IDENT, _) - if curr_kind.is_keyword(Edition::CURRENT) || curr_kind.is_literal() => - { - " " - } - (_, IDENT | LIFETIME_IDENT) - if prev_kind.is_keyword(Edition::CURRENT) || prev_kind.is_literal() => + _ if (prev_kind.is_any_identifier() + || prev_kind == LIFETIME_IDENT + || prev_kind.is_literal()) + && (curr_kind.is_any_identifier() + || curr_kind == LIFETIME_IDENT + || curr_kind.is_literal()) => { " " }