1#![allow(clippy::crate_in_macro_def)]
3
4use intern::{Symbol, sym};
5use span::Span;
6use syntax::ToSmolStr;
7use tt::IdentIsRaw;
8
9use crate::{name::Name, tt::TopSubtreeBuilder};
10
11pub(crate) fn dollar_crate(span: Span) -> tt::Ident<Span> {
12 tt::Ident { sym: sym::dollar_crate, span, is_raw: tt::IdentIsRaw::No }
13}
14
15#[doc(hidden)]
21#[macro_export]
22macro_rules! quote_impl__ {
23 ($span:ident $builder:ident) => {};
24
25 ( @SUBTREE($span:ident $builder:ident) $delim:ident $($tt:tt)* ) => {
26 {
27 $builder.open($crate::tt::DelimiterKind::$delim, $span);
28 $crate::builtin::quote::__quote!($span $builder $($tt)*);
29 $builder.close($span);
30 }
31 };
32
33 ( @PUNCT($span:ident $builder:ident) $first:literal ) => {
34 $builder.push(
35 $crate::tt::Leaf::Punct($crate::tt::Punct {
36 char: $first,
37 spacing: $crate::tt::Spacing::Alone,
38 span: $span,
39 })
40 );
41 };
42
43 ( @PUNCT($span:ident $builder:ident) $first:literal, $sec:literal ) => {
44 $builder.extend([
45 $crate::tt::Leaf::Punct($crate::tt::Punct {
46 char: $first,
47 spacing: $crate::tt::Spacing::Joint,
48 span: $span,
49 }),
50 $crate::tt::Leaf::Punct($crate::tt::Punct {
51 char: $sec,
52 spacing: $crate::tt::Spacing::Alone,
53 span: $span,
54 })
55 ]);
56 };
57
58 ($span:ident $builder:ident # $first:ident $($tail:tt)* ) => {
60 $crate::builtin::quote::ToTokenTree::to_tokens($first, $span, $builder);
61 $crate::builtin::quote::__quote!($span $builder $($tail)*);
62 };
63
64 ($span:ident $builder:ident # # $first:ident $($tail:tt)* ) => {{
65 ::std::iter::IntoIterator::into_iter($first).for_each(|it| $crate::builtin::quote::ToTokenTree::to_tokens(it, $span, $builder));
66 $crate::builtin::quote::__quote!($span $builder $($tail)*);
67 }};
68
69 ($span:ident $builder:ident { $($tt:tt)* } ) => { $crate::builtin::quote::__quote!(@SUBTREE($span $builder) Brace $($tt)*) };
71 ($span:ident $builder:ident [ $($tt:tt)* ] ) => { $crate::builtin::quote::__quote!(@SUBTREE($span $builder) Bracket $($tt)*) };
73 ($span:ident $builder:ident ( $($tt:tt)* ) ) => { $crate::builtin::quote::__quote!(@SUBTREE($span $builder) Parenthesis $($tt)*) };
75
76 ($span:ident $builder:ident $tt:literal ) => { $crate::builtin::quote::ToTokenTree::to_tokens($tt, $span, $builder) };
78 ($span:ident $builder:ident $tt:ident ) => {
80 $builder.push(
81 $crate::tt::Leaf::Ident($crate::tt::Ident {
82 sym: intern::Symbol::intern(stringify!($tt)),
83 span: $span,
84 is_raw: tt::IdentIsRaw::No,
85 })
86 );
87 };
88
89 ($span:ident $builder:ident -> ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '-', '>')};
92 ($span:ident $builder:ident => ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '=', '>')};
93 ($span:ident $builder:ident & ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '&')};
94 ($span:ident $builder:ident , ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) ',')};
95 ($span:ident $builder:ident : ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) ':')};
96 ($span:ident $builder:ident ; ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) ';')};
97 ($span:ident $builder:ident :: ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) ':', ':')};
98 ($span:ident $builder:ident . ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '.')};
99 ($span:ident $builder:ident < ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '<')};
100 ($span:ident $builder:ident > ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '>')};
101 ($span:ident $builder:ident ! ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '!')};
102 ($span:ident $builder:ident # ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '#')};
103 ($span:ident $builder:ident $ ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '$')};
104 ($span:ident $builder:ident * ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '*')};
105 ($span:ident $builder:ident = ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '=')};
106
107 ($span:ident $builder:ident $first:tt $($tail:tt)+ ) => {{
108 $crate::builtin::quote::__quote!($span $builder $first);
109 $crate::builtin::quote::__quote!($span $builder $($tail)*);
110 }};
111}
112pub use quote_impl__ as __quote;
113
114#[macro_export]
117macro_rules! quote {
118 ($span:ident=> $($tt:tt)* ) => {
119 {
120 let mut builder = $crate::tt::TopSubtreeBuilder::new($crate::tt::Delimiter {
121 kind: $crate::tt::DelimiterKind::Invisible,
122 open: $span,
123 close: $span,
124 });
125 #[allow(unused)]
126 let builder_ref = &mut builder;
127 $crate::builtin::quote::__quote!($span builder_ref $($tt)*);
128 builder.build_skip_top_subtree()
129 }
130 }
131}
132pub use quote;
133
134pub trait ToTokenTree {
135 fn to_tokens(self, span: Span, builder: &mut TopSubtreeBuilder);
136}
137
138pub struct WithDelimiter<'a> {
140 pub delimiter: crate::tt::Delimiter,
141 pub token_trees: crate::tt::TokenTreesView<'a>,
142}
143
144impl ToTokenTree for WithDelimiter<'_> {
145 fn to_tokens(self, span: Span, builder: &mut TopSubtreeBuilder) {
146 builder.open(self.delimiter.kind, self.delimiter.open);
147 self.token_trees.to_tokens(span, builder);
148 builder.close(self.delimiter.close);
149 }
150}
151
152impl ToTokenTree for crate::tt::TokenTreesView<'_> {
153 fn to_tokens(self, _: Span, builder: &mut TopSubtreeBuilder) {
154 builder.extend_with_tt(self);
155 }
156}
157
158impl ToTokenTree for crate::tt::SubtreeView<'_> {
159 fn to_tokens(self, _: Span, builder: &mut TopSubtreeBuilder) {
160 builder.extend_with_tt(self.as_token_trees());
161 }
162}
163
164impl ToTokenTree for crate::tt::TopSubtree {
165 fn to_tokens(self, _: Span, builder: &mut TopSubtreeBuilder) {
166 builder.extend_tt_dangerous(self.0);
167 }
168}
169
170impl ToTokenTree for crate::tt::TtElement<'_> {
171 fn to_tokens(self, _: Span, builder: &mut TopSubtreeBuilder) {
172 match self {
173 crate::tt::TtElement::Leaf(leaf) => builder.push(leaf.clone()),
174 crate::tt::TtElement::Subtree(subtree, subtree_iter) => {
175 builder.extend_tt_dangerous(
176 std::iter::once(crate::tt::TokenTree::Subtree(subtree.clone()))
177 .chain(subtree_iter.remaining().flat_tokens().iter().cloned()),
178 );
179 }
180 }
181 }
182}
183
184macro_rules! impl_to_to_tokentrees {
185 ($($span:ident: $ty:ty => $this:ident $im:block;)*) => {
186 $(
187 impl ToTokenTree for $ty {
188 fn to_tokens($this, $span: Span, builder: &mut TopSubtreeBuilder) {
189 let leaf: crate::tt::Leaf = $im.into();
190 builder.push(leaf);
191 }
192 }
193 )*
194 }
195}
196impl<T: ToTokenTree + Clone> ToTokenTree for &T {
197 fn to_tokens(self, span: Span, builder: &mut TopSubtreeBuilder) {
198 self.clone().to_tokens(span, builder);
199 }
200}
201
202impl_to_to_tokentrees! {
203 span: u32 => self { crate::tt::Literal{symbol: Symbol::integer(self as _), span, kind: tt::LitKind::Integer, suffix: None } };
204 span: usize => self { crate::tt::Literal{symbol: Symbol::integer(self as _), span, kind: tt::LitKind::Integer, suffix: None } };
205 span: i32 => self { crate::tt::Literal{symbol: Symbol::integer(self as _), span, kind: tt::LitKind::Integer, suffix: None } };
206 span: bool => self { crate::tt::Ident{sym: if self { sym::true_ } else { sym::false_ }, span, is_raw: tt::IdentIsRaw::No } };
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{symbol: Symbol::intern(&self.escape_default().to_smolstr()), span, kind: tt::LitKind::Str, suffix: None }};
212 span: String => self { crate::tt::Literal{symbol: Symbol::intern(&self.escape_default().to_smolstr()), span, kind: tt::LitKind::Str, suffix: None }};
213 span: Name => self {
214 let (is_raw, s) = IdentIsRaw::split_from_symbol(self.as_str());
215 crate::tt::Ident{sym: Symbol::intern(s), span, is_raw }
216 };
217 span: Symbol => self {
218 let (is_raw, s) = IdentIsRaw::split_from_symbol(self.as_str());
219 crate::tt::Ident{sym: Symbol::intern(s), span, is_raw }
220 };
221}
222
223#[cfg(test)]
224mod tests {
225 use crate::tt;
226 use ::tt::IdentIsRaw;
227 use expect_test::expect;
228 use intern::Symbol;
229 use span::{Edition, ROOT_ERASED_FILE_AST_ID, SpanAnchor, SyntaxContext};
230 use syntax::{TextRange, TextSize};
231
232 use super::quote;
233
234 const DUMMY: tt::Span = tt::Span {
235 range: TextRange::empty(TextSize::new(0)),
236 anchor: SpanAnchor {
237 file_id: span::EditionedFileId::new(
238 span::FileId::from_raw(0xe4e4e),
239 span::Edition::CURRENT,
240 ),
241 ast_id: ROOT_ERASED_FILE_AST_ID,
242 },
243 ctx: SyntaxContext::root(Edition::CURRENT),
244 };
245
246 #[test]
247 fn test_quote_delimiters() {
248 assert_eq!(quote!(DUMMY =>{}).to_string(), "{}");
249 assert_eq!(quote!(DUMMY =>()).to_string(), "()");
250 assert_eq!(quote!(DUMMY =>[]).to_string(), "[]");
251 }
252
253 #[test]
254 fn test_quote_idents() {
255 assert_eq!(quote!(DUMMY =>32).to_string(), "32");
256 assert_eq!(quote!(DUMMY =>struct).to_string(), "struct");
257 }
258
259 #[test]
260 fn test_quote_hash_simple_literal() {
261 let a = 20;
262 assert_eq!(quote!(DUMMY =>#a).to_string(), "20");
263 let s: String = "hello".into();
264 assert_eq!(quote!(DUMMY =>#s).to_string(), "\"hello\"");
265 }
266
267 fn mk_ident(name: &str) -> crate::tt::Ident {
268 let (is_raw, s) = IdentIsRaw::split_from_symbol(name);
269 crate::tt::Ident { sym: Symbol::intern(s), span: DUMMY, is_raw }
270 }
271
272 #[test]
273 fn test_quote_hash_token_tree() {
274 let a = mk_ident("hello");
275
276 let quoted = quote!(DUMMY =>#a);
277 assert_eq!(quoted.to_string(), "hello");
278 let t = format!("{quoted:#?}");
279 expect![[r#"
280 SUBTREE $$ 937550:Root[0000, 0]@0..0#ROOT2024 937550:Root[0000, 0]@0..0#ROOT2024
281 IDENT hello 937550:Root[0000, 0]@0..0#ROOT2024"#]]
282 .assert_eq(&t);
283 }
284
285 #[test]
286 fn test_quote_simple_derive_copy() {
287 let name = mk_ident("Foo");
288
289 let quoted = quote! {DUMMY =>
290 impl Clone for #name {
291 fn clone(&self) -> Self {
292 Self {}
293 }
294 }
295 };
296
297 assert_eq!(quoted.to_string(), "impl Clone for Foo {fn clone (& self) -> Self {Self {}}}");
298 }
299
300 #[test]
301 fn test_quote_derive_copy_hack() {
302 let struct_name = mk_ident("Foo");
308 let fields = [mk_ident("name"), mk_ident("id")];
309 let fields = fields.iter().map(|it| quote!(DUMMY =>#it: self.#it.clone(), ));
310
311 let mut builder = tt::TopSubtreeBuilder::new(crate::tt::Delimiter {
312 kind: crate::tt::DelimiterKind::Brace,
313 open: DUMMY,
314 close: DUMMY,
315 });
316 fields.for_each(|field| builder.extend_with_tt(field.view().as_token_trees()));
317 let list = builder.build();
318
319 let quoted = quote! {DUMMY =>
320 impl Clone for #struct_name {
321 fn clone(&self) -> Self {
322 Self #list
323 }
324 }
325 };
326
327 assert_eq!(
328 quoted.to_string(),
329 "impl Clone for Foo {fn clone (& self) -> Self {Self {name : self . name . clone () , id : self . id . clone () ,}}}"
330 );
331 }
332}