about summary refs log tree commit diff
path: root/src/tools
diff options
context:
space:
mode:
authorTadeo Kondrak <me@tadeo.ca>2025-06-13 17:51:10 -0600
committerTadeo Kondrak <me@tadeo.ca>2025-06-13 18:13:01 -0600
commit3f4a23e5a8994b49ab67321177c6d5683f8d6494 (patch)
tree4e45c309a51d6c74184b4a1c7fb80c8039733e1f /src/tools
parent0a74d467241d3b870f4913614881e8fd0ca46955 (diff)
downloadrust-3f4a23e5a8994b49ab67321177c6d5683f8d6494.tar.gz
rust-3f4a23e5a8994b49ab67321177c6d5683f8d6494.zip
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.
Diffstat (limited to 'src/tools')
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs22
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/meta_syntax.rs4
-rw-r--r--src/tools/rust-analyzer/crates/mbe/src/benchmark.rs4
-rw-r--r--src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs11
-rw-r--r--src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs4
-rw-r--r--src/tools/rust-analyzer/crates/mbe/src/parser.rs19
6 files changed, 56 insertions, 8 deletions
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs
index eea50d16f5a..ef93a2a790d 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs
+++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/meta_syntax.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/meta_syntax.rs
index 2d289b76833..2c94f0e8d09 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/meta_syntax.rs
+++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs b/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs
index db75dceae1c..04ac85ad43d 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs
+++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs b/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs
index 940aaacb02e..a8d5965d480 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs
+++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs b/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs
index ec277ba72e9..2c046df10f5 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs
+++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/mbe/src/parser.rs b/src/tools/rust-analyzer/crates/mbe/src/parser.rs
index fbc353d6103..711101260a0 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/parser.rs
+++ b/src/tools/rust-analyzer/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 {