blob: f00f3103d32a1755cbf9af227bc6654678c29890 [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))]
14
David Tolnayd67fb752017-12-27 13:50:29 -050015#[macro_use]
16extern crate failure;
Nika Layzell27726662017-10-24 23:16:35 -040017extern crate inflections;
David Tolnay2e0dba12017-12-27 01:54:40 -050018extern crate proc_macro2;
David Tolnayd67fb752017-12-27 13:50:29 -050019#[macro_use]
20extern crate quote;
David Tolnay5c4c0b52017-12-28 17:58:54 -050021#[macro_use]
David Tolnayd67fb752017-12-27 13:50:29 -050022extern crate syn;
Nika Layzell27726662017-10-24 23:16:35 -040023
David Tolnayd67fb752017-12-27 13:50:29 -050024use failure::{err_msg, Error};
David Tolnaye303b7c2018-05-20 16:46:35 -070025use proc_macro2::{Span, TokenStream};
David Tolnay01ed0ce2018-05-20 20:18:14 -070026use quote::ToTokens;
27use syn::{Attribute, Data, DataStruct, DeriveInput, Ident, Item};
Nika Layzell27726662017-10-24 23:16:35 -040028
David Tolnay01ed0ce2018-05-20 20:18:14 -070029use std::collections::BTreeMap;
David Tolnayf0d63bf2017-12-26 12:29:47 -050030use std::fmt::{self, Debug};
Nika Layzell27726662017-10-24 23:16:35 -040031use std::fs::File;
David Tolnay01ed0ce2018-05-20 20:18:14 -070032use std::io::{Read, Write};
Nika Layzell27726662017-10-24 23:16:35 -040033use std::path::Path;
Nika Layzell27726662017-10-24 23:16:35 -040034
35const SYN_CRATE_ROOT: &str = "../src/lib.rs";
36
37const FOLD_SRC: &str = "../src/gen/fold.rs";
38const VISIT_SRC: &str = "../src/gen/visit.rs";
39const VISIT_MUT_SRC: &str = "../src/gen/visit_mut.rs";
40
David Tolnayd67fb752017-12-27 13:50:29 -050041const IGNORED_MODS: &[&str] = &["fold", "visit", "visit_mut"];
Nika Layzell27726662017-10-24 23:16:35 -040042
Alex Crichton131308c2018-05-18 14:00:24 -070043const EXTRA_TYPES: &[&str] = &["Lifetime"];
David Tolnay4ba63a02017-12-28 15:53:05 -050044
Alex Crichtond261d092018-05-18 13:47:35 -070045const TERMINAL_TYPES: &[&str] = &["Span", "Ident"];
Nika Layzellefb83ba2017-12-19 18:23:55 -050046
Alex Crichtona74a1c82018-05-16 10:20:44 -070047fn path_eq(a: &syn::Path, b: &str) -> bool {
48 if a.global() {
David Tolnay01ed0ce2018-05-20 20:18:14 -070049 return false;
Nika Layzell27726662017-10-24 23:16:35 -040050 }
Alex Crichtona74a1c82018-05-16 10:20:44 -070051 if a.segments.len() != 1 {
David Tolnay01ed0ce2018-05-20 20:18:14 -070052 return false;
Alex Crichtona74a1c82018-05-16 10:20:44 -070053 }
David Tolnay446f7d62018-05-20 17:58:15 -070054 a.segments[0].ident == b
Nika Layzell27726662017-10-24 23:16:35 -040055}
56
Alex Crichton715862b2018-05-17 12:31:49 -070057fn get_features(attrs: &[Attribute], mut features: TokenStream) -> TokenStream {
Nika Layzell27726662017-10-24 23:16:35 -040058 for attr in attrs {
Alex Crichtona74a1c82018-05-16 10:20:44 -070059 if path_eq(&attr.path, "cfg") {
Nika Layzell27726662017-10-24 23:16:35 -040060 attr.to_tokens(&mut features);
61 }
62 }
63 features
64}
65
66#[derive(Clone)]
67pub struct AstItem {
David Tolnay3d772182017-12-28 17:18:53 -050068 ast: DeriveInput,
Alex Crichton715862b2018-05-17 12:31:49 -070069 features: TokenStream,
Nika Layzell27726662017-10-24 23:16:35 -040070 // True if this is an ast_enum_of_structs! item with a #full annotation.
71 eos_full: bool,
72}
73
David Tolnayf0d63bf2017-12-26 12:29:47 -050074impl Debug for AstItem {
Nika Layzell27726662017-10-24 23:16:35 -040075 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
76 f.debug_struct("AstItem")
David Tolnay3d772182017-12-28 17:18:53 -050077 .field("ast", &self.ast)
Nika Layzell27726662017-10-24 23:16:35 -040078 .field("features", &self.features.to_string())
79 .finish()
80 }
81}
82
83// NOTE: BTreeMap is used here instead of HashMap to have deterministic output.
David Tolnayc47f8422017-12-28 17:30:46 -050084type Lookup = BTreeMap<Ident, AstItem>;
Nika Layzell27726662017-10-24 23:16:35 -040085
David Tolnay01ed0ce2018-05-20 20:18:14 -070086fn load_file<P: AsRef<Path>>(
87 name: P,
88 features: &TokenStream,
89 lookup: &mut Lookup,
90) -> Result<(), Error> {
Nika Layzell27726662017-10-24 23:16:35 -040091 let name = name.as_ref();
David Tolnayea9ae892017-12-26 01:44:32 -050092 let parent = name.parent().ok_or_else(|| err_msg("no parent path"))?;
Nika Layzell27726662017-10-24 23:16:35 -040093
94 let mut f = File::open(name)?;
95 let mut src = String::new();
96 f.read_to_string(&mut src)?;
97
98 // Parse the file
David Tolnayd67fb752017-12-27 13:50:29 -050099 let file =
100 syn::parse_file(&src).map_err(|_| format_err!("failed to parse {}", name.display()))?;
Nika Layzell27726662017-10-24 23:16:35 -0400101
102 // Collect all of the interesting AstItems declared in this file or submodules.
David Tolnay4ba63a02017-12-28 15:53:05 -0500103 'items: for item in file.items {
104 match item {
105 Item::Mod(item) => {
Nika Layzell27726662017-10-24 23:16:35 -0400106 // Don't inspect inline modules.
David Tolnayc6b55bc2017-11-09 22:48:38 -0800107 if item.content.is_some() {
Nika Layzell27726662017-10-24 23:16:35 -0400108 continue;
109 }
110
111 // We don't want to try to load the generated rust files and
112 // parse them, so we ignore them here.
113 for name in IGNORED_MODS {
David Tolnay446f7d62018-05-20 17:58:15 -0700114 if item.ident == name {
Nika Layzell27726662017-10-24 23:16:35 -0400115 continue 'items;
116 }
117 }
118
119 // Lookup any #[cfg()] attributes on the module and add them to
120 // the feature set.
David Tolnay0a0d78c2018-01-05 15:24:01 -0800121 //
122 // The derive module is weird because it is built with either
123 // `full` or `derive` but exported only under `derive`.
David Tolnay446f7d62018-05-20 17:58:15 -0700124 let features = if item.ident == "derive" {
David Tolnay0a0d78c2018-01-05 15:24:01 -0800125 quote!(#[cfg(feature = "derive")])
126 } else {
127 get_features(&item.attrs, features.clone())
128 };
Nika Layzell27726662017-10-24 23:16:35 -0400129
130 // Look up the submodule file, and recursively parse it.
131 // XXX: Only handles same-directory .rs file submodules.
Alex Crichtona74a1c82018-05-16 10:20:44 -0700132 let path = parent.join(&format!("{}.rs", item.ident));
David Tolnayea9ae892017-12-26 01:44:32 -0500133 load_file(path, &features, lookup)?;
Nika Layzell27726662017-10-24 23:16:35 -0400134 }
David Tolnay4ba63a02017-12-28 15:53:05 -0500135 Item::Macro(item) => {
Nika Layzell27726662017-10-24 23:16:35 -0400136 // Lookip any #[cfg()] attributes directly on the macro
137 // invocation, and add them to the feature set.
138 let features = get_features(&item.attrs, features.clone());
139
140 // Try to parse the AstItem declaration out of the item.
David Tolnay01a77582018-01-01 20:00:51 -0800141 let tts = &item.mac.tts;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700142 let found = if path_eq(&item.mac.path, "ast_struct") {
David Tolnay01a77582018-01-01 20:00:51 -0800143 syn::parse_str::<parsing::AstStruct>(&quote!(#tts).to_string())
David Tolnayd67fb752017-12-27 13:50:29 -0500144 .map_err(|_| err_msg("failed to parse ast_struct"))?
145 .0
Alex Crichtona74a1c82018-05-16 10:20:44 -0700146 } else if path_eq(&item.mac.path, "ast_enum") {
David Tolnay01a77582018-01-01 20:00:51 -0800147 syn::parse_str::<parsing::AstEnum>(&quote!(#tts).to_string())
David Tolnayd67fb752017-12-27 13:50:29 -0500148 .map_err(|_| err_msg("failed to parse ast_enum"))?
149 .0
Alex Crichtona74a1c82018-05-16 10:20:44 -0700150 } else if path_eq(&item.mac.path, "ast_enum_of_structs") {
David Tolnay01a77582018-01-01 20:00:51 -0800151 syn::parse_str::<parsing::AstEnumOfStructs>(&quote!(#tts).to_string())
David Tolnay1cf80912017-12-31 18:35:12 -0500152 .map_err(|_| err_msg("failed to parse ast_enum_of_structs"))?
David Tolnayd67fb752017-12-27 13:50:29 -0500153 .0
Nika Layzell27726662017-10-24 23:16:35 -0400154 } else {
David Tolnayd67fb752017-12-27 13:50:29 -0500155 continue;
Nika Layzell27726662017-10-24 23:16:35 -0400156 };
157
158 // Record our features on the parsed AstItems.
159 for mut item in found {
160 features.to_tokens(&mut item.features);
Alex Crichtona74a1c82018-05-16 10:20:44 -0700161 lookup.insert(item.ast.ident.clone(), item);
Nika Layzell27726662017-10-24 23:16:35 -0400162 }
163 }
David Tolnay4ba63a02017-12-28 15:53:05 -0500164 Item::Struct(item) => {
165 let ident = item.ident;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700166 if EXTRA_TYPES.contains(&&ident.to_string()[..]) {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700167 lookup.insert(
168 ident.clone(),
169 AstItem {
170 ast: DeriveInput {
171 ident: ident,
172 vis: item.vis,
173 attrs: item.attrs,
174 generics: item.generics,
175 data: Data::Struct(DataStruct {
176 fields: item.fields,
177 struct_token: item.struct_token,
178 semi_token: item.semi_token,
179 }),
180 },
181 features: features.clone(),
182 eos_full: false,
David Tolnay4ba63a02017-12-28 15:53:05 -0500183 },
David Tolnay01ed0ce2018-05-20 20:18:14 -0700184 );
David Tolnay4ba63a02017-12-28 15:53:05 -0500185 }
186 }
Nika Layzell27726662017-10-24 23:16:35 -0400187 _ => {}
188 }
189 }
190 Ok(())
191}
192
193mod parsing {
194 use super::AstItem;
195
David Tolnay01ed0ce2018-05-20 20:18:14 -0700196 use proc_macro2::TokenStream;
David Tolnay1cf80912017-12-31 18:35:12 -0500197 use syn;
David Tolnayc5ab8c62017-12-26 16:43:39 -0500198 use syn::synom::*;
Nika Layzell27726662017-10-24 23:16:35 -0400199 use syn::*;
Nika Layzell27726662017-10-24 23:16:35 -0400200
201 // Parses #full - returns #[cfg(feature = "full")] if it is present, and
202 // nothing otherwise.
Alex Crichton715862b2018-05-17 12:31:49 -0700203 named!(full -> (TokenStream, bool), map!(option!(do_parse!(
David Tolnayf8db7ba2017-11-11 22:52:16 -0800204 punct!(#) >>
Nika Layzell27726662017-10-24 23:16:35 -0400205 id: syn!(Ident) >>
David Tolnay446f7d62018-05-20 17:58:15 -0700206 cond_reduce!(id == "full") >>
David Tolnayea9ae892017-12-26 01:44:32 -0500207 ()
Nika Layzell27726662017-10-24 23:16:35 -0400208 )), |s| if s.is_some() {
209 (quote!(#[cfg(feature = "full")]), true)
210 } else {
211 (quote!(), false)
212 }));
213
David Tolnay28c5a462017-12-27 01:59:30 -0500214 named!(manual_extra_traits -> (), do_parse!(
215 punct!(#) >>
216 id: syn!(Ident) >>
David Tolnay446f7d62018-05-20 17:58:15 -0700217 cond_reduce!(id == "manual_extra_traits") >>
David Tolnay28c5a462017-12-27 01:59:30 -0500218 ()
219 ));
220
Nika Layzell27726662017-10-24 23:16:35 -0400221 // Parses a simple AstStruct without the `pub struct` prefix.
222 named!(ast_struct_inner -> AstItem, do_parse!(
223 id: syn!(Ident) >>
224 features: full >>
David Tolnay28c5a462017-12-27 01:59:30 -0500225 option!(manual_extra_traits) >>
David Tolnay2e0dba12017-12-27 01:54:40 -0500226 rest: syn!(TokenStream) >>
Nika Layzell27726662017-10-24 23:16:35 -0400227 (AstItem {
David Tolnay01a77582018-01-01 20:00:51 -0800228 ast: syn::parse_str(&quote! {
David Tolnay2e0dba12017-12-27 01:54:40 -0500229 pub struct #id #rest
David Tolnay01a77582018-01-01 20:00:51 -0800230 }.to_string())?,
Nika Layzell27726662017-10-24 23:16:35 -0400231 features: features.0,
232 eos_full: features.1,
233 })
234 ));
235
236 // ast_struct! parsing
237 pub struct AstStruct(pub Vec<AstItem>);
238 impl Synom for AstStruct {
David Tolnayab919512017-12-30 23:31:51 -0500239 named!(parse -> Self, do_parse!(
David Tolnay2c136452017-12-27 14:13:32 -0500240 many0!(Attribute::parse_outer) >>
David Tolnayf8db7ba2017-11-11 22:52:16 -0800241 keyword!(pub) >>
242 keyword!(struct) >>
Nika Layzell27726662017-10-24 23:16:35 -0400243 res: call!(ast_struct_inner) >>
David Tolnayab919512017-12-30 23:31:51 -0500244 (AstStruct(vec![res]))
245 ));
Nika Layzell27726662017-10-24 23:16:35 -0400246 }
247
David Tolnay360efd22018-01-04 23:35:26 -0800248 named!(no_visit -> (), do_parse!(
249 punct!(#) >>
250 id: syn!(Ident) >>
David Tolnay446f7d62018-05-20 17:58:15 -0700251 cond_reduce!(id == "no_visit") >>
David Tolnay360efd22018-01-04 23:35:26 -0800252 ()
253 ));
254
Nika Layzell27726662017-10-24 23:16:35 -0400255 // ast_enum! parsing
256 pub struct AstEnum(pub Vec<AstItem>);
257 impl Synom for AstEnum {
David Tolnay360efd22018-01-04 23:35:26 -0800258 named!(parse -> Self, do_parse!(
259 many0!(Attribute::parse_outer) >>
260 keyword!(pub) >>
261 keyword!(enum) >>
262 id: syn!(Ident) >>
263 no_visit: option!(no_visit) >>
264 rest: syn!(TokenStream) >>
265 (AstEnum(if no_visit.is_some() {
266 vec![]
267 } else {
268 vec![AstItem {
269 ast: syn::parse_str(&quote! {
270 pub enum #id #rest
271 }.to_string())?,
272 features: quote!(),
273 eos_full: false,
274 }]
275 }))
276 ));
Nika Layzell27726662017-10-24 23:16:35 -0400277 }
278
279 // A single variant of an ast_enum_of_structs!
280 struct EosVariant {
281 name: Ident,
David Tolnayfcfb9002017-12-28 22:04:29 -0500282 member: Option<Path>,
Nika Layzell27726662017-10-24 23:16:35 -0400283 inner: Option<AstItem>,
284 }
285 named!(eos_variant -> EosVariant, do_parse!(
David Tolnay2c136452017-12-27 14:13:32 -0500286 many0!(Attribute::parse_outer) >>
David Tolnayf8db7ba2017-11-11 22:52:16 -0800287 keyword!(pub) >>
Nika Layzell27726662017-10-24 23:16:35 -0400288 variant: syn!(Ident) >>
David Tolnayfcfb9002017-12-28 22:04:29 -0500289 member: option!(map!(parens!(alt!(
Alex Crichtona74a1c82018-05-16 10:20:44 -0700290 call!(ast_struct_inner) => { |x: AstItem| (Path::from(x.ast.ident.clone()), Some(x)) }
Nika Layzell27726662017-10-24 23:16:35 -0400291 |
292 syn!(Path) => { |x| (x, None) }
David Tolnay8875fca2017-12-31 13:52:37 -0500293 )), |x| x.1)) >>
David Tolnayf8db7ba2017-11-11 22:52:16 -0800294 punct!(,) >>
Nika Layzell27726662017-10-24 23:16:35 -0400295 (EosVariant {
296 name: variant,
David Tolnayfcfb9002017-12-28 22:04:29 -0500297 member: member.clone().map(|x| x.0),
298 inner: member.map(|x| x.1).unwrap_or_default(),
Nika Layzell27726662017-10-24 23:16:35 -0400299 })
300 ));
301
302 // ast_enum_of_structs! parsing
303 pub struct AstEnumOfStructs(pub Vec<AstItem>);
304 impl Synom for AstEnumOfStructs {
David Tolnayab919512017-12-30 23:31:51 -0500305 named!(parse -> Self, do_parse!(
David Tolnay2c136452017-12-27 14:13:32 -0500306 many0!(Attribute::parse_outer) >>
David Tolnayf8db7ba2017-11-11 22:52:16 -0800307 keyword!(pub) >>
308 keyword!(enum) >>
Nika Layzell27726662017-10-24 23:16:35 -0400309 id: syn!(Ident) >>
David Tolnaye3d41b72017-12-31 15:24:00 -0500310 variants: braces!(many0!(eos_variant)) >>
Nika Layzell27726662017-10-24 23:16:35 -0400311 option!(syn!(Ident)) >> // do_not_generate_to_tokens
312 ({
313 // XXX: This is really gross - we shouldn't have to convert the
314 // tokens to strings to re-parse them.
315 let enum_item = {
David Tolnaye3d41b72017-12-31 15:24:00 -0500316 let variants = variants.1.iter().map(|v| {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700317 let name = v.name.clone();
David Tolnayfcfb9002017-12-28 22:04:29 -0500318 match v.member {
319 Some(ref member) => quote!(#name(#member)),
320 None => quote!(#name),
321 }
Nika Layzell27726662017-10-24 23:16:35 -0400322 });
David Tolnay01a77582018-01-01 20:00:51 -0800323 syn::parse_str(&quote! {
Nika Layzell27726662017-10-24 23:16:35 -0400324 pub enum #id { #(#variants),* }
David Tolnay01a77582018-01-01 20:00:51 -0800325 }.to_string())?
Nika Layzell27726662017-10-24 23:16:35 -0400326 };
327 let mut items = vec![AstItem {
David Tolnay3d772182017-12-28 17:18:53 -0500328 ast: enum_item,
Nika Layzell27726662017-10-24 23:16:35 -0400329 features: quote!(),
330 eos_full: false,
331 }];
David Tolnaye3d41b72017-12-31 15:24:00 -0500332 items.extend(variants.1.into_iter().filter_map(|v| v.inner));
Nika Layzell27726662017-10-24 23:16:35 -0400333 AstEnumOfStructs(items)
334 })
David Tolnayab919512017-12-30 23:31:51 -0500335 ));
Nika Layzell27726662017-10-24 23:16:35 -0400336 }
337}
338
339mod codegen {
340 use super::{AstItem, Lookup};
David Tolnay01ed0ce2018-05-20 20:18:14 -0700341 use proc_macro2::{Span, TokenStream};
Alex Crichton715862b2018-05-17 12:31:49 -0700342 use quote::ToTokens;
David Tolnayf0d63bf2017-12-26 12:29:47 -0500343 use std::fmt::{self, Display};
David Tolnay01ed0ce2018-05-20 20:18:14 -0700344 use syn::punctuated::Punctuated;
345 use syn::*;
Nika Layzell27726662017-10-24 23:16:35 -0400346
347 #[derive(Default)]
348 pub struct State {
349 pub visit_trait: String,
350 pub visit_impl: String,
351 pub visit_mut_trait: String,
352 pub visit_mut_impl: String,
353 pub fold_trait: String,
354 pub fold_impl: String,
355 }
356
David Tolnay4a918742017-12-28 16:54:41 -0500357 fn under_name(name: Ident) -> Ident {
Nika Layzell27726662017-10-24 23:16:35 -0400358 use inflections::Inflect;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700359 Ident::new(&name.to_string().to_snake_case(), Span::call_site())
Nika Layzell27726662017-10-24 23:16:35 -0400360 }
361
David Tolnay3d772182017-12-28 17:18:53 -0500362 enum RelevantType<'a> {
363 Box(&'a Type),
364 Vec(&'a Type),
David Tolnayf2cfd722017-12-31 18:02:51 -0500365 Punctuated(&'a Type),
David Tolnay3d772182017-12-28 17:18:53 -0500366 Option(&'a Type),
David Tolnayf2cfd722017-12-31 18:02:51 -0500367 Tuple(&'a Punctuated<Type, Token![,]>),
David Tolnay3d772182017-12-28 17:18:53 -0500368 Simple(&'a AstItem),
Alex Crichton715862b2018-05-17 12:31:49 -0700369 Token(TokenStream),
David Tolnay3d772182017-12-28 17:18:53 -0500370 Pass,
371 }
Nika Layzell27726662017-10-24 23:16:35 -0400372
David Tolnay3d772182017-12-28 17:18:53 -0500373 fn classify<'a>(ty: &'a Type, lookup: &'a Lookup) -> RelevantType<'a> {
374 match *ty {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700375 Type::Path(TypePath {
376 qself: None,
377 ref path,
378 }) => {
David Tolnay56080682018-01-06 14:01:52 -0800379 let last = path.segments.last().unwrap().into_value();
Alex Crichtona74a1c82018-05-16 10:20:44 -0700380 match &last.ident.to_string()[..] {
David Tolnay3d772182017-12-28 17:18:53 -0500381 "Box" => RelevantType::Box(first_arg(&last.arguments)),
382 "Vec" => RelevantType::Vec(first_arg(&last.arguments)),
David Tolnayf2cfd722017-12-31 18:02:51 -0500383 "Punctuated" => RelevantType::Punctuated(first_arg(&last.arguments)),
David Tolnay3d772182017-12-28 17:18:53 -0500384 "Option" => RelevantType::Option(first_arg(&last.arguments)),
David Tolnay1e01f9c2017-12-28 20:16:19 -0500385 "Brace" | "Bracket" | "Paren" | "Group" => {
Alex Crichton715862b2018-05-17 12:31:49 -0700386 RelevantType::Token(last.ident.clone().into_token_stream())
David Tolnay1e01f9c2017-12-28 20:16:19 -0500387 }
David Tolnay3d772182017-12-28 17:18:53 -0500388 _ => {
389 if let Some(item) = lookup.get(&last.ident) {
390 RelevantType::Simple(item)
391 } else {
392 RelevantType::Pass
393 }
394 }
395 }
Nika Layzell27726662017-10-24 23:16:35 -0400396 }
David Tolnay01ed0ce2018-05-20 20:18:14 -0700397 Type::Tuple(TypeTuple { ref elems, .. }) => RelevantType::Tuple(elems),
398 Type::Macro(TypeMacro { ref mac })
399 if mac.path.segments.last().unwrap().into_value().ident == "Token" =>
400 {
Alex Crichton715862b2018-05-17 12:31:49 -0700401 RelevantType::Token(mac.into_token_stream())
David Tolnaycc0f0372017-12-28 19:11:04 -0500402 }
David Tolnay3d772182017-12-28 17:18:53 -0500403 _ => RelevantType::Pass,
Nika Layzell27726662017-10-24 23:16:35 -0400404 }
405 }
406
407 #[derive(Debug, Eq, PartialEq, Copy, Clone)]
408 enum Kind {
409 Visit,
410 VisitMut,
411 Fold,
412 }
413
David Tolnayf0d63bf2017-12-26 12:29:47 -0500414 enum Operand {
Alex Crichton715862b2018-05-17 12:31:49 -0700415 Borrowed(TokenStream),
416 Owned(TokenStream),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500417 }
418
David Tolnay83db9272017-12-28 17:02:31 -0500419 use self::Kind::*;
David Tolnay01ed0ce2018-05-20 20:18:14 -0700420 use self::Operand::*;
David Tolnay83db9272017-12-28 17:02:31 -0500421
David Tolnayf0d63bf2017-12-26 12:29:47 -0500422 impl Operand {
Alex Crichton715862b2018-05-17 12:31:49 -0700423 fn tokens(&self) -> &TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500424 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500425 Borrowed(ref n) | Owned(ref n) => n,
David Tolnayf0d63bf2017-12-26 12:29:47 -0500426 }
427 }
428
Alex Crichton715862b2018-05-17 12:31:49 -0700429 fn ref_tokens(&self) -> TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500430 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500431 Borrowed(ref n) => n.clone(),
432 Owned(ref n) => quote!(&#n),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500433 }
434 }
435
Alex Crichton715862b2018-05-17 12:31:49 -0700436 fn ref_mut_tokens(&self) -> TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500437 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500438 Borrowed(ref n) => n.clone(),
439 Owned(ref n) => quote!(&mut #n),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500440 }
441 }
442
Alex Crichton715862b2018-05-17 12:31:49 -0700443 fn owned_tokens(&self) -> TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500444 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500445 Borrowed(ref n) => quote!(*#n),
446 Owned(ref n) => n.clone(),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500447 }
448 }
449 }
450
451 impl Display for Operand {
452 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
David Tolnay4e52d8a2017-12-28 15:54:50 -0500453 Display::fmt(self.tokens(), formatter)
David Tolnayf0d63bf2017-12-26 12:29:47 -0500454 }
455 }
456
Nika Layzellc08227a2017-12-04 16:30:17 -0500457 fn first_arg(params: &PathArguments) -> &Type {
Nika Layzell27726662017-10-24 23:16:35 -0400458 let data = match *params {
Nika Layzellc08227a2017-12-04 16:30:17 -0500459 PathArguments::AngleBracketed(ref data) => data,
460 _ => panic!("Expected at least 1 type argument here"),
Nika Layzell27726662017-10-24 23:16:35 -0400461 };
462
David Tolnay01ed0ce2018-05-20 20:18:14 -0700463 match **data
464 .args
David Tolnayd67fb752017-12-27 13:50:29 -0500465 .first()
466 .expect("Expected at least 1 type argument here")
David Tolnay56080682018-01-06 14:01:52 -0800467 .value()
David Tolnayd67fb752017-12-27 13:50:29 -0500468 {
David Tolnayea9ae892017-12-26 01:44:32 -0500469 GenericArgument::Type(ref ty) => ty,
Nika Layzellc08227a2017-12-04 16:30:17 -0500470 _ => panic!("Expected at least 1 type argument here"),
Nika Layzell357885a2017-12-04 15:47:07 -0500471 }
Nika Layzell27726662017-10-24 23:16:35 -0400472 }
473
David Tolnay01ed0ce2018-05-20 20:18:14 -0700474 fn simple_visit(item: &AstItem, kind: Kind, name: &Operand) -> String {
David Tolnay4a918742017-12-28 16:54:41 -0500475 match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500476 Visit => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500477 "_visitor.visit_{under_name}({name})",
Alex Crichtona74a1c82018-05-16 10:20:44 -0700478 under_name = under_name(item.ast.ident.clone()),
David Tolnay4a918742017-12-28 16:54:41 -0500479 name = name.ref_tokens(),
480 ),
David Tolnay83db9272017-12-28 17:02:31 -0500481 VisitMut => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500482 "_visitor.visit_{under_name}_mut({name})",
Alex Crichtona74a1c82018-05-16 10:20:44 -0700483 under_name = under_name(item.ast.ident.clone()),
David Tolnay4a918742017-12-28 16:54:41 -0500484 name = name.ref_mut_tokens(),
485 ),
David Tolnay83db9272017-12-28 17:02:31 -0500486 Fold => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500487 "_visitor.fold_{under_name}({name})",
Alex Crichtona74a1c82018-05-16 10:20:44 -0700488 under_name = under_name(item.ast.ident.clone()),
David Tolnay4a918742017-12-28 16:54:41 -0500489 name = name.owned_tokens(),
490 ),
Nika Layzell27726662017-10-24 23:16:35 -0400491 }
492 }
493
David Tolnay01ed0ce2018-05-20 20:18:14 -0700494 fn box_visit(elem: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<String> {
David Tolnay4a918742017-12-28 16:54:41 -0500495 let name = name.owned_tokens();
David Tolnay39d0a202017-12-28 18:19:00 -0500496 let res = visit(elem, lookup, kind, &Owned(quote!(*#name)))?;
David Tolnay4a918742017-12-28 16:54:41 -0500497 Some(match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500498 Fold => format!("Box::new({})", res),
499 Visit | VisitMut => res,
David Tolnay4a918742017-12-28 16:54:41 -0500500 })
Nika Layzell27726662017-10-24 23:16:35 -0400501 }
502
David Tolnay01ed0ce2018-05-20 20:18:14 -0700503 fn vec_visit(elem: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<String> {
David Tolnay4a918742017-12-28 16:54:41 -0500504 let operand = match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500505 Visit | VisitMut => Borrowed(quote!(it)),
506 Fold => Owned(quote!(it)),
David Tolnay4a918742017-12-28 16:54:41 -0500507 };
David Tolnay39d0a202017-12-28 18:19:00 -0500508 let val = visit(elem, lookup, kind, &operand)?;
David Tolnay4a918742017-12-28 16:54:41 -0500509 Some(match kind {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700510 Visit => format!(
511 "for it in {name} {{ {val} }}",
512 name = name.ref_tokens(),
513 val = val,
514 ),
515 VisitMut => format!(
516 "for it in {name} {{ {val} }}",
517 name = name.ref_mut_tokens(),
518 val = val,
519 ),
David Tolnay3d772182017-12-28 17:18:53 -0500520 Fold => format!(
521 "FoldHelper::lift({name}, |it| {{ {val} }})",
522 name = name.owned_tokens(),
523 val = val,
524 ),
525 })
526 }
527
David Tolnay6eff4da2018-01-01 20:27:45 -0800528 fn punctuated_visit(
David Tolnay3d772182017-12-28 17:18:53 -0500529 elem: &Type,
530 lookup: &Lookup,
531 kind: Kind,
532 name: &Operand,
533 ) -> Option<String> {
534 let operand = match kind {
535 Visit | VisitMut => Borrowed(quote!(it)),
536 Fold => Owned(quote!(it)),
537 };
David Tolnay39d0a202017-12-28 18:19:00 -0500538 let val = visit(elem, lookup, kind, &operand)?;
David Tolnay3d772182017-12-28 17:18:53 -0500539 Some(match kind {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700540 Visit => format!(
541 "for el in Punctuated::pairs({name}) {{ \
542 let it = el.value(); \
543 {val} \
544 }}",
545 name = name.ref_tokens(),
546 val = val,
547 ),
548 VisitMut => format!(
549 "for mut el in Punctuated::pairs_mut({name}) {{ \
550 let it = el.value_mut(); \
551 {val} \
552 }}",
553 name = name.ref_mut_tokens(),
554 val = val,
555 ),
David Tolnay83db9272017-12-28 17:02:31 -0500556 Fold => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500557 "FoldHelper::lift({name}, |it| {{ {val} }})",
558 name = name.owned_tokens(),
559 val = val,
560 ),
561 })
Nika Layzell27726662017-10-24 23:16:35 -0400562 }
563
David Tolnay01ed0ce2018-05-20 20:18:14 -0700564 fn option_visit(elem: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<String> {
David Tolnay4a918742017-12-28 16:54:41 -0500565 let it = match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500566 Visit | VisitMut => Borrowed(quote!(it)),
567 Fold => Owned(quote!(it)),
David Tolnay4a918742017-12-28 16:54:41 -0500568 };
David Tolnay39d0a202017-12-28 18:19:00 -0500569 let val = visit(elem, lookup, kind, &it)?;
David Tolnay4a918742017-12-28 16:54:41 -0500570 Some(match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500571 Visit => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500572 "if let Some(ref it) = {name} {{ {val} }}",
573 name = name.owned_tokens(),
574 val = val,
575 ),
David Tolnay83db9272017-12-28 17:02:31 -0500576 VisitMut => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500577 "if let Some(ref mut it) = {name} {{ {val} }}",
578 name = name.owned_tokens(),
579 val = val,
580 ),
David Tolnay83db9272017-12-28 17:02:31 -0500581 Fold => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500582 "({name}).map(|it| {{ {val} }})",
583 name = name.owned_tokens(),
584 val = val,
585 ),
586 })
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400587 }
588
David Tolnay5c4c0b52017-12-28 17:58:54 -0500589 fn tuple_visit(
David Tolnayf2cfd722017-12-31 18:02:51 -0500590 elems: &Punctuated<Type, Token![,]>,
David Tolnay5c4c0b52017-12-28 17:58:54 -0500591 lookup: &Lookup,
592 kind: Kind,
593 name: &Operand,
594 ) -> Option<String> {
595 let mut code = String::new();
David Tolnayf047c7a2017-12-31 02:29:04 -0500596 for (i, elem) in elems.iter().enumerate() {
David Tolnay5c4c0b52017-12-28 17:58:54 -0500597 let name = name.tokens();
David Tolnay14982012017-12-29 00:49:51 -0500598 let i = Index::from(i);
David Tolnay5c4c0b52017-12-28 17:58:54 -0500599 let it = Owned(quote!((#name).#i));
David Tolnay01ed0ce2018-05-20 20:18:14 -0700600 let val = visit(elem, lookup, kind, &it).unwrap_or_else(|| noop_visit(kind, &it));
David Tolnay5c4c0b52017-12-28 17:58:54 -0500601 code.push_str(&format!(" {}", val));
602 match kind {
603 Fold => code.push(','),
604 Visit | VisitMut => code.push(';'),
605 }
606 code.push('\n');
607 }
608 if code.is_empty() {
609 None
610 } else {
611 Some(match kind {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700612 Fold => format!("(\n{} )", code),
613 Visit | VisitMut => format!("\n{} ", code),
David Tolnay5c4c0b52017-12-28 17:58:54 -0500614 })
615 }
616 }
617
Alex Crichton715862b2018-05-17 12:31:49 -0700618 fn token_visit(ty: TokenStream, kind: Kind, name: &Operand) -> String {
David Tolnaycc0f0372017-12-28 19:11:04 -0500619 match kind {
620 Fold => format!(
David Tolnay1e01f9c2017-12-28 20:16:19 -0500621 "{ty}(tokens_helper(_visitor, &({name}).0))",
622 ty = ty,
David Tolnaycc0f0372017-12-28 19:11:04 -0500623 name = name.owned_tokens(),
624 ),
625 Visit => format!(
626 "tokens_helper(_visitor, &({name}).0)",
627 name = name.ref_tokens(),
628 ),
629 VisitMut => format!(
630 "tokens_helper(_visitor, &mut ({name}).0)",
631 name = name.ref_mut_tokens(),
632 ),
633 }
634 }
635
David Tolnay4a918742017-12-28 16:54:41 -0500636 fn noop_visit(kind: Kind, name: &Operand) -> String {
637 match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500638 Fold => name.owned_tokens().to_string(),
639 Visit | VisitMut => format!("// Skipped field {}", name),
David Tolnay4a918742017-12-28 16:54:41 -0500640 }
641 }
642
643 fn visit(ty: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<String> {
David Tolnay3d772182017-12-28 17:18:53 -0500644 match classify(ty, lookup) {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700645 RelevantType::Box(elem) => box_visit(elem, lookup, kind, name),
646 RelevantType::Vec(elem) => vec_visit(elem, lookup, kind, name),
647 RelevantType::Punctuated(elem) => punctuated_visit(elem, lookup, kind, name),
648 RelevantType::Option(elem) => option_visit(elem, lookup, kind, name),
649 RelevantType::Tuple(elems) => tuple_visit(elems, lookup, kind, name),
David Tolnay3d772182017-12-28 17:18:53 -0500650 RelevantType::Simple(item) => {
651 let mut res = simple_visit(item, kind, name);
652 Some(if item.eos_full {
653 format!("full!({res})", res = res)
654 } else {
655 res
656 })
657 }
David Tolnay01ed0ce2018-05-20 20:18:14 -0700658 RelevantType::Token(ty) => Some(token_visit(ty, kind, name)),
659 RelevantType::Pass => None,
Nika Layzell27726662017-10-24 23:16:35 -0400660 }
Nika Layzell27726662017-10-24 23:16:35 -0400661 }
662
663 pub fn generate(state: &mut State, lookup: &Lookup, s: &AstItem) {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700664 let under_name = under_name(s.ast.ident.clone());
Nika Layzell27726662017-10-24 23:16:35 -0400665
666 state.visit_trait.push_str(&format!(
667 "{features}\n\
Nika Layzellc86173a2017-11-18 13:55:22 -0500668 fn visit_{under_name}(&mut self, i: &'ast {ty}) {{ \
David Tolnayd67fb752017-12-27 13:50:29 -0500669 visit_{under_name}(self, i) \
Nika Layzell27726662017-10-24 23:16:35 -0400670 }}\n",
671 features = s.features,
672 under_name = under_name,
David Tolnay3d772182017-12-28 17:18:53 -0500673 ty = s.ast.ident,
Nika Layzell27726662017-10-24 23:16:35 -0400674 ));
675 state.visit_mut_trait.push_str(&format!(
676 "{features}\n\
Nika Layzella6f46c42017-10-26 15:26:16 -0400677 fn visit_{under_name}_mut(&mut self, i: &mut {ty}) {{ \
David Tolnayd67fb752017-12-27 13:50:29 -0500678 visit_{under_name}_mut(self, i) \
Nika Layzell27726662017-10-24 23:16:35 -0400679 }}\n",
680 features = s.features,
681 under_name = under_name,
David Tolnay3d772182017-12-28 17:18:53 -0500682 ty = s.ast.ident,
Nika Layzell27726662017-10-24 23:16:35 -0400683 ));
684 state.fold_trait.push_str(&format!(
685 "{features}\n\
686 fn fold_{under_name}(&mut self, i: {ty}) -> {ty} {{ \
David Tolnayd67fb752017-12-27 13:50:29 -0500687 fold_{under_name}(self, i) \
Nika Layzell27726662017-10-24 23:16:35 -0400688 }}\n",
689 features = s.features,
690 under_name = under_name,
David Tolnay3d772182017-12-28 17:18:53 -0500691 ty = s.ast.ident,
Nika Layzell27726662017-10-24 23:16:35 -0400692 ));
693
694 state.visit_impl.push_str(&format!(
695 "{features}\n\
David Tolnay4b4c4b62018-01-06 13:48:05 -0800696 pub fn visit_{under_name}<'ast, V: Visit<'ast> + ?Sized>(\
David Tolnayd67fb752017-12-27 13:50:29 -0500697 _visitor: &mut V, _i: &'ast {ty}) {{\n",
Nika Layzell27726662017-10-24 23:16:35 -0400698 features = s.features,
699 under_name = under_name,
David Tolnay3d772182017-12-28 17:18:53 -0500700 ty = s.ast.ident,
Nika Layzell27726662017-10-24 23:16:35 -0400701 ));
702 state.visit_mut_impl.push_str(&format!(
703 "{features}\n\
David Tolnay4b4c4b62018-01-06 13:48:05 -0800704 pub fn visit_{under_name}_mut<V: VisitMut + ?Sized>(\
David Tolnayd67fb752017-12-27 13:50:29 -0500705 _visitor: &mut V, _i: &mut {ty}) {{\n",
Nika Layzell27726662017-10-24 23:16:35 -0400706 features = s.features,
707 under_name = under_name,
David Tolnay3d772182017-12-28 17:18:53 -0500708 ty = s.ast.ident,
Nika Layzell27726662017-10-24 23:16:35 -0400709 ));
David Tolnayd0adf522017-12-29 01:30:07 -0500710 let before_fold_impl_len = state.fold_impl.len();
Nika Layzell27726662017-10-24 23:16:35 -0400711 state.fold_impl.push_str(&format!(
712 "{features}\n\
David Tolnay4b4c4b62018-01-06 13:48:05 -0800713 pub fn fold_{under_name}<V: Fold + ?Sized>(\
David Tolnayd67fb752017-12-27 13:50:29 -0500714 _visitor: &mut V, _i: {ty}) -> {ty} {{\n",
Nika Layzell27726662017-10-24 23:16:35 -0400715 features = s.features,
716 under_name = under_name,
David Tolnay3d772182017-12-28 17:18:53 -0500717 ty = s.ast.ident,
Nika Layzell27726662017-10-24 23:16:35 -0400718 ));
719
720 // XXX: This part is a disaster - I'm not sure how to make it cleaner though :'(
David Tolnaye3d41b72017-12-31 15:24:00 -0500721 match s.ast.data {
722 Data::Enum(ref e) => {
Nika Layzell27726662017-10-24 23:16:35 -0400723 state.visit_impl.push_str(" match *_i {\n");
724 state.visit_mut_impl.push_str(" match *_i {\n");
725 state.fold_impl.push_str(" match _i {\n");
726 for variant in &e.variants {
Alex Crichton715862b2018-05-17 12:31:49 -0700727 let fields: Vec<(&Field, TokenStream)> = match variant.fields {
David Tolnaye3d41b72017-12-31 15:24:00 -0500728 Fields::Named(..) => panic!("Doesn't support enum struct variants"),
729 Fields::Unnamed(ref fields) => {
David Tolnay6eff4da2018-01-01 20:27:45 -0800730 let binding = format!(" {}::{}(", s.ast.ident, variant.ident);
Nika Layzell27726662017-10-24 23:16:35 -0400731 state.visit_impl.push_str(&binding);
732 state.visit_mut_impl.push_str(&binding);
733 state.fold_impl.push_str(&binding);
734
David Tolnay01ed0ce2018-05-20 20:18:14 -0700735 let res = fields
736 .unnamed
David Tolnayd67fb752017-12-27 13:50:29 -0500737 .iter()
738 .enumerate()
739 .map(|(idx, el)| {
740 let name = format!("_binding_{}", idx);
Nika Layzell27726662017-10-24 23:16:35 -0400741
David Tolnayd67fb752017-12-27 13:50:29 -0500742 state.visit_impl.push_str("ref ");
743 state.visit_mut_impl.push_str("ref mut ");
Nika Layzell27726662017-10-24 23:16:35 -0400744
David Tolnayd67fb752017-12-27 13:50:29 -0500745 state.visit_impl.push_str(&name);
746 state.visit_mut_impl.push_str(&name);
747 state.fold_impl.push_str(&name);
748 state.visit_impl.push_str(", ");
749 state.visit_mut_impl.push_str(", ");
750 state.fold_impl.push_str(", ");
Nika Layzell27726662017-10-24 23:16:35 -0400751
David Tolnayd67fb752017-12-27 13:50:29 -0500752 let mut tokens = quote!();
Alex Crichtona74a1c82018-05-16 10:20:44 -0700753 Ident::new(&name, Span::call_site()).to_tokens(&mut tokens);
Nika Layzell27726662017-10-24 23:16:35 -0400754
David Tolnay6eff4da2018-01-01 20:27:45 -0800755 (el, tokens)
David Tolnayd67fb752017-12-27 13:50:29 -0500756 })
757 .collect();
Nika Layzell27726662017-10-24 23:16:35 -0400758
759 state.visit_impl.push_str(") => {\n");
760 state.visit_mut_impl.push_str(") => {\n");
761 state.fold_impl.push_str(") => {\n");
762
763 res
764 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500765 Fields::Unit => {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700766 state.visit_impl.push_str(&format!(
767 " {0}::{1} => {{ }}\n",
768 s.ast.ident, variant.ident
769 ));
770 state.visit_mut_impl.push_str(&format!(
771 " {0}::{1} => {{ }}\n",
772 s.ast.ident, variant.ident
773 ));
Nika Layzell27726662017-10-24 23:16:35 -0400774 state.fold_impl.push_str(&format!(
David Tolnay6702ade2017-12-30 23:38:15 -0500775 " {0}::{1} => {{ {0}::{1} }}\n",
David Tolnay01ed0ce2018-05-20 20:18:14 -0700776 s.ast.ident, variant.ident
Nika Layzell27726662017-10-24 23:16:35 -0400777 ));
David Tolnayd67fb752017-12-27 13:50:29 -0500778 continue;
Nika Layzell27726662017-10-24 23:16:35 -0400779 }
780 };
781
782 if fields.is_empty() {
783 state.visit_impl.push_str(" {}");
784 state.visit_mut_impl.push_str(") => {\n");
785 state.fold_impl.push_str(") => {\n");
786 }
David Tolnay01ed0ce2018-05-20 20:18:14 -0700787 state.fold_impl.push_str(&format!(
788 " {}::{} (\n",
789 s.ast.ident, variant.ident,
790 ));
Nika Layzell27726662017-10-24 23:16:35 -0400791 for (field, binding) in fields {
792 state.visit_impl.push_str(&format!(
793 " {};\n",
David Tolnay01ed0ce2018-05-20 20:18:14 -0700794 visit(&field.ty, lookup, Visit, &Borrowed(binding.clone()))
795 .unwrap_or_else(|| noop_visit(Visit, &Borrowed(binding.clone()))),
Nika Layzell27726662017-10-24 23:16:35 -0400796 ));
797 state.visit_mut_impl.push_str(&format!(
798 " {};\n",
David Tolnay01ed0ce2018-05-20 20:18:14 -0700799 visit(&field.ty, lookup, VisitMut, &Borrowed(binding.clone()))
800 .unwrap_or_else(|| noop_visit(
801 VisitMut,
802 &Borrowed(binding.clone())
803 )),
Nika Layzell27726662017-10-24 23:16:35 -0400804 ));
805 state.fold_impl.push_str(&format!(
806 " {},\n",
David Tolnay83db9272017-12-28 17:02:31 -0500807 visit(&field.ty, lookup, Fold, &Owned(binding.clone()))
David Tolnay01ed0ce2018-05-20 20:18:14 -0700808 .unwrap_or_else(|| noop_visit(Fold, &Owned(binding))),
Nika Layzell27726662017-10-24 23:16:35 -0400809 ));
810 }
811 state.fold_impl.push_str(" )\n");
812
813 state.visit_impl.push_str(" }\n");
814 state.visit_mut_impl.push_str(" }\n");
815 state.fold_impl.push_str(" }\n");
816 }
817 state.visit_impl.push_str(" }\n");
818 state.visit_mut_impl.push_str(" }\n");
819 state.fold_impl.push_str(" }\n");
820 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500821 Data::Struct(ref v) => {
Alex Crichton715862b2018-05-17 12:31:49 -0700822 let fields: Vec<(&Field, TokenStream)> = match v.fields {
David Tolnaye3d41b72017-12-31 15:24:00 -0500823 Fields::Named(ref fields) => {
David Tolnayd67fb752017-12-27 13:50:29 -0500824 state
825 .fold_impl
David Tolnay3d772182017-12-28 17:18:53 -0500826 .push_str(&format!(" {} {{\n", s.ast.ident));
David Tolnay01ed0ce2018-05-20 20:18:14 -0700827 fields
828 .named
David Tolnayd67fb752017-12-27 13:50:29 -0500829 .iter()
830 .map(|el| {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700831 let id = el.ident.clone();
David Tolnay6eff4da2018-01-01 20:27:45 -0800832 (el, quote!(_i.#id))
David Tolnayd67fb752017-12-27 13:50:29 -0500833 })
834 .collect()
Nika Layzell27726662017-10-24 23:16:35 -0400835 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500836 Fields::Unnamed(ref fields) => {
David Tolnayd67fb752017-12-27 13:50:29 -0500837 state
838 .fold_impl
David Tolnay3d772182017-12-28 17:18:53 -0500839 .push_str(&format!(" {} (\n", s.ast.ident));
David Tolnay01ed0ce2018-05-20 20:18:14 -0700840 fields
841 .unnamed
David Tolnayd67fb752017-12-27 13:50:29 -0500842 .iter()
843 .enumerate()
844 .map(|(idx, el)| {
David Tolnay14982012017-12-29 00:49:51 -0500845 let id = Index::from(idx);
David Tolnay6eff4da2018-01-01 20:27:45 -0800846 (el, quote!(_i.#id))
David Tolnayd67fb752017-12-27 13:50:29 -0500847 })
848 .collect()
Nika Layzell27726662017-10-24 23:16:35 -0400849 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500850 Fields::Unit => {
David Tolnay18837692018-05-20 20:17:20 -0700851 if s.ast.ident == "Ident" {
David Tolnaya23b4432018-05-20 20:38:44 -0700852 state.fold_impl.push_str(" let mut _i = _i;\n");
853 state.fold_impl.push_str(" let span = _visitor.fold_span(_i.span());\n");
854 state.fold_impl.push_str(" _i.set_span(span);\n");
David Tolnay18837692018-05-20 20:17:20 -0700855 }
David Tolnaya23b4432018-05-20 20:38:44 -0700856 state.fold_impl.push_str(" _i\n");
Nika Layzellefb83ba2017-12-19 18:23:55 -0500857 vec![]
858 }
Nika Layzell27726662017-10-24 23:16:35 -0400859 };
860
861 for (field, ref_toks) in fields {
David Tolnay83db9272017-12-28 17:02:31 -0500862 let ref_toks = Owned(ref_toks);
Nika Layzell27726662017-10-24 23:16:35 -0400863 state.visit_impl.push_str(&format!(
David Tolnayd67fb752017-12-27 13:50:29 -0500864 " {};\n",
David Tolnay83db9272017-12-28 17:02:31 -0500865 visit(&field.ty, lookup, Visit, &ref_toks)
David Tolnay01ed0ce2018-05-20 20:18:14 -0700866 .unwrap_or_else(|| noop_visit(Visit, &ref_toks,))
Nika Layzell27726662017-10-24 23:16:35 -0400867 ));
868 state.visit_mut_impl.push_str(&format!(
David Tolnayd67fb752017-12-27 13:50:29 -0500869 " {};\n",
David Tolnay83db9272017-12-28 17:02:31 -0500870 visit(&field.ty, lookup, VisitMut, &ref_toks)
David Tolnay01ed0ce2018-05-20 20:18:14 -0700871 .unwrap_or_else(|| noop_visit(VisitMut, &ref_toks,))
Nika Layzell27726662017-10-24 23:16:35 -0400872 ));
David Tolnay83db9272017-12-28 17:02:31 -0500873 let fold = visit(&field.ty, lookup, Fold, &ref_toks)
David Tolnay01ed0ce2018-05-20 20:18:14 -0700874 .unwrap_or_else(|| noop_visit(Fold, &ref_toks));
Nika Layzell27726662017-10-24 23:16:35 -0400875 if let Some(ref name) = field.ident {
David Tolnayd67fb752017-12-27 13:50:29 -0500876 state
877 .fold_impl
878 .push_str(&format!(" {}: {},\n", name, fold));
Nika Layzell27726662017-10-24 23:16:35 -0400879 } else {
880 state.fold_impl.push_str(&format!(" {},\n", fold));
881 }
882 }
883
David Tolnaye3d41b72017-12-31 15:24:00 -0500884 match v.fields {
885 Fields::Named(..) => state.fold_impl.push_str(" }\n"),
886 Fields::Unnamed(..) => state.fold_impl.push_str(" )\n"),
887 Fields::Unit => {}
Nika Layzell27726662017-10-24 23:16:35 -0400888 };
889 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500890 Data::Union(..) => panic!("Union not supported"),
Nika Layzell27726662017-10-24 23:16:35 -0400891 }
892
893 // Close the impl body
894 state.visit_impl.push_str("}\n");
895 state.visit_mut_impl.push_str("}\n");
896 state.fold_impl.push_str("}\n");
David Tolnayd0adf522017-12-29 01:30:07 -0500897
David Tolnay360efd22018-01-04 23:35:26 -0800898 if let Data::Struct(ref data) = s.ast.data {
899 if let Fields::Named(ref fields) = data.fields {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700900 if fields
901 .named
902 .iter()
903 .any(|field| field.vis == Visibility::Inherited)
904 {
David Tolnay360efd22018-01-04 23:35:26 -0800905 // Discard the generated impl if there are private fields.
906 // These have to be handwritten.
907 state.fold_impl.truncate(before_fold_impl_len);
908 }
909 }
David Tolnayd0adf522017-12-29 01:30:07 -0500910 }
Nika Layzell27726662017-10-24 23:16:35 -0400911 }
912}
913
914fn main() {
915 let mut lookup = BTreeMap::new();
David Tolnayea9ae892017-12-26 01:44:32 -0500916 load_file(SYN_CRATE_ROOT, &quote!(), &mut lookup).unwrap();
Nika Layzell27726662017-10-24 23:16:35 -0400917
Nika Layzellefb83ba2017-12-19 18:23:55 -0500918 // Load in any terminal types
919 for &tt in TERMINAL_TYPES {
920 use syn::*;
David Tolnayd67fb752017-12-27 13:50:29 -0500921 lookup.insert(
Alex Crichtona74a1c82018-05-16 10:20:44 -0700922 Ident::new(&tt, Span::call_site()),
David Tolnayd67fb752017-12-27 13:50:29 -0500923 AstItem {
David Tolnay3d772182017-12-28 17:18:53 -0500924 ast: DeriveInput {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700925 ident: Ident::new(tt, Span::call_site()),
David Tolnayd67fb752017-12-27 13:50:29 -0500926 vis: Visibility::Public(VisPublic {
927 pub_token: Default::default(),
928 }),
929 attrs: vec![],
930 generics: Default::default(),
David Tolnaye3d41b72017-12-31 15:24:00 -0500931 data: Data::Struct(DataStruct {
932 fields: Fields::Unit,
David Tolnayd67fb752017-12-27 13:50:29 -0500933 struct_token: Default::default(),
934 semi_token: None,
935 }),
936 },
hcplaa511792018-05-29 07:13:01 +0300937 features: TokenStream::new(),
David Tolnayd67fb752017-12-27 13:50:29 -0500938 eos_full: false,
Nika Layzellefb83ba2017-12-19 18:23:55 -0500939 },
David Tolnayd67fb752017-12-27 13:50:29 -0500940 );
Nika Layzellefb83ba2017-12-19 18:23:55 -0500941 }
942
Nika Layzell27726662017-10-24 23:16:35 -0400943 let mut state = Default::default();
944 for s in lookup.values() {
945 codegen::generate(&mut state, &lookup, s);
946 }
947
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400948 let full_macro = "
949#[cfg(feature = \"full\")]
950macro_rules! full {
951 ($e:expr) => { $e }
952}
953
David Tolnay0a0d78c2018-01-05 15:24:01 -0800954#[cfg(all(feature = \"derive\", not(feature = \"full\")))]
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400955macro_rules! full {
956 ($e:expr) => { unreachable!() }
957}
958";
959
Nika Layzell27726662017-10-24 23:16:35 -0400960 let mut fold_file = File::create(FOLD_SRC).unwrap();
David Tolnayd67fb752017-12-27 13:50:29 -0500961 write!(
962 fold_file,
963 "\
Nika Layzell27726662017-10-24 23:16:35 -0400964// THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT
965
David Tolnay0afc9b32017-12-27 13:38:24 -0500966#![cfg_attr(rustfmt, rustfmt_skip)]
967
Nika Layzell27726662017-10-24 23:16:35 -0400968// Unreachable code is generated sometimes without the full feature.
969#![allow(unreachable_code)]
David Tolnayf0d63bf2017-12-26 12:29:47 -0500970#![cfg_attr(feature = \"cargo-clippy\", allow(needless_pass_by_value))]
Nika Layzell27726662017-10-24 23:16:35 -0400971
Nika Layzella6f46c42017-10-26 15:26:16 -0400972use *;
David Tolnay0a0d78c2018-01-05 15:24:01 -0800973#[cfg(any(feature = \"full\", feature = \"derive\"))]
David Tolnay1e01f9c2017-12-28 20:16:19 -0500974use token::{{Brace, Bracket, Paren, Group}};
David Tolnaye303b7c2018-05-20 16:46:35 -0700975use proc_macro2::Span;
David Tolnay0a0d78c2018-01-05 15:24:01 -0800976#[cfg(any(feature = \"full\", feature = \"derive\"))]
David Tolnayf60f4262017-12-28 19:17:58 -0500977use gen::helper::fold::*;
Nika Layzell27726662017-10-24 23:16:35 -0400978
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400979{full_macro}
980
David Tolnayded2d682018-01-06 18:53:53 -0800981/// Syntax tree traversal to transform the nodes of an owned syntax tree.
Nika Layzell27726662017-10-24 23:16:35 -0400982///
David Tolnayded2d682018-01-06 18:53:53 -0800983/// See the [module documentation] for details.
Nika Layzell27726662017-10-24 23:16:35 -0400984///
David Tolnayded2d682018-01-06 18:53:53 -0800985/// [module documentation]: index.html
David Tolnay461d98e2018-01-07 11:07:19 -0800986///
987/// *This trait is available if Syn is built with the `\"fold\"` feature.*
David Tolnay4b4c4b62018-01-06 13:48:05 -0800988pub trait Fold {{
Nika Layzell27726662017-10-24 23:16:35 -0400989{fold_trait}
990}}
991
David Tolnayd3f32142018-05-20 20:21:12 -0700992#[cfg(any(feature = \"full\", feature = \"derive\"))]
David Tolnay360efd22018-01-04 23:35:26 -0800993macro_rules! fold_span_only {{
994 ($f:ident : $t:ident) => {{
David Tolnay4b4c4b62018-01-06 13:48:05 -0800995 pub fn $f<V: Fold + ?Sized>(_visitor: &mut V, mut _i: $t) -> $t {{
Alex Crichton9a4dca22018-03-28 06:32:19 -0700996 let span = _visitor.fold_span(_i.span());
997 _i.set_span(span);
David Tolnay360efd22018-01-04 23:35:26 -0800998 _i
999 }}
1000 }}
David Tolnayd0adf522017-12-29 01:30:07 -05001001}}
1002
David Tolnay360efd22018-01-04 23:35:26 -08001003#[cfg(any(feature = \"full\", feature = \"derive\"))]
1004fold_span_only!(fold_lit_byte: LitByte);
1005#[cfg(any(feature = \"full\", feature = \"derive\"))]
1006fold_span_only!(fold_lit_byte_str: LitByteStr);
1007#[cfg(any(feature = \"full\", feature = \"derive\"))]
1008fold_span_only!(fold_lit_char: LitChar);
1009#[cfg(any(feature = \"full\", feature = \"derive\"))]
1010fold_span_only!(fold_lit_float: LitFloat);
1011#[cfg(any(feature = \"full\", feature = \"derive\"))]
1012fold_span_only!(fold_lit_int: LitInt);
1013#[cfg(any(feature = \"full\", feature = \"derive\"))]
1014fold_span_only!(fold_lit_str: LitStr);
David Tolnayd0adf522017-12-29 01:30:07 -05001015
Nika Layzell27726662017-10-24 23:16:35 -04001016{fold_impl}
1017",
David Tolnayd67fb752017-12-27 13:50:29 -05001018 full_macro = full_macro,
1019 fold_trait = state.fold_trait,
1020 fold_impl = state.fold_impl
1021 ).unwrap();
Nika Layzell27726662017-10-24 23:16:35 -04001022
1023 let mut visit_file = File::create(VISIT_SRC).unwrap();
David Tolnayd67fb752017-12-27 13:50:29 -05001024 write!(
1025 visit_file,
1026 "\
Nika Layzell27726662017-10-24 23:16:35 -04001027// THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT
1028
David Tolnay0afc9b32017-12-27 13:38:24 -05001029#![cfg_attr(rustfmt, rustfmt_skip)]
1030
David Tolnayf0d63bf2017-12-26 12:29:47 -05001031#![cfg_attr(feature = \"cargo-clippy\", allow(match_same_arms))]
1032
Nika Layzella6f46c42017-10-26 15:26:16 -04001033use *;
David Tolnay0a0d78c2018-01-05 15:24:01 -08001034#[cfg(any(feature = \"full\", feature = \"derive\"))]
David Tolnay6eff4da2018-01-01 20:27:45 -08001035use punctuated::Punctuated;
David Tolnaye303b7c2018-05-20 16:46:35 -07001036use proc_macro2::Span;
David Tolnay0a0d78c2018-01-05 15:24:01 -08001037#[cfg(any(feature = \"full\", feature = \"derive\"))]
David Tolnaycc0f0372017-12-28 19:11:04 -05001038use gen::helper::visit::*;
Nika Layzell27726662017-10-24 23:16:35 -04001039
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001040{full_macro}
1041
David Tolnayded2d682018-01-06 18:53:53 -08001042/// Syntax tree traversal to walk a shared borrow of a syntax tree.
Nika Layzell27726662017-10-24 23:16:35 -04001043///
David Tolnayded2d682018-01-06 18:53:53 -08001044/// See the [module documentation] for details.
1045///
1046/// [module documentation]: index.html
David Tolnay461d98e2018-01-07 11:07:19 -08001047///
1048/// *This trait is available if Syn is built with the `\"visit\"` feature.*
David Tolnay4b4c4b62018-01-06 13:48:05 -08001049pub trait Visit<'ast> {{
Nika Layzell27726662017-10-24 23:16:35 -04001050{visit_trait}
1051}}
1052
1053{visit_impl}
1054",
David Tolnayd67fb752017-12-27 13:50:29 -05001055 full_macro = full_macro,
1056 visit_trait = state.visit_trait,
1057 visit_impl = state.visit_impl
1058 ).unwrap();
Nika Layzell27726662017-10-24 23:16:35 -04001059
1060 let mut visit_mut_file = File::create(VISIT_MUT_SRC).unwrap();
David Tolnayd67fb752017-12-27 13:50:29 -05001061 write!(
1062 visit_mut_file,
1063 "\
Nika Layzell27726662017-10-24 23:16:35 -04001064// THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT
1065
David Tolnay0afc9b32017-12-27 13:38:24 -05001066#![cfg_attr(rustfmt, rustfmt_skip)]
1067
David Tolnayf0d63bf2017-12-26 12:29:47 -05001068#![cfg_attr(feature = \"cargo-clippy\", allow(match_same_arms))]
1069
Nika Layzella6f46c42017-10-26 15:26:16 -04001070use *;
David Tolnay0a0d78c2018-01-05 15:24:01 -08001071#[cfg(any(feature = \"full\", feature = \"derive\"))]
David Tolnay6eff4da2018-01-01 20:27:45 -08001072use punctuated::Punctuated;
David Tolnaye303b7c2018-05-20 16:46:35 -07001073use proc_macro2::Span;
David Tolnay0a0d78c2018-01-05 15:24:01 -08001074#[cfg(any(feature = \"full\", feature = \"derive\"))]
David Tolnaycc0f0372017-12-28 19:11:04 -05001075use gen::helper::visit_mut::*;
Nika Layzell27726662017-10-24 23:16:35 -04001076
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001077{full_macro}
1078
David Tolnayded2d682018-01-06 18:53:53 -08001079/// Syntax tree traversal to mutate an exclusive borrow of a syntax tree in
1080/// place.
Nika Layzell27726662017-10-24 23:16:35 -04001081///
David Tolnayded2d682018-01-06 18:53:53 -08001082/// See the [module documentation] for details.
1083///
1084/// [module documentation]: index.html
David Tolnay461d98e2018-01-07 11:07:19 -08001085///
1086/// *This trait is available if Syn is built with the `\"visit-mut\"` feature.*
David Tolnay4b4c4b62018-01-06 13:48:05 -08001087pub trait VisitMut {{
Nika Layzell27726662017-10-24 23:16:35 -04001088{visit_mut_trait}
1089}}
1090
1091{visit_mut_impl}
1092",
David Tolnayd67fb752017-12-27 13:50:29 -05001093 full_macro = full_macro,
1094 visit_mut_trait = state.visit_mut_trait,
1095 visit_mut_impl = state.visit_mut_impl
1096 ).unwrap();
Nika Layzell27726662017-10-24 23:16:35 -04001097}