rust/crates/hir-expand/src/quote.rs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

300 lines
9.5 KiB
Rust
Raw Normal View History

2019-11-11 00:14:39 -06:00
//! A simplified version of quote-crate like quasi quote macro
// A helper macro quote macro
// FIXME:
// 1. Not all puncts are handled
// 2. #()* pattern repetition not supported now
// * But we can do it manually, see `test_quote_derive_copy_hack`
#[doc(hidden)]
#[macro_export]
macro_rules! __quote {
() => {
2023-01-31 04:49:49 -06:00
Vec::<crate::tt::TokenTree>::new()
2019-11-11 00:14:39 -06:00
};
( @SUBTREE $delim:ident $($tt:tt)* ) => {
{
let children = $crate::__quote!($($tt)*);
2023-01-31 04:49:49 -06:00
crate::tt::Subtree {
delimiter: crate::tt::Delimiter {
kind: crate::tt::DelimiterKind::$delim,
open: <crate::tt::SpanData as crate::tt::Span>::DUMMY,
close: <crate::tt::SpanData as crate::tt::Span>::DUMMY,
2023-01-31 04:49:49 -06:00
},
2019-11-11 00:14:39 -06:00
token_trees: $crate::quote::IntoTt::to_tokens(children),
2020-02-18 07:32:19 -06:00
}
2019-11-11 00:14:39 -06:00
}
};
( @PUNCT $first:literal ) => {
{
vec![
2023-01-31 04:49:49 -06:00
crate::tt::Leaf::Punct(crate::tt::Punct {
2019-11-11 00:14:39 -06:00
char: $first,
2023-01-31 04:49:49 -06:00
spacing: crate::tt::Spacing::Alone,
span: <crate::tt::SpanData as crate::tt::Span>::DUMMY,
2019-11-11 00:14:39 -06:00
}).into()
]
}
};
( @PUNCT $first:literal, $sec:literal ) => {
{
vec![
2023-01-31 04:49:49 -06:00
crate::tt::Leaf::Punct(crate::tt::Punct {
2019-11-11 00:14:39 -06:00
char: $first,
2023-01-31 04:49:49 -06:00
spacing: crate::tt::Spacing::Joint,
span: <crate::tt::SpanData as crate::tt::Span>::DUMMY,
2019-11-11 00:14:39 -06:00
}).into(),
2023-01-31 04:49:49 -06:00
crate::tt::Leaf::Punct(crate::tt::Punct {
2019-11-11 00:14:39 -06:00
char: $sec,
2023-01-31 04:49:49 -06:00
spacing: crate::tt::Spacing::Alone,
span: <crate::tt::SpanData as crate::tt::Span>::DUMMY,
2019-11-11 00:14:39 -06:00
}).into()
]
}
};
// hash variable
( # $first:ident $($tail:tt)* ) => {
{
let token = $crate::quote::ToTokenTree::to_token($first);
let mut tokens = vec![token.into()];
let mut tail_tokens = $crate::quote::IntoTt::to_tokens($crate::__quote!($($tail)*));
tokens.append(&mut tail_tokens);
tokens
}
};
( ## $first:ident $($tail:tt)* ) => {
{
2023-01-31 04:49:49 -06:00
let mut tokens = $first.into_iter().map($crate::quote::ToTokenTree::to_token).collect::<Vec<crate::tt::TokenTree>>();
let mut tail_tokens = $crate::quote::IntoTt::to_tokens($crate::__quote!($($tail)*));
tokens.append(&mut tail_tokens);
tokens
}
};
2019-11-11 00:14:39 -06:00
// Brace
( { $($tt:tt)* } ) => { $crate::__quote!(@SUBTREE Brace $($tt)*) };
// Bracket
( [ $($tt:tt)* ] ) => { $crate::__quote!(@SUBTREE Bracket $($tt)*) };
// Parenthesis
( ( $($tt:tt)* ) ) => { $crate::__quote!(@SUBTREE Parenthesis $($tt)*) };
// Literal
( $tt:literal ) => { vec![$crate::quote::ToTokenTree::to_token($tt).into()] };
// Ident
( $tt:ident ) => {
vec![ {
2023-01-31 04:49:49 -06:00
crate::tt::Leaf::Ident(crate::tt::Ident {
2019-11-11 00:14:39 -06:00
text: stringify!($tt).into(),
span: <crate::tt::SpanData as crate::tt::Span>::DUMMY,
2019-11-11 00:14:39 -06:00
}).into()
}]
};
// Puncts
// FIXME: Not all puncts are handled
( -> ) => {$crate::__quote!(@PUNCT '-', '>')};
( & ) => {$crate::__quote!(@PUNCT '&')};
( , ) => {$crate::__quote!(@PUNCT ',')};
( : ) => {$crate::__quote!(@PUNCT ':')};
2020-03-11 10:08:12 -05:00
( ; ) => {$crate::__quote!(@PUNCT ';')};
( :: ) => {$crate::__quote!(@PUNCT ':', ':')};
2019-11-11 00:14:39 -06:00
( . ) => {$crate::__quote!(@PUNCT '.')};
( < ) => {$crate::__quote!(@PUNCT '<')};
( > ) => {$crate::__quote!(@PUNCT '>')};
( ! ) => {$crate::__quote!(@PUNCT '!')};
2019-11-11 00:14:39 -06:00
( $first:tt $($tail:tt)+ ) => {
{
let mut tokens = $crate::quote::IntoTt::to_tokens($crate::__quote!($first));
let mut tail_tokens = $crate::quote::IntoTt::to_tokens($crate::__quote!($($tail)*));
tokens.append(&mut tail_tokens);
tokens
}
};
}
/// FIXME:
/// It probably should implement in proc-macro
#[macro_export]
macro_rules! quote {
( $($tt:tt)* ) => {
$crate::quote::IntoTt::to_subtree($crate::__quote!($($tt)*))
}
}
pub(crate) trait IntoTt {
2023-01-31 04:49:49 -06:00
fn to_subtree(self) -> crate::tt::Subtree;
fn to_tokens(self) -> Vec<crate::tt::TokenTree>;
2019-11-11 00:14:39 -06:00
}
2023-01-31 04:49:49 -06:00
impl IntoTt for Vec<crate::tt::TokenTree> {
fn to_subtree(self) -> crate::tt::Subtree {
crate::tt::Subtree { delimiter: crate::tt::Delimiter::unspecified(), token_trees: self }
2019-11-11 00:14:39 -06:00
}
2023-01-31 04:49:49 -06:00
fn to_tokens(self) -> Vec<crate::tt::TokenTree> {
2019-11-11 00:14:39 -06:00
self
}
}
2023-01-31 04:49:49 -06:00
impl IntoTt for crate::tt::Subtree {
fn to_subtree(self) -> crate::tt::Subtree {
2019-11-11 00:14:39 -06:00
self
}
2023-01-31 04:49:49 -06:00
fn to_tokens(self) -> Vec<crate::tt::TokenTree> {
vec![crate::tt::TokenTree::Subtree(self)]
2019-11-11 00:14:39 -06:00
}
}
pub(crate) trait ToTokenTree {
2023-01-31 04:49:49 -06:00
fn to_token(self) -> crate::tt::TokenTree;
2019-11-11 00:14:39 -06:00
}
2023-01-31 04:49:49 -06:00
impl ToTokenTree for crate::tt::TokenTree {
fn to_token(self) -> crate::tt::TokenTree {
2019-11-11 00:14:39 -06:00
self
}
}
impl ToTokenTree for &crate::tt::TokenTree {
fn to_token(self) -> crate::tt::TokenTree {
self.clone()
}
}
2023-01-31 04:49:49 -06:00
impl ToTokenTree for crate::tt::Subtree {
fn to_token(self) -> crate::tt::TokenTree {
2019-11-11 00:14:39 -06:00
self.into()
}
}
macro_rules! impl_to_to_tokentrees {
($($ty:ty => $this:ident $im:block);*) => {
$(
impl ToTokenTree for $ty {
2023-01-31 04:49:49 -06:00
fn to_token($this) -> crate::tt::TokenTree {
let leaf: crate::tt::Leaf = $im.into();
2019-11-11 00:14:39 -06:00
leaf.into()
}
}
impl ToTokenTree for &$ty {
2023-01-31 04:49:49 -06:00
fn to_token($this) -> crate::tt::TokenTree {
let leaf: crate::tt::Leaf = $im.clone().into();
2019-11-11 00:14:39 -06:00
leaf.into()
}
}
)*
}
}
impl_to_to_tokentrees! {
u32 => self { crate::tt::Literal{text: self.to_string().into(), span: <crate::tt::SpanData as crate::tt::Span>::DUMMY} };
usize => self { crate::tt::Literal{text: self.to_string().into(), span: <crate::tt::SpanData as crate::tt::Span>::DUMMY} };
i32 => self { crate::tt::Literal{text: self.to_string().into(), span: <crate::tt::SpanData as crate::tt::Span>::DUMMY} };
bool => self { crate::tt::Ident{text: self.to_string().into(), span: <crate::tt::SpanData as crate::tt::Span>::DUMMY} };
2023-01-31 04:49:49 -06:00
crate::tt::Leaf => self { self };
crate::tt::Literal => self { self };
crate::tt::Ident => self { self };
crate::tt::Punct => self { self };
&str => self { crate::tt::Literal{text: format!("\"{}\"", self.escape_default()).into(), span: <crate::tt::SpanData as crate::tt::Span>::DUMMY}};
String => self { crate::tt::Literal{text: format!("\"{}\"", self.escape_default()).into(), span: <crate::tt::SpanData as crate::tt::Span>::DUMMY}}
2019-11-11 00:14:39 -06:00
}
#[cfg(test)]
mod tests {
use expect_test::expect;
2019-11-11 00:14:39 -06:00
#[test]
fn test_quote_delimiters() {
assert_eq!(quote!({}).to_string(), "{}");
assert_eq!(quote!(()).to_string(), "()");
assert_eq!(quote!([]).to_string(), "[]");
}
#[test]
fn test_quote_idents() {
assert_eq!(quote!(32).to_string(), "32");
assert_eq!(quote!(struct).to_string(), "struct");
}
#[test]
fn test_quote_hash_simple_literal() {
let a = 20;
assert_eq!(quote!(#a).to_string(), "20");
let s: String = "hello".into();
2019-11-15 08:13:41 -06:00
assert_eq!(quote!(#s).to_string(), "\"hello\"");
2019-11-11 00:14:39 -06:00
}
2023-01-31 04:49:49 -06:00
fn mk_ident(name: &str) -> crate::tt::Ident {
crate::tt::Ident {
text: name.into(),
span: <crate::tt::SpanData as crate::tt::Span>::DUMMY,
}
2019-11-11 00:14:39 -06:00
}
#[test]
fn test_quote_hash_token_tree() {
let a = mk_ident("hello");
let quoted = quote!(#a);
assert_eq!(quoted.to_string(), "hello");
let t = format!("{quoted:?}");
expect![[r#"
SUBTREE $$ SpanData { range: 0..0, anchor: SpanAnchor(FileId(0), 0), ctx: SyntaxContextId(0) } SpanData { range: 0..0, anchor: SpanAnchor(FileId(0), 0), ctx: SyntaxContextId(0) }
IDENT hello SpanData { range: 0..0, anchor: SpanAnchor(FileId(0), 0), ctx: SyntaxContextId(0) }"#]].assert_eq(&t);
2019-11-11 00:14:39 -06:00
}
#[test]
fn test_quote_simple_derive_copy() {
let name = mk_ident("Foo");
let quoted = quote! {
impl Clone for #name {
fn clone(&self) -> Self {
Self {}
}
}
};
assert_eq!(quoted.to_string(), "impl Clone for Foo {fn clone (& self) -> Self {Self {}}}");
}
#[test]
fn test_quote_derive_copy_hack() {
// Assume the given struct is:
// struct Foo {
// name: String,
// id: u32,
// }
let struct_name = mk_ident("Foo");
let fields = [mk_ident("name"), mk_ident("id")];
2022-03-12 07:26:16 -06:00
let fields = fields.iter().flat_map(|it| quote!(#it: self.#it.clone(), ).token_trees);
2019-11-11 00:14:39 -06:00
2023-01-31 04:49:49 -06:00
let list = crate::tt::Subtree {
delimiter: crate::tt::Delimiter {
kind: crate::tt::DelimiterKind::Brace,
open: <crate::tt::SpanData as crate::tt::Span>::DUMMY,
close: <crate::tt::SpanData as crate::tt::Span>::DUMMY,
2023-01-31 04:49:49 -06:00
},
2019-12-12 11:41:44 -06:00
token_trees: fields.collect(),
};
2019-11-11 00:14:39 -06:00
let quoted = quote! {
impl Clone for #struct_name {
fn clone(&self) -> Self {
Self #list
}
}
};
assert_eq!(quoted.to_string(), "impl Clone for Foo {fn clone (& self) -> Self {Self {name : self . name . clone () , id : self . id . clone () ,}}}");
}
}