blob: 6eaf024818fe028d8df9409440774a511c8ce462 [file] [log] [blame]
David Tolnayea9ae892017-12-26 01:44:32 -05001//! This crate automatically generates the definition of the `Visitor`,
2//! `VisitorMut`, and `Folder` 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 quote::{ToTokens, Tokens};
David Tolnay4ba63a02017-12-28 15:53:05 -050025use syn::{Attribute, Body, BodyStruct, DeriveInput, Ident, Item};
David Tolnayd67fb752017-12-27 13:50:29 -050026use failure::{err_msg, Error};
Nika Layzell27726662017-10-24 23:16:35 -040027
David Tolnay8ed806a2017-12-26 01:28:28 -050028use std::io::{Read, Write};
David Tolnayf0d63bf2017-12-26 12:29:47 -050029use std::fmt::{self, Debug};
Nika Layzell27726662017-10-24 23:16:35 -040030use std::fs::File;
31use std::path::Path;
32use std::collections::BTreeMap;
33
34const SYN_CRATE_ROOT: &str = "../src/lib.rs";
35
36const FOLD_SRC: &str = "../src/gen/fold.rs";
37const VISIT_SRC: &str = "../src/gen/visit.rs";
38const VISIT_MUT_SRC: &str = "../src/gen/visit_mut.rs";
39
David Tolnayd67fb752017-12-27 13:50:29 -050040const IGNORED_MODS: &[&str] = &["fold", "visit", "visit_mut"];
Nika Layzell27726662017-10-24 23:16:35 -040041
David Tolnay4ba63a02017-12-28 15:53:05 -050042const EXTRA_TYPES: &[&str] = &["Ident", "Lifetime", "Lit"];
43
44const TERMINAL_TYPES: &[&str] = &["Span"];
Nika Layzellefb83ba2017-12-19 18:23:55 -050045
Nika Layzell27726662017-10-24 23:16:35 -040046fn path_eq(a: &syn::Path, b: &syn::Path) -> bool {
47 if a.global() != b.global() || a.segments.len() != b.segments.len() {
48 return false;
49 }
David Tolnayd67fb752017-12-27 13:50:29 -050050 a.segments
51 .iter()
52 .zip(b.segments.iter())
Nika Layzell27726662017-10-24 23:16:35 -040053 .all(|(a, b)| a.item().ident.as_ref() == b.item().ident.as_ref())
54}
55
56fn get_features(attrs: &[Attribute], mut features: Tokens) -> Tokens {
57 for attr in attrs {
58 if path_eq(&attr.path, &"cfg".into()) {
59 attr.to_tokens(&mut features);
60 }
61 }
62 features
63}
64
65#[derive(Clone)]
66pub struct AstItem {
David Tolnay3d772182017-12-28 17:18:53 -050067 ast: DeriveInput,
Nika Layzell27726662017-10-24 23:16:35 -040068 features: Tokens,
69 // True if this is an ast_enum_of_structs! item with a #full annotation.
70 eos_full: bool,
71}
72
David Tolnayf0d63bf2017-12-26 12:29:47 -050073impl Debug for AstItem {
Nika Layzell27726662017-10-24 23:16:35 -040074 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
75 f.debug_struct("AstItem")
David Tolnay3d772182017-12-28 17:18:53 -050076 .field("ast", &self.ast)
Nika Layzell27726662017-10-24 23:16:35 -040077 .field("features", &self.features.to_string())
78 .finish()
79 }
80}
81
82// NOTE: BTreeMap is used here instead of HashMap to have deterministic output.
David Tolnayc47f8422017-12-28 17:30:46 -050083type Lookup = BTreeMap<Ident, AstItem>;
Nika Layzell27726662017-10-24 23:16:35 -040084
David Tolnayd67fb752017-12-27 13:50:29 -050085fn load_file<P: AsRef<Path>>(name: P, features: &Tokens, lookup: &mut Lookup) -> Result<(), Error> {
Nika Layzell27726662017-10-24 23:16:35 -040086 let name = name.as_ref();
David Tolnayea9ae892017-12-26 01:44:32 -050087 let parent = name.parent().ok_or_else(|| err_msg("no parent path"))?;
Nika Layzell27726662017-10-24 23:16:35 -040088
89 let mut f = File::open(name)?;
90 let mut src = String::new();
91 f.read_to_string(&mut src)?;
92
93 // Parse the file
David Tolnayd67fb752017-12-27 13:50:29 -050094 let file =
95 syn::parse_file(&src).map_err(|_| format_err!("failed to parse {}", name.display()))?;
Nika Layzell27726662017-10-24 23:16:35 -040096
97 // Collect all of the interesting AstItems declared in this file or submodules.
David Tolnay4ba63a02017-12-28 15:53:05 -050098 'items: for item in file.items {
99 match item {
100 Item::Mod(item) => {
Nika Layzell27726662017-10-24 23:16:35 -0400101 // Don't inspect inline modules.
David Tolnayc6b55bc2017-11-09 22:48:38 -0800102 if item.content.is_some() {
Nika Layzell27726662017-10-24 23:16:35 -0400103 continue;
104 }
105
106 // We don't want to try to load the generated rust files and
107 // parse them, so we ignore them here.
108 for name in IGNORED_MODS {
David Tolnayc6b55bc2017-11-09 22:48:38 -0800109 if item.ident.as_ref() == *name {
Nika Layzell27726662017-10-24 23:16:35 -0400110 continue 'items;
111 }
112 }
113
114 // Lookup any #[cfg()] attributes on the module and add them to
115 // the feature set.
116 let features = get_features(&item.attrs, features.clone());
117
118 // Look up the submodule file, and recursively parse it.
119 // XXX: Only handles same-directory .rs file submodules.
David Tolnayc6b55bc2017-11-09 22:48:38 -0800120 let path = parent.join(&format!("{}.rs", item.ident.as_ref()));
David Tolnayea9ae892017-12-26 01:44:32 -0500121 load_file(path, &features, lookup)?;
Nika Layzell27726662017-10-24 23:16:35 -0400122 }
David Tolnay4ba63a02017-12-28 15:53:05 -0500123 Item::Macro(item) => {
Nika Layzell27726662017-10-24 23:16:35 -0400124 // Lookip any #[cfg()] attributes directly on the macro
125 // invocation, and add them to the feature set.
126 let features = get_features(&item.attrs, features.clone());
127
128 // Try to parse the AstItem declaration out of the item.
David Tolnayc6b55bc2017-11-09 22:48:38 -0800129 let found = if path_eq(&item.mac.path, &"ast_struct".into()) {
David Tolnayd67fb752017-12-27 13:50:29 -0500130 syn::parse_tokens::<parsing::AstStruct>(item.mac.tokens.clone().into_tokens())
131 .map_err(|_| err_msg("failed to parse ast_struct"))?
132 .0
David Tolnayc6b55bc2017-11-09 22:48:38 -0800133 } else if path_eq(&item.mac.path, &"ast_enum".into()) {
David Tolnayd67fb752017-12-27 13:50:29 -0500134 syn::parse_tokens::<parsing::AstEnum>(item.mac.tokens.clone().into_tokens())
135 .map_err(|_| err_msg("failed to parse ast_enum"))?
136 .0
David Tolnayc6b55bc2017-11-09 22:48:38 -0800137 } else if path_eq(&item.mac.path, &"ast_enum_of_structs".into()) {
Nika Layzell27726662017-10-24 23:16:35 -0400138 syn::parse_tokens::<parsing::AstEnumOfStructs>(
David Tolnayd67fb752017-12-27 13:50:29 -0500139 item.mac.tokens.clone().into_tokens(),
140 ).map_err(|_| err_msg("failed to parse ast_enum_of_structs"))?
141 .0
Nika Layzell27726662017-10-24 23:16:35 -0400142 } else {
David Tolnayd67fb752017-12-27 13:50:29 -0500143 continue;
Nika Layzell27726662017-10-24 23:16:35 -0400144 };
145
146 // Record our features on the parsed AstItems.
147 for mut item in found {
148 features.to_tokens(&mut item.features);
David Tolnay3d772182017-12-28 17:18:53 -0500149 lookup.insert(item.ast.ident, item);
Nika Layzell27726662017-10-24 23:16:35 -0400150 }
151 }
David Tolnay4ba63a02017-12-28 15:53:05 -0500152 Item::Struct(item) => {
153 let ident = item.ident;
154 if EXTRA_TYPES.contains(&ident.as_ref()) {
155 lookup.insert(ident, AstItem {
David Tolnay3d772182017-12-28 17:18:53 -0500156 ast: DeriveInput {
David Tolnay4ba63a02017-12-28 15:53:05 -0500157 ident: ident,
158 vis: item.vis,
159 attrs: item.attrs,
160 generics: item.generics,
161 body: Body::Struct(BodyStruct {
162 data: item.data,
163 struct_token: item.struct_token,
164 semi_token: item.semi_token,
165 }),
166 },
167 features: features.clone(),
168 eos_full: false,
169 });
170 }
171 }
Nika Layzell27726662017-10-24 23:16:35 -0400172 _ => {}
173 }
174 }
175 Ok(())
176}
177
178mod parsing {
179 use super::AstItem;
180
David Tolnayc5ab8c62017-12-26 16:43:39 -0500181 use syn::synom::*;
Nika Layzell27726662017-10-24 23:16:35 -0400182 use syn::*;
Nika Layzell27726662017-10-24 23:16:35 -0400183 use quote::Tokens;
David Tolnay2e0dba12017-12-27 01:54:40 -0500184 use proc_macro2::TokenStream;
Nika Layzell27726662017-10-24 23:16:35 -0400185
186 // Parses #full - returns #[cfg(feature = "full")] if it is present, and
187 // nothing otherwise.
188 named!(full -> (Tokens, bool), map!(option!(do_parse!(
David Tolnayf8db7ba2017-11-11 22:52:16 -0800189 punct!(#) >>
Nika Layzell27726662017-10-24 23:16:35 -0400190 id: syn!(Ident) >>
David Tolnay28c5a462017-12-27 01:59:30 -0500191 cond_reduce!(id == "full", epsilon!()) >>
David Tolnayea9ae892017-12-26 01:44:32 -0500192 ()
Nika Layzell27726662017-10-24 23:16:35 -0400193 )), |s| if s.is_some() {
194 (quote!(#[cfg(feature = "full")]), true)
195 } else {
196 (quote!(), false)
197 }));
198
David Tolnay28c5a462017-12-27 01:59:30 -0500199 named!(manual_extra_traits -> (), do_parse!(
200 punct!(#) >>
201 id: syn!(Ident) >>
202 cond_reduce!(id == "manual_extra_traits", epsilon!()) >>
203 ()
204 ));
205
Nika Layzell27726662017-10-24 23:16:35 -0400206 // Parses a simple AstStruct without the `pub struct` prefix.
207 named!(ast_struct_inner -> AstItem, do_parse!(
208 id: syn!(Ident) >>
209 features: full >>
David Tolnay28c5a462017-12-27 01:59:30 -0500210 option!(manual_extra_traits) >>
David Tolnay2e0dba12017-12-27 01:54:40 -0500211 rest: syn!(TokenStream) >>
Nika Layzell27726662017-10-24 23:16:35 -0400212 (AstItem {
David Tolnay3d772182017-12-28 17:18:53 -0500213 ast: parse_tokens::<DeriveInput>(quote! {
David Tolnay2e0dba12017-12-27 01:54:40 -0500214 pub struct #id #rest
Nika Layzell27726662017-10-24 23:16:35 -0400215 })?,
216 features: features.0,
217 eos_full: features.1,
218 })
219 ));
220
221 // ast_struct! parsing
222 pub struct AstStruct(pub Vec<AstItem>);
223 impl Synom for AstStruct {
224 named!(parse -> Self, map!(braces!(do_parse!(
David Tolnay2c136452017-12-27 14:13:32 -0500225 many0!(Attribute::parse_outer) >>
David Tolnayf8db7ba2017-11-11 22:52:16 -0800226 keyword!(pub) >>
227 keyword!(struct) >>
Nika Layzell27726662017-10-24 23:16:35 -0400228 res: call!(ast_struct_inner) >>
229 (res)
230 )), |x| AstStruct(vec![x.0])));
231 }
232
233 // ast_enum! parsing
234 pub struct AstEnum(pub Vec<AstItem>);
235 impl Synom for AstEnum {
236 named!(parse -> Self, map!(braces!(syn!(DeriveInput)), |x| {
237 AstEnum(vec![AstItem {
David Tolnay3d772182017-12-28 17:18:53 -0500238 ast: x.0,
Nika Layzell27726662017-10-24 23:16:35 -0400239 features: quote!(),
240 eos_full: false,
241 }])
242 }));
243 }
244
245 // A single variant of an ast_enum_of_structs!
246 struct EosVariant {
247 name: Ident,
David Tolnayfcfb9002017-12-28 22:04:29 -0500248 member: Option<Path>,
Nika Layzell27726662017-10-24 23:16:35 -0400249 inner: Option<AstItem>,
250 }
251 named!(eos_variant -> EosVariant, do_parse!(
David Tolnay2c136452017-12-27 14:13:32 -0500252 many0!(Attribute::parse_outer) >>
David Tolnayf8db7ba2017-11-11 22:52:16 -0800253 keyword!(pub) >>
Nika Layzell27726662017-10-24 23:16:35 -0400254 variant: syn!(Ident) >>
David Tolnayfcfb9002017-12-28 22:04:29 -0500255 member: option!(map!(parens!(alt!(
David Tolnay3d772182017-12-28 17:18:53 -0500256 call!(ast_struct_inner) => { |x: AstItem| (Path::from(x.ast.ident), Some(x)) }
Nika Layzell27726662017-10-24 23:16:35 -0400257 |
258 syn!(Path) => { |x| (x, None) }
David Tolnayfcfb9002017-12-28 22:04:29 -0500259 )), |x| x.0)) >>
David Tolnayf8db7ba2017-11-11 22:52:16 -0800260 punct!(,) >>
Nika Layzell27726662017-10-24 23:16:35 -0400261 (EosVariant {
262 name: variant,
David Tolnayfcfb9002017-12-28 22:04:29 -0500263 member: member.clone().map(|x| x.0),
264 inner: member.map(|x| x.1).unwrap_or_default(),
Nika Layzell27726662017-10-24 23:16:35 -0400265 })
266 ));
267
268 // ast_enum_of_structs! parsing
269 pub struct AstEnumOfStructs(pub Vec<AstItem>);
270 impl Synom for AstEnumOfStructs {
271 named!(parse -> Self, map!(braces!(do_parse!(
David Tolnay2c136452017-12-27 14:13:32 -0500272 many0!(Attribute::parse_outer) >>
David Tolnayf8db7ba2017-11-11 22:52:16 -0800273 keyword!(pub) >>
274 keyword!(enum) >>
Nika Layzell27726662017-10-24 23:16:35 -0400275 id: syn!(Ident) >>
David Tolnay2c136452017-12-27 14:13:32 -0500276 body: braces!(many0!(eos_variant)) >>
Nika Layzell27726662017-10-24 23:16:35 -0400277 option!(syn!(Ident)) >> // do_not_generate_to_tokens
278 ({
279 // XXX: This is really gross - we shouldn't have to convert the
280 // tokens to strings to re-parse them.
281 let enum_item = {
282 let variants = body.0.iter().map(|v| {
283 let name = v.name;
David Tolnayfcfb9002017-12-28 22:04:29 -0500284 match v.member {
285 Some(ref member) => quote!(#name(#member)),
286 None => quote!(#name),
287 }
Nika Layzell27726662017-10-24 23:16:35 -0400288 });
289 parse_tokens::<DeriveInput>(quote! {
290 pub enum #id { #(#variants),* }
291 })?
292 };
293 let mut items = vec![AstItem {
David Tolnay3d772182017-12-28 17:18:53 -0500294 ast: enum_item,
Nika Layzell27726662017-10-24 23:16:35 -0400295 features: quote!(),
296 eos_full: false,
297 }];
298 items.extend(body.0.into_iter().filter_map(|v| v.inner));
299 AstEnumOfStructs(items)
300 })
301 )), |x| x.0));
302 }
303}
304
305mod codegen {
306 use super::{AstItem, Lookup};
307 use syn::*;
David Tolnay5c4c0b52017-12-28 17:58:54 -0500308 use syn::delimited::Delimited;
David Tolnayd67fb752017-12-27 13:50:29 -0500309 use quote::{ToTokens, Tokens};
David Tolnayf0d63bf2017-12-26 12:29:47 -0500310 use std::fmt::{self, Display};
Nika Layzell27726662017-10-24 23:16:35 -0400311
312 #[derive(Default)]
313 pub struct State {
314 pub visit_trait: String,
315 pub visit_impl: String,
316 pub visit_mut_trait: String,
317 pub visit_mut_impl: String,
318 pub fold_trait: String,
319 pub fold_impl: String,
320 }
321
David Tolnay4a918742017-12-28 16:54:41 -0500322 fn under_name(name: Ident) -> Ident {
Nika Layzell27726662017-10-24 23:16:35 -0400323 use inflections::Inflect;
324 name.as_ref().to_snake_case().into()
325 }
326
David Tolnay3d772182017-12-28 17:18:53 -0500327 enum RelevantType<'a> {
328 Box(&'a Type),
329 Vec(&'a Type),
330 Delimited(&'a Type),
331 Option(&'a Type),
David Tolnay5c4c0b52017-12-28 17:58:54 -0500332 Tuple(&'a Delimited<Type, Token![,]>),
David Tolnay3d772182017-12-28 17:18:53 -0500333 Simple(&'a AstItem),
David Tolnay1e01f9c2017-12-28 20:16:19 -0500334 Token(Tokens),
David Tolnay3d772182017-12-28 17:18:53 -0500335 Pass,
336 }
Nika Layzell27726662017-10-24 23:16:35 -0400337
David Tolnay3d772182017-12-28 17:18:53 -0500338 fn classify<'a>(ty: &'a Type, lookup: &'a Lookup) -> RelevantType<'a> {
339 match *ty {
340 Type::Path(TypePath { qself: None, ref path }) => {
341 let last = path.segments.last().unwrap().into_item();
342 match last.ident.as_ref() {
343 "Box" => RelevantType::Box(first_arg(&last.arguments)),
344 "Vec" => RelevantType::Vec(first_arg(&last.arguments)),
345 "Delimited" => RelevantType::Delimited(first_arg(&last.arguments)),
346 "Option" => RelevantType::Option(first_arg(&last.arguments)),
David Tolnay1e01f9c2017-12-28 20:16:19 -0500347 "Brace" | "Bracket" | "Paren" | "Group" => {
348 RelevantType::Token(last.ident.into_tokens())
349 }
David Tolnay3d772182017-12-28 17:18:53 -0500350 _ => {
351 if let Some(item) = lookup.get(&last.ident) {
352 RelevantType::Simple(item)
353 } else {
354 RelevantType::Pass
355 }
356 }
357 }
Nika Layzell27726662017-10-24 23:16:35 -0400358 }
David Tolnayeadbda32017-12-29 02:33:47 -0500359 Type::Tuple(TypeTuple { ref elems, .. }) => {
360 RelevantType::Tuple(elems)
David Tolnay5c4c0b52017-12-28 17:58:54 -0500361 }
David Tolnaycc0f0372017-12-28 19:11:04 -0500362 Type::Macro(ref mac) if mac.path.segments.last().unwrap().into_item().ident == "Token" => {
David Tolnay1e01f9c2017-12-28 20:16:19 -0500363 RelevantType::Token(mac.into_tokens())
David Tolnaycc0f0372017-12-28 19:11:04 -0500364 }
David Tolnay3d772182017-12-28 17:18:53 -0500365 _ => RelevantType::Pass,
Nika Layzell27726662017-10-24 23:16:35 -0400366 }
367 }
368
369 #[derive(Debug, Eq, PartialEq, Copy, Clone)]
370 enum Kind {
371 Visit,
372 VisitMut,
373 Fold,
374 }
375
David Tolnayf0d63bf2017-12-26 12:29:47 -0500376 enum Operand {
377 Borrowed(Tokens),
378 Owned(Tokens),
379 }
380
David Tolnay83db9272017-12-28 17:02:31 -0500381 use self::Operand::*;
382 use self::Kind::*;
383
David Tolnayf0d63bf2017-12-26 12:29:47 -0500384 impl Operand {
385 fn tokens(&self) -> &Tokens {
386 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500387 Borrowed(ref n) | Owned(ref n) => n,
David Tolnayf0d63bf2017-12-26 12:29:47 -0500388 }
389 }
390
391 fn ref_tokens(&self) -> Tokens {
392 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500393 Borrowed(ref n) => n.clone(),
394 Owned(ref n) => quote!(&#n),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500395 }
396 }
397
398 fn ref_mut_tokens(&self) -> Tokens {
399 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500400 Borrowed(ref n) => n.clone(),
401 Owned(ref n) => quote!(&mut #n),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500402 }
403 }
404
405 fn owned_tokens(&self) -> Tokens {
406 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500407 Borrowed(ref n) => quote!(*#n),
408 Owned(ref n) => n.clone(),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500409 }
410 }
411 }
412
413 impl Display for Operand {
414 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
David Tolnay4e52d8a2017-12-28 15:54:50 -0500415 Display::fmt(self.tokens(), formatter)
David Tolnayf0d63bf2017-12-26 12:29:47 -0500416 }
417 }
418
Nika Layzellc08227a2017-12-04 16:30:17 -0500419 fn first_arg(params: &PathArguments) -> &Type {
Nika Layzell27726662017-10-24 23:16:35 -0400420 let data = match *params {
Nika Layzellc08227a2017-12-04 16:30:17 -0500421 PathArguments::AngleBracketed(ref data) => data,
422 _ => panic!("Expected at least 1 type argument here"),
Nika Layzell27726662017-10-24 23:16:35 -0400423 };
424
David Tolnayd67fb752017-12-27 13:50:29 -0500425 match **data.args
426 .first()
427 .expect("Expected at least 1 type argument here")
428 .item()
429 {
David Tolnayea9ae892017-12-26 01:44:32 -0500430 GenericArgument::Type(ref ty) => ty,
Nika Layzellc08227a2017-12-04 16:30:17 -0500431 _ => panic!("Expected at least 1 type argument here"),
Nika Layzell357885a2017-12-04 15:47:07 -0500432 }
Nika Layzell27726662017-10-24 23:16:35 -0400433 }
434
435 fn simple_visit(
David Tolnay3d772182017-12-28 17:18:53 -0500436 item: &AstItem,
Nika Layzell27726662017-10-24 23:16:35 -0400437 kind: Kind,
David Tolnayf0d63bf2017-12-26 12:29:47 -0500438 name: &Operand,
David Tolnay4a918742017-12-28 16:54:41 -0500439 ) -> String {
440 match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500441 Visit => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500442 "_visitor.visit_{under_name}({name})",
David Tolnay3d772182017-12-28 17:18:53 -0500443 under_name = under_name(item.ast.ident),
David Tolnay4a918742017-12-28 16:54:41 -0500444 name = name.ref_tokens(),
445 ),
David Tolnay83db9272017-12-28 17:02:31 -0500446 VisitMut => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500447 "_visitor.visit_{under_name}_mut({name})",
David Tolnay3d772182017-12-28 17:18:53 -0500448 under_name = under_name(item.ast.ident),
David Tolnay4a918742017-12-28 16:54:41 -0500449 name = name.ref_mut_tokens(),
450 ),
David Tolnay83db9272017-12-28 17:02:31 -0500451 Fold => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500452 "_visitor.fold_{under_name}({name})",
David Tolnay3d772182017-12-28 17:18:53 -0500453 under_name = under_name(item.ast.ident),
David Tolnay4a918742017-12-28 16:54:41 -0500454 name = name.owned_tokens(),
455 ),
Nika Layzell27726662017-10-24 23:16:35 -0400456 }
457 }
458
459 fn box_visit(
David Tolnay3d772182017-12-28 17:18:53 -0500460 elem: &Type,
Nika Layzell27726662017-10-24 23:16:35 -0400461 lookup: &Lookup,
462 kind: Kind,
David Tolnayf0d63bf2017-12-26 12:29:47 -0500463 name: &Operand,
Nika Layzell27726662017-10-24 23:16:35 -0400464 ) -> Option<String> {
David Tolnay4a918742017-12-28 16:54:41 -0500465 let name = name.owned_tokens();
David Tolnay39d0a202017-12-28 18:19:00 -0500466 let res = visit(elem, lookup, kind, &Owned(quote!(*#name)))?;
David Tolnay4a918742017-12-28 16:54:41 -0500467 Some(match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500468 Fold => format!("Box::new({})", res),
469 Visit | VisitMut => res,
David Tolnay4a918742017-12-28 16:54:41 -0500470 })
Nika Layzell27726662017-10-24 23:16:35 -0400471 }
472
473 fn vec_visit(
David Tolnay3d772182017-12-28 17:18:53 -0500474 elem: &Type,
Nika Layzell27726662017-10-24 23:16:35 -0400475 lookup: &Lookup,
476 kind: Kind,
David Tolnayf0d63bf2017-12-26 12:29:47 -0500477 name: &Operand,
Nika Layzell27726662017-10-24 23:16:35 -0400478 ) -> Option<String> {
David Tolnay4a918742017-12-28 16:54:41 -0500479 let operand = match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500480 Visit | VisitMut => Borrowed(quote!(it)),
481 Fold => Owned(quote!(it)),
David Tolnay4a918742017-12-28 16:54:41 -0500482 };
David Tolnay39d0a202017-12-28 18:19:00 -0500483 let val = visit(elem, lookup, kind, &operand)?;
David Tolnay4a918742017-12-28 16:54:41 -0500484 Some(match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500485 Visit => {
David Tolnay3d772182017-12-28 17:18:53 -0500486 format!(
487 "for it in {name} {{ {val} }}",
488 name = name.ref_tokens(),
489 val = val,
490 )
Nika Layzell27726662017-10-24 23:16:35 -0400491 }
David Tolnay83db9272017-12-28 17:02:31 -0500492 VisitMut => {
David Tolnay3d772182017-12-28 17:18:53 -0500493 format!(
494 "for it in {name} {{ {val} }}",
495 name = name.ref_mut_tokens(),
496 val = val,
497 )
498 }
499 Fold => format!(
500 "FoldHelper::lift({name}, |it| {{ {val} }})",
501 name = name.owned_tokens(),
502 val = val,
503 ),
504 })
505 }
506
507 fn delimited_visit(
508 elem: &Type,
509 lookup: &Lookup,
510 kind: Kind,
511 name: &Operand,
512 ) -> Option<String> {
513 let operand = match kind {
514 Visit | VisitMut => Borrowed(quote!(it)),
515 Fold => Owned(quote!(it)),
516 };
David Tolnay39d0a202017-12-28 18:19:00 -0500517 let val = visit(elem, lookup, kind, &operand)?;
David Tolnay3d772182017-12-28 17:18:53 -0500518 Some(match kind {
519 Visit => {
520 format!(
521 "for el in {name} {{ \
522 let it = el.item(); \
523 {val} \
524 }}",
525 name = name.ref_tokens(),
526 val = val,
527 )
528 }
529 VisitMut => {
530 format!(
531 "for mut el in {name} {{ \
532 let it = el.item_mut(); \
533 {val} \
534 }}",
535 name = name.ref_mut_tokens(),
536 val = val,
537 )
David Tolnay4a918742017-12-28 16:54:41 -0500538 }
David Tolnay83db9272017-12-28 17:02:31 -0500539 Fold => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500540 "FoldHelper::lift({name}, |it| {{ {val} }})",
541 name = name.owned_tokens(),
542 val = val,
543 ),
544 })
Nika Layzell27726662017-10-24 23:16:35 -0400545 }
546
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400547 fn option_visit(
David Tolnay3d772182017-12-28 17:18:53 -0500548 elem: &Type,
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400549 lookup: &Lookup,
550 kind: Kind,
David Tolnayf0d63bf2017-12-26 12:29:47 -0500551 name: &Operand,
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400552 ) -> Option<String> {
David Tolnay4a918742017-12-28 16:54:41 -0500553 let it = match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500554 Visit | VisitMut => Borrowed(quote!(it)),
555 Fold => Owned(quote!(it)),
David Tolnay4a918742017-12-28 16:54:41 -0500556 };
David Tolnay39d0a202017-12-28 18:19:00 -0500557 let val = visit(elem, lookup, kind, &it)?;
David Tolnay4a918742017-12-28 16:54:41 -0500558 Some(match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500559 Visit => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500560 "if let Some(ref it) = {name} {{ {val} }}",
561 name = name.owned_tokens(),
562 val = val,
563 ),
David Tolnay83db9272017-12-28 17:02:31 -0500564 VisitMut => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500565 "if let Some(ref mut it) = {name} {{ {val} }}",
566 name = name.owned_tokens(),
567 val = val,
568 ),
David Tolnay83db9272017-12-28 17:02:31 -0500569 Fold => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500570 "({name}).map(|it| {{ {val} }})",
571 name = name.owned_tokens(),
572 val = val,
573 ),
574 })
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400575 }
576
David Tolnay5c4c0b52017-12-28 17:58:54 -0500577 fn tuple_visit(
578 elems: &Delimited<Type, Token![,]>,
579 lookup: &Lookup,
580 kind: Kind,
581 name: &Operand,
582 ) -> Option<String> {
583 let mut code = String::new();
584 for (i, elem) in elems.items().enumerate() {
585 let name = name.tokens();
David Tolnay14982012017-12-29 00:49:51 -0500586 let i = Index::from(i);
David Tolnay5c4c0b52017-12-28 17:58:54 -0500587 let it = Owned(quote!((#name).#i));
David Tolnay39d0a202017-12-28 18:19:00 -0500588 let val = visit(elem, lookup, kind, &it)
David Tolnay5c4c0b52017-12-28 17:58:54 -0500589 .unwrap_or_else(|| noop_visit(kind, &it));
590 code.push_str(&format!(" {}", val));
591 match kind {
592 Fold => code.push(','),
593 Visit | VisitMut => code.push(';'),
594 }
595 code.push('\n');
596 }
597 if code.is_empty() {
598 None
599 } else {
600 Some(match kind {
601 Fold => {
602 format!("(\n{} )", code)
603 }
604 Visit | VisitMut => {
605 format!("\n{} ", code)
606 }
607 })
608 }
609 }
610
David Tolnay1e01f9c2017-12-28 20:16:19 -0500611 fn token_visit(ty: Tokens, kind: Kind, name: &Operand) -> String {
David Tolnaycc0f0372017-12-28 19:11:04 -0500612 match kind {
613 Fold => format!(
David Tolnay1e01f9c2017-12-28 20:16:19 -0500614 "{ty}(tokens_helper(_visitor, &({name}).0))",
615 ty = ty,
David Tolnaycc0f0372017-12-28 19:11:04 -0500616 name = name.owned_tokens(),
617 ),
618 Visit => format!(
619 "tokens_helper(_visitor, &({name}).0)",
620 name = name.ref_tokens(),
621 ),
622 VisitMut => format!(
623 "tokens_helper(_visitor, &mut ({name}).0)",
624 name = name.ref_mut_tokens(),
625 ),
626 }
627 }
628
David Tolnay4a918742017-12-28 16:54:41 -0500629 fn noop_visit(kind: Kind, name: &Operand) -> String {
630 match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500631 Fold => name.owned_tokens().to_string(),
632 Visit | VisitMut => format!("// Skipped field {}", name),
David Tolnay4a918742017-12-28 16:54:41 -0500633 }
634 }
635
636 fn visit(ty: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<String> {
David Tolnay3d772182017-12-28 17:18:53 -0500637 match classify(ty, lookup) {
638 RelevantType::Box(elem) => {
639 box_visit(elem, lookup, kind, name)
David Tolnay4a918742017-12-28 16:54:41 -0500640 }
David Tolnay3d772182017-12-28 17:18:53 -0500641 RelevantType::Vec(elem) => {
642 vec_visit(elem, lookup, kind, name)
David Tolnay4a918742017-12-28 16:54:41 -0500643 }
David Tolnay3d772182017-12-28 17:18:53 -0500644 RelevantType::Delimited(elem) => {
645 delimited_visit(elem, lookup, kind, name)
David Tolnay4a918742017-12-28 16:54:41 -0500646 }
David Tolnay3d772182017-12-28 17:18:53 -0500647 RelevantType::Option(elem) => {
648 option_visit(elem, lookup, kind, name)
649 }
David Tolnay5c4c0b52017-12-28 17:58:54 -0500650 RelevantType::Tuple(elems) => {
651 tuple_visit(elems, lookup, kind, name)
652 }
David Tolnay3d772182017-12-28 17:18:53 -0500653 RelevantType::Simple(item) => {
654 let mut res = simple_visit(item, kind, name);
655 Some(if item.eos_full {
656 format!("full!({res})", res = res)
657 } else {
658 res
659 })
660 }
David Tolnay1e01f9c2017-12-28 20:16:19 -0500661 RelevantType::Token(ty) => {
662 Some(token_visit(ty, kind, name))
David Tolnaycc0f0372017-12-28 19:11:04 -0500663 }
David Tolnay3d772182017-12-28 17:18:53 -0500664 RelevantType::Pass => {
665 None
Nika Layzell27726662017-10-24 23:16:35 -0400666 }
667 }
Nika Layzell27726662017-10-24 23:16:35 -0400668 }
669
670 pub fn generate(state: &mut State, lookup: &Lookup, s: &AstItem) {
David Tolnay3d772182017-12-28 17:18:53 -0500671 let under_name = under_name(s.ast.ident);
Nika Layzell27726662017-10-24 23:16:35 -0400672
673 state.visit_trait.push_str(&format!(
674 "{features}\n\
Nika Layzellc86173a2017-11-18 13:55:22 -0500675 fn visit_{under_name}(&mut self, i: &'ast {ty}) {{ \
David Tolnayd67fb752017-12-27 13:50:29 -0500676 visit_{under_name}(self, i) \
Nika Layzell27726662017-10-24 23:16:35 -0400677 }}\n",
678 features = s.features,
679 under_name = under_name,
David Tolnay3d772182017-12-28 17:18:53 -0500680 ty = s.ast.ident,
Nika Layzell27726662017-10-24 23:16:35 -0400681 ));
682 state.visit_mut_trait.push_str(&format!(
683 "{features}\n\
Nika Layzella6f46c42017-10-26 15:26:16 -0400684 fn visit_{under_name}_mut(&mut self, i: &mut {ty}) {{ \
David Tolnayd67fb752017-12-27 13:50:29 -0500685 visit_{under_name}_mut(self, i) \
Nika Layzell27726662017-10-24 23:16:35 -0400686 }}\n",
687 features = s.features,
688 under_name = under_name,
David Tolnay3d772182017-12-28 17:18:53 -0500689 ty = s.ast.ident,
Nika Layzell27726662017-10-24 23:16:35 -0400690 ));
691 state.fold_trait.push_str(&format!(
692 "{features}\n\
693 fn fold_{under_name}(&mut self, i: {ty}) -> {ty} {{ \
David Tolnayd67fb752017-12-27 13:50:29 -0500694 fold_{under_name}(self, i) \
Nika Layzell27726662017-10-24 23:16:35 -0400695 }}\n",
696 features = s.features,
697 under_name = under_name,
David Tolnay3d772182017-12-28 17:18:53 -0500698 ty = s.ast.ident,
Nika Layzell27726662017-10-24 23:16:35 -0400699 ));
700
701 state.visit_impl.push_str(&format!(
702 "{features}\n\
Nika Layzellc86173a2017-11-18 13:55:22 -0500703 pub fn visit_{under_name}<'ast, V: Visitor<'ast> + ?Sized>(\
David Tolnayd67fb752017-12-27 13:50:29 -0500704 _visitor: &mut V, _i: &'ast {ty}) {{\n",
Nika Layzell27726662017-10-24 23:16:35 -0400705 features = s.features,
706 under_name = under_name,
David Tolnay3d772182017-12-28 17:18:53 -0500707 ty = s.ast.ident,
Nika Layzell27726662017-10-24 23:16:35 -0400708 ));
709 state.visit_mut_impl.push_str(&format!(
710 "{features}\n\
Nika Layzella6f46c42017-10-26 15:26:16 -0400711 pub fn visit_{under_name}_mut<V: VisitorMut + ?Sized>(\
David Tolnayd67fb752017-12-27 13:50:29 -0500712 _visitor: &mut V, _i: &mut {ty}) {{\n",
Nika Layzell27726662017-10-24 23:16:35 -0400713 features = s.features,
714 under_name = under_name,
David Tolnay3d772182017-12-28 17:18:53 -0500715 ty = s.ast.ident,
Nika Layzell27726662017-10-24 23:16:35 -0400716 ));
David Tolnayd0adf522017-12-29 01:30:07 -0500717 let before_fold_impl_len = state.fold_impl.len();
Nika Layzell27726662017-10-24 23:16:35 -0400718 state.fold_impl.push_str(&format!(
719 "{features}\n\
Nika Layzella6f46c42017-10-26 15:26:16 -0400720 pub fn fold_{under_name}<V: Folder + ?Sized>(\
David Tolnayd67fb752017-12-27 13:50:29 -0500721 _visitor: &mut V, _i: {ty}) -> {ty} {{\n",
Nika Layzell27726662017-10-24 23:16:35 -0400722 features = s.features,
723 under_name = under_name,
David Tolnay3d772182017-12-28 17:18:53 -0500724 ty = s.ast.ident,
Nika Layzell27726662017-10-24 23:16:35 -0400725 ));
726
727 // XXX: This part is a disaster - I'm not sure how to make it cleaner though :'(
David Tolnay3d772182017-12-28 17:18:53 -0500728 match s.ast.body {
Nika Layzell27726662017-10-24 23:16:35 -0400729 Body::Enum(ref e) => {
David Tolnay3d772182017-12-28 17:18:53 -0500730 let use_decl = format!(" use ::{}::*;\n", s.ast.ident);
Nika Layzell27726662017-10-24 23:16:35 -0400731 state.visit_impl.push_str(&use_decl);
732 state.visit_mut_impl.push_str(&use_decl);
733 state.fold_impl.push_str(&use_decl);
734
735 state.visit_impl.push_str(" match *_i {\n");
736 state.visit_mut_impl.push_str(" match *_i {\n");
737 state.fold_impl.push_str(" match _i {\n");
738 for variant in &e.variants {
739 let fields: Vec<(&Field, Tokens)> = match variant.item().data {
David Tolnayd67fb752017-12-27 13:50:29 -0500740 VariantData::Struct(..) => panic!("Doesn't support enum struct variants"),
Nika Layzell27726662017-10-24 23:16:35 -0400741 VariantData::Tuple(ref fields, ..) => {
742 let binding = format!(" {}(", variant.item().ident);
743 state.visit_impl.push_str(&binding);
744 state.visit_mut_impl.push_str(&binding);
745 state.fold_impl.push_str(&binding);
746
David Tolnayd67fb752017-12-27 13:50:29 -0500747 let res = fields
748 .iter()
749 .enumerate()
750 .map(|(idx, el)| {
751 let name = format!("_binding_{}", idx);
Nika Layzell27726662017-10-24 23:16:35 -0400752
David Tolnayd67fb752017-12-27 13:50:29 -0500753 state.visit_impl.push_str("ref ");
754 state.visit_mut_impl.push_str("ref mut ");
Nika Layzell27726662017-10-24 23:16:35 -0400755
David Tolnayd67fb752017-12-27 13:50:29 -0500756 state.visit_impl.push_str(&name);
757 state.visit_mut_impl.push_str(&name);
758 state.fold_impl.push_str(&name);
759 state.visit_impl.push_str(", ");
760 state.visit_mut_impl.push_str(", ");
761 state.fold_impl.push_str(", ");
Nika Layzell27726662017-10-24 23:16:35 -0400762
David Tolnayd67fb752017-12-27 13:50:29 -0500763 let mut tokens = quote!();
764 Ident::from(name).to_tokens(&mut tokens);
Nika Layzell27726662017-10-24 23:16:35 -0400765
David Tolnayd67fb752017-12-27 13:50:29 -0500766 (*el.item(), tokens)
767 })
768 .collect();
Nika Layzell27726662017-10-24 23:16:35 -0400769
770 state.visit_impl.push_str(") => {\n");
771 state.visit_mut_impl.push_str(") => {\n");
772 state.fold_impl.push_str(") => {\n");
773
774 res
775 }
776 VariantData::Unit => {
David Tolnayd67fb752017-12-27 13:50:29 -0500777 state
778 .visit_impl
779 .push_str(&format!(" {} => {{ }}\n", variant.item().ident,));
780 state
781 .visit_mut_impl
782 .push_str(&format!(" {} => {{ }}\n", variant.item().ident,));
Nika Layzell27726662017-10-24 23:16:35 -0400783 state.fold_impl.push_str(&format!(
784 " {0} => {{ {0} }}\n",
785 variant.item().ident
786 ));
David Tolnayd67fb752017-12-27 13:50:29 -0500787 continue;
Nika Layzell27726662017-10-24 23:16:35 -0400788 }
789 };
790
791 if fields.is_empty() {
792 state.visit_impl.push_str(" {}");
793 state.visit_mut_impl.push_str(") => {\n");
794 state.fold_impl.push_str(") => {\n");
795 }
David Tolnayd67fb752017-12-27 13:50:29 -0500796 state
797 .fold_impl
798 .push_str(&format!(" {} (\n", variant.item().ident,));
Nika Layzell27726662017-10-24 23:16:35 -0400799 for (field, binding) in fields {
800 state.visit_impl.push_str(&format!(
801 " {};\n",
David Tolnayd67fb752017-12-27 13:50:29 -0500802 visit(
803 &field.ty,
804 lookup,
David Tolnay83db9272017-12-28 17:02:31 -0500805 Visit,
806 &Borrowed(binding.clone())
David Tolnay4a918742017-12-28 16:54:41 -0500807 ).unwrap_or_else(|| noop_visit(
David Tolnay83db9272017-12-28 17:02:31 -0500808 Visit,
809 &Borrowed(binding.clone())
David Tolnay4a918742017-12-28 16:54:41 -0500810 )),
Nika Layzell27726662017-10-24 23:16:35 -0400811 ));
812 state.visit_mut_impl.push_str(&format!(
813 " {};\n",
David Tolnayd67fb752017-12-27 13:50:29 -0500814 visit(
815 &field.ty,
816 lookup,
David Tolnay83db9272017-12-28 17:02:31 -0500817 VisitMut,
818 &Borrowed(binding.clone())
David Tolnay4a918742017-12-28 16:54:41 -0500819 ).unwrap_or_else(|| noop_visit(
David Tolnay83db9272017-12-28 17:02:31 -0500820 VisitMut,
821 &Borrowed(binding.clone())
David Tolnay4a918742017-12-28 16:54:41 -0500822 )),
Nika Layzell27726662017-10-24 23:16:35 -0400823 ));
824 state.fold_impl.push_str(&format!(
825 " {},\n",
David Tolnay83db9272017-12-28 17:02:31 -0500826 visit(&field.ty, lookup, Fold, &Owned(binding.clone()))
David Tolnay4a918742017-12-28 16:54:41 -0500827 .unwrap_or_else(|| noop_visit(
David Tolnay83db9272017-12-28 17:02:31 -0500828 Fold,
829 &Owned(binding)
David Tolnay4a918742017-12-28 16:54:41 -0500830 )),
Nika Layzell27726662017-10-24 23:16:35 -0400831 ));
832 }
833 state.fold_impl.push_str(" )\n");
834
835 state.visit_impl.push_str(" }\n");
836 state.visit_mut_impl.push_str(" }\n");
837 state.fold_impl.push_str(" }\n");
838 }
839 state.visit_impl.push_str(" }\n");
840 state.visit_mut_impl.push_str(" }\n");
841 state.fold_impl.push_str(" }\n");
842 }
843 Body::Struct(ref v) => {
844 let fields: Vec<(&Field, Tokens)> = match v.data {
845 VariantData::Struct(ref fields, ..) => {
David Tolnayd67fb752017-12-27 13:50:29 -0500846 state
847 .fold_impl
David Tolnay3d772182017-12-28 17:18:53 -0500848 .push_str(&format!(" {} {{\n", s.ast.ident));
David Tolnayd67fb752017-12-27 13:50:29 -0500849 fields
850 .iter()
851 .map(|el| {
852 let id = el.item().ident;
853 (*el.item(), quote!(_i.#id))
854 })
855 .collect()
Nika Layzell27726662017-10-24 23:16:35 -0400856 }
857 VariantData::Tuple(ref fields, ..) => {
David Tolnayd67fb752017-12-27 13:50:29 -0500858 state
859 .fold_impl
David Tolnay3d772182017-12-28 17:18:53 -0500860 .push_str(&format!(" {} (\n", s.ast.ident));
David Tolnayd67fb752017-12-27 13:50:29 -0500861 fields
862 .iter()
863 .enumerate()
864 .map(|(idx, el)| {
David Tolnay14982012017-12-29 00:49:51 -0500865 let id = Index::from(idx);
David Tolnayd67fb752017-12-27 13:50:29 -0500866 (*el.item(), quote!(_i.#id))
867 })
868 .collect()
Nika Layzell27726662017-10-24 23:16:35 -0400869 }
Nika Layzellefb83ba2017-12-19 18:23:55 -0500870 VariantData::Unit => {
871 state.fold_impl.push_str(" _i\n");
872 vec![]
873 }
Nika Layzell27726662017-10-24 23:16:35 -0400874 };
875
876 for (field, ref_toks) in fields {
David Tolnay83db9272017-12-28 17:02:31 -0500877 let ref_toks = Owned(ref_toks);
Nika Layzell27726662017-10-24 23:16:35 -0400878 state.visit_impl.push_str(&format!(
David Tolnayd67fb752017-12-27 13:50:29 -0500879 " {};\n",
David Tolnay83db9272017-12-28 17:02:31 -0500880 visit(&field.ty, lookup, Visit, &ref_toks)
David Tolnay4a918742017-12-28 16:54:41 -0500881 .unwrap_or_else(|| noop_visit(
David Tolnay83db9272017-12-28 17:02:31 -0500882 Visit,
David Tolnay4a918742017-12-28 16:54:41 -0500883 &ref_toks,
884 ))
Nika Layzell27726662017-10-24 23:16:35 -0400885 ));
886 state.visit_mut_impl.push_str(&format!(
David Tolnayd67fb752017-12-27 13:50:29 -0500887 " {};\n",
David Tolnay83db9272017-12-28 17:02:31 -0500888 visit(&field.ty, lookup, VisitMut, &ref_toks)
David Tolnay4a918742017-12-28 16:54:41 -0500889 .unwrap_or_else(|| noop_visit(
David Tolnay83db9272017-12-28 17:02:31 -0500890 VisitMut,
David Tolnay4a918742017-12-28 16:54:41 -0500891 &ref_toks,
892 ))
Nika Layzell27726662017-10-24 23:16:35 -0400893 ));
David Tolnay83db9272017-12-28 17:02:31 -0500894 let fold = visit(&field.ty, lookup, Fold, &ref_toks)
David Tolnay4a918742017-12-28 16:54:41 -0500895 .unwrap_or_else(|| noop_visit(
David Tolnay83db9272017-12-28 17:02:31 -0500896 Fold,
David Tolnay4a918742017-12-28 16:54:41 -0500897 &ref_toks,
898 ));
Nika Layzell27726662017-10-24 23:16:35 -0400899 if let Some(ref name) = field.ident {
David Tolnayd67fb752017-12-27 13:50:29 -0500900 state
901 .fold_impl
902 .push_str(&format!(" {}: {},\n", name, fold));
Nika Layzell27726662017-10-24 23:16:35 -0400903 } else {
904 state.fold_impl.push_str(&format!(" {},\n", fold));
905 }
906 }
907
908 match v.data {
909 VariantData::Struct(..) => state.fold_impl.push_str(" }\n"),
910 VariantData::Tuple(..) => state.fold_impl.push_str(" )\n"),
911 VariantData::Unit => {}
912 };
913 }
914 }
915
916 // Close the impl body
917 state.visit_impl.push_str("}\n");
918 state.visit_mut_impl.push_str("}\n");
919 state.fold_impl.push_str("}\n");
David Tolnayd0adf522017-12-29 01:30:07 -0500920
921 if s.ast.ident == "Ident" || s.ast.ident == "Lifetime" {
922 // Discard the generated impl. These have private fields and are
923 // handwritten.
924 state.fold_impl.truncate(before_fold_impl_len);
925 }
Nika Layzell27726662017-10-24 23:16:35 -0400926 }
927}
928
929fn main() {
930 let mut lookup = BTreeMap::new();
David Tolnayea9ae892017-12-26 01:44:32 -0500931 load_file(SYN_CRATE_ROOT, &quote!(), &mut lookup).unwrap();
Nika Layzell27726662017-10-24 23:16:35 -0400932
Nika Layzellefb83ba2017-12-19 18:23:55 -0500933 // Load in any terminal types
934 for &tt in TERMINAL_TYPES {
935 use syn::*;
David Tolnayd67fb752017-12-27 13:50:29 -0500936 lookup.insert(
937 Ident::from(tt),
938 AstItem {
David Tolnay3d772182017-12-28 17:18:53 -0500939 ast: DeriveInput {
David Tolnayd67fb752017-12-27 13:50:29 -0500940 ident: Ident::from(tt),
941 vis: Visibility::Public(VisPublic {
942 pub_token: Default::default(),
943 }),
944 attrs: vec![],
945 generics: Default::default(),
946 body: Body::Struct(BodyStruct {
947 data: VariantData::Unit,
948 struct_token: Default::default(),
949 semi_token: None,
950 }),
951 },
952 features: Default::default(),
953 eos_full: false,
Nika Layzellefb83ba2017-12-19 18:23:55 -0500954 },
David Tolnayd67fb752017-12-27 13:50:29 -0500955 );
Nika Layzellefb83ba2017-12-19 18:23:55 -0500956 }
957
Nika Layzell27726662017-10-24 23:16:35 -0400958 let mut state = Default::default();
959 for s in lookup.values() {
960 codegen::generate(&mut state, &lookup, s);
961 }
962
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400963 let full_macro = "
964#[cfg(feature = \"full\")]
965macro_rules! full {
966 ($e:expr) => { $e }
967}
968
969#[cfg(not(feature = \"full\"))]
970macro_rules! full {
971 ($e:expr) => { unreachable!() }
972}
973";
974
Nika Layzell27726662017-10-24 23:16:35 -0400975 let mut fold_file = File::create(FOLD_SRC).unwrap();
David Tolnayd67fb752017-12-27 13:50:29 -0500976 write!(
977 fold_file,
978 "\
Nika Layzell27726662017-10-24 23:16:35 -0400979// THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT
980
981//! A Folder represents an AST->AST fold; it accepts an AST piece,
982//! and returns a piece of the same type.
983
David Tolnay0afc9b32017-12-27 13:38:24 -0500984#![cfg_attr(rustfmt, rustfmt_skip)]
985
Nika Layzell27726662017-10-24 23:16:35 -0400986// Unreachable code is generated sometimes without the full feature.
987#![allow(unreachable_code)]
David Tolnayf0d63bf2017-12-26 12:29:47 -0500988#![cfg_attr(feature = \"cargo-clippy\", allow(needless_pass_by_value))]
Nika Layzell27726662017-10-24 23:16:35 -0400989
Nika Layzella6f46c42017-10-26 15:26:16 -0400990use *;
David Tolnay1e01f9c2017-12-28 20:16:19 -0500991use token::{{Brace, Bracket, Paren, Group}};
David Tolnay98942562017-12-26 21:24:35 -0500992use proc_macro2::Span;
David Tolnayf60f4262017-12-28 19:17:58 -0500993use gen::helper::fold::*;
Nika Layzell27726662017-10-24 23:16:35 -0400994
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400995{full_macro}
996
Nika Layzell27726662017-10-24 23:16:35 -0400997/// AST->AST fold.
998///
999/// Each method of the Folder trait is a hook to be potentially overridden. Each
1000/// method's default implementation recursively visits the substructure of the
1001/// input via the `walk` functions, which perform an \"identity fold\", that
1002/// is, they return the same structure that they are given (for example the
1003/// `fold_file` method by default calls `fold::walk_file`).
1004///
1005/// If you want to ensure that your code handles every variant
1006/// explicitly, you need to override each method. (And you also need
1007/// to monitor future changes to `Folder` in case a new method with a
1008/// new default implementation gets introduced.)
1009pub trait Folder {{
1010{fold_trait}
1011}}
1012
David Tolnayd0adf522017-12-29 01:30:07 -05001013pub fn fold_ident<V: Folder + ?Sized>(_visitor: &mut V, mut _i: Ident) -> Ident {{
1014 _i.span = _visitor.fold_span(_i.span);
1015 _i
1016}}
1017
1018pub fn fold_lifetime<V: Folder + ?Sized>(_visitor: &mut V, mut _i: Lifetime) -> Lifetime {{
1019 _i.span = _visitor.fold_span(_i.span);
1020 _i
1021}}
1022
Nika Layzell27726662017-10-24 23:16:35 -04001023{fold_impl}
1024",
David Tolnayd67fb752017-12-27 13:50:29 -05001025 full_macro = full_macro,
1026 fold_trait = state.fold_trait,
1027 fold_impl = state.fold_impl
1028 ).unwrap();
Nika Layzell27726662017-10-24 23:16:35 -04001029
1030 let mut visit_file = File::create(VISIT_SRC).unwrap();
David Tolnayd67fb752017-12-27 13:50:29 -05001031 write!(
1032 visit_file,
1033 "\
Nika Layzell27726662017-10-24 23:16:35 -04001034// THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT
1035
1036//! AST walker. Each overridden visit method has full control over what
1037//! happens with its node, it can do its own traversal of the node's children,
1038//! call `visit::walk_*` to apply the default traversal algorithm, or prevent
1039//! deeper traversal by doing nothing.
1040
David Tolnay0afc9b32017-12-27 13:38:24 -05001041#![cfg_attr(rustfmt, rustfmt_skip)]
1042
David Tolnayf0d63bf2017-12-26 12:29:47 -05001043#![cfg_attr(feature = \"cargo-clippy\", allow(match_same_arms))]
1044
Nika Layzella6f46c42017-10-26 15:26:16 -04001045use *;
David Tolnay98942562017-12-26 21:24:35 -05001046use proc_macro2::Span;
David Tolnaycc0f0372017-12-28 19:11:04 -05001047use gen::helper::visit::*;
Nika Layzell27726662017-10-24 23:16:35 -04001048
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001049{full_macro}
1050
Nika Layzell27726662017-10-24 23:16:35 -04001051/// Each method of the Visitor trait is a hook to be potentially
1052/// overridden. Each method's default implementation recursively visits
1053/// the substructure of the input via the corresponding `walk` method;
1054/// e.g. the `visit_mod` method by default calls `visit::walk_mod`.
1055///
1056/// If you want to ensure that your code handles every variant
1057/// explicitly, you need to override each method. (And you also need
1058/// to monitor future changes to `Visitor` in case a new method with a
1059/// new default implementation gets introduced.)
Nika Layzellc86173a2017-11-18 13:55:22 -05001060pub trait Visitor<'ast> {{
Nika Layzell27726662017-10-24 23:16:35 -04001061{visit_trait}
1062}}
1063
1064{visit_impl}
1065",
David Tolnayd67fb752017-12-27 13:50:29 -05001066 full_macro = full_macro,
1067 visit_trait = state.visit_trait,
1068 visit_impl = state.visit_impl
1069 ).unwrap();
Nika Layzell27726662017-10-24 23:16:35 -04001070
1071 let mut visit_mut_file = File::create(VISIT_MUT_SRC).unwrap();
David Tolnayd67fb752017-12-27 13:50:29 -05001072 write!(
1073 visit_mut_file,
1074 "\
Nika Layzell27726662017-10-24 23:16:35 -04001075// THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT
1076
1077//! AST walker. Each overridden visit method has full control over what
1078//! happens with its node, it can do its own traversal of the node's children,
1079//! call `visit::walk_*` to apply the default traversal algorithm, or prevent
1080//! deeper traversal by doing nothing.
1081
David Tolnay0afc9b32017-12-27 13:38:24 -05001082#![cfg_attr(rustfmt, rustfmt_skip)]
1083
David Tolnayf0d63bf2017-12-26 12:29:47 -05001084#![cfg_attr(feature = \"cargo-clippy\", allow(match_same_arms))]
1085
Nika Layzella6f46c42017-10-26 15:26:16 -04001086use *;
David Tolnay98942562017-12-26 21:24:35 -05001087use proc_macro2::Span;
David Tolnaycc0f0372017-12-28 19:11:04 -05001088use gen::helper::visit_mut::*;
Nika Layzell27726662017-10-24 23:16:35 -04001089
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001090{full_macro}
1091
Nika Layzell27726662017-10-24 23:16:35 -04001092/// Each method of the VisitorMut trait is a hook to be potentially
1093/// overridden. Each method's default implementation recursively visits
1094/// the substructure of the input via the corresponding `walk` method;
1095/// e.g. the `visit_mod` method by default calls `visit::walk_mod`.
1096///
1097/// If you want to ensure that your code handles every variant
1098/// explicitly, you need to override each method. (And you also need
1099/// to monitor future changes to `VisitorMut` in case a new method with a
1100/// new default implementation gets introduced.)
1101pub trait VisitorMut {{
1102{visit_mut_trait}
1103}}
1104
1105{visit_mut_impl}
1106",
David Tolnayd67fb752017-12-27 13:50:29 -05001107 full_macro = full_macro,
1108 visit_mut_trait = state.visit_mut_trait,
1109 visit_mut_impl = state.visit_mut_impl
1110 ).unwrap();
Nika Layzell27726662017-10-24 23:16:35 -04001111}