Separate out the fold codegen
diff --git a/codegen/src/fold.rs b/codegen/src/fold.rs
new file mode 100644
index 0000000..3122473
--- /dev/null
+++ b/codegen/src/fold.rs
@@ -0,0 +1,402 @@
+use crate::file;
+use quote::quote;
+use syn_codegen as types;
+
+const FOLD_SRC: &str = "../src/gen/fold.rs";
+
+mod codegen {
+    use inflections::Inflect;
+    use proc_macro2::{Span, TokenStream};
+    use quote::{quote, TokenStreamExt};
+    use syn::*;
+    use syn_codegen as types;
+
+    #[derive(Default)]
+    pub struct State {
+        pub fold_trait: TokenStream,
+        pub fold_impl: TokenStream,
+    }
+
+    fn under_name(name: &str) -> Ident {
+        Ident::new(&name.to_snake_case(), Span::call_site())
+    }
+
+    fn simple_visit(item: &str, name: &TokenStream) -> TokenStream {
+        let ident = under_name(item);
+
+        let method = Ident::new(&format!("fold_{}", ident), Span::call_site());
+        quote! {
+            _visitor.#method(#name)
+        }
+    }
+
+    fn box_visit(
+        elem: &types::Type,
+        features: &types::Features,
+        defs: &types::Definitions,
+        name: &TokenStream,
+    ) -> Option<TokenStream> {
+        let res = visit(elem, features, defs, &quote!(*#name))?;
+        Some(quote! {
+            Box::new(#res)
+        })
+    }
+
+    fn vec_visit(
+        elem: &types::Type,
+        features: &types::Features,
+        defs: &types::Definitions,
+        name: &TokenStream,
+    ) -> Option<TokenStream> {
+        let operand = quote!(it);
+        let val = visit(elem, features, defs, &operand)?;
+        Some(quote! {
+            FoldHelper::lift(#name, |it| { #val })
+        })
+    }
+
+    fn punctuated_visit(
+        elem: &types::Type,
+        features: &types::Features,
+        defs: &types::Definitions,
+        name: &TokenStream,
+    ) -> Option<TokenStream> {
+        let operand = quote!(it);
+        let val = visit(elem, features, defs, &operand)?;
+        Some(quote! {
+            FoldHelper::lift(#name, |it| { #val })
+        })
+    }
+
+    fn option_visit(
+        elem: &types::Type,
+        features: &types::Features,
+        defs: &types::Definitions,
+        name: &TokenStream,
+    ) -> Option<TokenStream> {
+        let it = quote!(it);
+        let val = visit(elem, features, defs, &it)?;
+        Some(quote! {
+            (#name).map(|it| { #val })
+        })
+    }
+
+    fn tuple_visit(
+        elems: &[types::Type],
+        features: &types::Features,
+        defs: &types::Definitions,
+        name: &TokenStream,
+    ) -> Option<TokenStream> {
+        if elems.is_empty() {
+            return None;
+        }
+
+        let mut code = TokenStream::new();
+        for (i, elem) in elems.iter().enumerate() {
+            let i = Index::from(i);
+            let it = quote!((#name).#i);
+            let val = visit(elem, features, defs, &it).unwrap_or(it);
+            code.append_all(val);
+            code.append_all(quote!(,));
+        }
+        Some(quote! {
+            (#code)
+        })
+    }
+
+    fn token_punct_visit(repr: &str, name: &TokenStream) -> TokenStream {
+        let ty: TokenStream = syn::parse_str(&format!("Token![{}]", repr)).unwrap();
+        quote! {
+            #ty(tokens_helper(_visitor, &#name.spans))
+        }
+    }
+
+    fn token_keyword_visit(repr: &str, name: &TokenStream) -> TokenStream {
+        let ty: TokenStream = syn::parse_str(&format!("Token![{}]", repr)).unwrap();
+        quote! {
+            #ty(tokens_helper(_visitor, &#name.span))
+        }
+    }
+
+    fn token_group_visit(ty: &str, name: &TokenStream) -> TokenStream {
+        let ty = Ident::new(ty, Span::call_site());
+        quote! {
+            #ty(tokens_helper(_visitor, &#name.span))
+        }
+    }
+
+    fn visit(
+        ty: &types::Type,
+        features: &types::Features,
+        defs: &types::Definitions,
+        name: &TokenStream,
+    ) -> Option<TokenStream> {
+        match ty {
+            types::Type::Box(t) => box_visit(&*t, features, defs, name),
+            types::Type::Vec(t) => vec_visit(&*t, features, defs, name),
+            types::Type::Punctuated(p) => punctuated_visit(&p.element, features, defs, name),
+            types::Type::Option(t) => option_visit(&*t, features, defs, name),
+            types::Type::Tuple(t) => tuple_visit(t, features, defs, name),
+            types::Type::Token(t) => {
+                let repr = &defs.tokens[t];
+                let is_keyword = repr.chars().next().unwrap().is_alphabetic();
+                if is_keyword {
+                    Some(token_keyword_visit(repr, name))
+                } else {
+                    Some(token_punct_visit(repr, name))
+                }
+            }
+            types::Type::Group(t) => Some(token_group_visit(&t[..], name)),
+            types::Type::Syn(t) => {
+                fn requires_full(features: &types::Features) -> bool {
+                    features.any.contains("full") && features.any.len() == 1
+                }
+
+                let res = simple_visit(t, name);
+
+                let target = defs.types.iter().find(|ty| ty.ident == *t).unwrap();
+
+                Some(
+                    if requires_full(&target.features) && !requires_full(features) {
+                        quote! {
+                            full!(#res)
+                        }
+                    } else {
+                        res
+                    },
+                )
+            }
+            types::Type::Ext(t) if super::TERMINAL_TYPES.contains(&&t[..]) => {
+                Some(simple_visit(t, name))
+            }
+            types::Type::Ext(_) | types::Type::Std(_) => None,
+        }
+    }
+
+    fn visit_features(features: &types::Features) -> TokenStream {
+        let features = &features.any;
+        match features.len() {
+            0 => quote!(),
+            1 => quote!(#[cfg(feature = #(#features)*)]),
+            _ => quote!(#[cfg(any(#(feature = #features),*))]),
+        }
+    }
+
+    pub fn generate(state: &mut State, s: &types::Node, defs: &types::Definitions) {
+        let features = visit_features(&s.features);
+        let under_name = under_name(&s.ident);
+        let ty = Ident::new(&s.ident, Span::call_site());
+        let fold_fn = Ident::new(&format!("fold_{}", under_name), Span::call_site());
+
+        let mut fold_impl = TokenStream::new();
+
+        match &s.data {
+            types::Data::Enum(variants) => {
+                let mut fold_variants = TokenStream::new();
+
+                for (variant, fields) in variants {
+                    let variant_ident = Ident::new(variant, Span::call_site());
+
+                    if fields.is_empty() {
+                        fold_variants.append_all(quote! {
+                            #ty::#variant_ident => {
+                                #ty::#variant_ident
+                            }
+                        });
+                    } else {
+                        let mut bind_fold_fields = TokenStream::new();
+                        let mut fold_fields = TokenStream::new();
+
+                        for (idx, ty) in fields.iter().enumerate() {
+                            let name = format!("_binding_{}", idx);
+                            let binding = Ident::new(&name, Span::call_site());
+
+                            bind_fold_fields.append_all(quote! {
+                                #binding,
+                            });
+
+                            let owned_binding = quote!(#binding);
+
+                            fold_fields.append_all(
+                                visit(ty, &s.features, defs, &owned_binding)
+                                    .unwrap_or(owned_binding),
+                            );
+
+                            fold_fields.append_all(quote!(,));
+                        }
+
+                        fold_variants.append_all(quote! {
+                            #ty::#variant_ident(#bind_fold_fields) => {
+                                #ty::#variant_ident(
+                                    #fold_fields
+                                )
+                            }
+                        });
+                    }
+                }
+
+                fold_impl.append_all(quote! {
+                    match _i {
+                        #fold_variants
+                    }
+                });
+            }
+            types::Data::Struct(fields) => {
+                let mut fold_fields = TokenStream::new();
+
+                for (field, ty) in fields {
+                    let id = Ident::new(&field, Span::call_site());
+                    let ref_toks = quote!(_i.#id);
+                    let fold = visit(&ty, &s.features, defs, &ref_toks).unwrap_or(ref_toks);
+
+                    fold_fields.append_all(quote! {
+                        #id: #fold,
+                    });
+                }
+
+                if !fields.is_empty() {
+                    fold_impl.append_all(quote! {
+                        #ty {
+                            #fold_fields
+                        }
+                    })
+                } else {
+                    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
+                    });
+                }
+            }
+            types::Data::Private => {
+                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
+                });
+            }
+        }
+
+        let include_fold_impl = match &s.data {
+            types::Data::Private => super::TERMINAL_TYPES.contains(&s.ident.as_str()),
+            types::Data::Struct(_) | types::Data::Enum(_) => true,
+        };
+
+        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
+                }
+            });
+        }
+    }
+}
+
+const TERMINAL_TYPES: &[&str] = &["Span", "Ident"];
+
+pub fn generate(defs: &types::Definitions) {
+    let mut state = codegen::State::default();
+    for s in &defs.types {
+        codegen::generate(&mut state, s, defs);
+    }
+    for tt in TERMINAL_TYPES {
+        let s = types::Node {
+            ident: tt.to_string(),
+            features: types::Features::default(),
+            data: types::Data::Private,
+        };
+        codegen::generate(&mut state, &s, defs);
+    }
+
+    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!()
+            };
+        }
+    };
+
+    let fold_trait = state.fold_trait;
+    let fold_impl = state.fold_impl;
+    file::write(
+        FOLD_SRC,
+        quote! {
+            // Unreachable code is generated sometimes without the full feature.
+            #![allow(unreachable_code)]
+
+            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
+
+            /// 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"))]
+            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
+        },
+    );
+}
diff --git a/codegen/src/gen.rs b/codegen/src/gen.rs
index 527662e..6dd6349 100644
--- a/codegen/src/gen.rs
+++ b/codegen/src/gen.rs
@@ -2,7 +2,6 @@
 use quote::quote;
 use syn_codegen as types;
 
