From 95ddeaed8ea6b72f50b1ecbd33d33b4847abf006 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Fri, 10 Dec 2021 15:17:31 +0100 Subject: [PATCH] Fix `concat!` with captured expression --- .../macro_expansion_tests/builtin_fn_macro.rs | 34 +++++++++++++++++++ crates/hir_expand/src/builtin_fn_macro.rs | 13 ++++++- 2 files changed, 46 insertions(+), 1 deletion(-) 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 @@ -313,6 +313,40 @@ macro_rules! concat {} ); } +#[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 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> { 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.