about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-08-08 11:28:42 +0000
committerbors <bors@rust-lang.org>2022-08-08 11:28:42 +0000
commitb569bbbaccfc0aa24cae76a6a62c40adc74842bd (patch)
tree20b3c42b0c6bbdc364585d58214c962e62c8a07e
parent634cfe3d72e785c843ca5d412b12be137b2e14fb (diff)
parent4d5873e92f50f3f08f98f04ade4768f9d9ab380f (diff)
downloadrust-b569bbbaccfc0aa24cae76a6a62c40adc74842bd.tar.gz
rust-b569bbbaccfc0aa24cae76a6a62c40adc74842bd.zip
Auto merge of #12942 - lowr:fix/concat-with-char, r=Veykril
fix: make `concat!` work with char

Fixes #12921

- I avoided making `unquote_str()` take char literals as well because it's depended on by another function `parse_string()` that's only supposed to take strings.
- Even with this patch, we don't output `\0` as `\u{0}` which #12921 pointed out ~~, but we're not actually responsible for serializing it but rowan is~~. They are functionally equivalent and I don't think it'd cause any confusion, but we *could* try escaping them before serialization (for reference, `rustc -Zunpretty=expanded`, which `cargo expand` uses under the hood, [makes use of `str::escape_default()`](https://github.com/rust-lang/rust/blob/3830ecaa8db798d2727cbdfa4ddf314ff938f268/compiler/rustc_ast/src/util/literal.rs#L161).
-rw-r--r--crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs4
-rw-r--r--crates/hir-expand/src/builtin_fn_macro.rs14
-rw-r--r--crates/hir-expand/src/quote.rs4
3 files changed, 16 insertions, 6 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 92dffa7f372..4f626105a53 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
@@ -295,13 +295,13 @@ fn test_concat_expand() {
 #[rustc_builtin_macro]
 macro_rules! concat {}
 
-fn main() { concat!("foo", "r", 0, r#"bar"#, "\n", false); }
+fn main() { concat!("foo", "r", 0, r#"bar"#, "\n", false, '"', '\0'); }
 "##,
         expect![[r##"
 #[rustc_builtin_macro]
 macro_rules! concat {}
 
-fn main() { "foor0bar\nfalse"; }
+fn main() { "foor0bar\nfalse\"\u{0}"; }
 "##]],
     );
 }
diff --git a/crates/hir-expand/src/builtin_fn_macro.rs b/crates/hir-expand/src/builtin_fn_macro.rs
index 76da7c9f1ee..c21b35cdc09 100644
--- a/crates/hir-expand/src/builtin_fn_macro.rs
+++ b/crates/hir-expand/src/builtin_fn_macro.rs
@@ -357,6 +357,12 @@ fn unquote_str(lit: &tt::Literal) -> Option<String> {
     token.value().map(|it| it.into_owned())
 }
 
+fn unquote_char(lit: &tt::Literal) -> Option<char> {
+    let lit = ast::make::tokens::literal(&lit.to_string());
+    let token = ast::Char::cast(lit)?;
+    token.value()
+}
+
 fn unquote_byte_string(lit: &tt::Literal) -> Option<Vec<u8>> {
     let lit = ast::make::tokens::literal(&lit.to_string());
     let token = ast::ByteString::cast(lit)?;
@@ -408,8 +414,12 @@ fn concat_expand(
                 // concat works with string and char literals, so remove any quotes.
                 // It also works with integer, float and boolean literals, so just use the rest
                 // as-is.
-                let component = unquote_str(it).unwrap_or_else(|| it.text.to_string());
-                text.push_str(&component);
+                if let Some(c) = unquote_char(it) {
+                    text.push(c);
+                } else {
+                    let component = unquote_str(it).unwrap_or_else(|| it.text.to_string());
+                    text.push_str(&component);
+                }
             }
             // handle boolean literals
             tt::TokenTree::Leaf(tt::Leaf::Ident(id))
diff --git a/crates/hir-expand/src/quote.rs b/crates/hir-expand/src/quote.rs
index 82f410ecda9..e839e97bf02 100644
--- a/crates/hir-expand/src/quote.rs
+++ b/crates/hir-expand/src/quote.rs
@@ -196,8 +196,8 @@ impl_to_to_tokentrees! {
     tt::Literal => self { self };
     tt::Ident => self { self };
     tt::Punct => self { self };
-    &str => self { tt::Literal{text: format!("\"{}\"", self.escape_debug()).into(), id: tt::TokenId::unspecified()}};
-    String => self { tt::Literal{text: format!("\"{}\"", self.escape_debug()).into(), id: tt::TokenId::unspecified()}}
+    &str => self { tt::Literal{text: format!("\"{}\"", self.escape_default()).into(), id: tt::TokenId::unspecified()}};
+    String => self { tt::Literal{text: format!("\"{}\"", self.escape_default()).into(), id: tt::TokenId::unspecified()}}
 }
 
 #[cfg(test)]