-const FOLD_SRC: &str = "../src/gen/fold.rs";
 const VISIT_SRC: &str = "../src/gen/visit.rs";
 const VISIT_MUT_SRC: &str = "../src/gen/visit_mut.rs";
 
@@ -19,8 +18,6 @@
         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: &str) -> Ident {
@@ -31,7 +28,6 @@
     enum Kind {
         Visit,
         VisitMut,
-        Fold,
     }
 
     enum Operand {
@@ -89,13 +85,6 @@
                     _visitor.#method(#name)
                 }
             }
-            Fold => {
-                let method = Ident::new(&format!("fold_{}", ident), Span::call_site());
-                let name = name.owned_tokens();
-                quote! {
-                    _visitor.#method(#name)
-                }
-            }
         }
     }
 
@@ -108,12 +97,7 @@
     ) -> Option<TokenStream> {
         let name = name.owned_tokens();
         let res = visit(elem, features, defs, kind, &Owned(quote!(*#name)))?;
-        Some(match kind {
-            Fold => quote! {
-                Box::new(#res)
-            },
-            Visit | VisitMut => res,
-        })
+        Some(res)
     }
 
     fn vec_visit(
@@ -123,10 +107,7 @@
         kind: Kind,
         name: &Operand,
     ) -> Option<TokenStream> {
-        let operand = match kind {
-            Visit | VisitMut => Borrowed(quote!(it)),
-            Fold => Owned(quote!(it)),
-        };
+        let operand = Borrowed(quote!(it));
         let val = visit(elem, features, defs, kind, &operand)?;
         Some(match kind {
             Visit => {
@@ -145,12 +126,6 @@
                     }
                 }
             }
-            Fold => {
-                let name = name.owned_tokens();
-                quote! {
-                    FoldHelper::lift(#name, |it| { #val })
-                }
-            }
         })
     }
 
@@ -161,10 +136,7 @@
         kind: Kind,
         name: &Operand,
     ) -> Option<TokenStream> {
-        let operand = match kind {
-            Visit | VisitMut => Borrowed(quote!(it)),
-            Fold => Owned(quote!(it)),
-        };
+        let operand = Borrowed(quote!(it));
         let val = visit(elem, features, defs, kind, &operand)?;
         Some(match kind {
             Visit => {
@@ -185,12 +157,6 @@
                     }
                 }
             }
-            Fold => {
-                let name = name.owned_tokens();
-                quote! {
-                    FoldHelper::lift(#name, |it| { #val })
-                }
-            }
         })
     }
 
@@ -201,10 +167,7 @@
         kind: Kind,
         name: &Operand,
     ) -> Option<TokenStream> {
-        let it = match kind {
-            Visit | VisitMut => Borrowed(quote!(it)),
-            Fold => Owned(quote!(it)),
-        };
+        let it = Borrowed(quote!(it));
         let val = visit(elem, features, defs, kind, &it)?;
         let name = name.owned_tokens();
         Some(match kind {
@@ -218,9 +181,6 @@
                     #val
                 }
             },
-            Fold => quote! {
-                (#name).map(|it| { #val })
-            },
         })
     }
 
