1 //! A simplified version of quote-crate like quasi quote macro
3 use base_db
::span
::SpanData
;
5 // A helper macro quote macro
7 // 1. Not all puncts are handled
8 // 2. #()* pattern repetition not supported now
9 // * But we can do it manually, see `test_quote_derive_copy_hack`
12 macro_rules
! __quote
{
14 Vec
::<crate::tt
::TokenTree
>::new()
17 ( @
SUBTREE($span
:ident
) $delim
:ident $
($tt
:tt
)* ) => {
19 let children
= $
crate::__quote
!($span $
($tt
)*);
21 delimiter
: crate::tt
::Delimiter
{
22 kind
: crate::tt
::DelimiterKind
::$delim
,
26 token_trees
: $
crate::quote
::IntoTt
::to_tokens(children
),
31 ( @
PUNCT($span
:ident
) $first
:literal
) => {
34 crate::tt
::Leaf
::Punct(crate::tt
::Punct
{
36 spacing
: crate::tt
::Spacing
::Alone
,
43 ( @
PUNCT($span
:ident
) $first
:literal
, $sec
:literal
) => {
46 crate::tt
::Leaf
::Punct(crate::tt
::Punct
{
48 spacing
: crate::tt
::Spacing
::Joint
,
51 crate::tt
::Leaf
::Punct(crate::tt
::Punct
{
53 spacing
: crate::tt
::Spacing
::Alone
,
61 ($span
:ident
# $first:ident $($tail:tt)* ) => {
63 let token
= $
crate::quote
::ToTokenTree
::to_token($first
, $span
);
64 let mut tokens
= vec
![token
.into()];
65 let mut tail_tokens
= $
crate::quote
::IntoTt
::to_tokens($
crate::__quote
!($span $
($tail
)*));
66 tokens
.append(&mut tail_tokens
);
71 ($span
:ident
## $first:ident $($tail:tt)* ) => {
73 let mut tokens
= $first
.into_iter().map(|it
| $
crate::quote
::ToTokenTree
::to_token(it
, $span
)).collect
::<Vec
<crate::tt
::TokenTree
>>();
74 let mut tail_tokens
= $
crate::quote
::IntoTt
::to_tokens($
crate::__quote
!($span $
($tail
)*));
75 tokens
.append(&mut tail_tokens
);
81 ($span
:ident { $($tt:tt)* }
) => { $crate::__quote!(@SUBTREE($span) Brace $($tt)*) }
;
83 ($span
:ident
[ $
($tt
:tt
)* ] ) => { $crate::__quote!(@SUBTREE($span) Bracket $($tt)*) }
;
85 ($span
:ident ( $
($tt
:tt
)* ) ) => { $crate::__quote!(@SUBTREE($span) Parenthesis $($tt)*) }
;
88 ($span
:ident $tt
:literal
) => { vec![$crate::quote::ToTokenTree::to_token($tt, $span).into()] }
;
90 ($span
:ident $tt
:ident
) => {
92 crate::tt
::Leaf
::Ident(crate::tt
::Ident
{
93 text
: stringify
!($tt
).into(),
100 // FIXME: Not all puncts are handled
101 ($span
:ident
-> ) => {$crate::__quote!(@PUNCT($span) '-', '>')}
;
102 ($span
:ident
& ) => {$crate::__quote!(@PUNCT($span) '&')}
;
103 ($span
:ident
, ) => {$crate::__quote!(@PUNCT($span) ',')}
;
104 ($span
:ident
: ) => {$crate::__quote!(@PUNCT($span) ':')}
;
105 ($span
:ident
; ) => {$crate::__quote!(@PUNCT($span) ';')}
;
106 ($span
:ident
:: ) => {$crate::__quote!(@PUNCT($span) ':', ':')}
;
107 ($span
:ident
. ) => {$crate::__quote!(@PUNCT($span) '.')}
;
108 ($span
:ident
< ) => {$crate::__quote!(@PUNCT($span) '<')}
;
109 ($span
:ident
> ) => {$crate::__quote!(@PUNCT($span) '>')}
;
110 ($span
:ident
! ) => {$crate::__quote!(@PUNCT($span) '!')}
;
112 ($span
:ident $first
:tt $
($tail
:tt
)+ ) => {
114 let mut tokens
= $
crate::quote
::IntoTt
::to_tokens($
crate::__quote
!($span $first
));
115 let mut tail_tokens
= $
crate::quote
::IntoTt
::to_tokens($
crate::__quote
!($span $
($tail
)*));
117 tokens
.append(&mut tail_tokens
);
124 /// It probably should implement in proc-macro
127 ($span
:ident
=> $
($tt
:tt
)* ) => {
128 $
crate::quote
::IntoTt
::to_subtree($
crate::__quote
!($span $
($tt
)*), $span
)
132 pub(crate) trait IntoTt
{
133 fn to_subtree(self, span
: SpanData
) -> crate::tt
::Subtree
;
134 fn to_tokens(self) -> Vec
<crate::tt
::TokenTree
>;
137 impl IntoTt
for Vec
<crate::tt
::TokenTree
> {
138 fn to_subtree(self, span
: SpanData
) -> crate::tt
::Subtree
{
140 delimiter
: crate::tt
::Delimiter
::invisible_spanned(span
),
145 fn to_tokens(self) -> Vec
<crate::tt
::TokenTree
> {
150 impl IntoTt
for crate::tt
::Subtree
{
151 fn to_subtree(self, _
: SpanData
) -> crate::tt
::Subtree
{
155 fn to_tokens(self) -> Vec
<crate::tt
::TokenTree
> {
156 vec
![crate::tt
::TokenTree
::Subtree(self)]
160 pub(crate) trait ToTokenTree
{
161 fn to_token(self, span
: SpanData
) -> crate::tt
::TokenTree
;
164 impl ToTokenTree
for crate::tt
::TokenTree
{
165 fn to_token(self, _
: SpanData
) -> crate::tt
::TokenTree
{
170 impl ToTokenTree
for &crate::tt
::TokenTree
{
171 fn to_token(self, _
: SpanData
) -> crate::tt
::TokenTree
{
176 impl ToTokenTree
for crate::tt
::Subtree
{
177 fn to_token(self, _
: SpanData
) -> crate::tt
::TokenTree
{
182 macro_rules
! impl_to_to_tokentrees
{
183 ($
($span
:ident
: $ty
:ty
=> $this
:ident $im
:block
);*) => {
185 impl ToTokenTree
for $ty
{
186 fn to_token($this
, $span
: SpanData
) -> crate::tt
::TokenTree
{
187 let leaf
: crate::tt
::Leaf
= $im
.into();
192 impl ToTokenTree
for &$ty
{
193 fn to_token($this
, $span
: SpanData
) -> crate::tt
::TokenTree
{
194 let leaf
: crate::tt
::Leaf
= $im
.clone().into();
202 impl_to_to_tokentrees
! {
203 span
: u32 => self { crate::tt::Literal{text: self.to_string().into(), span}
};
204 span
: usize => self { crate::tt::Literal{text: self.to_string().into(), span}
};
205 span
: i32 => self { crate::tt::Literal{text: self.to_string().into(), span}
};
206 span
: bool
=> self { crate::tt::Ident{text: self.to_string().into(), span}
};
207 _span
: crate::tt
::Leaf
=> self { self }
;
208 _span
: crate::tt
::Literal
=> self { self }
;
209 _span
: crate::tt
::Ident
=> self { self }
;
210 _span
: crate::tt
::Punct
=> self { self }
;
211 span
: &str => self { crate::tt::Literal{text: format!("\"{}
\"", self.escape_default()).into(), span}};
212 span: String => self { crate::tt::Literal{text: format!("\"{}\"", self.escape_default()).into(), span
}}
219 span
::{SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID}
,
222 use expect_test
::expect
;
223 use syntax
::{TextRange, TextSize}
;
225 const DUMMY
: tt
::SpanData
= tt
::SpanData
{
226 range
: TextRange
::empty(TextSize
::new(0)),
227 anchor
: SpanAnchor { file_id: FileId::BOGUS, ast_id: ROOT_ERASED_FILE_AST_ID }
,
228 ctx
: SyntaxContextId
::ROOT
,
232 fn test_quote_delimiters() {
233 assert_eq
!(quote
!(DUMMY
=>{}
).to_string(), "{}");
234 assert_eq
!(quote
!(DUMMY
=>()).to_string(), "()");
235 assert_eq
!(quote
!(DUMMY
=>[]).to_string(), "[]");
239 fn test_quote_idents() {
240 assert_eq
!(quote
!(DUMMY
=>32).to_string(), "32");
241 assert_eq
!(quote
!(DUMMY
=>struct).to_string(), "struct");
245 fn test_quote_hash_simple_literal() {
247 assert_eq
!(quote
!(DUMMY
=>#a).to_string(), "20");
248 let s
: String
= "hello".into();
249 assert_eq
!(quote
!(DUMMY
=>#s).to_string(), "\"hello\"");
252 fn mk_ident(name
: &str) -> crate::tt
::Ident
{
253 crate::tt
::Ident { text: name.into(), span: DUMMY }
257 fn test_quote_hash_token_tree() {
258 let a
= mk_ident("hello");
260 let quoted
= quote
!(DUMMY
=>#a);
261 assert_eq
!(quoted
.to_string(), "hello");
262 let t
= format
!("{quoted:?}");
264 SUBTREE $$ SpanData { range: 0..0, anchor: SpanAnchor(FileId(937550), 0), ctx: SyntaxContextId(0) } SpanData { range: 0..0, anchor: SpanAnchor(FileId(937550), 0), ctx: SyntaxContextId(0) }
265 IDENT hello SpanData { range: 0..0, anchor: SpanAnchor(FileId(937550), 0), ctx: SyntaxContextId(0) }"#]].assert_eq(&t);
269 fn test_quote_simple_derive_copy() {
270 let name
= mk_ident("Foo");
272 let quoted
= quote
! {DUMMY
=>
273 impl Clone
for #name {
274 fn clone(&self) -> Self {
280 assert_eq
!(quoted
.to_string(), "impl Clone for Foo {fn clone (& self) -> Self {Self {}}}");
284 fn test_quote_derive_copy_hack() {
285 // Assume the given struct is:
290 let struct_name
= mk_ident("Foo");
291 let fields
= [mk_ident("name"), mk_ident("id")];
293 fields
.iter().flat_map(|it
| quote
!(DUMMY
=>#it: self.#it.clone(), ).token_trees);
295 let list
= crate::tt
::Subtree
{
296 delimiter
: crate::tt
::Delimiter
{
297 kind
: crate::tt
::DelimiterKind
::Brace
,
301 token_trees
: fields
.collect(),
304 let quoted
= quote
! {DUMMY
=>
305 impl Clone
for #struct_name {
306 fn clone(&self) -> Self {
312 assert_eq
!(quoted
.to_string(), "impl Clone for Foo {fn clone (& self) -> Self {Self {name : self . name . clone () , id : self . id . clone () ,}}}");