Change codegen to build code as tokens
diff --git a/codegen/src/main.rs b/codegen/src/main.rs
index ed6bfc8..431b33f 100644
--- a/codegen/src/main.rs
+++ b/codegen/src/main.rs
@@ -11,6 +11,7 @@
 //! 3. The path to `syn` is hardcoded.
 
 #![cfg_attr(feature = "cargo-clippy", allow(redundant_closure))]
+#![recursion_limit = "128"]
 
 #[macro_use]
 extern crate failure;
@@ -30,7 +31,7 @@
 use std::collections::BTreeMap;
 use std::fmt::{self, Debug};
 use std::fs::File;
-use std::io::Read;
+use std::io::{Read, Write};
 use std::path::Path;
 
 const SYN_CRATE_ROOT: &str = "../src/lib.rs";
@@ -340,19 +341,18 @@
 mod codegen {
     use super::{AstItem, Lookup};
     use proc_macro2::{Span, TokenStream};
-    use quote::ToTokens;
-    use std::fmt::{self, Display};
+    use quote::{ToTokens, TokenStreamExt};
     use syn::punctuated::Punctuated;
     use syn::*;
 
     #[derive(Default)]
     pub struct State {
-        pub visit_trait: String,
-        pub visit_impl: String,
-        pub visit_mut_trait: String,
-        pub visit_mut_impl: String,
-        pub fold_trait: String,
-        pub fold_impl: String,
+        pub visit_trait: TokenStream,
+        pub visit_impl: TokenStream,
+        pub visit_mut_trait: TokenStream,
+        pub visit_mut_impl: TokenStream,
+        pub fold_trait: TokenStream,
+        pub fold_impl: TokenStream,
     }
 
     fn under_name(name: Ident) -> Ident {
@@ -449,12 +449,6 @@
         }
     }
 
-    impl Display for Operand {
-        fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
-            Display::fmt(self.tokens(), formatter)
-        }
-    }
-
     fn first_arg(params: &PathArguments) -> &Type {
         let data = match *params {
             PathArguments::AngleBracketed(ref data) => data,
@@ -472,57 +466,74 @@
         }
     }
 