@@ -240,29 +200,16 @@
             let name = name.tokens();
             let i = Index::from(i);
             let it = Owned(quote!((#name).#i));
-            let val =
-                visit(elem, features, defs, kind, &it).unwrap_or_else(|| noop_visit(kind, &it));
+            let val = visit(elem, features, defs, kind, &it).unwrap_or_else(|| noop_visit(&it));
             code.append_all(val);
-            match kind {
-                Fold => code.append_all(quote!(,)),
-                Visit | VisitMut => code.append_all(quote!(;)),
-            }
+            code.append_all(quote!(;));
         }
-        Some(match kind {
-            Fold => quote! {
-                (#code)
-            },
-            Visit | VisitMut => code,
-        })
+        Some(code)
     }
 
-    fn token_punct_visit(repr: &str, kind: Kind, name: &Operand) -> TokenStream {
-        let ty: TokenStream = syn::parse_str(&format!("Token![{}]", repr)).unwrap();
+    fn token_punct_visit(kind: Kind, name: &Operand) -> TokenStream {
         let name = name.tokens();
         match kind {
-            Fold => quote! {
-                #ty(tokens_helper(_visitor, &#name.spans))
-            },
             Visit => quote! {
                 tokens_helper(_visitor, &#name.spans)
             },
@@ -272,13 +219,9 @@
         }
     }
 
-    fn token_keyword_visit(repr: &str, kind: Kind, name: &Operand) -> TokenStream {
-        let ty: TokenStream = syn::parse_str(&format!("Token![{}]", repr)).unwrap();
+    fn token_keyword_visit(kind: Kind, name: &Operand) -> TokenStream {
         let name = name.tokens();
         match kind {
-            Fold => quote! {
-                #ty(tokens_helper(_visitor, &#name.span))
-            },
             Visit => quote! {
                 tokens_helper(_visitor, &#name.span)
             },
@@ -288,13 +231,9 @@
         }
     }
 
-    fn token_group_visit(ty: &str, kind: Kind, name: &Operand) -> TokenStream {
-        let ty = Ident::new(ty, Span::call_site());
+    fn token_group_visit(kind: Kind, name: &Operand) -> TokenStream {
         let name = name.tokens();
         match kind {
-            Fold => quote! {
-                #ty(tokens_helper(_visitor, &#name.span))
-            },
             Visit => quote! {
                 tokens_helper(_visitor, &#name.span)
             },
@@ -304,15 +243,10 @@
         }
     }
 
-    fn noop_visit(kind: Kind, name: &Operand) -> TokenStream {
-        match kind {
-            Fold => name.owned_tokens(),
-            Visit | VisitMut => {
-                let name = name.tokens();
-                quote! {
-                    skip!(#name)
-                }
-            }
+    fn noop_visit(name: &Operand) -> TokenStream {
+        let name = name.tokens();
+        quote! {
+            skip!(#name)
         }
     }
 
@@ -333,12 +267,12 @@
                 let repr = &defs.tokens[t];
                 let is_keyword = repr.chars().next().unwrap().is_alphabetic();
                 if is_keyword {
-                    Some(token_keyword_visit(repr, kind, name))
+                    Some(token_keyword_visit(kind, name))
                 } else {
-                    Some(token_punct_visit(repr, kind, name))
+                    Some(token_punct_visit(kind, name))
                 }
             }
-            types::Type::Group(t) => Some(token_group_visit(&t[..], kind, name)),
+            types::Type::Group(_) => Some(token_group_visit(kind, name)),
             types::Type::Syn(t) => {
                 fn requires_full(features: &types::Features) -> bool {
                     features.any.contains("full") && features.any.len() == 1
@@ -380,17 +314,14 @@
         let ty = Ident::new(&s.ident, Span::call_site());
         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());
 
         let mut visit_impl = TokenStream::new();
         let mut visit_mut_impl = TokenStream::new();
-        let mut fold_impl = TokenStream::new();
 
         match &s.data {
             types::Data::Enum(variants) => {
                 let mut visit_variants = TokenStream::new();
                 let mut visit_mut_variants = TokenStream::new();
-                let mut fold_variants = TokenStream::new();
 
                 for (variant, fields) in variants {
                     let variant_ident = Ident::new(variant, Span::call_site());
@@ -402,19 +333,12 @@
                         visit_mut_variants.append_all(quote! {
                             #ty::#variant_ident => {}
                         });
-                        fold_variants.append_all(quote! {
-                            #ty::#variant_ident => {
-                                #ty::#variant_ident
-                            }
-                        });
                     } else {
                         let mut bind_visit_fields = TokenStream::new();
                         let mut bind_visit_mut_fields = TokenStream::new();
-                        let mut bind_fold_fields = TokenStream::new();
 
                         let mut visit_fields = TokenStream::new();
                         let mut visit_mut_fields = TokenStream::new();
-                        let mut fold_fields = TokenStream::new();
 
                         for (idx, ty) in fields.iter().enumerate() {
                             let name = format!("_binding_{}", idx);
@@ -426,29 +350,20 @@
                             bind_visit_mut_fields.append_all(quote! {
                                 ref mut #binding,
                             });
-                            bind_fold_fields.append_all(quote! {
-                                #binding,
-                            });
 
                             let borrowed_binding = Borrowed(quote!(#binding));
-                            let owned_binding = Owned(quote!(#binding));
 
                             visit_fields.append_all(
                                 visit(ty, &s.features, defs, Visit, &borrowed_binding)
-                                    .unwrap_or_else(|| noop_visit(Visit, &borrowed_binding)),
+                                    .unwrap_or_else(|| noop_visit(&borrowed_binding)),
                             );
                             visit_mut_fields.append_all(
                                 visit(ty, &s.features, defs, VisitMut, &borrowed_binding)
-                                    .unwrap_or_else(|| noop_visit(VisitMut, &borrowed_binding)),
-                            );
-                            fold_fields.append_all(
-                                visit(ty, &s.features, defs, Fold, &owned_binding)
-                                    .unwrap_or_else(|| noop_visit(Fold, &owned_binding)),
+                                    .unwrap_or_else(|| noop_visit(&borrowed_binding)),
                             );
 
                             visit_fields.append_all(quote!(;));
                             visit_mut_fields.append_all(quote!(;));
-                            fold_fields.append_all(quote!(,));
                         }
 
                         visit_variants.append_all(quote! {
@@ -462,14 +377,6 @@
                                 #visit_mut_fields
                             }
                         });
-
-                        fold_variants.append_all(quote! {
-                            #ty::#variant_ident(#bind_fold_fields) => {
-                                #ty::#variant_ident(
-                                    #fold_fields
-                                )
-                            }
-                        });
                     }
                 }
 
@@ -484,75 +391,26 @@
                         #visit_mut_variants
                     }
                 });
-
-                fold_impl.append_all(quote! {
-                    match _i {
-                        #fold_variants
-                    }
-                });
             }
             types::Data::Struct(fields) => {
-                let mut fold_fields = TokenStream::new();
-
                 for (field, ty) in fields {
                     let id = Ident::new(&field, Span::call_site());
                     let ref_toks = Owned(quote!(_i.#id));
                     let visit_field = visit(&ty, &s.features, defs, Visit, &ref_toks)
-                        .unwrap_or_else(|| noop_visit(Visit, &ref_toks));
+                        .unwrap_or_else(|| noop_visit(&ref_toks));
                     visit_impl.append_all(quote! {
                         #visit_field;
                     });
                     let visit_mut_field = visit(&ty, &s.features, defs, VisitMut, &ref_toks)
-                        .unwrap_or_else(|| noop_visit(VisitMut, &ref_toks));
+                        .unwrap_or_else(|| noop_visit(&ref_toks));
                     visit_mut_impl.append_all(quote! {
                         #visit_mut_field;
                     });
-                    let fold = visit(&ty, &s.features, defs, Fold, &ref_toks)
-                        .unwrap_or_else(|| noop_visit(Fold, &ref_toks));
-
-                    fold_fields.append_all(quote! {
-                        #id: #fold,
-                    });
-                }
-
-                if !fields.is_empty() {
-                    fold_impl.append_all(quote! {
-                        #ty {
-                            #fold_fields
-                        }
-                    })
-                } else {
-                    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
-                    });
                 }
             }
-            types::Data::Private => {
-                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
-                });
-            }
+            types::Data::Private => {}
         }
 
-        let include_fold_impl = match &s.data {
-            types::Data::Private => super::TERMINAL_TYPES.contains(&s.ident.as_str()),
-            types::Data::Struct(_) | types::Data::Enum(_) => true,
-        };
-
         state.visit_trait.append_all(quote! {
             #features
             fn #visit_fn(&mut self, i: &'ast #ty) {
@@ -584,24 +442,6 @@
                 #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
-                }
-            });
-        }
     }
 }
 
@@ -644,62 +484,6 @@
         }
     };
 
-    let fold_trait = state.fold_trait;
-    let fold_impl = state.fold_impl;
-    file::write(
-        FOLD_SRC,
-        quote! {
-            // Unreachable code is generated sometimes without the full feature.
-            #![allow(unreachable_code)]
-
-            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
-
-            /// 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"))]
-            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
-        },
-    );
-
     let visit_trait = state.visit_trait;
     let visit_impl = state.visit_impl;
     file::write(
diff --git a/codegen/src/main.rs b/codegen/src/main.rs
index 01c3ec0..a83cbfc 100644
--- a/codegen/src/main.rs
+++ b/codegen/src/main.rs
@@ -13,6 +13,7 @@
 #![allow(clippy::needless_pass_by_value)]
 
 mod file;
+mod fold;
 mod gen;
 mod json;
 mod parse;
@@ -22,4 +23,5 @@
     let defs = parse::parse();
     json::generate(&defs);
     gen::generate(&defs);
+    fold::generate(&defs);
 }