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 06039e95c5..6e91301ecd 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 @@ -313,6 +313,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( diff --git a/crates/hir_expand/src/builtin_fn_macro.rs b/crates/hir_expand/src/builtin_fn_macro.rs index 3add4c1103..57e66e5cc4 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> { 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.