blob: 431b33f207bb96be6370aebed1b9f5bccfdf73c5 [file] [log] [blame]
David Tolnay4b4c4b62018-01-06 13:48:05 -08001//! This crate automatically generates the definition of the `Visit`,
2//! `VisitMut`, and `Fold` traits in `syn` based on the `syn` source. It
Nika Layzell27726662017-10-24 23:16:35 -04003//! 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
David Tolnayea9ae892017-12-26 01:44:32 -050013#![cfg_attr(feature = "cargo-clippy", allow(redundant_closure))]
David Tolnay6af48992018-08-01 11:16:28 -070014#![recursion_limit = "128"]
David Tolnayea9ae892017-12-26 01:44:32 -050015
David Tolnayd67fb752017-12-27 13:50:29 -050016#[macro_use]
17extern crate failure;
Nika Layzell27726662017-10-24 23:16:35 -040018extern crate inflections;
David Tolnay2e0dba12017-12-27 01:54:40 -050019extern crate proc_macro2;
David Tolnayd67fb752017-12-27 13:50:29 -050020#[macro_use]
21extern crate quote;
David Tolnay5c4c0b52017-12-28 17:58:54 -050022#[macro_use]
David Tolnayd67fb752017-12-27 13:50:29 -050023extern crate syn;
David Tolnay8c81f622018-07-31 23:34:35 -070024extern crate rustfmt_nightly as rustfmt;
Nika Layzell27726662017-10-24 23:16:35 -040025
David Tolnayd67fb752017-12-27 13:50:29 -050026use failure::{err_msg, Error};
David Tolnaye303b7c2018-05-20 16:46:35 -070027use proc_macro2::{Span, TokenStream};
David Tolnay01ed0ce2018-05-20 20:18:14 -070028use quote::ToTokens;
29use syn::{Attribute, Data, DataStruct, DeriveInput, Ident, Item};
Nika Layzell27726662017-10-24 23:16:35 -040030
David Tolnay01ed0ce2018-05-20 20:18:14 -070031use std::collections::BTreeMap;
David Tolnayf0d63bf2017-12-26 12:29:47 -050032use std::fmt::{self, Debug};
Nika Layzell27726662017-10-24 23:16:35 -040033use std::fs::File;
David Tolnay6af48992018-08-01 11:16:28 -070034use std::io::{Read, Write};
Nika Layzell27726662017-10-24 23:16:35 -040035use std::path::Path;
Nika Layzell27726662017-10-24 23:16:35 -040036
37const SYN_CRATE_ROOT: &str = "../src/lib.rs";
38
39const FOLD_SRC: &str = "../src/gen/fold.rs";
40const VISIT_SRC: &str = "../src/gen/visit.rs";
41const VISIT_MUT_SRC: &str = "../src/gen/visit_mut.rs";
42
David Tolnayd67fb752017-12-27 13:50:29 -050043const IGNORED_MODS: &[&str] = &["fold", "visit", "visit_mut"];
Nika Layzell27726662017-10-24 23:16:35 -040044
Alex Crichton131308c2018-05-18 14:00:24 -070045const EXTRA_TYPES: &[&str] = &["Lifetime"];
David Tolnay4ba63a02017-12-28 15:53:05 -050046
Alex Crichtond261d092018-05-18 13:47:35 -070047const TERMINAL_TYPES: &[&str] = &["Span", "Ident"];
Nika Layzellefb83ba2017-12-19 18:23:55 -050048
Alex Crichtona74a1c82018-05-16 10:20:44 -070049fn path_eq(a: &syn::Path, b: &str) -> bool {
50 if a.global() {
David Tolnay01ed0ce2018-05-20 20:18:14 -070051 return false;
Nika Layzell27726662017-10-24 23:16:35 -040052 }
Alex Crichtona74a1c82018-05-16 10:20:44 -070053 if a.segments.len() != 1 {
David Tolnay01ed0ce2018-05-20 20:18:14 -070054 return false;
Alex Crichtona74a1c82018-05-16 10:20:44 -070055 }
David Tolnay446f7d62018-05-20 17:58:15 -070056 a.segments[0].ident == b
Nika Layzell27726662017-10-24 23:16:35 -040057}
58
Alex Crichton715862b2018-05-17 12:31:49 -070059fn get_features(attrs: &[Attribute], mut features: TokenStream) -> TokenStream {
Nika Layzell27726662017-10-24 23:16:35 -040060 for attr in attrs {
Alex Crichtona74a1c82018-05-16 10:20:44 -070061 if path_eq(&attr.path, "cfg") {
Nika Layzell27726662017-10-24 23:16:35 -040062 attr.to_tokens(&mut features);
63 }
64 }
65 features
66}
67
68#[derive(Clone)]
69pub struct AstItem {
David Tolnay3d772182017-12-28 17:18:53 -050070 ast: DeriveInput,
Alex Crichton715862b2018-05-17 12:31:49 -070071 features: TokenStream,
Nika Layzell27726662017-10-24 23:16:35 -040072 // True if this is an ast_enum_of_structs! item with a #full annotation.
73 eos_full: bool,
74}
75
David Tolnayf0d63bf2017-12-26 12:29:47 -050076impl Debug for AstItem {
Nika Layzell27726662017-10-24 23:16:35 -040077 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
78 f.debug_struct("AstItem")
David Tolnay3d772182017-12-28 17:18:53 -050079 .field("ast", &self.ast)
Nika Layzell27726662017-10-24 23:16:35 -040080 .field("features", &self.features.to_string())
81 .finish()
82 }
83}
84
85// NOTE: BTreeMap is used here instead of HashMap to have deterministic output.
David Tolnayc47f8422017-12-28 17:30:46 -050086type Lookup = BTreeMap<Ident, AstItem>;
Nika Layzell27726662017-10-24 23:16:35 -040087
David Tolnay01ed0ce2018-05-20 20:18:14 -070088fn load_file<P: AsRef<Path>>(
89 name: P,
90 features: &TokenStream,
91 lookup: &mut Lookup,
92) -> Result<(), Error> {
Nika Layzell27726662017-10-24 23:16:35 -040093 let name = name.as_ref();
David Tolnayea9ae892017-12-26 01:44:32 -050094 let parent = name.parent().ok_or_else(|| err_msg("no parent path"))?;
Nika Layzell27726662017-10-24 23:16:35 -040095
96 let mut f = File::open(name)?;
97 let mut src = String::new();
98 f.read_to_string(&mut src)?;
99
100 // Parse the file
David Tolnayd67fb752017-12-27 13:50:29 -0500101 let file =
102 syn::parse_file(&src).map_err(|_| format_err!("failed to parse {}", name.display()))?;
Nika Layzell27726662017-10-24 23:16:35 -0400103
104 // Collect all of the interesting AstItems declared in this file or submodules.
David Tolnay4ba63a02017-12-28 15:53:05 -0500105 'items: for item in file.items {
106 match item {
107 Item::Mod(item) => {
Nika Layzell27726662017-10-24 23:16:35 -0400108 // Don't inspect inline modules.
David Tolnayc6b55bc2017-11-09 22:48:38 -0800109 if item.content.is_some() {
Nika Layzell27726662017-10-24 23:16:35 -0400110 continue;
111 }
112
113 // We don't want to try to load the generated rust files and
114 // parse them, so we ignore them here.
115 for name in IGNORED_MODS {
David Tolnay446f7d62018-05-20 17:58:15 -0700116 if item.ident == name {
Nika Layzell27726662017-10-24 23:16:35 -0400117 continue 'items;
118 }
119 }
120
121 // Lookup any #[cfg()] attributes on the module and add them to
122 // the feature set.
David Tolnay0a0d78c2018-01-05 15:24:01 -0800123 //
124 // The derive module is weird because it is built with either
125 // `full` or `derive` but exported only under `derive`.
David Tolnay446f7d62018-05-20 17:58:15 -0700126 let features = if item.ident == "derive" {
David Tolnay0a0d78c2018-01-05 15:24:01 -0800127 quote!(#[cfg(feature = "derive")])
128 } else {
129 get_features(&item.attrs, features.clone())
130 };
Nika Layzell27726662017-10-24 23:16:35 -0400131
132 // Look up the submodule file, and recursively parse it.
133 // XXX: Only handles same-directory .rs file submodules.
Alex Crichtona74a1c82018-05-16 10:20:44 -0700134 let path = parent.join(&format!("{}.rs", item.ident));
David Tolnayea9ae892017-12-26 01:44:32 -0500135 load_file(path, &features, lookup)?;
Nika Layzell27726662017-10-24 23:16:35 -0400136 }
David Tolnay4ba63a02017-12-28 15:53:05 -0500137 Item::Macro(item) => {
Nika Layzell27726662017-10-24 23:16:35 -0400138 // Lookip any #[cfg()] attributes directly on the macro
139 // invocation, and add them to the feature set.
140 let features = get_features(&item.attrs, features.clone());
141
142 // Try to parse the AstItem declaration out of the item.
David Tolnay01a77582018-01-01 20:00:51 -0800143 let tts = &item.mac.tts;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700144 let found = if path_eq(&item.mac.path, "ast_struct") {
David Tolnay01a77582018-01-01 20:00:51 -0800145 syn::parse_str::<parsing::AstStruct>(&quote!(#tts).to_string())
David Tolnayd67fb752017-12-27 13:50:29 -0500146 .map_err(|_| err_msg("failed to parse ast_struct"))?
147 .0
Alex Crichtona74a1c82018-05-16 10:20:44 -0700148 } else if path_eq(&item.mac.path, "ast_enum") {
David Tolnay01a77582018-01-01 20:00:51 -0800149 syn::parse_str::<parsing::AstEnum>(&quote!(#tts).to_string())
David Tolnayd67fb752017-12-27 13:50:29 -0500150 .map_err(|_| err_msg("failed to parse ast_enum"))?
151 .0
Alex Crichtona74a1c82018-05-16 10:20:44 -0700152 } else if path_eq(&item.mac.path, "ast_enum_of_structs") {
David Tolnay01a77582018-01-01 20:00:51 -0800153 syn::parse_str::<parsing::AstEnumOfStructs>(&quote!(#tts).to_string())
David Tolnay1cf80912017-12-31 18:35:12 -0500154 .map_err(|_| err_msg("failed to parse ast_enum_of_structs"))?
David Tolnayd67fb752017-12-27 13:50:29 -0500155 .0
Nika Layzell27726662017-10-24 23:16:35 -0400156 } else {
David Tolnayd67fb752017-12-27 13:50:29 -0500157 continue;
Nika Layzell27726662017-10-24 23:16:35 -0400158 };
159
160 // Record our features on the parsed AstItems.
161 for mut item in found {
162 features.to_tokens(&mut item.features);
Alex Crichtona74a1c82018-05-16 10:20:44 -0700163 lookup.insert(item.ast.ident.clone(), item);
Nika Layzell27726662017-10-24 23:16:35 -0400164 }
165 }
David Tolnay4ba63a02017-12-28 15:53:05 -0500166 Item::Struct(item) => {
167 let ident = item.ident;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700168 if EXTRA_TYPES.contains(&&ident.to_string()[..]) {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700169 lookup.insert(
170 ident.clone(),
171 AstItem {
172 ast: DeriveInput {
173 ident: ident,
174 vis: item.vis,
175 attrs: item.attrs,
176 generics: item.generics,
177 data: Data::Struct(DataStruct {
178 fields: item.fields,
179 struct_token: item.struct_token,
180 semi_token: item.semi_token,
181 }),
182 },
183 features: features.clone(),
184 eos_full: false,
David Tolnay4ba63a02017-12-28 15:53:05 -0500185 },
David Tolnay01ed0ce2018-05-20 20:18:14 -0700186 );
David Tolnay4ba63a02017-12-28 15:53:05 -0500187 }
188 }
Nika Layzell27726662017-10-24 23:16:35 -0400189 _ => {}
190 }
191 }
192 Ok(())
193}
194
195mod parsing {
196 use super::AstItem;
197
David Tolnay01ed0ce2018-05-20 20:18:14 -0700198 use proc_macro2::TokenStream;
David Tolnay1cf80912017-12-31 18:35:12 -0500199 use syn;
David Tolnayc5ab8c62017-12-26 16:43:39 -0500200 use syn::synom::*;
Nika Layzell27726662017-10-24 23:16:35 -0400201 use syn::*;
Nika Layzell27726662017-10-24 23:16:35 -0400202
203 // Parses #full - returns #[cfg(feature = "full")] if it is present, and
204 // nothing otherwise.
Alex Crichton715862b2018-05-17 12:31:49 -0700205 named!(full -> (TokenStream, bool), map!(option!(do_parse!(
David Tolnayf8db7ba2017-11-11 22:52:16 -0800206 punct!(#) >>
Nika Layzell27726662017-10-24 23:16:35 -0400207 id: syn!(Ident) >>
David Tolnay446f7d62018-05-20 17:58:15 -0700208 cond_reduce!(id == "full") >>
David Tolnayea9ae892017-12-26 01:44:32 -0500209 ()
Nika Layzell27726662017-10-24 23:16:35 -0400210 )), |s| if s.is_some() {
211 (quote!(#[cfg(feature = "full")]), true)
212 } else {
213 (quote!(), false)
214 }));
215
David Tolnay28c5a462017-12-27 01:59:30 -0500216 named!(manual_extra_traits -> (), do_parse!(
217 punct!(#) >>
218 id: syn!(Ident) >>
David Tolnay446f7d62018-05-20 17:58:15 -0700219 cond_reduce!(id == "manual_extra_traits") >>
David Tolnay28c5a462017-12-27 01:59:30 -0500220 ()
221 ));
222
Nika Layzell27726662017-10-24 23:16:35 -0400223 // Parses a simple AstStruct without the `pub struct` prefix.
224 named!(ast_struct_inner -> AstItem, do_parse!(
225 id: syn!(Ident) >>
226 features: full >>
David Tolnay28c5a462017-12-27 01:59:30 -0500227 option!(manual_extra_traits) >>
David Tolnay2e0dba12017-12-27 01:54:40 -0500228 rest: syn!(TokenStream) >>
Nika Layzell27726662017-10-24 23:16:35 -0400229 (AstItem {
David Tolnay01a77582018-01-01 20:00:51 -0800230 ast: syn::parse_str(&quote! {
David Tolnay2e0dba12017-12-27 01:54:40 -0500231 pub struct #id #rest
David Tolnay01a77582018-01-01 20:00:51 -0800232 }.to_string())?,
Nika Layzell27726662017-10-24 23:16:35 -0400233 features: features.0,
234 eos_full: features.1,
235 })
236 ));
237
238 // ast_struct! parsing
239 pub struct AstStruct(pub Vec<AstItem>);
240 impl Synom for AstStruct {
David Tolnayab919512017-12-30 23:31:51 -0500241 named!(parse -> Self, do_parse!(
David Tolnay2c136452017-12-27 14:13:32 -0500242 many0!(Attribute::parse_outer) >>
David Tolnayf8db7ba2017-11-11 22:52:16 -0800243 keyword!(pub) >>
244 keyword!(struct) >>
Nika Layzell27726662017-10-24 23:16:35 -0400245 res: call!(ast_struct_inner) >>
David Tolnayab919512017-12-30 23:31:51 -0500246 (AstStruct(vec![res]))
247 ));
Nika Layzell27726662017-10-24 23:16:35 -0400248 }
249
David Tolnay360efd22018-01-04 23:35:26 -0800250 named!(no_visit -> (), do_parse!(
251 punct!(#) >>
252 id: syn!(Ident) >>
David Tolnay446f7d62018-05-20 17:58:15 -0700253 cond_reduce!(id == "no_visit") >>
David Tolnay360efd22018-01-04 23:35:26 -0800254 ()
255 ));
256
Nika Layzell27726662017-10-24 23:16:35 -0400257 // ast_enum! parsing
258 pub struct AstEnum(pub Vec<AstItem>);
259 impl Synom for AstEnum {
David Tolnay360efd22018-01-04 23:35:26 -0800260 named!(parse -> Self, do_parse!(
261 many0!(Attribute::parse_outer) >>
262 keyword!(pub) >>
263 keyword!(enum) >>
264 id: syn!(Ident) >>
265 no_visit: option!(no_visit) >>
266 rest: syn!(TokenStream) >>
267 (AstEnum(if no_visit.is_some() {
268 vec![]
269 } else {
270 vec![AstItem {
271 ast: syn::parse_str(&quote! {
272 pub enum #id #rest
273 }.to_string())?,
274 features: quote!(),
275 eos_full: false,
276 }]
277 }))
278 ));
Nika Layzell27726662017-10-24 23:16:35 -0400279 }
280
281 // A single variant of an ast_enum_of_structs!
282 struct EosVariant {
283 name: Ident,
David Tolnayfcfb9002017-12-28 22:04:29 -0500284 member: Option<Path>,
Nika Layzell27726662017-10-24 23:16:35 -0400285 inner: Option<AstItem>,
286 }
287 named!(eos_variant -> EosVariant, do_parse!(
David Tolnay2c136452017-12-27 14:13:32 -0500288 many0!(Attribute::parse_outer) >>
David Tolnayf8db7ba2017-11-11 22:52:16 -0800289 keyword!(pub) >>
Nika Layzell27726662017-10-24 23:16:35 -0400290 variant: syn!(Ident) >>
David Tolnayfcfb9002017-12-28 22:04:29 -0500291 member: option!(map!(parens!(alt!(
Alex Crichtona74a1c82018-05-16 10:20:44 -0700292 call!(ast_struct_inner) => { |x: AstItem| (Path::from(x.ast.ident.clone()), Some(x)) }
Nika Layzell27726662017-10-24 23:16:35 -0400293 |
294 syn!(Path) => { |x| (x, None) }
David Tolnay8875fca2017-12-31 13:52:37 -0500295 )), |x| x.1)) >>
David Tolnayf8db7ba2017-11-11 22:52:16 -0800296 punct!(,) >>
Nika Layzell27726662017-10-24 23:16:35 -0400297 (EosVariant {
298 name: variant,
David Tolnayfcfb9002017-12-28 22:04:29 -0500299 member: member.clone().map(|x| x.0),
300 inner: member.map(|x| x.1).unwrap_or_default(),
Nika Layzell27726662017-10-24 23:16:35 -0400301 })
302 ));
303
304 // ast_enum_of_structs! parsing
305 pub struct AstEnumOfStructs(pub Vec<AstItem>);
306 impl Synom for AstEnumOfStructs {
David Tolnayab919512017-12-30 23:31:51 -0500307 named!(parse -> Self, do_parse!(
David Tolnay2c136452017-12-27 14:13:32 -0500308 many0!(Attribute::parse_outer) >>
David Tolnayf8db7ba2017-11-11 22:52:16 -0800309 keyword!(pub) >>
310 keyword!(enum) >>
Nika Layzell27726662017-10-24 23:16:35 -0400311 id: syn!(Ident) >>
David Tolnaye3d41b72017-12-31 15:24:00 -0500312 variants: braces!(many0!(eos_variant)) >>
Nika Layzell27726662017-10-24 23:16:35 -0400313 option!(syn!(Ident)) >> // do_not_generate_to_tokens
314 ({
315 // XXX: This is really gross - we shouldn't have to convert the
316 // tokens to strings to re-parse them.
317 let enum_item = {
David Tolnaye3d41b72017-12-31 15:24:00 -0500318 let variants = variants.1.iter().map(|v| {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700319 let name = v.name.clone();
David Tolnayfcfb9002017-12-28 22:04:29 -0500320 match v.member {
321 Some(ref member) => quote!(#name(#member)),
322 None => quote!(#name),
323 }
Nika Layzell27726662017-10-24 23:16:35 -0400324 });
David Tolnay01a77582018-01-01 20:00:51 -0800325 syn::parse_str(&quote! {
Nika Layzell27726662017-10-24 23:16:35 -0400326 pub enum #id { #(#variants),* }
David Tolnay01a77582018-01-01 20:00:51 -0800327 }.to_string())?
Nika Layzell27726662017-10-24 23:16:35 -0400328 };
329 let mut items = vec![AstItem {
David Tolnay3d772182017-12-28 17:18:53 -0500330 ast: enum_item,
Nika Layzell27726662017-10-24 23:16:35 -0400331 features: quote!(),
332 eos_full: false,
333 }];
David Tolnaye3d41b72017-12-31 15:24:00 -0500334 items.extend(variants.1.into_iter().filter_map(|v| v.inner));
Nika Layzell27726662017-10-24 23:16:35 -0400335 AstEnumOfStructs(items)
336 })
David Tolnayab919512017-12-30 23:31:51 -0500337 ));
Nika Layzell27726662017-10-24 23:16:35 -0400338 }
339}
340
341mod codegen {
342 use super::{AstItem, Lookup};
David Tolnay01ed0ce2018-05-20 20:18:14 -0700343 use proc_macro2::{Span, TokenStream};
David Tolnay6af48992018-08-01 11:16:28 -0700344 use quote::{ToTokens, TokenStreamExt};
David Tolnay01ed0ce2018-05-20 20:18:14 -0700345 use syn::punctuated::Punctuated;
346 use syn::*;
Nika Layzell27726662017-10-24 23:16:35 -0400347
348 #[derive(Default)]
349 pub struct State {
David Tolnay6af48992018-08-01 11:16:28 -0700350 pub visit_trait: TokenStream,
351 pub visit_impl: TokenStream,
352 pub visit_mut_trait: TokenStream,
353 pub visit_mut_impl: TokenStream,
354 pub fold_trait: TokenStream,
355 pub fold_impl: TokenStream,
Nika Layzell27726662017-10-24 23:16:35 -0400356 }
357
David Tolnay4a918742017-12-28 16:54:41 -0500358 fn under_name(name: Ident) -> Ident {
Nika Layzell27726662017-10-24 23:16:35 -0400359 use inflections::Inflect;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700360 Ident::new(&name.to_string().to_snake_case(), Span::call_site())
Nika Layzell27726662017-10-24 23:16:35 -0400361 }
362
David Tolnay3d772182017-12-28 17:18:53 -0500363 enum RelevantType<'a> {
364 Box(&'a Type),
365 Vec(&'a Type),
David Tolnayf2cfd722017-12-31 18:02:51 -0500366 Punctuated(&'a Type),
David Tolnay3d772182017-12-28 17:18:53 -0500367 Option(&'a Type),
David Tolnayf2cfd722017-12-31 18:02:51 -0500368 Tuple(&'a Punctuated<Type, Token![,]>),
David Tolnay3d772182017-12-28 17:18:53 -0500369 Simple(&'a AstItem),
Alex Crichton715862b2018-05-17 12:31:49 -0700370 Token(TokenStream),
David Tolnay3d772182017-12-28 17:18:53 -0500371 Pass,
372 }
Nika Layzell27726662017-10-24 23:16:35 -0400373
David Tolnay3d772182017-12-28 17:18:53 -0500374 fn classify<'a>(ty: &'a Type, lookup: &'a Lookup) -> RelevantType<'a> {
375 match *ty {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700376 Type::Path(TypePath {
377 qself: None,
378 ref path,
379 }) => {
David Tolnay56080682018-01-06 14:01:52 -0800380 let last = path.segments.last().unwrap().into_value();
Alex Crichtona74a1c82018-05-16 10:20:44 -0700381 match &last.ident.to_string()[..] {
David Tolnay3d772182017-12-28 17:18:53 -0500382 "Box" => RelevantType::Box(first_arg(&last.arguments)),
383 "Vec" => RelevantType::Vec(first_arg(&last.arguments)),
David Tolnayf2cfd722017-12-31 18:02:51 -0500384 "Punctuated" => RelevantType::Punctuated(first_arg(&last.arguments)),
David Tolnay3d772182017-12-28 17:18:53 -0500385 "Option" => RelevantType::Option(first_arg(&last.arguments)),
David Tolnay1e01f9c2017-12-28 20:16:19 -0500386 "Brace" | "Bracket" | "Paren" | "Group" => {
Alex Crichton715862b2018-05-17 12:31:49 -0700387 RelevantType::Token(last.ident.clone().into_token_stream())
David Tolnay1e01f9c2017-12-28 20:16:19 -0500388 }
David Tolnay3d772182017-12-28 17:18:53 -0500389 _ => {
390 if let Some(item) = lookup.get(&last.ident) {
391 RelevantType::Simple(item)
392 } else {
393 RelevantType::Pass
394 }
395 }
396 }
Nika Layzell27726662017-10-24 23:16:35 -0400397 }
David Tolnay01ed0ce2018-05-20 20:18:14 -0700398 Type::Tuple(TypeTuple { ref elems, .. }) => RelevantType::Tuple(elems),
399 Type::Macro(TypeMacro { ref mac })
400 if mac.path.segments.last().unwrap().into_value().ident == "Token" =>
401 {
Alex Crichton715862b2018-05-17 12:31:49 -0700402 RelevantType::Token(mac.into_token_stream())
David Tolnaycc0f0372017-12-28 19:11:04 -0500403 }
David Tolnay3d772182017-12-28 17:18:53 -0500404 _ => RelevantType::Pass,
Nika Layzell27726662017-10-24 23:16:35 -0400405 }
406 }
407
408 #[derive(Debug, Eq, PartialEq, Copy, Clone)]
409 enum Kind {
410 Visit,
411 VisitMut,
412 Fold,
413 }
414
David Tolnayf0d63bf2017-12-26 12:29:47 -0500415 enum Operand {
Alex Crichton715862b2018-05-17 12:31:49 -0700416 Borrowed(TokenStream),
417 Owned(TokenStream),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500418 }
419
David Tolnay83db9272017-12-28 17:02:31 -0500420 use self::Kind::*;
David Tolnay01ed0ce2018-05-20 20:18:14 -0700421 use self::Operand::*;
David Tolnay83db9272017-12-28 17:02:31 -0500422
David Tolnayf0d63bf2017-12-26 12:29:47 -0500423 impl Operand {
Alex Crichton715862b2018-05-17 12:31:49 -0700424 fn tokens(&self) -> &TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500425 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500426 Borrowed(ref n) | Owned(ref n) => n,
David Tolnayf0d63bf2017-12-26 12:29:47 -0500427 }
428 }
429
Alex Crichton715862b2018-05-17 12:31:49 -0700430 fn ref_tokens(&self) -> TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500431 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500432 Borrowed(ref n) => n.clone(),
433 Owned(ref n) => quote!(&#n),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500434 }
435 }
436
Alex Crichton715862b2018-05-17 12:31:49 -0700437 fn ref_mut_tokens(&self) -> TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500438 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500439 Borrowed(ref n) => n.clone(),
440 Owned(ref n) => quote!(&mut #n),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500441 }
442 }
443
Alex Crichton715862b2018-05-17 12:31:49 -0700444 fn owned_tokens(&self) -> TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500445 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500446 Borrowed(ref n) => quote!(*#n),
447 Owned(ref n) => n.clone(),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500448 }
449 }
450 }
451
Nika Layzellc08227a2017-12-04 16:30:17 -0500452 fn first_arg(params: &PathArguments) -> &Type {
Nika Layzell27726662017-10-24 23:16:35 -0400453 let data = match *params {
Nika Layzellc08227a2017-12-04 16:30:17 -0500454 PathArguments::AngleBracketed(ref data) => data,
455 _ => panic!("Expected at least 1 type argument here"),
Nika Layzell27726662017-10-24 23:16:35 -0400456 };
457
David Tolnay01ed0ce2018-05-20 20:18:14 -0700458 match **data
459 .args
David Tolnayd67fb752017-12-27 13:50:29 -0500460 .first()
461 .expect("Expected at least 1 type argument here")
David Tolnay56080682018-01-06 14:01:52 -0800462 .value()
David Tolnayd67fb752017-12-27 13:50:29 -0500463 {
David Tolnayea9ae892017-12-26 01:44:32 -0500464 GenericArgument::Type(ref ty) => ty,
Nika Layzellc08227a2017-12-04 16:30:17 -0500465 _ => panic!("Expected at least 1 type argument here"),
Nika Layzell357885a2017-12-04 15:47:07 -0500466 }
Nika Layzell27726662017-10-24 23:16:35 -0400467 }
468
David Tolnay6af48992018-08-01 11:16:28 -0700469 fn simple_visit(item: &AstItem, kind: Kind, name: &Operand) -> TokenStream {
470 let ident = under_name(item.ast.ident.clone());
471
David Tolnay4a918742017-12-28 16:54:41 -0500472 match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700473 Visit => {
474 let method = Ident::new(&format!("visit_{}", ident), Span::call_site());
475 let name = name.ref_tokens();
476 quote! {
477 _visitor.#method(#name)
478 }
479 }
480 VisitMut => {
481 let method = Ident::new(&format!("visit_{}_mut", ident), Span::call_site());
482 let name = name.ref_mut_tokens();
483 quote! {
484 _visitor.#method(#name)
485 }
486 }
487 Fold => {
488 let method = Ident::new(&format!("fold_{}", ident), Span::call_site());
489 let name = name.owned_tokens();
490 quote! {
491 _visitor.#method(#name)
492 }
493 }
Nika Layzell27726662017-10-24 23:16:35 -0400494 }
495 }
496
David Tolnay6af48992018-08-01 11:16:28 -0700497 fn box_visit(elem: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<TokenStream> {
David Tolnay4a918742017-12-28 16:54:41 -0500498 let name = name.owned_tokens();
David Tolnay39d0a202017-12-28 18:19:00 -0500499 let res = visit(elem, lookup, kind, &Owned(quote!(*#name)))?;
David Tolnay4a918742017-12-28 16:54:41 -0500500 Some(match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700501 Fold => quote! {
502 Box::new(#res)
503 },
David Tolnay83db9272017-12-28 17:02:31 -0500504 Visit | VisitMut => res,
David Tolnay4a918742017-12-28 16:54:41 -0500505 })
Nika Layzell27726662017-10-24 23:16:35 -0400506 }
507
David Tolnay6af48992018-08-01 11:16:28 -0700508 fn vec_visit(elem: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<TokenStream> {
David Tolnay4a918742017-12-28 16:54:41 -0500509 let operand = match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500510 Visit | VisitMut => Borrowed(quote!(it)),
511 Fold => Owned(quote!(it)),
David Tolnay4a918742017-12-28 16:54:41 -0500512 };
David Tolnay39d0a202017-12-28 18:19:00 -0500513 let val = visit(elem, lookup, kind, &operand)?;
David Tolnay4a918742017-12-28 16:54:41 -0500514 Some(match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700515 Visit => {
516 let name = name.ref_tokens();
517 quote! {
518 for it in #name {
519 #val
520 }
521 }
522 }
523 VisitMut => {
524 let name = name.ref_mut_tokens();
525 quote! {
526 for it in #name {
527 #val
528 }
529 }
530 }
531 Fold => {
532 let name = name.owned_tokens();
533 quote! {
534 FoldHelper::lift(#name, |it| { #val })
535 }
536 }
David Tolnay3d772182017-12-28 17:18:53 -0500537 })
538 }
539
David Tolnay6eff4da2018-01-01 20:27:45 -0800540 fn punctuated_visit(
David Tolnay3d772182017-12-28 17:18:53 -0500541 elem: &Type,
542 lookup: &Lookup,
543 kind: Kind,
544 name: &Operand,
David Tolnay6af48992018-08-01 11:16:28 -0700545 ) -> Option<TokenStream> {
David Tolnay3d772182017-12-28 17:18:53 -0500546 let operand = match kind {
547 Visit | VisitMut => Borrowed(quote!(it)),
548 Fold => Owned(quote!(it)),
549 };
David Tolnay39d0a202017-12-28 18:19:00 -0500550 let val = visit(elem, lookup, kind, &operand)?;
David Tolnay3d772182017-12-28 17:18:53 -0500551 Some(match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700552 Visit => {
553 let name = name.ref_tokens();
554 quote! {
555 for el in Punctuated::pairs(#name) {
556 let it = el.value();
557 #val
558 }
559 }
560 }
561 VisitMut => {
562 let name = name.ref_mut_tokens();
563 quote! {
564 for mut el in Punctuated::pairs_mut(#name) {
565 let it = el.value_mut();
566 #val
567 }
568 }
569 }
570 Fold => {
571 let name = name.owned_tokens();
572 quote! {
573 FoldHelper::lift(#name, |it| { #val })
574 }
575 }
David Tolnay4a918742017-12-28 16:54:41 -0500576 })
Nika Layzell27726662017-10-24 23:16:35 -0400577 }
578
David Tolnay6af48992018-08-01 11:16:28 -0700579 fn option_visit(
580 elem: &Type,
581 lookup: &Lookup,
582 kind: Kind,
583 name: &Operand,
584 ) -> Option<TokenStream> {
David Tolnay4a918742017-12-28 16:54:41 -0500585 let it = match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500586 Visit | VisitMut => Borrowed(quote!(it)),
587 Fold => Owned(quote!(it)),
David Tolnay4a918742017-12-28 16:54:41 -0500588 };
David Tolnay39d0a202017-12-28 18:19:00 -0500589 let val = visit(elem, lookup, kind, &it)?;
David Tolnay6af48992018-08-01 11:16:28 -0700590 let name = name.owned_tokens();
David Tolnay4a918742017-12-28 16:54:41 -0500591 Some(match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700592 Visit => quote! {
593 if let Some(ref it) = #name {
594 #val
595 }
596 },
597 VisitMut => quote! {
598 if let Some(ref mut it) = #name {
599 #val
600 }
601 },
602 Fold => quote! {
603 (#name).map(|it| { #val })
604 },
David Tolnay4a918742017-12-28 16:54:41 -0500605 })
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400606 }
607
David Tolnay5c4c0b52017-12-28 17:58:54 -0500608 fn tuple_visit(
David Tolnayf2cfd722017-12-31 18:02:51 -0500609 elems: &Punctuated<Type, Token![,]>,
David Tolnay5c4c0b52017-12-28 17:58:54 -0500610 lookup: &Lookup,
611 kind: Kind,
612 name: &Operand,
David Tolnay6af48992018-08-01 11:16:28 -0700613 ) -> Option<TokenStream> {
614 if elems.is_empty() {
615 return None;
616 }
617
618 let mut code = TokenStream::new();
David Tolnayf047c7a2017-12-31 02:29:04 -0500619 for (i, elem) in elems.iter().enumerate() {
David Tolnay5c4c0b52017-12-28 17:58:54 -0500620 let name = name.tokens();
David Tolnay14982012017-12-29 00:49:51 -0500621 let i = Index::from(i);
David Tolnay5c4c0b52017-12-28 17:58:54 -0500622 let it = Owned(quote!((#name).#i));
David Tolnay01ed0ce2018-05-20 20:18:14 -0700623 let val = visit(elem, lookup, kind, &it).unwrap_or_else(|| noop_visit(kind, &it));
David Tolnay6af48992018-08-01 11:16:28 -0700624 code.append_all(val);
David Tolnay5c4c0b52017-12-28 17:58:54 -0500625 match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700626 Fold => code.append_all(quote!(,)),
627 Visit | VisitMut => code.append_all(quote!(;)),
David Tolnay5c4c0b52017-12-28 17:58:54 -0500628 }
David Tolnay5c4c0b52017-12-28 17:58:54 -0500629 }
David Tolnay6af48992018-08-01 11:16:28 -0700630 Some(match kind {
631 Fold => quote! {
632 (#code)
633 },
634 Visit | VisitMut => code,
635 })
David Tolnay5c4c0b52017-12-28 17:58:54 -0500636 }
637
David Tolnay6af48992018-08-01 11:16:28 -0700638 fn token_visit(ty: TokenStream, kind: Kind, name: &Operand) -> TokenStream {
639 let name = name.tokens();
David Tolnaycc0f0372017-12-28 19:11:04 -0500640 match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700641 Fold => quote! {
642 #ty(tokens_helper(_visitor, &(#name).0))
643 },
644 Visit => quote! {
645 tokens_helper(_visitor, &(#name).0)
646 },
647 VisitMut => quote! {
648 tokens_helper(_visitor, &mut (#name).0)
649 },
David Tolnaycc0f0372017-12-28 19:11:04 -0500650 }
651 }
652
David Tolnay6af48992018-08-01 11:16:28 -0700653 fn noop_visit(kind: Kind, name: &Operand) -> TokenStream {
David Tolnay4a918742017-12-28 16:54:41 -0500654 match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700655 Fold => name.owned_tokens(),
656 Visit | VisitMut => {
657 let name = name.tokens();
658 quote! {
659 skip!(#name)
660 }
661 }
David Tolnay4a918742017-12-28 16:54:41 -0500662 }
663 }
664
David Tolnay6af48992018-08-01 11:16:28 -0700665 fn visit(ty: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<TokenStream> {
David Tolnay3d772182017-12-28 17:18:53 -0500666 match classify(ty, lookup) {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700667 RelevantType::Box(elem) => box_visit(elem, lookup, kind, name),
668 RelevantType::Vec(elem) => vec_visit(elem, lookup, kind, name),
669 RelevantType::Punctuated(elem) => punctuated_visit(elem, lookup, kind, name),
670 RelevantType::Option(elem) => option_visit(elem, lookup, kind, name),
671 RelevantType::Tuple(elems) => tuple_visit(elems, lookup, kind, name),
David Tolnay3d772182017-12-28 17:18:53 -0500672 RelevantType::Simple(item) => {
673 let mut res = simple_visit(item, kind, name);
674 Some(if item.eos_full {
David Tolnay6af48992018-08-01 11:16:28 -0700675 quote! {
676 full!(#res)
677 }
David Tolnay3d772182017-12-28 17:18:53 -0500678 } else {
679 res
680 })
681 }
David Tolnay01ed0ce2018-05-20 20:18:14 -0700682 RelevantType::Token(ty) => Some(token_visit(ty, kind, name)),
683 RelevantType::Pass => None,
Nika Layzell27726662017-10-24 23:16:35 -0400684 }
Nika Layzell27726662017-10-24 23:16:35 -0400685 }
686
687 pub fn generate(state: &mut State, lookup: &Lookup, s: &AstItem) {
David Tolnay6af48992018-08-01 11:16:28 -0700688 let features = &s.features;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700689 let under_name = under_name(s.ast.ident.clone());
David Tolnay6af48992018-08-01 11:16:28 -0700690 let ty = &s.ast.ident;
691 let visit_fn = Ident::new(&format!("visit_{}", under_name), Span::call_site());
692 let visit_mut_fn = Ident::new(&format!("visit_{}_mut", under_name), Span::call_site());
693 let fold_fn = Ident::new(&format!("fold_{}", under_name), Span::call_site());
Nika Layzell27726662017-10-24 23:16:35 -0400694
David Tolnay6af48992018-08-01 11:16:28 -0700695 let mut visit_impl = TokenStream::new();
696 let mut visit_mut_impl = TokenStream::new();
697 let mut fold_impl = TokenStream::new();
Nika Layzell27726662017-10-24 23:16:35 -0400698
David Tolnaye3d41b72017-12-31 15:24:00 -0500699 match s.ast.data {
700 Data::Enum(ref e) => {
David Tolnay6af48992018-08-01 11:16:28 -0700701 let mut visit_variants = TokenStream::new();
702 let mut visit_mut_variants = TokenStream::new();
703 let mut fold_variants = TokenStream::new();
704
Nika Layzell27726662017-10-24 23:16:35 -0400705 for variant in &e.variants {
David Tolnay6af48992018-08-01 11:16:28 -0700706 let variant_ident = &variant.ident;
707
708 match variant.fields {
David Tolnaye3d41b72017-12-31 15:24:00 -0500709 Fields::Named(..) => panic!("Doesn't support enum struct variants"),
710 Fields::Unnamed(ref fields) => {
David Tolnay6af48992018-08-01 11:16:28 -0700711 let mut bind_visit_fields = TokenStream::new();
712 let mut bind_visit_mut_fields = TokenStream::new();
713 let mut bind_fold_fields = TokenStream::new();
Nika Layzell27726662017-10-24 23:16:35 -0400714
David Tolnay6af48992018-08-01 11:16:28 -0700715 let mut visit_fields = TokenStream::new();
716 let mut visit_mut_fields = TokenStream::new();
717 let mut fold_fields = TokenStream::new();
Nika Layzell27726662017-10-24 23:16:35 -0400718
David Tolnay6af48992018-08-01 11:16:28 -0700719 for (idx, field) in fields.unnamed.iter().enumerate() {
720 let name = format!("_binding_{}", idx);
721 let binding = Ident::new(&name, Span::call_site());
Nika Layzell27726662017-10-24 23:16:35 -0400722
David Tolnay6af48992018-08-01 11:16:28 -0700723 bind_visit_fields.append_all(quote! {
724 ref #binding,
725 });
726 bind_visit_mut_fields.append_all(quote! {
727 ref mut #binding,
728 });
729 bind_fold_fields.append_all(quote! {
730 #binding,
731 });
Nika Layzell27726662017-10-24 23:16:35 -0400732
David Tolnay6af48992018-08-01 11:16:28 -0700733 let borrowed_binding = Borrowed(quote!(#binding));
734 let owned_binding = Owned(quote!(#binding));
Nika Layzell27726662017-10-24 23:16:35 -0400735
David Tolnay6af48992018-08-01 11:16:28 -0700736 visit_fields.append_all(
737 visit(&field.ty, lookup, Visit, &borrowed_binding)
738 .unwrap_or_else(|| noop_visit(Visit, &borrowed_binding)),
739 );
740 visit_mut_fields.append_all(
741 visit(&field.ty, lookup, VisitMut, &borrowed_binding)
742 .unwrap_or_else(|| noop_visit(VisitMut, &borrowed_binding)),
743 );
744 fold_fields.append_all(
745 visit(&field.ty, lookup, Fold, &owned_binding)
746 .unwrap_or_else(|| noop_visit(Fold, &owned_binding)),
747 );
Nika Layzell27726662017-10-24 23:16:35 -0400748
David Tolnay6af48992018-08-01 11:16:28 -0700749 visit_fields.append_all(quote!(;));
750 visit_mut_fields.append_all(quote!(;));
751 fold_fields.append_all(quote!(,));
752 }
Nika Layzell27726662017-10-24 23:16:35 -0400753
David Tolnay6af48992018-08-01 11:16:28 -0700754 visit_variants.append_all(quote! {
755 #ty::#variant_ident(#bind_visit_fields) => {
756 #visit_fields
757 }
758 });
759
760 visit_mut_variants.append_all(quote! {
761 #ty::#variant_ident(#bind_visit_mut_fields) => {
762 #visit_mut_fields
763 }
764 });
765
766 fold_variants.append_all(quote! {
767 #ty::#variant_ident(#bind_fold_fields) => {
768 #ty::#variant_ident(
769 #fold_fields
770 )
771 }
772 });
Nika Layzell27726662017-10-24 23:16:35 -0400773 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500774 Fields::Unit => {
David Tolnay6af48992018-08-01 11:16:28 -0700775 visit_variants.append_all(quote! {
776 #ty::#variant_ident => {}
777 });
778 visit_mut_variants.append_all(quote! {
779 #ty::#variant_ident => {}
780 });
781 fold_variants.append_all(quote! {
782 #ty::#variant_ident => {
783 #ty::#variant_ident
784 }
785 });
Nika Layzell27726662017-10-24 23:16:35 -0400786 }
Nika Layzell27726662017-10-24 23:16:35 -0400787 }
Nika Layzell27726662017-10-24 23:16:35 -0400788 }
David Tolnay6af48992018-08-01 11:16:28 -0700789
790 visit_impl.append_all(quote! {
791 match *_i {
792 #visit_variants
793 }
794 });
795
796 visit_mut_impl.append_all(quote! {
797 match *_i {
798 #visit_mut_variants
799 }
800 });
801
802 fold_impl.append_all(quote! {
803 match _i {
804 #fold_variants
805 }
806 });
Nika Layzell27726662017-10-24 23:16:35 -0400807 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500808 Data::Struct(ref v) => {
Alex Crichton715862b2018-05-17 12:31:49 -0700809 let fields: Vec<(&Field, TokenStream)> = match v.fields {
David Tolnay6af48992018-08-01 11:16:28 -0700810 Fields::Named(ref fields) => fields
811 .named
812 .iter()
813 .map(|el| {
814 let id = el.ident.clone();
815 (el, quote!(_i.#id))
816 }).collect(),
817 Fields::Unnamed(ref fields) => fields
818 .unnamed
819 .iter()
820 .enumerate()
821 .map(|(idx, el)| {
822 let id = Index::from(idx);
823 (el, quote!(_i.#id))
824 }).collect(),
825 Fields::Unit => Vec::new(),
Nika Layzell27726662017-10-24 23:16:35 -0400826 };
827
David Tolnay6af48992018-08-01 11:16:28 -0700828 let mut fold_fields = TokenStream::new();
829
Nika Layzell27726662017-10-24 23:16:35 -0400830 for (field, ref_toks) in fields {
David Tolnay83db9272017-12-28 17:02:31 -0500831 let ref_toks = Owned(ref_toks);
David Tolnay6af48992018-08-01 11:16:28 -0700832 let visit_field = visit(&field.ty, lookup, Visit, &ref_toks)
833 .unwrap_or_else(|| noop_visit(Visit, &ref_toks));
834 visit_impl.append_all(quote! {
835 #visit_field;
836 });
837 let visit_mut_field = visit(&field.ty, lookup, VisitMut, &ref_toks)
838 .unwrap_or_else(|| noop_visit(VisitMut, &ref_toks));
839 visit_mut_impl.append_all(quote! {
840 #visit_mut_field;
841 });
David Tolnay83db9272017-12-28 17:02:31 -0500842 let fold = visit(&field.ty, lookup, Fold, &ref_toks)
David Tolnay01ed0ce2018-05-20 20:18:14 -0700843 .unwrap_or_else(|| noop_visit(Fold, &ref_toks));
Nika Layzell27726662017-10-24 23:16:35 -0400844 if let Some(ref name) = field.ident {
David Tolnay6af48992018-08-01 11:16:28 -0700845 fold_fields.append_all(quote! {
846 #name: #fold,
847 });
Nika Layzell27726662017-10-24 23:16:35 -0400848 } else {
David Tolnay6af48992018-08-01 11:16:28 -0700849 fold_fields.append_all(quote! {
850 #fold,
851 });
Nika Layzell27726662017-10-24 23:16:35 -0400852 }
853 }
854
David Tolnaye3d41b72017-12-31 15:24:00 -0500855 match v.fields {
David Tolnay6af48992018-08-01 11:16:28 -0700856 Fields::Named(..) | Fields::Unnamed(..) => fold_impl.append_all(quote! {
857 #ty {
858 #fold_fields
859 }
860 }),
861 Fields::Unit => {
862 if ty == "Ident" {
863 fold_impl.append_all(quote! {
864 let mut _i = _i;
865 let span = _visitor.fold_span(_i.span());
866 _i.set_span(span);
867 });
868 }
869 fold_impl.append_all(quote! {
870 _i
871 });
872 }
Nika Layzell27726662017-10-24 23:16:35 -0400873 };
874 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500875 Data::Union(..) => panic!("Union not supported"),
Nika Layzell27726662017-10-24 23:16:35 -0400876 }
877
David Tolnay6af48992018-08-01 11:16:28 -0700878 let mut include_fold_impl = true;
David Tolnay360efd22018-01-04 23:35:26 -0800879 if let Data::Struct(ref data) = s.ast.data {
880 if let Fields::Named(ref fields) = data.fields {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700881 if fields
882 .named
883 .iter()
884 .any(|field| field.vis == Visibility::Inherited)
885 {
David Tolnay360efd22018-01-04 23:35:26 -0800886 // Discard the generated impl if there are private fields.
887 // These have to be handwritten.
David Tolnay6af48992018-08-01 11:16:28 -0700888 include_fold_impl = false;
David Tolnay360efd22018-01-04 23:35:26 -0800889 }
890 }
David Tolnayd0adf522017-12-29 01:30:07 -0500891 }
David Tolnay6af48992018-08-01 11:16:28 -0700892
893 state.visit_trait.append_all(quote! {
894 #features
895 fn #visit_fn(&mut self, i: &'ast #ty) {
896 #visit_fn(self, i)
897 }
898 });
899
900 state.visit_impl.append_all(quote! {
901 #features
902 pub fn #visit_fn<'ast, V: Visit<'ast> + ?Sized>(
903 _visitor: &mut V, _i: &'ast #ty
904 ) {
905 #visit_impl
906 }
907 });
908
909 state.visit_mut_trait.append_all(quote! {
910 #features
911 fn #visit_mut_fn(&mut self, i: &mut #ty) {
912 #visit_mut_fn(self, i)
913 }
914 });
915
916 state.visit_mut_impl.append_all(quote! {
917 #features
918 pub fn #visit_mut_fn<V: VisitMut + ?Sized>(
919 _visitor: &mut V, _i: &mut #ty
920 ) {
921 #visit_mut_impl
922 }
923 });
924
925 state.fold_trait.append_all(quote! {
926 #features
927 fn #fold_fn(&mut self, i: #ty) -> #ty {
928 #fold_fn(self, i)
929 }
930 });
931
932 if include_fold_impl {
933 state.fold_impl.append_all(quote! {
934 #features
935 pub fn #fold_fn<V: Fold + ?Sized>(
936 _visitor: &mut V, _i: #ty
937 ) -> #ty {
938 #fold_impl
939 }
940 });
941 }
Nika Layzell27726662017-10-24 23:16:35 -0400942 }
943}
944
David Tolnay6af48992018-08-01 11:16:28 -0700945fn write_file(path: &str, content: TokenStream) {
David Tolnay8c81f622018-07-31 23:34:35 -0700946 let mut file = File::create(path).unwrap();
David Tolnay6af48992018-08-01 11:16:28 -0700947 write!(
948 file,
949 "// THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT\n\n"
950 ).unwrap();
David Tolnay8c81f622018-07-31 23:34:35 -0700951 let mut config = rustfmt::Config::default();
952 config.set().emit_mode(rustfmt::EmitMode::Stdout);
953 config.set().verbose(rustfmt::Verbosity::Quiet);
954 let mut session = rustfmt::Session::new(config, Some(&mut file));
David Tolnay6af48992018-08-01 11:16:28 -0700955 session
956 .format(rustfmt::Input::Text(content.to_string()))
957 .unwrap();
David Tolnay8c81f622018-07-31 23:34:35 -0700958}
959
Nika Layzell27726662017-10-24 23:16:35 -0400960fn main() {
961 let mut lookup = BTreeMap::new();
David Tolnayea9ae892017-12-26 01:44:32 -0500962 load_file(SYN_CRATE_ROOT, &quote!(), &mut lookup).unwrap();
Nika Layzell27726662017-10-24 23:16:35 -0400963
Nika Layzellefb83ba2017-12-19 18:23:55 -0500964 // Load in any terminal types
965 for &tt in TERMINAL_TYPES {
966 use syn::*;
David Tolnayd67fb752017-12-27 13:50:29 -0500967 lookup.insert(
Alex Crichtona74a1c82018-05-16 10:20:44 -0700968 Ident::new(&tt, Span::call_site()),
David Tolnayd67fb752017-12-27 13:50:29 -0500969 AstItem {
David Tolnay3d772182017-12-28 17:18:53 -0500970 ast: DeriveInput {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700971 ident: Ident::new(tt, Span::call_site()),
David Tolnayd67fb752017-12-27 13:50:29 -0500972 vis: Visibility::Public(VisPublic {
David Tolnay6af48992018-08-01 11:16:28 -0700973 pub_token: <Token![pub]>::default(),
David Tolnayd67fb752017-12-27 13:50:29 -0500974 }),
975 attrs: vec![],
David Tolnay6af48992018-08-01 11:16:28 -0700976 generics: Generics::default(),
David Tolnaye3d41b72017-12-31 15:24:00 -0500977 data: Data::Struct(DataStruct {
978 fields: Fields::Unit,
David Tolnay6af48992018-08-01 11:16:28 -0700979 struct_token: <Token![struct]>::default(),
David Tolnayd67fb752017-12-27 13:50:29 -0500980 semi_token: None,
981 }),
982 },
hcplaa511792018-05-29 07:13:01 +0300983 features: TokenStream::new(),
David Tolnayd67fb752017-12-27 13:50:29 -0500984 eos_full: false,
Nika Layzellefb83ba2017-12-19 18:23:55 -0500985 },
David Tolnayd67fb752017-12-27 13:50:29 -0500986 );
Nika Layzellefb83ba2017-12-19 18:23:55 -0500987 }
988
David Tolnay6af48992018-08-01 11:16:28 -0700989 let mut state = codegen::State::default();
Nika Layzell27726662017-10-24 23:16:35 -0400990 for s in lookup.values() {
991 codegen::generate(&mut state, &lookup, s);
992 }
993
David Tolnay6af48992018-08-01 11:16:28 -0700994 let full_macro = quote! {
995 #[cfg(feature = "full")]
996 macro_rules! full {
997 ($e:expr) => {
998 $e
999 };
1000 }
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001001
David Tolnay6af48992018-08-01 11:16:28 -07001002 #[cfg(all(feature = "derive", not(feature = "full")))]
1003 macro_rules! full {
1004 ($e:expr) => {
1005 unreachable!()
1006 };
1007 }
1008 };
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001009
David Tolnay6af48992018-08-01 11:16:28 -07001010 let skip_macro = quote! {
1011 #[cfg(any(feature = "full", feature = "derive"))]
1012 macro_rules! skip {
1013 ($($tt:tt)*) => {};
1014 }
1015 };
1016
1017 let fold_trait = state.fold_trait;
1018 let fold_impl = state.fold_impl;
David Tolnayae0009e2018-08-01 00:40:00 -07001019 write_file(
1020 FOLD_SRC,
David Tolnay6af48992018-08-01 11:16:28 -07001021 quote! {
1022 // Unreachable code is generated sometimes without the full feature.
1023 #![allow(unreachable_code)]
1024 #![cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))]
Nika Layzell27726662017-10-24 23:16:35 -04001025
David Tolnay6af48992018-08-01 11:16:28 -07001026 use *;
1027 #[cfg(any(feature = "full", feature = "derive"))]
1028 use token::{Brace, Bracket, Paren, Group};
1029 use proc_macro2::Span;
1030 #[cfg(any(feature = "full", feature = "derive"))]
1031 use gen::helper::fold::*;
Nika Layzell27726662017-10-24 23:16:35 -04001032
David Tolnay6af48992018-08-01 11:16:28 -07001033 #full_macro
Nika Layzell27726662017-10-24 23:16:35 -04001034
David Tolnay6af48992018-08-01 11:16:28 -07001035 /// Syntax tree traversal to transform the nodes of an owned syntax tree.
1036 ///
1037 /// See the [module documentation] for details.
1038 ///
1039 /// [module documentation]: index.html
1040 ///
1041 /// *This trait is available if Syn is built with the `"fold"` feature.*
1042 pub trait Fold {
1043 #fold_trait
1044 }
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001045
David Tolnay6af48992018-08-01 11:16:28 -07001046 #[cfg(any(feature = "full", feature = "derive"))]
1047 macro_rules! fold_span_only {
1048 ($f:ident : $t:ident) => {
1049 pub fn $f<V: Fold + ?Sized>(_visitor: &mut V, mut _i: $t) -> $t {
1050 let span = _visitor.fold_span(_i.span());
1051 _i.set_span(span);
1052 _i
1053 }
1054 }
1055 }
Nika Layzell27726662017-10-24 23:16:35 -04001056
David Tolnay6af48992018-08-01 11:16:28 -07001057 #[cfg(any(feature = "full", feature = "derive"))]
1058 fold_span_only!(fold_lit_byte: LitByte);
1059 #[cfg(any(feature = "full", feature = "derive"))]
1060 fold_span_only!(fold_lit_byte_str: LitByteStr);
1061 #[cfg(any(feature = "full", feature = "derive"))]
1062 fold_span_only!(fold_lit_char: LitChar);
1063 #[cfg(any(feature = "full", feature = "derive"))]
1064 fold_span_only!(fold_lit_float: LitFloat);
1065 #[cfg(any(feature = "full", feature = "derive"))]
1066 fold_span_only!(fold_lit_int: LitInt);
1067 #[cfg(any(feature = "full", feature = "derive"))]
1068 fold_span_only!(fold_lit_str: LitStr);
David Tolnayd0adf522017-12-29 01:30:07 -05001069
David Tolnay6af48992018-08-01 11:16:28 -07001070 #fold_impl
1071 },
David Tolnayae0009e2018-08-01 00:40:00 -07001072 );
Nika Layzell27726662017-10-24 23:16:35 -04001073
David Tolnay6af48992018-08-01 11:16:28 -07001074 let visit_trait = state.visit_trait;
1075 let visit_impl = state.visit_impl;
David Tolnayae0009e2018-08-01 00:40:00 -07001076 write_file(
1077 VISIT_SRC,
David Tolnay6af48992018-08-01 11:16:28 -07001078 quote! {
1079 #![cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
Nika Layzell27726662017-10-24 23:16:35 -04001080
David Tolnay6af48992018-08-01 11:16:28 -07001081 use *;
1082 #[cfg(any(feature = "full", feature = "derive"))]
1083 use punctuated::Punctuated;
1084 use proc_macro2::Span;
1085 #[cfg(any(feature = "full", feature = "derive"))]
1086 use gen::helper::visit::*;
David Tolnayf0d63bf2017-12-26 12:29:47 -05001087
David Tolnay6af48992018-08-01 11:16:28 -07001088 #full_macro
1089 #skip_macro
Nika Layzell27726662017-10-24 23:16:35 -04001090
David Tolnay6af48992018-08-01 11:16:28 -07001091 /// Syntax tree traversal to walk a shared borrow of a syntax tree.
1092 ///
1093 /// See the [module documentation] for details.
1094 ///
1095 /// [module documentation]: index.html
1096 ///
1097 /// *This trait is available if Syn is built with the `"visit"` feature.*
1098 pub trait Visit<'ast> {
1099 #visit_trait
1100 }
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001101
David Tolnay6af48992018-08-01 11:16:28 -07001102 #visit_impl
1103 },
David Tolnayae0009e2018-08-01 00:40:00 -07001104 );
Nika Layzell27726662017-10-24 23:16:35 -04001105
David Tolnay6af48992018-08-01 11:16:28 -07001106 let visit_mut_trait = state.visit_mut_trait;
1107 let visit_mut_impl = state.visit_mut_impl;
David Tolnayae0009e2018-08-01 00:40:00 -07001108 write_file(
1109 VISIT_MUT_SRC,
David Tolnay6af48992018-08-01 11:16:28 -07001110 quote! {
1111 #![cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
Nika Layzell27726662017-10-24 23:16:35 -04001112
David Tolnay6af48992018-08-01 11:16:28 -07001113 use *;
1114 #[cfg(any(feature = "full", feature = "derive"))]
1115 use punctuated::Punctuated;
1116 use proc_macro2::Span;
1117 #[cfg(any(feature = "full", feature = "derive"))]
1118 use gen::helper::visit_mut::*;
David Tolnayf0d63bf2017-12-26 12:29:47 -05001119
David Tolnay6af48992018-08-01 11:16:28 -07001120 #full_macro
1121 #skip_macro
Nika Layzell27726662017-10-24 23:16:35 -04001122
David Tolnay6af48992018-08-01 11:16:28 -07001123 /// Syntax tree traversal to mutate an exclusive borrow of a syntax tree in
1124 /// place.
1125 ///
1126 /// See the [module documentation] for details.
1127 ///
1128 /// [module documentation]: index.html
1129 ///
1130 /// *This trait is available if Syn is built with the `"visit-mut"` feature.*
1131 pub trait VisitMut {
1132 #visit_mut_trait
1133 }
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001134
David Tolnay6af48992018-08-01 11:16:28 -07001135 #visit_mut_impl
1136 },
David Tolnayae0009e2018-08-01 00:40:00 -07001137 );
Nika Layzell27726662017-10-24 23:16:35 -04001138}