blob: 634a07dab3dbd23eac4ed250f7eaeff6cdef435b [file] [log] [blame]
Carl Lerche058ff472019-02-13 16:23:52 -08001//! This crate automatically generates the definition of the `Visit`,
2//! `VisitMut`, and `Fold` traits in `syn` based on the `syn` source. It
3//! discovers structs and enums declared with the `ast_*` macros and generates
4//! the functions for those types.
5//!
6//! It makes a few assumptions about the target crate:
7//! 1. All structs which are discovered must be re-exported in the root of the
8//! crate, even if they were declared in a submodule.
9//! 2. This code cannot discover submodules which are located in subdirectories
10//! - only submodules located in the same directory.
11//! 3. The path to `syn` is hardcoded.
12
13use crate::types;
David Tolnay14d463e2019-02-15 14:23:51 -080014use indexmap::IndexMap;
Carl Lerche058ff472019-02-13 16:23:52 -080015use proc_macro2::TokenStream;
16
17use std::fs::File;
18use std::io::Write;
19
20const FOLD_SRC: &str = "../src/gen/fold.rs";
21const VISIT_SRC: &str = "../src/gen/visit.rs";
22const VISIT_MUT_SRC: &str = "../src/gen/visit_mut.rs";
23
24mod codegen {
25 use crate::types;
26 use inflections::Inflect;
27 use proc_macro2::{Span, TokenStream};
28 use quote::TokenStreamExt;
29 use syn::*;
30
31 #[derive(Default)]
32 pub struct State {
33 pub visit_trait: TokenStream,
34 pub visit_impl: TokenStream,
35 pub visit_mut_trait: TokenStream,
36 pub visit_mut_impl: TokenStream,
37 pub fold_trait: TokenStream,
38 pub fold_impl: TokenStream,
39 }
40
41 fn under_name(name: &str) -> Ident {
42 Ident::new(&name.to_snake_case(), Span::call_site())
43 }
44
45 #[derive(Debug, Eq, PartialEq, Copy, Clone)]
46 enum Kind {
47 Visit,
48 VisitMut,
49 Fold,
50 }
51
52 enum Operand {
53 Borrowed(TokenStream),
54 Owned(TokenStream),
55 }
56
57 use self::Kind::*;
58 use self::Operand::*;
59
60 impl Operand {
61 fn tokens(&self) -> &TokenStream {
62 match *self {
63 Borrowed(ref n) | Owned(ref n) => n,
64 }
65 }
66
67 fn ref_tokens(&self) -> TokenStream {
68 match *self {
69 Borrowed(ref n) => n.clone(),
70 Owned(ref n) => quote!(&#n),
71 }
72 }
73
74 fn ref_mut_tokens(&self) -> TokenStream {
75 match *self {
76 Borrowed(ref n) => n.clone(),
77 Owned(ref n) => quote!(&mut #n),
78 }
79 }
80
81 fn owned_tokens(&self) -> TokenStream {
82 match *self {
83 Borrowed(ref n) => quote!(*#n),
84 Owned(ref n) => n.clone(),
85 }
86 }
87 }
88
89 fn simple_visit(item: &str, kind: Kind, name: &Operand) -> TokenStream {
90 let ident = under_name(item);
91
92 match kind {
93 Visit => {
94 let method = Ident::new(&format!("visit_{}", ident), Span::call_site());
95 let name = name.ref_tokens();
96 quote! {
97 _visitor.#method(#name)
98 }
99 }
100 VisitMut => {
101 let method = Ident::new(&format!("visit_{}_mut", ident), Span::call_site());
102 let name = name.ref_mut_tokens();
103 quote! {
104 _visitor.#method(#name)
105 }
106 }
107 Fold => {
108 let method = Ident::new(&format!("fold_{}", ident), Span::call_site());
109 let name = name.owned_tokens();
110 quote! {
111 _visitor.#method(#name)
112 }
113 }
114 }
115 }
116
117 fn box_visit(
118 elem: &types::Type,
119 features: &types::Features,
David Tolnay157c7eb2019-02-15 13:21:48 -0800120 defs: &types::Definitions,
Carl Lerche058ff472019-02-13 16:23:52 -0800121 kind: Kind,
122 name: &Operand,
123 ) -> Option<TokenStream> {
124 let name = name.owned_tokens();
David Tolnay157c7eb2019-02-15 13:21:48 -0800125 let res = visit(elem, features, defs, kind, &Owned(quote!(*#name)))?;
Carl Lerche058ff472019-02-13 16:23:52 -0800126 Some(match kind {
127 Fold => quote! {
128 Box::new(#res)
129 },
130 Visit | VisitMut => res,
131 })
132 }
133
134 fn vec_visit(
135 elem: &types::Type,
136 features: &types::Features,
David Tolnay157c7eb2019-02-15 13:21:48 -0800137 defs: &types::Definitions,
Carl Lerche058ff472019-02-13 16:23:52 -0800138 kind: Kind,
139 name: &Operand,
140 ) -> Option<TokenStream> {
141 let operand = match kind {
142 Visit | VisitMut => Borrowed(quote!(it)),
143 Fold => Owned(quote!(it)),
144 };
David Tolnay157c7eb2019-02-15 13:21:48 -0800145 let val = visit(elem, features, defs, kind, &operand)?;
Carl Lerche058ff472019-02-13 16:23:52 -0800146 Some(match kind {
147 Visit => {
148 let name = name.ref_tokens();
149 quote! {
150 for it in #name {
151 #val
152 }
153 }
154 }
155 VisitMut => {
156 let name = name.ref_mut_tokens();
157 quote! {
158 for it in #name {
159 #val
160 }
161 }
162 }
163 Fold => {
164 let name = name.owned_tokens();
165 quote! {
166 FoldHelper::lift(#name, |it| { #val })
167 }
168 }
169 })
170 }
171
172 fn punctuated_visit(
173 elem: &types::Type,
174 features: &types::Features,
David Tolnay157c7eb2019-02-15 13:21:48 -0800175 defs: &types::Definitions,
Carl Lerche058ff472019-02-13 16:23:52 -0800176 kind: Kind,
177 name: &Operand,
178 ) -> Option<TokenStream> {
179 let operand = match kind {
180 Visit | VisitMut => Borrowed(quote!(it)),
181 Fold => Owned(quote!(it)),
182 };
David Tolnay157c7eb2019-02-15 13:21:48 -0800183 let val = visit(elem, features, defs, kind, &operand)?;
Carl Lerche058ff472019-02-13 16:23:52 -0800184 Some(match kind {
185 Visit => {
186 let name = name.ref_tokens();
187 quote! {
188 for el in Punctuated::pairs(#name) {
189 let it = el.value();
190 #val
191 }
192 }
193 }
194 VisitMut => {
195 let name = name.ref_mut_tokens();
196 quote! {
197 for mut el in Punctuated::pairs_mut(#name) {
198 let it = el.value_mut();
199 #val
200 }
201 }
202 }
203 Fold => {
204 let name = name.owned_tokens();
205 quote! {
206 FoldHelper::lift(#name, |it| { #val })
207 }
208 }
209 })
210 }
211
212 fn option_visit(
213 elem: &types::Type,
214 features: &types::Features,
David Tolnay157c7eb2019-02-15 13:21:48 -0800215 defs: &types::Definitions,
Carl Lerche058ff472019-02-13 16:23:52 -0800216 kind: Kind,
217 name: &Operand,
218 ) -> Option<TokenStream> {
219 let it = match kind {
220 Visit | VisitMut => Borrowed(quote!(it)),
221 Fold => Owned(quote!(it)),
222 };
David Tolnay157c7eb2019-02-15 13:21:48 -0800223 let val = visit(elem, features, defs, kind, &it)?;
Carl Lerche058ff472019-02-13 16:23:52 -0800224 let name = name.owned_tokens();
225 Some(match kind {
226 Visit => quote! {
227 if let Some(ref it) = #name {
228 #val
229 }
230 },
231 VisitMut => quote! {
232 if let Some(ref mut it) = #name {
233 #val
234 }
235 },
236 Fold => quote! {
237 (#name).map(|it| { #val })
238 },
239 })
240 }
241
242 fn tuple_visit(
243 elems: &[types::Type],
244 features: &types::Features,
David Tolnay157c7eb2019-02-15 13:21:48 -0800245 defs: &types::Definitions,
Carl Lerche058ff472019-02-13 16:23:52 -0800246 kind: Kind,
247 name: &Operand,
248 ) -> Option<TokenStream> {
249 if elems.is_empty() {
250 return None;
251 }
252
253 let mut code = TokenStream::new();
254 for (i, elem) in elems.iter().enumerate() {
255 let name = name.tokens();
256 let i = Index::from(i);
257 let it = Owned(quote!((#name).#i));
258 let val =
David Tolnay157c7eb2019-02-15 13:21:48 -0800259 visit(elem, features, defs, kind, &it).unwrap_or_else(|| noop_visit(kind, &it));
Carl Lerche058ff472019-02-13 16:23:52 -0800260 code.append_all(val);
261 match kind {
262 Fold => code.append_all(quote!(,)),
263 Visit | VisitMut => code.append_all(quote!(;)),
264 }
265 }
266 Some(match kind {
267 Fold => quote! {
268 (#code)
269 },
270 Visit | VisitMut => code,
271 })
272 }
273
David Tolnay157c7eb2019-02-15 13:21:48 -0800274 fn token_punct_visit(repr: &str, kind: Kind, name: &Operand) -> TokenStream {
275 let ty: TokenStream = syn::parse_str(&format!("Token![{}]", repr)).unwrap();
Carl Lerche058ff472019-02-13 16:23:52 -0800276 let name = name.tokens();
277 match kind {
278 Fold => quote! {
279 #ty(tokens_helper(_visitor, &#name.spans))
280 },
281 Visit => quote! {
282 tokens_helper(_visitor, &#name.spans)
283 },
284 VisitMut => quote! {
285 tokens_helper(_visitor, &mut #name.spans)
286 },
287 }
288 }
289
David Tolnay157c7eb2019-02-15 13:21:48 -0800290 fn token_keyword_visit(repr: &str, kind: Kind, name: &Operand) -> TokenStream {
291 let ty: TokenStream = syn::parse_str(&format!("Token![{}]", repr)).unwrap();
Carl Lerche058ff472019-02-13 16:23:52 -0800292 let name = name.tokens();
293 match kind {
294 Fold => quote! {
295 #ty(tokens_helper(_visitor, &#name.span))
296 },
297 Visit => quote! {
298 tokens_helper(_visitor, &#name.span)
299 },
300 VisitMut => quote! {
301 tokens_helper(_visitor, &mut #name.span)
302 },
303 }
304 }
305
306 fn token_group_visit(ty: &str, kind: Kind, name: &Operand) -> TokenStream {
307 let ty = Ident::new(ty, Span::call_site());
308 let name = name.tokens();
309 match kind {
310 Fold => quote! {
311 #ty(tokens_helper(_visitor, &#name.span))
312 },
313 Visit => quote! {
314 tokens_helper(_visitor, &#name.span)
315 },
316 VisitMut => quote! {
317 tokens_helper(_visitor, &mut #name.span)
318 },
319 }
320 }
321
322 fn noop_visit(kind: Kind, name: &Operand) -> TokenStream {
323 match kind {
324 Fold => name.owned_tokens(),
325 Visit | VisitMut => {
326 let name = name.tokens();
327 quote! {
328 skip!(#name)
329 }
330 }
331 }
332 }
333
334 fn visit(
335 ty: &types::Type,
336 features: &types::Features,
David Tolnay157c7eb2019-02-15 13:21:48 -0800337 defs: &types::Definitions,
Carl Lerche058ff472019-02-13 16:23:52 -0800338 kind: Kind,
339 name: &Operand,
340 ) -> Option<TokenStream> {
341 match ty {
David Tolnay157c7eb2019-02-15 13:21:48 -0800342 types::Type::Box(t) => box_visit(&*t, features, defs, kind, name),
343 types::Type::Vec(t) => vec_visit(&*t, features, defs, kind, name),
David Tolnay47fe7402019-02-15 14:35:25 -0800344 types::Type::Punctuated(p) => punctuated_visit(p.element(), features, defs, kind, name),
David Tolnay157c7eb2019-02-15 13:21:48 -0800345 types::Type::Option(t) => option_visit(&*t, features, defs, kind, name),
346 types::Type::Tuple(t) => tuple_visit(t, features, defs, kind, name),
Carl Lerche058ff472019-02-13 16:23:52 -0800347 types::Type::Token(t) => {
David Tolnay157c7eb2019-02-15 13:21:48 -0800348 let repr = &defs.tokens[t];
349 let is_keyword = repr.chars().next().unwrap().is_alphabetic();
350 if is_keyword {
351 Some(token_keyword_visit(repr, kind, name))
Carl Lerche058ff472019-02-13 16:23:52 -0800352 } else {
David Tolnay157c7eb2019-02-15 13:21:48 -0800353 Some(token_punct_visit(repr, kind, name))
Carl Lerche058ff472019-02-13 16:23:52 -0800354 }
355 }
David Tolnay295141b2019-02-15 12:45:33 -0800356 types::Type::Group(t) => Some(token_group_visit(&t[..], kind, name)),
David Tolnayd3076572019-02-15 13:32:44 -0800357 types::Type::Syn(t) => {
Carl Lerche058ff472019-02-13 16:23:52 -0800358 fn requires_full(features: &types::Features) -> bool {
359 features.contains("full") && features.len() == 1
360 }
361
362 let mut res = simple_visit(t, kind, name);
363
David Tolnay157c7eb2019-02-15 13:21:48 -0800364 let target = defs.types.iter().find(|ty| ty.ident() == t).unwrap();
Carl Lerche058ff472019-02-13 16:23:52 -0800365
366 Some(
367 if requires_full(target.features()) && !requires_full(features) {
368 quote! {
369 full!(#res)
370 }
371 } else {
372 res
373 },
374 )
375 }
Carl Lerchecbf7cc12019-02-15 14:09:31 -0800376 types::Type::Ext(t) if super::TERMINAL_TYPES.contains(&&t[..]) => {
377 Some(simple_visit(t, kind, name))
378 }
Carl Lerche058ff472019-02-13 16:23:52 -0800379 types::Type::Ext(_) | types::Type::Std(_) => None,
380 }
381 }
382
383 fn visit_features(features: &types::Features) -> TokenStream {
384 match features.len() {
385 0 => quote!(),
386 1 => {
387 let feature = &features[0];
388 quote!(#[cfg(feature = #feature)])
389 }
390 _ => {
391 let features = features.iter().map(|feature| quote!(feature = #feature));
392
393 quote!(#[cfg(any( #(#features),* ))])
394 }
395 }
396 }
397
David Tolnay157c7eb2019-02-15 13:21:48 -0800398 pub fn generate(state: &mut State, s: &types::Node, defs: &types::Definitions) {
Carl Lerche058ff472019-02-13 16:23:52 -0800399 let features = visit_features(s.features());
400 let under_name = under_name(s.ident());
401 let ty = Ident::new(s.ident(), Span::call_site());
402 let visit_fn = Ident::new(&format!("visit_{}", under_name), Span::call_site());
403 let visit_mut_fn = Ident::new(&format!("visit_{}_mut", under_name), Span::call_site());
404 let fold_fn = Ident::new(&format!("fold_{}", under_name), Span::call_site());
405
406 let mut visit_impl = TokenStream::new();
407 let mut visit_mut_impl = TokenStream::new();
408 let mut fold_impl = TokenStream::new();
409
410 match s {
David Tolnayf9bb8ff2019-02-15 13:10:14 -0800411 types::Node::Enum(ref e) => {
Carl Lerche058ff472019-02-13 16:23:52 -0800412 let mut visit_variants = TokenStream::new();
413 let mut visit_mut_variants = TokenStream::new();
414 let mut fold_variants = TokenStream::new();
415
416 for variant in e.variants() {
417 let variant_ident = Ident::new(variant.ident(), Span::call_site());
418
419 if variant.fields().is_empty() {
420 visit_variants.append_all(quote! {
421 #ty::#variant_ident => {}
422 });
423 visit_mut_variants.append_all(quote! {
424 #ty::#variant_ident => {}
425 });
426 fold_variants.append_all(quote! {
427 #ty::#variant_ident => {
428 #ty::#variant_ident
429 }
430 });
431 } else {
432 let mut bind_visit_fields = TokenStream::new();
433 let mut bind_visit_mut_fields = TokenStream::new();
434 let mut bind_fold_fields = TokenStream::new();
435
436 let mut visit_fields = TokenStream::new();
437 let mut visit_mut_fields = TokenStream::new();
438 let mut fold_fields = TokenStream::new();
439
440 for (idx, ty) in variant.fields().iter().enumerate() {
441 let name = format!("_binding_{}", idx);
442 let binding = Ident::new(&name, Span::call_site());
443
444 bind_visit_fields.append_all(quote! {
445 ref #binding,
446 });
447 bind_visit_mut_fields.append_all(quote! {
448 ref mut #binding,
449 });
450 bind_fold_fields.append_all(quote! {
451 #binding,
452 });
453
454 let borrowed_binding = Borrowed(quote!(#binding));
455 let owned_binding = Owned(quote!(#binding));
456
457 visit_fields.append_all(
David Tolnay157c7eb2019-02-15 13:21:48 -0800458 visit(ty, s.features(), defs, Visit, &borrowed_binding)
Carl Lerche058ff472019-02-13 16:23:52 -0800459 .unwrap_or_else(|| noop_visit(Visit, &borrowed_binding)),
460 );
461 visit_mut_fields.append_all(
David Tolnay157c7eb2019-02-15 13:21:48 -0800462 visit(ty, s.features(), defs, VisitMut, &borrowed_binding)
Carl Lerche058ff472019-02-13 16:23:52 -0800463 .unwrap_or_else(|| noop_visit(VisitMut, &borrowed_binding)),
464 );
465 fold_fields.append_all(
David Tolnay157c7eb2019-02-15 13:21:48 -0800466 visit(ty, s.features(), defs, Fold, &owned_binding)
Carl Lerche058ff472019-02-13 16:23:52 -0800467 .unwrap_or_else(|| noop_visit(Fold, &owned_binding)),
468 );
469
470 visit_fields.append_all(quote!(;));
471 visit_mut_fields.append_all(quote!(;));
472 fold_fields.append_all(quote!(,));
473 }
474
475 visit_variants.append_all(quote! {
476 #ty::#variant_ident(#bind_visit_fields) => {
477 #visit_fields
478 }
479 });
480
481 visit_mut_variants.append_all(quote! {
482 #ty::#variant_ident(#bind_visit_mut_fields) => {
483 #visit_mut_fields
484 }
485 });
486
487 fold_variants.append_all(quote! {
488 #ty::#variant_ident(#bind_fold_fields) => {
489 #ty::#variant_ident(
490 #fold_fields
491 )
492 }
493 });
494 }
495 }
496
497 visit_impl.append_all(quote! {
498 match *_i {
499 #visit_variants
500 }
501 });
502
503 visit_mut_impl.append_all(quote! {
504 match *_i {
505 #visit_mut_variants
506 }
507 });
508
509 fold_impl.append_all(quote! {
510 match _i {
511 #fold_variants
512 }
513 });
514 }
David Tolnayf9bb8ff2019-02-15 13:10:14 -0800515 types::Node::Struct(ref v) => {
Carl Lerche058ff472019-02-13 16:23:52 -0800516 let mut fold_fields = TokenStream::new();
517
David Tolnay14d463e2019-02-15 14:23:51 -0800518 for (field, ty) in v.fields() {
519 let id = Ident::new(field, Span::call_site());
Carl Lerche058ff472019-02-13 16:23:52 -0800520 let ref_toks = Owned(quote!(_i.#id));
David Tolnay14d463e2019-02-15 14:23:51 -0800521 let visit_field = visit(ty, v.features(), defs, Visit, &ref_toks)
Carl Lerche058ff472019-02-13 16:23:52 -0800522 .unwrap_or_else(|| noop_visit(Visit, &ref_toks));
523 visit_impl.append_all(quote! {
524 #visit_field;
525 });
David Tolnay47fe7402019-02-15 14:35:25 -0800526 let visit_mut_field = visit(ty, v.features(), defs, VisitMut, &ref_toks)
527 .unwrap_or_else(|| noop_visit(VisitMut, &ref_toks));
Carl Lerche058ff472019-02-13 16:23:52 -0800528 visit_mut_impl.append_all(quote! {
529 #visit_mut_field;
530 });
David Tolnay14d463e2019-02-15 14:23:51 -0800531 let fold = visit(ty, v.features(), defs, Fold, &ref_toks)
Carl Lerche058ff472019-02-13 16:23:52 -0800532 .unwrap_or_else(|| noop_visit(Fold, &ref_toks));
533
534 fold_fields.append_all(quote! {
535 #id: #fold,
536 });
537 }
538
539 if !v.fields().is_empty() {
540 fold_impl.append_all(quote! {
541 #ty {
542 #fold_fields
543 }
544 })
545 } else {
546 if ty == "Ident" {
547 fold_impl.append_all(quote! {
548 let mut _i = _i;
549 let span = _visitor.fold_span(_i.span());
550 _i.set_span(span);
551 });
552 }
553 fold_impl.append_all(quote! {
554 _i
555 });
556 }
557 }
558 }
559
560 let mut include_fold_impl = true;
David Tolnayf9bb8ff2019-02-15 13:10:14 -0800561 if let types::Node::Struct(ref data) = s {
David Tolnay8964fff2019-02-15 14:34:51 -0800562 if data.fields().is_empty() && !super::TERMINAL_TYPES.contains(&&s.ident()) {
Carl Lerche058ff472019-02-13 16:23:52 -0800563 include_fold_impl = false;
564 }
565 }
566
567 state.visit_trait.append_all(quote! {
568 #features
569 fn #visit_fn(&mut self, i: &'ast #ty) {
570 #visit_fn(self, i)
571 }
572 });
573
574 state.visit_impl.append_all(quote! {
575 #features
576 pub fn #visit_fn<'ast, V: Visit<'ast> + ?Sized>(
577 _visitor: &mut V, _i: &'ast #ty
578 ) {
579 #visit_impl
580 }
581 });
582
583 state.visit_mut_trait.append_all(quote! {
584 #features
585 fn #visit_mut_fn(&mut self, i: &mut #ty) {
586 #visit_mut_fn(self, i)
587 }
588 });
589
590 state.visit_mut_impl.append_all(quote! {
591 #features
592 pub fn #visit_mut_fn<V: VisitMut + ?Sized>(
593 _visitor: &mut V, _i: &mut #ty
594 ) {
595 #visit_mut_impl
596 }
597 });
598
599 state.fold_trait.append_all(quote! {
600 #features
601 fn #fold_fn(&mut self, i: #ty) -> #ty {
602 #fold_fn(self, i)
603 }
604 });
605
606 if include_fold_impl {
607 state.fold_impl.append_all(quote! {
608 #features
609 pub fn #fold_fn<V: Fold + ?Sized>(
610 _visitor: &mut V, _i: #ty
611 ) -> #ty {
612 #fold_impl
613 }
614 });
615 }
616 }
617}
618
619fn write_file(path: &str, content: TokenStream) {
620 let mut file = File::create(path).unwrap();
621 write!(
622 file,
623 "// THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT\n\n"
624 )
625 .unwrap();
626 let mut config = rustfmt::Config::default();
627 config.set().emit_mode(rustfmt::EmitMode::Stdout);
628 config.set().verbose(rustfmt::Verbosity::Quiet);
629 config.set().format_macro_matchers(true);
630 config.set().normalize_doc_attributes(true);
631 let mut session = rustfmt::Session::new(config, Some(&mut file));
632 session
633 .format(rustfmt::Input::Text(content.to_string()))
634 .unwrap();
635}
636
Carl Lerchecbf7cc12019-02-15 14:09:31 -0800637const TERMINAL_TYPES: &[&str] = &["Span", "Ident"];
638
David Tolnayf9bb8ff2019-02-15 13:10:14 -0800639pub fn generate(defs: &types::Definitions) {
Carl Lerchecbf7cc12019-02-15 14:09:31 -0800640 let mut defs = defs.clone();
641
642 for &tt in TERMINAL_TYPES {
643 defs.insert(types::Node::Struct(types::Struct::new(
David Tolnay47fe7402019-02-15 14:35:25 -0800644 tt.to_string(),
645 types::Features::default(),
646 IndexMap::new(),
647 )));
Carl Lerchecbf7cc12019-02-15 14:09:31 -0800648 }
649
Carl Lerche058ff472019-02-13 16:23:52 -0800650 let mut state = codegen::State::default();
David Tolnayf9bb8ff2019-02-15 13:10:14 -0800651 for s in &defs.types {
Carl Lerchecbf7cc12019-02-15 14:09:31 -0800652 codegen::generate(&mut state, s, &defs);
Carl Lerche058ff472019-02-13 16:23:52 -0800653 }
654
655 let full_macro = quote! {
656 #[cfg(feature = "full")]
657 macro_rules! full {
658 ($e:expr) => {
659 $e
660 };
661 }
662
663 #[cfg(all(feature = "derive", not(feature = "full")))]
664 macro_rules! full {
665 ($e:expr) => {
666 unreachable!()
667 };
668 }
669 };
670
671 let skip_macro = quote! {
672 #[cfg(any(feature = "full", feature = "derive"))]
673 macro_rules! skip {
674 ($($tt:tt)*) => {};
675 }
676 };
677
678 let fold_trait = state.fold_trait;
679 let fold_impl = state.fold_impl;
680 write_file(
681 FOLD_SRC,
682 quote! {
683 // Unreachable code is generated sometimes without the full feature.
684 #![allow(unreachable_code)]
685
686 use *;
687 #[cfg(any(feature = "full", feature = "derive"))]
688 use token::{Brace, Bracket, Paren, Group};
689 use proc_macro2::Span;
690 #[cfg(any(feature = "full", feature = "derive"))]
691 use gen::helper::fold::*;
692
693 #full_macro
694
695 /// Syntax tree traversal to transform the nodes of an owned syntax tree.
696 ///
697 /// See the [module documentation] for details.
698 ///
699 /// [module documentation]: index.html
700 ///
701 /// *This trait is available if Syn is built with the `"fold"` feature.*
702 pub trait Fold {
703 #fold_trait
704 }
705
706 #[cfg(any(feature = "full", feature = "derive"))]
707 macro_rules! fold_span_only {
708 ($f:ident : $t:ident) => {
709 pub fn $f<V: Fold + ?Sized>(_visitor: &mut V, mut _i: $t) -> $t {
710 let span = _visitor.fold_span(_i.span());
711 _i.set_span(span);
712 _i
713 }
714 }
715 }
716
717 #[cfg(any(feature = "full", feature = "derive"))]
718 fold_span_only!(fold_lit_byte: LitByte);
719 #[cfg(any(feature = "full", feature = "derive"))]
720 fold_span_only!(fold_lit_byte_str: LitByteStr);
721 #[cfg(any(feature = "full", feature = "derive"))]
722 fold_span_only!(fold_lit_char: LitChar);
723 #[cfg(any(feature = "full", feature = "derive"))]
724 fold_span_only!(fold_lit_float: LitFloat);
725 #[cfg(any(feature = "full", feature = "derive"))]
726 fold_span_only!(fold_lit_int: LitInt);
727 #[cfg(any(feature = "full", feature = "derive"))]
728 fold_span_only!(fold_lit_str: LitStr);
729
730 #fold_impl
731 },
732 );
733
734 let visit_trait = state.visit_trait;
735 let visit_impl = state.visit_impl;
736 write_file(
737 VISIT_SRC,
738 quote! {
739 #![cfg_attr(feature = "cargo-clippy", allow(trivially_copy_pass_by_ref))]
740
741 use *;
742 #[cfg(any(feature = "full", feature = "derive"))]
743 use punctuated::Punctuated;
744 use proc_macro2::Span;
745 #[cfg(any(feature = "full", feature = "derive"))]
746 use gen::helper::visit::*;
747
748 #full_macro
749 #skip_macro
750
751 /// Syntax tree traversal to walk a shared borrow of a syntax tree.
752 ///
753 /// See the [module documentation] for details.
754 ///
755 /// [module documentation]: index.html
756 ///
757 /// *This trait is available if Syn is built with the `"visit"` feature.*
758 pub trait Visit<'ast> {
759 #visit_trait
760 }
761
762 #visit_impl
763 },
764 );
765
766 let visit_mut_trait = state.visit_mut_trait;
767 let visit_mut_impl = state.visit_mut_impl;
768 write_file(
769 VISIT_MUT_SRC,
770 quote! {
771 use *;
772 #[cfg(any(feature = "full", feature = "derive"))]
773 use punctuated::Punctuated;
774 use proc_macro2::Span;
775 #[cfg(any(feature = "full", feature = "derive"))]
776 use gen::helper::visit_mut::*;
777
778 #full_macro
779 #skip_macro
780
781 /// Syntax tree traversal to mutate an exclusive borrow of a syntax tree in
782 /// place.
783 ///
784 /// See the [module documentation] for details.
785 ///
786 /// [module documentation]: index.html
787 ///
788 /// *This trait is available if Syn is built with the `"visit-mut"` feature.*
789 pub trait VisitMut {
790 #visit_mut_trait
791 }
792
793 #visit_mut_impl
794 },
795 );
796}