-    fn simple_visit(item: &AstItem, kind: Kind, name: &Operand) -> String {
+    fn simple_visit(item: &AstItem, kind: Kind, name: &Operand) -> TokenStream {
+        let ident = under_name(item.ast.ident.clone());
+
         match kind {
-            Visit => format!(
-                "_visitor.visit_{under_name}({name})",
-                under_name = under_name(item.ast.ident.clone()),
-                name = name.ref_tokens(),
-            ),
-            VisitMut => format!(
-                "_visitor.visit_{under_name}_mut({name})",
-                under_name = under_name(item.ast.ident.clone()),
-                name = name.ref_mut_tokens(),
-            ),
-            Fold => format!(
-                "_visitor.fold_{under_name}({name})",
-                under_name = under_name(item.ast.ident.clone()),
-                name = name.owned_tokens(),
-            ),
+            Visit => {
+                let method = Ident::new(&format!("visit_{}", ident), Span::call_site());
+                let name = name.ref_tokens();
+                quote! {
+                    _visitor.#method(#name)
+                }
+            }
+            VisitMut => {
+                let method = Ident::new(&format!("visit_{}_mut", ident), Span::call_site());
+                let name = name.ref_mut_tokens();
+                quote! {
+                    _visitor.#method(#name)
+                }
+            }
+            Fold => {
+                let method = Ident::new(&format!("fold_{}", ident), Span::call_site());
+                let name = name.owned_tokens();
+                quote! {
+                    _visitor.#method(#name)
+                }
+            }
         }
     }
 
-    fn box_visit(elem: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<String> {
+    fn box_visit(elem: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<TokenStream> {
         let name = name.owned_tokens();
         let res = visit(elem, lookup, kind, &Owned(quote!(*#name)))?;
         Some(match kind {
-            Fold => format!("Box::new({})", res),
+            Fold => quote! {
+                Box::new(#res)
+            },
             Visit | VisitMut => res,
         })
     }
 
-    fn vec_visit(elem: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<String> {
+    fn vec_visit(elem: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<TokenStream> {
         let operand = match kind {
             Visit | VisitMut => Borrowed(quote!(it)),
             Fold => Owned(quote!(it)),
         };
         let val = visit(elem, lookup, kind, &operand)?;
         Some(match kind {
-            Visit => format!(
-                "for it in {name} {{ {val} }}",
-                name = name.ref_tokens(),
-                val = val,
-            ),
-            VisitMut => format!(
-                "for it in {name} {{ {val} }}",
-                name = name.ref_mut_tokens(),
-                val = val,
-            ),
-            Fold => format!(
-                "FoldHelper::lift({name}, |it| {{ {val} }})",
-                name = name.owned_tokens(),
-                val = val,
-            ),
+            Visit => {
+                let name = name.ref_tokens();
+                quote! {
+                    for it in #name {
+                        #val
+                    }
+                }
+            }
+            VisitMut => {
+                let name = name.ref_mut_tokens();
+                quote! {
+                    for it in #name {
+                        #val
+                    }
+                }
+            }
+            Fold => {
+                let name = name.owned_tokens();
+                quote! {
+                    FoldHelper::lift(#name, |it| { #val })
+                }
+            }
         })
     }
 
@@ -531,59 +542,66 @@
         lookup: &Lookup,
         kind: Kind,
         name: &Operand,
-    ) -> Option<String> {
+    ) -> Option<TokenStream> {
         let operand = match kind {
             Visit | VisitMut => Borrowed(quote!(it)),
             Fold => Owned(quote!(it)),
         };
         let val = visit(elem, lookup, kind, &operand)?;
         Some(match kind {
-            Visit => format!(
-                "for el in Punctuated::pairs({name}) {{ \
-                 let it = el.value(); \
-                 {val} \
-                 }}",
-                name = name.ref_tokens(),
-                val = val,
-            ),
-            VisitMut => format!(
-                "for mut el in Punctuated::pairs_mut({name}) {{ \
-                 let it = el.value_mut(); \
-                 {val} \
-                 }}",
-                name = name.ref_mut_tokens(),
-                val = val,
-            ),
-            Fold => format!(
-                "FoldHelper::lift({name}, |it| {{ {val} }})",
-                name = name.owned_tokens(),
-                val = val,
-            ),
+            Visit => {
+                let name = name.ref_tokens();
+                quote! {
+                    for el in Punctuated::pairs(#name) {
+                        let it = el.value();
+                        #val
+                    }
+                }
+            }
+            VisitMut => {
+                let name = name.ref_mut_tokens();
+                quote! {
+                    for mut el in Punctuated::pairs_mut(#name) {
+                        let it = el.value_mut();
+                        #val
+                    }
+                }
+            }
+            Fold => {
+                let name = name.owned_tokens();
+                quote! {
+                    FoldHelper::lift(#name, |it| { #val })
+                }
+            }
         })
     }
 
-    fn option_visit(elem: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<String> {
+    fn option_visit(
+        elem: &Type,
+        lookup: &Lookup,
+        kind: Kind,
+        name: &Operand,
+    ) -> Option<TokenStream> {
         let it = match kind {
             Visit | VisitMut => Borrowed(quote!(it)),
             Fold => Owned(quote!(it)),
         };
         let val = visit(elem, lookup, kind, &it)?;
+        let name = name.owned_tokens();
         Some(match kind {
-            Visit => format!(
-                "if let Some(ref it) = {name} {{ {val} }}",
-                name = name.owned_tokens(),
-                val = val,
-            ),
-            VisitMut => format!(
-                "if let Some(ref mut it) = {name} {{ {val} }}",
-                name = name.owned_tokens(),
-                val = val,
-            ),
-            Fold => format!(
-                "({name}).map(|it| {{ {val} }})",
-                name = name.owned_tokens(),
-                val = val,
-            ),
+            Visit => quote! {
+                if let Some(ref it) = #name {
+                    #val
+                }
+            },
+            VisitMut => quote! {
+                if let Some(ref mut it) = #name {
+                    #val
+                }
+            },
+            Fold => quote! {
+                (#name).map(|it| { #val })
+            },
         })
     }
 
@@ -592,53 +610,59 @@
         lookup: &Lookup,
         kind: Kind,
         name: &Operand,
-    ) -> Option<String> {
-        let mut code = String::new();
+    ) -> Option<TokenStream> {
+        if elems.is_empty() {
+            return None;
+        }
+
+        let mut code = TokenStream::new();
         for (i, elem) in elems.iter().enumerate() {
             let name = name.tokens();
             let i = Index::from(i);
             let it = Owned(quote!((#name).#i));
             let val = visit(elem, lookup, kind, &it).unwrap_or_else(|| noop_visit(kind, &it));
-            code.push_str(&format!("            {}", val));
+            code.append_all(val);
             match kind {
-                Fold => code.push(','),
-                Visit | VisitMut => code.push(';'),
+                Fold => code.append_all(quote!(,)),
+                Visit | VisitMut => code.append_all(quote!(;)),
             }
-            code.push('\n');
         }
-        if code.is_empty() {
-            None
-        } else {
-            Some(match kind {
-                Fold => format!("(\n{}        )", code),
-                Visit | VisitMut => format!("\n{}        ", code),
-            })
-        }
+        Some(match kind {
+            Fold => quote! {
+                (#code)
+            },
+            Visit | VisitMut => code,
+        })
     }
 
-    fn token_visit(ty: TokenStream, kind: Kind, name: &Operand) -> String {
+    fn token_visit(ty: TokenStream, kind: Kind, name: &Operand) -> TokenStream {
+        let name = name.tokens();
         match kind {
-            Fold => format!(
-                "{ty}(tokens_helper(_visitor, &({name}).0))",
-                ty = ty,
-                name = name.tokens(),
-            ),
-            Visit => format!("tokens_helper(_visitor, &({name}).0)", name = name.tokens(),),
-            VisitMut => format!(
-                "tokens_helper(_visitor, &mut ({name}).0)",
-                name = name.tokens(),
-            ),
+            Fold => quote! {
+                #ty(tokens_helper(_visitor, &(#name).0))
+            },
+            Visit => quote! {
+                tokens_helper(_visitor, &(#name).0)
+            },
+            VisitMut => quote! {
+                tokens_helper(_visitor, &mut (#name).0)
+            },
         }
     }
 
-    fn noop_visit(kind: Kind, name: &Operand) -> String {
+    fn noop_visit(kind: Kind, name: &Operand) -> TokenStream {
         match kind {
-            Fold => name.owned_tokens().to_string(),
-            Visit | VisitMut => format!("// Skipped field {}", name),
+            Fold => name.owned_tokens(),
+            Visit | VisitMut => {
+                let name = name.tokens();
+                quote! {
+                    skip!(#name)
+                }
+            }
         }
     }
 
-    fn visit(ty: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<String> {
+    fn visit(ty: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<TokenStream> {
         match classify(ty, lookup) {
             RelevantType::Box(elem) => box_visit(elem, lookup, kind, name),
             RelevantType::Vec(elem) => vec_visit(elem, lookup, kind, name),
@@ -648,7 +672,9 @@
             RelevantType::Simple(item) => {
                 let mut res = simple_visit(item, kind, name);
                 Some(if item.eos_full {
-                    format!("full!({res})", res = res)
+                    quote! {
+                        full!(#res)
+                    }
                 } else {
                     res
                 })
@@ -659,239 +685,197 @@
     }
 
     pub fn generate(state: &mut State, lookup: &Lookup, s: &AstItem) {
+        let features = &s.features;
         let under_name = under_name(s.ast.ident.clone());
+        let ty = &s.ast.ident;
+        let visit_fn = Ident::new(&format!("visit_{}", under_name), Span::call_site());
+        let visit_mut_fn = Ident::new(&format!("visit_{}_mut", under_name), Span::call_site());
+        let fold_fn = Ident::new(&format!("fold_{}", under_name), Span::call_site());
 
-        state.visit_trait.push_str(&format!(
-            "{features}\n\
-             fn visit_{under_name}(&mut self, i: &'ast {ty}) {{ \
-             visit_{under_name}(self, i) \
-             }}\n",
-            features = s.features,
-            under_name = under_name,
-            ty = s.ast.ident,
-        ));
-        state.visit_mut_trait.push_str(&format!(
-            "{features}\n\
-             fn visit_{under_name}_mut(&mut self, i: &mut {ty}) {{ \
-             visit_{under_name}_mut(self, i) \
-             }}\n",
-            features = s.features,
-            under_name = under_name,
-            ty = s.ast.ident,
-        ));
-        state.fold_trait.push_str(&format!(
-            "{features}\n\
-             fn fold_{under_name}(&mut self, i: {ty}) -> {ty} {{ \
-             fold_{under_name}(self, i) \
-             }}\n",
-            features = s.features,
-            under_name = under_name,
-            ty = s.ast.ident,
-        ));
+        let mut visit_impl = TokenStream::new();
+        let mut visit_mut_impl = TokenStream::new();
+        let mut fold_impl = TokenStream::new();
 
-        state.visit_impl.push_str(&format!(
-            "{features}\n\
-             pub fn visit_{under_name}<'ast, V: Visit<'ast> + ?Sized>(\
-             _visitor: &mut V, _i: &'ast {ty}) {{\n",
-            features = s.features,
-            under_name = under_name,
-            ty = s.ast.ident,
-        ));
-        state.visit_mut_impl.push_str(&format!(
-            "{features}\n\
-             pub fn visit_{under_name}_mut<V: VisitMut + ?Sized>(\
-             _visitor: &mut V, _i: &mut {ty}) {{\n",
-            features = s.features,
-            under_name = under_name,
-            ty = s.ast.ident,
-        ));
-        let before_fold_impl_len = state.fold_impl.len();
-        state.fold_impl.push_str(&format!(
-            "{features}\n\
-             pub fn fold_{under_name}<V: Fold + ?Sized>(\
-             _visitor: &mut V, _i: {ty}) -> {ty} {{\n",
-            features = s.features,
-            under_name = under_name,
-            ty = s.ast.ident,
-        ));
-
-        // XXX:  This part is a disaster - I'm not sure how to make it cleaner though :'(
         match s.ast.data {
             Data::Enum(ref e) => {
-                state.visit_impl.push_str("    match *_i {\n");
-                state.visit_mut_impl.push_str("    match *_i {\n");
-                state.fold_impl.push_str("    match _i {\n");
+                let mut visit_variants = TokenStream::new();
+                let mut visit_mut_variants = TokenStream::new();
+                let mut fold_variants = TokenStream::new();
+
                 for variant in &e.variants {
-                    let fields: Vec<(&Field, TokenStream)> = match variant.fields {
+                    let variant_ident = &variant.ident;
+
+                    match variant.fields {
                         Fields::Named(..) => panic!("Doesn't support enum struct variants"),
                         Fields::Unnamed(ref fields) => {
-                            let binding = format!("        {}::{}(", s.ast.ident, variant.ident);
-                            state.visit_impl.push_str(&binding);
-                            state.visit_mut_impl.push_str(&binding);
-                            state.fold_impl.push_str(&binding);
+                            let mut bind_visit_fields = TokenStream::new();
+                            let mut bind_visit_mut_fields = TokenStream::new();
+                            let mut bind_fold_fields = TokenStream::new();
 
-                            let res = fields
-                                .unnamed
-                                .iter()
-                                .enumerate()
-                                .map(|(idx, el)| {
-                                    let name = format!("_binding_{}", idx);
+                            let mut visit_fields = TokenStream::new();
+                            let mut visit_mut_fields = TokenStream::new();
+                            let mut fold_fields = TokenStream::new();
 
-                                    state.visit_impl.push_str("ref ");
-                                    state.visit_mut_impl.push_str("ref mut ");
+                            for (idx, field) in fields.unnamed.iter().enumerate() {
+                                let name = format!("_binding_{}", idx);
+                                let binding = Ident::new(&name, Span::call_site());
 
-                                    state.visit_impl.push_str(&name);
-                                    state.visit_mut_impl.push_str(&name);
-                                    state.fold_impl.push_str(&name);
-                                    state.visit_impl.push_str(", ");
-                                    state.visit_mut_impl.push_str(", ");
-                                    state.fold_impl.push_str(", ");
+                                bind_visit_fields.append_all(quote! {
+                                    ref #binding,
+                                });
+                                bind_visit_mut_fields.append_all(quote! {
+                                    ref mut #binding,
+                                });
+                                bind_fold_fields.append_all(quote! {
+                                    #binding,
+                                });
 
-                                    let mut tokens = quote!();
-                                    Ident::new(&name, Span::call_site()).to_tokens(&mut tokens);
+                                let borrowed_binding = Borrowed(quote!(#binding));
+                                let owned_binding = Owned(quote!(#binding));
 
-                                    (el, tokens)
-                                }).collect();
+                                visit_fields.append_all(
+                                    visit(&field.ty, lookup, Visit, &borrowed_binding)
+                                        .unwrap_or_else(|| noop_visit(Visit, &borrowed_binding)),
+                                );
+                                visit_mut_fields.append_all(
+                                    visit(&field.ty, lookup, VisitMut, &borrowed_binding)
+                                        .unwrap_or_else(|| noop_visit(VisitMut, &borrowed_binding)),
+                                );
+                                fold_fields.append_all(
+                                    visit(&field.ty, lookup, Fold, &owned_binding)
+                                        .unwrap_or_else(|| noop_visit(Fold, &owned_binding)),
+                                );
 
-                            state.visit_impl.push_str(") => {\n");
-                            state.visit_mut_impl.push_str(") => {\n");
-                            state.fold_impl.push_str(") => {\n");
+                                visit_fields.append_all(quote!(;));
+                                visit_mut_fields.append_all(quote!(;));
+                                fold_fields.append_all(quote!(,));
+                            }
 
-                            res
+                            visit_variants.append_all(quote! {
+                                #ty::#variant_ident(#bind_visit_fields) => {
+                                    #visit_fields
+                                }
+                            });
+
+                            visit_mut_variants.append_all(quote! {
+                                #ty::#variant_ident(#bind_visit_mut_fields) => {
+                                    #visit_mut_fields
+                                }
+                            });
+
+                            fold_variants.append_all(quote! {
+                                #ty::#variant_ident(#bind_fold_fields) => {
+                                    #ty::#variant_ident(
+                                        #fold_fields
+                                    )
+                                }
+                            });
                         }
                         Fields::Unit => {
-                            state.visit_impl.push_str(&format!(
-                                "        {0}::{1} => {{ }}\n",
-                                s.ast.ident, variant.ident
-                            ));
-                            state.visit_mut_impl.push_str(&format!(
-                                "        {0}::{1} => {{ }}\n",
-                                s.ast.ident, variant.ident
-                            ));
-                            state.fold_impl.push_str(&format!(
-                                "        {0}::{1} => {{ {0}::{1} }}\n",
-                                s.ast.ident, variant.ident
-                            ));
-                            continue;
+                            visit_variants.append_all(quote! {
+                                #ty::#variant_ident => {}
+                            });
+                            visit_mut_variants.append_all(quote! {
+                                #ty::#variant_ident => {}
+                            });
+                            fold_variants.append_all(quote! {
+                                #ty::#variant_ident => {
+                                    #ty::#variant_ident
+                                }
+                            });
                         }
-                    };
-
-                    if fields.is_empty() {
-                        state.visit_impl.push_str("            {}");
-                        state.visit_mut_impl.push_str(") => {\n");
-                        state.fold_impl.push_str(") => {\n");
                     }
-                    state.fold_impl.push_str(&format!(
-                        "            {}::{} (\n",
-                        s.ast.ident, variant.ident,
-                    ));
-                    for (field, binding) in fields {
-                        state.visit_impl.push_str(&format!(
-                            "            {};\n",
-                            visit(&field.ty, lookup, Visit, &Borrowed(binding.clone()))
-                                .unwrap_or_else(|| noop_visit(Visit, &Borrowed(binding.clone()))),
-                        ));
-                        state.visit_mut_impl.push_str(&format!(
-                            "            {};\n",
-                            visit(&field.ty, lookup, VisitMut, &Borrowed(binding.clone()))
-                                .unwrap_or_else(|| noop_visit(
-                                    VisitMut,
-                                    &Borrowed(binding.clone())
-                                )),
-                        ));
-                        state.fold_impl.push_str(&format!(
-                            "                {},\n",
-                            visit(&field.ty, lookup, Fold, &Owned(binding.clone()))
-                                .unwrap_or_else(|| noop_visit(Fold, &Owned(binding))),
-                        ));
-                    }
-                    state.fold_impl.push_str("            )\n");
-
-                    state.visit_impl.push_str("        }\n");
-                    state.visit_mut_impl.push_str("        }\n");
-                    state.fold_impl.push_str("        }\n");
                 }
-                state.visit_impl.push_str("    }\n");
-                state.visit_mut_impl.push_str("    }\n");
-                state.fold_impl.push_str("    }\n");
+
+                visit_impl.append_all(quote! {
+                    match *_i {
+                        #visit_variants
+                    }
+                });
+
+                visit_mut_impl.append_all(quote! {
+                    match *_i {
+                        #visit_mut_variants
+                    }
+                });
+
+                fold_impl.append_all(quote! {
+                    match _i {
+                        #fold_variants
+                    }
+                });
             }
             Data::Struct(ref v) => {
                 let fields: Vec<(&Field, TokenStream)> = match v.fields {
-                    Fields::Named(ref fields) => {
-                        state
-                            .fold_impl
-                            .push_str(&format!("    {} {{\n", s.ast.ident));
-                        fields
-                            .named
-                            .iter()
-                            .map(|el| {
-                                let id = el.ident.clone();
-                                (el, quote!(_i.#id))
-                            }).collect()
-                    }
-                    Fields::Unnamed(ref fields) => {
-                        state
-                            .fold_impl
-                            .push_str(&format!("    {} (\n", s.ast.ident));
-                        fields
-                            .unnamed
-                            .iter()
-                            .enumerate()
-                            .map(|(idx, el)| {
-                                let id = Index::from(idx);
-                                (el, quote!(_i.#id))
-                            }).collect()
-                    }
-                    Fields::Unit => {
-                        if s.ast.ident == "Ident" {
-                            state.fold_impl.push_str("    let mut _i = _i;\n");
-                            state
-                                .fold_impl
-                                .push_str("    let span = _visitor.fold_span(_i.span());\n");
-                            state.fold_impl.push_str("    _i.set_span(span);\n");
-                        }
-                        state.fold_impl.push_str("    _i\n");
-                        vec![]
-                    }
+                    Fields::Named(ref fields) => fields
+                        .named
+                        .iter()
+                        .map(|el| {
+                            let id = el.ident.clone();
+                            (el, quote!(_i.#id))
+                        }).collect(),
+                    Fields::Unnamed(ref fields) => fields
+                        .unnamed
+                        .iter()
+                        .enumerate()
+                        .map(|(idx, el)| {
+                            let id = Index::from(idx);
+                            (el, quote!(_i.#id))
+                        }).collect(),
+                    Fields::Unit => Vec::new(),
                 };
 
+                let mut fold_fields = TokenStream::new();
+
                 for (field, ref_toks) in fields {
                     let ref_toks = Owned(ref_toks);
-                    state.visit_impl.push_str(&format!(
-                        "    {};\n",
-                        visit(&field.ty, lookup, Visit, &ref_toks)
-                            .unwrap_or_else(|| noop_visit(Visit, &ref_toks,))
-                    ));
-                    state.visit_mut_impl.push_str(&format!(
-                        "    {};\n",
-                        visit(&field.ty, lookup, VisitMut, &ref_toks)
-                            .unwrap_or_else(|| noop_visit(VisitMut, &ref_toks,))
-                    ));
+                    let visit_field = visit(&field.ty, lookup, Visit, &ref_toks)
+                        .unwrap_or_else(|| noop_visit(Visit, &ref_toks));
+                    visit_impl.append_all(quote! {
+                        #visit_field;
+                    });
+                    let visit_mut_field = visit(&field.ty, lookup, VisitMut, &ref_toks)
+                        .unwrap_or_else(|| noop_visit(VisitMut, &ref_toks));
+                    visit_mut_impl.append_all(quote! {
+                        #visit_mut_field;
+                    });
                     let fold = visit(&field.ty, lookup, Fold, &ref_toks)
                         .unwrap_or_else(|| noop_visit(Fold, &ref_toks));
                     if let Some(ref name) = field.ident {
-                        state
-                            .fold_impl
-                            .push_str(&format!("        {}: {},\n", name, fold));
+                        fold_fields.append_all(quote! {
+                            #name: #fold,
+                        });
                     } else {
-                        state.fold_impl.push_str(&format!("        {},\n", fold));
+                        fold_fields.append_all(quote! {
+                            #fold,
+                        });
                     }
                 }
 
                 match v.fields {
-                    Fields::Named(..) => state.fold_impl.push_str("    }\n"),
-                    Fields::Unnamed(..) => state.fold_impl.push_str("    )\n"),
-                    Fields::Unit => {}
+                    Fields::Named(..) | Fields::Unnamed(..) => fold_impl.append_all(quote! {
+                        #ty {
+                            #fold_fields
+                        }
+                    }),
+                    Fields::Unit => {
+                        if ty == "Ident" {
+                            fold_impl.append_all(quote! {
+                                let mut _i = _i;
+                                let span = _visitor.fold_span(_i.span());
+                                _i.set_span(span);
+                            });
+                        }
+                        fold_impl.append_all(quote! {
+                            _i
+                        });
+                    }
                 };
             }
             Data::Union(..) => panic!("Union not supported"),
         }
 
-        // Close the impl body
-        state.visit_impl.push_str("}\n");
-        state.visit_mut_impl.push_str("}\n");
-        state.fold_impl.push_str("}\n");
-
+        let mut include_fold_impl = true;
         if let Data::Struct(ref data) = s.ast.data {
             if let Fields::Named(ref fields) = data.fields {
                 if fields
@@ -901,20 +885,76 @@
                 {
                     // Discard the generated impl if there are private fields.
                     // These have to be handwritten.
-                    state.fold_impl.truncate(before_fold_impl_len);
+                    include_fold_impl = false;
                 }
             }
         }
+
+        state.visit_trait.append_all(quote! {
+            #features
+            fn #visit_fn(&mut self, i: &'ast #ty) {
+                #visit_fn(self, i)
+            }
+        });
+
+        state.visit_impl.append_all(quote! {
+            #features
+            pub fn #visit_fn<'ast, V: Visit<'ast> + ?Sized>(
+                _visitor: &mut V, _i: &'ast #ty
+            ) {
+                #visit_impl
+            }
+        });
+
+        state.visit_mut_trait.append_all(quote! {
+            #features
+            fn #visit_mut_fn(&mut self, i: &mut #ty) {
+                #visit_mut_fn(self, i)
+            }
+        });
+
+        state.visit_mut_impl.append_all(quote! {
+            #features
+            pub fn #visit_mut_fn<V: VisitMut + ?Sized>(
+                _visitor: &mut V, _i: &mut #ty
+            ) {
+                #visit_mut_impl
+            }
+        });
+
+        state.fold_trait.append_all(quote! {
+            #features
+            fn #fold_fn(&mut self, i: #ty) -> #ty {
+                #fold_fn(self, i)
+            }
+        });
+
+        if include_fold_impl {
+            state.fold_impl.append_all(quote! {
+                #features
+                pub fn #fold_fn<V: Fold + ?Sized>(
+                    _visitor: &mut V, _i: #ty
+                ) -> #ty {
+                    #fold_impl
+                }
+            });
+        }
     }
 }
 
-fn write_file(path: &str, content: String) {
+fn write_file(path: &str, content: TokenStream) {
     let mut file = File::create(path).unwrap();
+    write!(
+        file,
+        "// THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT\n\n"
+    ).unwrap();
     let mut config = rustfmt::Config::default();
     config.set().emit_mode(rustfmt::EmitMode::Stdout);
     config.set().verbose(rustfmt::Verbosity::Quiet);
     let mut session = rustfmt::Session::new(config, Some(&mut file));
-    session.format(rustfmt::Input::Text(content)).unwrap();
+    session
+        .format(rustfmt::Input::Text(content.to_string()))
+        .unwrap();
 }
 
 fn main() {
@@ -930,13 +970,13 @@
                 ast: DeriveInput {
                     ident: Ident::new(tt, Span::call_site()),
                     vis: Visibility::Public(VisPublic {
-                        pub_token: Default::default(),
+                        pub_token: <Token![pub]>::default(),
                     }),
                     attrs: vec![],
-                    generics: Default::default(),
+                    generics: Generics::default(),
                     data: Data::Struct(DataStruct {
                         fields: Fields::Unit,
-                        struct_token: Default::default(),
+                        struct_token: <Token![struct]>::default(),
                         semi_token: None,
                     }),
                 },
@@ -946,155 +986,153 @@
         );
     }
 
-    let mut state = Default::default();
+    let mut state = codegen::State::default();
     for s in lookup.values() {
         codegen::generate(&mut state, &lookup, s);
     }
 
-    let full_macro = "
-#[cfg(feature = \"full\")]
-macro_rules! full {
-    ($e:expr) => { $e }
-}
+    let full_macro = quote! {
+        #[cfg(feature = "full")]
+        macro_rules! full {
+            ($e:expr) => {
+                $e
+            };
+        }
 
-#[cfg(all(feature = \"derive\", not(feature = \"full\")))]
-macro_rules! full {
-    ($e:expr) => { unreachable!() }
-}
-";
+        #[cfg(all(feature = "derive", not(feature = "full")))]
+        macro_rules! full {
+            ($e:expr) => {
+                unreachable!()
+            };
+        }
+    };
 
+    let skip_macro = quote! {
+        #[cfg(any(feature = "full", feature = "derive"))]
+        macro_rules! skip {
+            ($($tt:tt)*) => {};
+        }
+    };
+
+    let fold_trait = state.fold_trait;
+    let fold_impl = state.fold_impl;
     write_file(
         FOLD_SRC,
-        format!(
-            "\
-// THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT
+        quote! {
+            // Unreachable code is generated sometimes without the full feature.
+            #![allow(unreachable_code)]
+            #![cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))]
 
-// Unreachable code is generated sometimes without the full feature.
-#![allow(unreachable_code)]
-#![cfg_attr(feature = \"cargo-clippy\", allow(needless_pass_by_value))]
+            use *;
+            #[cfg(any(feature = "full", feature = "derive"))]
+            use token::{Brace, Bracket, Paren, Group};
+            use proc_macro2::Span;
+            #[cfg(any(feature = "full", feature = "derive"))]
+            use gen::helper::fold::*;
 
-use *;
-#[cfg(any(feature = \"full\", feature = \"derive\"))]
-use token::{{Brace, Bracket, Paren, Group}};
-use proc_macro2::Span;
-#[cfg(any(feature = \"full\", feature = \"derive\"))]
-use gen::helper::fold::*;
+            #full_macro
 
-{full_macro}
+            /// Syntax tree traversal to transform the nodes of an owned syntax tree.
+            ///
+            /// See the [module documentation] for details.
+            ///
+            /// [module documentation]: index.html
+            ///
+            /// *This trait is available if Syn is built with the `"fold"` feature.*
+            pub trait Fold {
+                #fold_trait
+            }
 
-/// Syntax tree traversal to transform the nodes of an owned syntax tree.
-///
-/// See the [module documentation] for details.
-///
-/// [module documentation]: index.html
-///
-/// *This trait is available if Syn is built with the `\"fold\"` feature.*
-pub trait Fold {{
-{fold_trait}
-}}
+            #[cfg(any(feature = "full", feature = "derive"))]
+            macro_rules! fold_span_only {
+                ($f:ident : $t:ident) => {
+                    pub fn $f<V: Fold + ?Sized>(_visitor: &mut V, mut _i: $t) -> $t {
+                        let span = _visitor.fold_span(_i.span());
+                        _i.set_span(span);
+                        _i
+                    }
+                }
+            }
 
-#[cfg(any(feature = \"full\", feature = \"derive\"))]
-macro_rules! fold_span_only {{
-    ($f:ident : $t:ident) => {{
-        pub fn $f<V: Fold + ?Sized>(_visitor: &mut V, mut _i: $t) -> $t {{
-            let span = _visitor.fold_span(_i.span());
-            _i.set_span(span);
-            _i
-        }}
-    }}
-}}
+            #[cfg(any(feature = "full", feature = "derive"))]
+            fold_span_only!(fold_lit_byte: LitByte);
+            #[cfg(any(feature = "full", feature = "derive"))]
+            fold_span_only!(fold_lit_byte_str: LitByteStr);
+            #[cfg(any(feature = "full", feature = "derive"))]
+            fold_span_only!(fold_lit_char: LitChar);
+            #[cfg(any(feature = "full", feature = "derive"))]
+            fold_span_only!(fold_lit_float: LitFloat);
+            #[cfg(any(feature = "full", feature = "derive"))]
+            fold_span_only!(fold_lit_int: LitInt);
+            #[cfg(any(feature = "full", feature = "derive"))]
+            fold_span_only!(fold_lit_str: LitStr);
 
-#[cfg(any(feature = \"full\", feature = \"derive\"))]
-fold_span_only!(fold_lit_byte: LitByte);
-#[cfg(any(feature = \"full\", feature = \"derive\"))]
-fold_span_only!(fold_lit_byte_str: LitByteStr);
-#[cfg(any(feature = \"full\", feature = \"derive\"))]
-fold_span_only!(fold_lit_char: LitChar);
-#[cfg(any(feature = \"full\", feature = \"derive\"))]
-fold_span_only!(fold_lit_float: LitFloat);
-#[cfg(any(feature = \"full\", feature = \"derive\"))]
-fold_span_only!(fold_lit_int: LitInt);
-#[cfg(any(feature = \"full\", feature = \"derive\"))]
-fold_span_only!(fold_lit_str: LitStr);
-
-{fold_impl}
-",
-            full_macro = full_macro,
-            fold_trait = state.fold_trait,
-            fold_impl = state.fold_impl
-        ),
+            #fold_impl
+        },
     );
 
+    let visit_trait = state.visit_trait;
+    let visit_impl = state.visit_impl;
     write_file(
         VISIT_SRC,
-        format!(
-            "\
-// THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT
+        quote! {
+            #![cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
 
-#![cfg_attr(feature = \"cargo-clippy\", allow(match_same_arms))]
+            use *;
+            #[cfg(any(feature = "full", feature = "derive"))]
+            use punctuated::Punctuated;
+            use proc_macro2::Span;
+            #[cfg(any(feature = "full", feature = "derive"))]
+            use gen::helper::visit::*;
 
-use *;
-#[cfg(any(feature = \"full\", feature = \"derive\"))]
-use punctuated::Punctuated;
-use proc_macro2::Span;
-#[cfg(any(feature = \"full\", feature = \"derive\"))]
-use gen::helper::visit::*;
+            #full_macro
+            #skip_macro
 
-{full_macro}
+            /// Syntax tree traversal to walk a shared borrow of a syntax tree.
+            ///
+            /// See the [module documentation] for details.
+            ///
+            /// [module documentation]: index.html
+            ///
+            /// *This trait is available if Syn is built with the `"visit"` feature.*
+            pub trait Visit<'ast> {
+                #visit_trait
+            }
 
-/// Syntax tree traversal to walk a shared borrow of a syntax tree.
-///
-/// See the [module documentation] for details.
-///
-/// [module documentation]: index.html
-///
-/// *This trait is available if Syn is built with the `\"visit\"` feature.*
-pub trait Visit<'ast> {{
-{visit_trait}
-}}
-
-{visit_impl}
-",
-            full_macro = full_macro,
-            visit_trait = state.visit_trait,
-            visit_impl = state.visit_impl
-        ),
+            #visit_impl
+        },
     );
 
+    let visit_mut_trait = state.visit_mut_trait;
+    let visit_mut_impl = state.visit_mut_impl;
     write_file(
         VISIT_MUT_SRC,
-        format!(
-            "\
-// THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT
+        quote! {
+            #![cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
 
-#![cfg_attr(feature = \"cargo-clippy\", allow(match_same_arms))]
+            use *;
+            #[cfg(any(feature = "full", feature = "derive"))]
+            use punctuated::Punctuated;
+            use proc_macro2::Span;
+            #[cfg(any(feature = "full", feature = "derive"))]
+            use gen::helper::visit_mut::*;
 
-use *;
-#[cfg(any(feature = \"full\", feature = \"derive\"))]
-use punctuated::Punctuated;
-use proc_macro2::Span;
-#[cfg(any(feature = \"full\", feature = \"derive\"))]
-use gen::helper::visit_mut::*;
+            #full_macro
+            #skip_macro
 
-{full_macro}
+            /// Syntax tree traversal to mutate an exclusive borrow of a syntax tree in
+            /// place.
+            ///
+            /// See the [module documentation] for details.
+            ///
+            /// [module documentation]: index.html
+            ///
+            /// *This trait is available if Syn is built with the `"visit-mut"` feature.*
+            pub trait VisitMut {
+                #visit_mut_trait
+            }
 
-/// Syntax tree traversal to mutate an exclusive borrow of a syntax tree in
-/// place.
-///
-/// See the [module documentation] for details.
-///
-/// [module documentation]: index.html
-///
-/// *This trait is available if Syn is built with the `\"visit-mut\"` feature.*
-pub trait VisitMut {{
-{visit_mut_trait}
-}}
-
-{visit_mut_impl}
-",
-            full_macro = full_macro,
-            visit_mut_trait = state.visit_mut_trait,
-            visit_mut_impl = state.visit_mut_impl
-        ),
+            #visit_mut_impl
+        },
     );
 }