about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crates/hir_def/src/macro_expansion_tests/builtin_fn_macro.rs34
-rw-r--r--crates/hir_expand/src/builtin_fn_macro.rs13
2 files changed, 46 insertions, 1 deletions
diff --git a/crates/hir_def/src/macro_expansion_tests/builtin_fn_macro.rs b/crates/hir_def/src/macro_expansion_tests/builtin_fn_macro.rs
index 06039e95c5f..6e91301ecdc 100644
--- a/crates/hir_def/src/macro_expansion_tests/builtin_fn_macro.rs
+++ b/crates/hir_def/src/macro_expansion_tests/builtin_fn_macro.rs
@@ -314,6 +314,40 @@ fn main() { "foor0bar\nfalse"; }
 }
 
 #[test]
+fn test_concat_with_captured_expr() {
+    check(
+        r##"
+#[rustc_builtin_macro]
+macro_rules! concat {}
+
+macro_rules! surprise {
+    () => { "s" };
+}
+
+macro_rules! stuff {
+    ($string:expr) => { concat!($string) };
+}
+
+fn main() { concat!(surprise!()); }
+"##,
+        expect![[r##"
+#[rustc_builtin_macro]
+macro_rules! concat {}
+
+macro_rules! surprise {
+    () => { "s" };
+}
+
+macro_rules! stuff {
+    ($string:expr) => { concat!($string) };
+}
+
+fn main() { "s"; }
+"##]],
+    );
+}
+
+#[test]
 fn test_concat_idents_expand() {
     check(
         r##"
diff --git a/crates/hir_expand/src/builtin_fn_macro.rs b/crates/hir_expand/src/builtin_fn_macro.rs
index 3add4c11030..57e66e5cc4c 100644
--- a/crates/hir_expand/src/builtin_fn_macro.rs
+++ b/crates/hir_expand/src/builtin_fn_macro.rs
@@ -386,7 +386,18 @@ fn concat_expand(
 ) -> ExpandResult<Option<ExpandedEager>> {
     let mut err = None;
     let mut text = String::new();
-    for (i, t) in tt.token_trees.iter().enumerate() {
+    for (i, mut t) in tt.token_trees.iter().enumerate() {
+        // FIXME: hack on top of a hack: `$e:expr` captures get surrounded in parentheses
+        // to ensure the right parsing order, so skip the parentheses here. Ideally we'd
+        // implement rustc's model. cc https://github.com/rust-analyzer/rust-analyzer/pull/10623
+        if let tt::TokenTree::Subtree(tt::Subtree { delimiter: Some(delim), token_trees }) = t {
+            if let [tt] = &**token_trees {
+                if delim.kind == tt::DelimiterKind::Parenthesis {
+                    t = tt;
+                }
+            }
+        }
+
         match t {
             tt::TokenTree::Leaf(tt::Leaf::Literal(it)) if i % 2 == 0 => {
                 // concat works with string and char literals, so remove any quotes.