blob: e849d669ec5e70a4f311b4bdbe9df09794e6e1ab [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 Tolnaye3d41b72017-12-31 15:24:00 -050025use syn::{Attribute, Data, DataStruct, 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 Tolnay360efd22018-01-04 23:35:26 -080042const EXTRA_TYPES: &[&str] = &["Ident", "Lifetime"];
David Tolnay4ba63a02017-12-28 15:53:05 -050043
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())
David Tolnay0a0d78c2018-01-05 15:24:01 -080053 .all(|(a, b)| a.ident == b.ident)
Nika Layzell27726662017-10-24 23:16:35 -040054}
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 Tolnay0a0d78c2018-01-05 15:24:01 -0800109 if item.ident == 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.
David Tolnay0a0d78c2018-01-05 15:24:01 -0800116 //
117 // The derive module is weird because it is built with either
118 // `full` or `derive` but exported only under `derive`.
119 let features = if item.ident == "derive" {
120 quote!(#[cfg(feature = "derive")])
121 } else {
122 get_features(&item.attrs, features.clone())
123 };
Nika Layzell27726662017-10-24 23:16:35 -0400124
125 // Look up the submodule file, and recursively parse it.
126 // XXX: Only handles same-directory .rs file submodules.
David Tolnayc6b55bc2017-11-09 22:48:38 -0800127 let path = parent.join(&format!("{}.rs", item.ident.as_ref()));
David Tolnayea9ae892017-12-26 01:44:32 -0500128 load_file(path, &features, lookup)?;
Nika Layzell27726662017-10-24 23:16:35 -0400129 }
David Tolnay4ba63a02017-12-28 15:53:05 -0500130 Item::Macro(item) => {
Nika Layzell27726662017-10-24 23:16:35 -0400131 // Lookip any #[cfg()] attributes directly on the macro
132 // invocation, and add them to the feature set.
133 let features = get_features(&item.attrs, features.clone());
134
135 // Try to parse the AstItem declaration out of the item.
David Tolnay01a77582018-01-01 20:00:51 -0800136 let tts = &item.mac.tts;
David Tolnayc6b55bc2017-11-09 22:48:38 -0800137 let found = if path_eq(&item.mac.path, &"ast_struct".into()) {
David Tolnay01a77582018-01-01 20:00:51 -0800138 syn::parse_str::<parsing::AstStruct>(&quote!(#tts).to_string())
David Tolnayd67fb752017-12-27 13:50:29 -0500139 .map_err(|_| err_msg("failed to parse ast_struct"))?
140 .0
David Tolnayc6b55bc2017-11-09 22:48:38 -0800141 } else if path_eq(&item.mac.path, &"ast_enum".into()) {
David Tolnay01a77582018-01-01 20:00:51 -0800142 syn::parse_str::<parsing::AstEnum>(&quote!(#tts).to_string())
David Tolnayd67fb752017-12-27 13:50:29 -0500143 .map_err(|_| err_msg("failed to parse ast_enum"))?
144 .0
David Tolnayc6b55bc2017-11-09 22:48:38 -0800145 } else if path_eq(&item.mac.path, &"ast_enum_of_structs".into()) {
David Tolnay01a77582018-01-01 20:00:51 -0800146 syn::parse_str::<parsing::AstEnumOfStructs>(&quote!(#tts).to_string())
David Tolnay1cf80912017-12-31 18:35:12 -0500147 .map_err(|_| err_msg("failed to parse ast_enum_of_structs"))?
David Tolnayd67fb752017-12-27 13:50:29 -0500148 .0
Nika Layzell27726662017-10-24 23:16:35 -0400149 } else {
David Tolnayd67fb752017-12-27 13:50:29 -0500150 continue;
Nika Layzell27726662017-10-24 23:16:35 -0400151 };
152
153 // Record our features on the parsed AstItems.
154 for mut item in found {
155 features.to_tokens(&mut item.features);
David Tolnay3d772182017-12-28 17:18:53 -0500156 lookup.insert(item.ast.ident, item);
Nika Layzell27726662017-10-24 23:16:35 -0400157 }
158 }
David Tolnay4ba63a02017-12-28 15:53:05 -0500159 Item::Struct(item) => {
160 let ident = item.ident;
161 if EXTRA_TYPES.contains(&ident.as_ref()) {
162 lookup.insert(ident, AstItem {
David Tolnay3d772182017-12-28 17:18:53 -0500163 ast: DeriveInput {
David Tolnay4ba63a02017-12-28 15:53:05 -0500164 ident: ident,
165 vis: item.vis,
166 attrs: item.attrs,
167 generics: item.generics,
David Tolnaye3d41b72017-12-31 15:24:00 -0500168 data: Data::Struct(DataStruct {
169 fields: item.fields,
David Tolnay4ba63a02017-12-28 15:53:05 -0500170 struct_token: item.struct_token,
171 semi_token: item.semi_token,
172 }),
173 },
174 features: features.clone(),
175 eos_full: false,
176 });
177 }
178 }
Nika Layzell27726662017-10-24 23:16:35 -0400179 _ => {}
180 }
181 }
182 Ok(())
183}
184
185mod parsing {
186 use super::AstItem;
187
David Tolnay1cf80912017-12-31 18:35:12 -0500188 use syn;
David Tolnayc5ab8c62017-12-26 16:43:39 -0500189 use syn::synom::*;
Nika Layzell27726662017-10-24 23:16:35 -0400190 use syn::*;
Nika Layzell27726662017-10-24 23:16:35 -0400191 use quote::Tokens;
David Tolnay2e0dba12017-12-27 01:54:40 -0500192 use proc_macro2::TokenStream;
Nika Layzell27726662017-10-24 23:16:35 -0400193
194 // Parses #full - returns #[cfg(feature = "full")] if it is present, and
195 // nothing otherwise.
196 named!(full -> (Tokens, bool), map!(option!(do_parse!(
David Tolnayf8db7ba2017-11-11 22:52:16 -0800197 punct!(#) >>
Nika Layzell27726662017-10-24 23:16:35 -0400198 id: syn!(Ident) >>
David Tolnay28c5a462017-12-27 01:59:30 -0500199 cond_reduce!(id == "full", epsilon!()) >>
David Tolnayea9ae892017-12-26 01:44:32 -0500200 ()
Nika Layzell27726662017-10-24 23:16:35 -0400201 )), |s| if s.is_some() {
202 (quote!(#[cfg(feature = "full")]), true)
203 } else {
204 (quote!(), false)
205 }));
206
David Tolnay28c5a462017-12-27 01:59:30 -0500207 named!(manual_extra_traits -> (), do_parse!(
208 punct!(#) >>
209 id: syn!(Ident) >>
210 cond_reduce!(id == "manual_extra_traits", epsilon!()) >>
211 ()
212 ));
213
Nika Layzell27726662017-10-24 23:16:35 -0400214 // Parses a simple AstStruct without the `pub struct` prefix.
215 named!(ast_struct_inner -> AstItem, do_parse!(
216 id: syn!(Ident) >>
217 features: full >>
David Tolnay28c5a462017-12-27 01:59:30 -0500218 option!(manual_extra_traits) >>
David Tolnay2e0dba12017-12-27 01:54:40 -0500219 rest: syn!(TokenStream) >>
Nika Layzell27726662017-10-24 23:16:35 -0400220 (AstItem {
David Tolnay01a77582018-01-01 20:00:51 -0800221 ast: syn::parse_str(&quote! {
David Tolnay2e0dba12017-12-27 01:54:40 -0500222 pub struct #id #rest
David Tolnay01a77582018-01-01 20:00:51 -0800223 }.to_string())?,
Nika Layzell27726662017-10-24 23:16:35 -0400224 features: features.0,
225 eos_full: features.1,
226 })
227 ));
228
229 // ast_struct! parsing
230 pub struct AstStruct(pub Vec<AstItem>);
231 impl Synom for AstStruct {
David Tolnayab919512017-12-30 23:31:51 -0500232 named!(parse -> Self, do_parse!(
David Tolnay2c136452017-12-27 14:13:32 -0500233 many0!(Attribute::parse_outer) >>
David Tolnayf8db7ba2017-11-11 22:52:16 -0800234 keyword!(pub) >>
235 keyword!(struct) >>
Nika Layzell27726662017-10-24 23:16:35 -0400236 res: call!(ast_struct_inner) >>
David Tolnayab919512017-12-30 23:31:51 -0500237 (AstStruct(vec![res]))
238 ));
Nika Layzell27726662017-10-24 23:16:35 -0400239 }
240
David Tolnay360efd22018-01-04 23:35:26 -0800241 named!(no_visit -> (), do_parse!(
242 punct!(#) >>
243 id: syn!(Ident) >>
244 cond_reduce!(id == "no_visit", epsilon!()) >>
245 ()
246 ));
247
Nika Layzell27726662017-10-24 23:16:35 -0400248 // ast_enum! parsing
249 pub struct AstEnum(pub Vec<AstItem>);
250 impl Synom for AstEnum {
David Tolnay360efd22018-01-04 23:35:26 -0800251 named!(parse -> Self, do_parse!(
252 many0!(Attribute::parse_outer) >>
253 keyword!(pub) >>
254 keyword!(enum) >>
255 id: syn!(Ident) >>
256 no_visit: option!(no_visit) >>
257 rest: syn!(TokenStream) >>
258 (AstEnum(if no_visit.is_some() {
259 vec![]
260 } else {
261 vec![AstItem {
262 ast: syn::parse_str(&quote! {
263 pub enum #id #rest
264 }.to_string())?,
265 features: quote!(),
266 eos_full: false,
267 }]
268 }))
269 ));
Nika Layzell27726662017-10-24 23:16:35 -0400270 }
271
272 // A single variant of an ast_enum_of_structs!
273 struct EosVariant {
274 name: Ident,
David Tolnayfcfb9002017-12-28 22:04:29 -0500275 member: Option<Path>,
Nika Layzell27726662017-10-24 23:16:35 -0400276 inner: Option<AstItem>,
277 }
278 named!(eos_variant -> EosVariant, do_parse!(
David Tolnay2c136452017-12-27 14:13:32 -0500279 many0!(Attribute::parse_outer) >>
David Tolnayf8db7ba2017-11-11 22:52:16 -0800280 keyword!(pub) >>
Nika Layzell27726662017-10-24 23:16:35 -0400281 variant: syn!(Ident) >>
David Tolnayfcfb9002017-12-28 22:04:29 -0500282 member: option!(map!(parens!(alt!(
David Tolnay3d772182017-12-28 17:18:53 -0500283 call!(ast_struct_inner) => { |x: AstItem| (Path::from(x.ast.ident), Some(x)) }
Nika Layzell27726662017-10-24 23:16:35 -0400284 |
285 syn!(Path) => { |x| (x, None) }
David Tolnay8875fca2017-12-31 13:52:37 -0500286 )), |x| x.1)) >>
David Tolnayf8db7ba2017-11-11 22:52:16 -0800287 punct!(,) >>
Nika Layzell27726662017-10-24 23:16:35 -0400288 (EosVariant {
289 name: variant,
David Tolnayfcfb9002017-12-28 22:04:29 -0500290 member: member.clone().map(|x| x.0),
291 inner: member.map(|x| x.1).unwrap_or_default(),
Nika Layzell27726662017-10-24 23:16:35 -0400292 })
293 ));
294
295 // ast_enum_of_structs! parsing
296 pub struct AstEnumOfStructs(pub Vec<AstItem>);
297 impl Synom for AstEnumOfStructs {
David Tolnayab919512017-12-30 23:31:51 -0500298 named!(parse -> Self, do_parse!(
David Tolnay2c136452017-12-27 14:13:32 -0500299 many0!(Attribute::parse_outer) >>
David Tolnayf8db7ba2017-11-11 22:52:16 -0800300 keyword!(pub) >>
301 keyword!(enum) >>
Nika Layzell27726662017-10-24 23:16:35 -0400302 id: syn!(Ident) >>
David Tolnaye3d41b72017-12-31 15:24:00 -0500303 variants: braces!(many0!(eos_variant)) >>
Nika Layzell27726662017-10-24 23:16:35 -0400304 option!(syn!(Ident)) >> // do_not_generate_to_tokens
305 ({
306 // XXX: This is really gross - we shouldn't have to convert the
307 // tokens to strings to re-parse them.
308 let enum_item = {
David Tolnaye3d41b72017-12-31 15:24:00 -0500309 let variants = variants.1.iter().map(|v| {
Nika Layzell27726662017-10-24 23:16:35 -0400310 let name = v.name;
David Tolnayfcfb9002017-12-28 22:04:29 -0500311 match v.member {
312 Some(ref member) => quote!(#name(#member)),
313 None => quote!(#name),
314 }
Nika Layzell27726662017-10-24 23:16:35 -0400315 });
David Tolnay01a77582018-01-01 20:00:51 -0800316 syn::parse_str(&quote! {
Nika Layzell27726662017-10-24 23:16:35 -0400317 pub enum #id { #(#variants),* }
David Tolnay01a77582018-01-01 20:00:51 -0800318 }.to_string())?
Nika Layzell27726662017-10-24 23:16:35 -0400319 };
320 let mut items = vec![AstItem {
David Tolnay3d772182017-12-28 17:18:53 -0500321 ast: enum_item,
Nika Layzell27726662017-10-24 23:16:35 -0400322 features: quote!(),
323 eos_full: false,
324 }];
David Tolnaye3d41b72017-12-31 15:24:00 -0500325 items.extend(variants.1.into_iter().filter_map(|v| v.inner));
Nika Layzell27726662017-10-24 23:16:35 -0400326 AstEnumOfStructs(items)
327 })
David Tolnayab919512017-12-30 23:31:51 -0500328 ));
Nika Layzell27726662017-10-24 23:16:35 -0400329 }
330}
331
332mod codegen {
333 use super::{AstItem, Lookup};
334 use syn::*;
David Tolnayf2cfd722017-12-31 18:02:51 -0500335 use syn::punctuated::Punctuated;
David Tolnayd67fb752017-12-27 13:50:29 -0500336 use quote::{ToTokens, Tokens};
David Tolnayf0d63bf2017-12-26 12:29:47 -0500337 use std::fmt::{self, Display};
Nika Layzell27726662017-10-24 23:16:35 -0400338
339 #[derive(Default)]
340 pub struct State {
341 pub visit_trait: String,
342 pub visit_impl: String,
343 pub visit_mut_trait: String,
344 pub visit_mut_impl: String,
345 pub fold_trait: String,
346 pub fold_impl: String,
347 }
348
David Tolnay4a918742017-12-28 16:54:41 -0500349 fn under_name(name: Ident) -> Ident {
Nika Layzell27726662017-10-24 23:16:35 -0400350 use inflections::Inflect;
351 name.as_ref().to_snake_case().into()
352 }
353
David Tolnay3d772182017-12-28 17:18:53 -0500354 enum RelevantType<'a> {
355 Box(&'a Type),
356 Vec(&'a Type),
David Tolnayf2cfd722017-12-31 18:02:51 -0500357 Punctuated(&'a Type),
David Tolnay3d772182017-12-28 17:18:53 -0500358 Option(&'a Type),
David Tolnayf2cfd722017-12-31 18:02:51 -0500359 Tuple(&'a Punctuated<Type, Token![,]>),
David Tolnay3d772182017-12-28 17:18:53 -0500360 Simple(&'a AstItem),
David Tolnay1e01f9c2017-12-28 20:16:19 -0500361 Token(Tokens),
David Tolnay3d772182017-12-28 17:18:53 -0500362 Pass,
363 }
Nika Layzell27726662017-10-24 23:16:35 -0400364
David Tolnay3d772182017-12-28 17:18:53 -0500365 fn classify<'a>(ty: &'a Type, lookup: &'a Lookup) -> RelevantType<'a> {
366 match *ty {
367 Type::Path(TypePath { qself: None, ref path }) => {
368 let last = path.segments.last().unwrap().into_item();
369 match last.ident.as_ref() {
370 "Box" => RelevantType::Box(first_arg(&last.arguments)),
371 "Vec" => RelevantType::Vec(first_arg(&last.arguments)),
David Tolnayf2cfd722017-12-31 18:02:51 -0500372 "Punctuated" => RelevantType::Punctuated(first_arg(&last.arguments)),
David Tolnay3d772182017-12-28 17:18:53 -0500373 "Option" => RelevantType::Option(first_arg(&last.arguments)),
David Tolnay1e01f9c2017-12-28 20:16:19 -0500374 "Brace" | "Bracket" | "Paren" | "Group" => {
375 RelevantType::Token(last.ident.into_tokens())
376 }
David Tolnay3d772182017-12-28 17:18:53 -0500377 _ => {
378 if let Some(item) = lookup.get(&last.ident) {
379 RelevantType::Simple(item)
380 } else {
381 RelevantType::Pass
382 }
383 }
384 }
Nika Layzell27726662017-10-24 23:16:35 -0400385 }
David Tolnayeadbda32017-12-29 02:33:47 -0500386 Type::Tuple(TypeTuple { ref elems, .. }) => {
387 RelevantType::Tuple(elems)
David Tolnay5c4c0b52017-12-28 17:58:54 -0500388 }
David Tolnay323279a2017-12-29 11:26:32 -0500389 Type::Macro(TypeMacro { ref mac }) if mac.path.segments.last().unwrap().into_item().ident == "Token" => {
David Tolnay1e01f9c2017-12-28 20:16:19 -0500390 RelevantType::Token(mac.into_tokens())
David Tolnaycc0f0372017-12-28 19:11:04 -0500391 }
David Tolnay3d772182017-12-28 17:18:53 -0500392 _ => RelevantType::Pass,
Nika Layzell27726662017-10-24 23:16:35 -0400393 }
394 }
395
396 #[derive(Debug, Eq, PartialEq, Copy, Clone)]
397 enum Kind {
398 Visit,
399 VisitMut,
400 Fold,
401 }
402
David Tolnayf0d63bf2017-12-26 12:29:47 -0500403 enum Operand {
404 Borrowed(Tokens),
405 Owned(Tokens),
406 }
407
David Tolnay83db9272017-12-28 17:02:31 -0500408 use self::Operand::*;
409 use self::Kind::*;
410
David Tolnayf0d63bf2017-12-26 12:29:47 -0500411 impl Operand {
412 fn tokens(&self) -> &Tokens {
413 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500414 Borrowed(ref n) | Owned(ref n) => n,
David Tolnayf0d63bf2017-12-26 12:29:47 -0500415 }
416 }
417
418 fn ref_tokens(&self) -> Tokens {
419 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500420 Borrowed(ref n) => n.clone(),
421 Owned(ref n) => quote!(&#n),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500422 }
423 }
424
425 fn ref_mut_tokens(&self) -> Tokens {
426 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500427 Borrowed(ref n) => n.clone(),
428 Owned(ref n) => quote!(&mut #n),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500429 }
430 }
431
432 fn owned_tokens(&self) -> Tokens {
433 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500434 Borrowed(ref n) => quote!(*#n),
435 Owned(ref n) => n.clone(),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500436 }
437 }
438 }
439
440 impl Display for Operand {
441 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
David Tolnay4e52d8a2017-12-28 15:54:50 -0500442 Display::fmt(self.tokens(), formatter)
David Tolnayf0d63bf2017-12-26 12:29:47 -0500443 }
444 }
445
Nika Layzellc08227a2017-12-04 16:30:17 -0500446 fn first_arg(params: &PathArguments) -> &Type {
Nika Layzell27726662017-10-24 23:16:35 -0400447 let data = match *params {
Nika Layzellc08227a2017-12-04 16:30:17 -0500448 PathArguments::AngleBracketed(ref data) => data,
449 _ => panic!("Expected at least 1 type argument here"),
Nika Layzell27726662017-10-24 23:16:35 -0400450 };
451
David Tolnayd67fb752017-12-27 13:50:29 -0500452 match **data.args
453 .first()
454 .expect("Expected at least 1 type argument here")
455 .item()
456 {
David Tolnayea9ae892017-12-26 01:44:32 -0500457 GenericArgument::Type(ref ty) => ty,
Nika Layzellc08227a2017-12-04 16:30:17 -0500458 _ => panic!("Expected at least 1 type argument here"),
Nika Layzell357885a2017-12-04 15:47:07 -0500459 }
Nika Layzell27726662017-10-24 23:16:35 -0400460 }
461
462 fn simple_visit(
David Tolnay3d772182017-12-28 17:18:53 -0500463 item: &AstItem,
Nika Layzell27726662017-10-24 23:16:35 -0400464 kind: Kind,
David Tolnayf0d63bf2017-12-26 12:29:47 -0500465 name: &Operand,
David Tolnay4a918742017-12-28 16:54:41 -0500466 ) -> String {
467 match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500468 Visit => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500469 "_visitor.visit_{under_name}({name})",
David Tolnay3d772182017-12-28 17:18:53 -0500470 under_name = under_name(item.ast.ident),
David Tolnay4a918742017-12-28 16:54:41 -0500471 name = name.ref_tokens(),
472 ),
David Tolnay83db9272017-12-28 17:02:31 -0500473 VisitMut => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500474 "_visitor.visit_{under_name}_mut({name})",
David Tolnay3d772182017-12-28 17:18:53 -0500475 under_name = under_name(item.ast.ident),
David Tolnay4a918742017-12-28 16:54:41 -0500476 name = name.ref_mut_tokens(),
477 ),
David Tolnay83db9272017-12-28 17:02:31 -0500478 Fold => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500479 "_visitor.fold_{under_name}({name})",
David Tolnay3d772182017-12-28 17:18:53 -0500480 under_name = under_name(item.ast.ident),
David Tolnay4a918742017-12-28 16:54:41 -0500481 name = name.owned_tokens(),
482 ),
Nika Layzell27726662017-10-24 23:16:35 -0400483 }
484 }
485
486 fn box_visit(
David Tolnay3d772182017-12-28 17:18:53 -0500487 elem: &Type,
Nika Layzell27726662017-10-24 23:16:35 -0400488 lookup: &Lookup,
489 kind: Kind,
David Tolnayf0d63bf2017-12-26 12:29:47 -0500490 name: &Operand,
Nika Layzell27726662017-10-24 23:16:35 -0400491 ) -> Option<String> {
David Tolnay4a918742017-12-28 16:54:41 -0500492 let name = name.owned_tokens();
David Tolnay39d0a202017-12-28 18:19:00 -0500493 let res = visit(elem, lookup, kind, &Owned(quote!(*#name)))?;
David Tolnay4a918742017-12-28 16:54:41 -0500494 Some(match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500495 Fold => format!("Box::new({})", res),
496 Visit | VisitMut => res,
David Tolnay4a918742017-12-28 16:54:41 -0500497 })
Nika Layzell27726662017-10-24 23:16:35 -0400498 }
499
500 fn vec_visit(
David Tolnay3d772182017-12-28 17:18:53 -0500501 elem: &Type,
Nika Layzell27726662017-10-24 23:16:35 -0400502 lookup: &Lookup,
503 kind: Kind,
David Tolnayf0d63bf2017-12-26 12:29:47 -0500504 name: &Operand,
Nika Layzell27726662017-10-24 23:16:35 -0400505 ) -> Option<String> {
David Tolnay4a918742017-12-28 16:54:41 -0500506 let operand = match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500507 Visit | VisitMut => Borrowed(quote!(it)),
508 Fold => Owned(quote!(it)),
David Tolnay4a918742017-12-28 16:54:41 -0500509 };
David Tolnay39d0a202017-12-28 18:19:00 -0500510 let val = visit(elem, lookup, kind, &operand)?;
David Tolnay4a918742017-12-28 16:54:41 -0500511 Some(match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500512 Visit => {
David Tolnay3d772182017-12-28 17:18:53 -0500513 format!(
514 "for it in {name} {{ {val} }}",
515 name = name.ref_tokens(),
516 val = val,
517 )
Nika Layzell27726662017-10-24 23:16:35 -0400518 }
David Tolnay83db9272017-12-28 17:02:31 -0500519 VisitMut => {
David Tolnay3d772182017-12-28 17:18:53 -0500520 format!(
521 "for it in {name} {{ {val} }}",
522 name = name.ref_mut_tokens(),
523 val = val,
524 )
525 }
526 Fold => format!(
527 "FoldHelper::lift({name}, |it| {{ {val} }})",
528 name = name.owned_tokens(),
529 val = val,
530 ),
531 })
532 }
533
David Tolnay6eff4da2018-01-01 20:27:45 -0800534 fn punctuated_visit(
David Tolnay3d772182017-12-28 17:18:53 -0500535 elem: &Type,
536 lookup: &Lookup,
537 kind: Kind,
538 name: &Operand,
539 ) -> Option<String> {
540 let operand = match kind {
541 Visit | VisitMut => Borrowed(quote!(it)),
542 Fold => Owned(quote!(it)),
543 };
David Tolnay39d0a202017-12-28 18:19:00 -0500544 let val = visit(elem, lookup, kind, &operand)?;
David Tolnay3d772182017-12-28 17:18:53 -0500545 Some(match kind {
546 Visit => {
547 format!(
David Tolnay6eff4da2018-01-01 20:27:45 -0800548 "for el in Punctuated::elements({name}) {{ \
David Tolnay3d772182017-12-28 17:18:53 -0500549 let it = el.item(); \
550 {val} \
551 }}",
552 name = name.ref_tokens(),
553 val = val,
554 )
555 }
556 VisitMut => {
557 format!(
David Tolnay6eff4da2018-01-01 20:27:45 -0800558 "for mut el in Punctuated::elements_mut({name}) {{ \
David Tolnay3d772182017-12-28 17:18:53 -0500559 let it = el.item_mut(); \
560 {val} \
561 }}",
562 name = name.ref_mut_tokens(),
563 val = val,
564 )
David Tolnay4a918742017-12-28 16:54:41 -0500565 }
David Tolnay83db9272017-12-28 17:02:31 -0500566 Fold => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500567 "FoldHelper::lift({name}, |it| {{ {val} }})",
568 name = name.owned_tokens(),
569 val = val,
570 ),
571 })
Nika Layzell27726662017-10-24 23:16:35 -0400572 }
573
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400574 fn option_visit(
David Tolnay3d772182017-12-28 17:18:53 -0500575 elem: &Type,
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400576 lookup: &Lookup,
577 kind: Kind,
David Tolnayf0d63bf2017-12-26 12:29:47 -0500578 name: &Operand,
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400579 ) -> Option<String> {
David Tolnay4a918742017-12-28 16:54:41 -0500580 let it = match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500581 Visit | VisitMut => Borrowed(quote!(it)),
582 Fold => Owned(quote!(it)),
David Tolnay4a918742017-12-28 16:54:41 -0500583 };
David Tolnay39d0a202017-12-28 18:19:00 -0500584 let val = visit(elem, lookup, kind, &it)?;
David Tolnay4a918742017-12-28 16:54:41 -0500585 Some(match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500586 Visit => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500587 "if let Some(ref it) = {name} {{ {val} }}",
588 name = name.owned_tokens(),
589 val = val,
590 ),
David Tolnay83db9272017-12-28 17:02:31 -0500591 VisitMut => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500592 "if let Some(ref mut it) = {name} {{ {val} }}",
593 name = name.owned_tokens(),
594 val = val,
595 ),
David Tolnay83db9272017-12-28 17:02:31 -0500596 Fold => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500597 "({name}).map(|it| {{ {val} }})",
598 name = name.owned_tokens(),
599 val = val,
600 ),
601 })
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400602 }
603
David Tolnay5c4c0b52017-12-28 17:58:54 -0500604 fn tuple_visit(
David Tolnayf2cfd722017-12-31 18:02:51 -0500605 elems: &Punctuated<Type, Token![,]>,
David Tolnay5c4c0b52017-12-28 17:58:54 -0500606 lookup: &Lookup,
607 kind: Kind,
608 name: &Operand,
609 ) -> Option<String> {
610 let mut code = String::new();
David Tolnayf047c7a2017-12-31 02:29:04 -0500611 for (i, elem) in elems.iter().enumerate() {
David Tolnay5c4c0b52017-12-28 17:58:54 -0500612 let name = name.tokens();
David Tolnay14982012017-12-29 00:49:51 -0500613 let i = Index::from(i);
David Tolnay5c4c0b52017-12-28 17:58:54 -0500614 let it = Owned(quote!((#name).#i));
David Tolnay6eff4da2018-01-01 20:27:45 -0800615 let val = visit(elem, lookup, kind, &it)
David Tolnay5c4c0b52017-12-28 17:58:54 -0500616 .unwrap_or_else(|| noop_visit(kind, &it));
617 code.push_str(&format!(" {}", val));
618 match kind {
619 Fold => code.push(','),
620 Visit | VisitMut => code.push(';'),
621 }
622 code.push('\n');
623 }
624 if code.is_empty() {
625 None
626 } else {
627 Some(match kind {
628 Fold => {
629 format!("(\n{} )", code)
630 }
631 Visit | VisitMut => {
632 format!("\n{} ", code)
633 }
634 })
635 }
636 }
637
David Tolnay1e01f9c2017-12-28 20:16:19 -0500638 fn token_visit(ty: Tokens, kind: Kind, name: &Operand) -> String {
David Tolnaycc0f0372017-12-28 19:11:04 -0500639 match kind {
640 Fold => format!(
David Tolnay1e01f9c2017-12-28 20:16:19 -0500641 "{ty}(tokens_helper(_visitor, &({name}).0))",
642 ty = ty,
David Tolnaycc0f0372017-12-28 19:11:04 -0500643 name = name.owned_tokens(),
644 ),
645 Visit => format!(
646 "tokens_helper(_visitor, &({name}).0)",
647 name = name.ref_tokens(),
648 ),
649 VisitMut => format!(
650 "tokens_helper(_visitor, &mut ({name}).0)",
651 name = name.ref_mut_tokens(),
652 ),
653 }
654 }
655
David Tolnay4a918742017-12-28 16:54:41 -0500656 fn noop_visit(kind: Kind, name: &Operand) -> String {
657 match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500658 Fold => name.owned_tokens().to_string(),
659 Visit | VisitMut => format!("// Skipped field {}", name),
David Tolnay4a918742017-12-28 16:54:41 -0500660 }
661 }
662
663 fn visit(ty: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<String> {
David Tolnay3d772182017-12-28 17:18:53 -0500664 match classify(ty, lookup) {
665 RelevantType::Box(elem) => {
666 box_visit(elem, lookup, kind, name)
David Tolnay4a918742017-12-28 16:54:41 -0500667 }
David Tolnay3d772182017-12-28 17:18:53 -0500668 RelevantType::Vec(elem) => {
669 vec_visit(elem, lookup, kind, name)
David Tolnay4a918742017-12-28 16:54:41 -0500670 }
David Tolnayf2cfd722017-12-31 18:02:51 -0500671 RelevantType::Punctuated(elem) => {
David Tolnay6eff4da2018-01-01 20:27:45 -0800672 punctuated_visit(elem, lookup, kind, name)
David Tolnay4a918742017-12-28 16:54:41 -0500673 }
David Tolnay3d772182017-12-28 17:18:53 -0500674 RelevantType::Option(elem) => {
675 option_visit(elem, lookup, kind, name)
676 }
David Tolnay5c4c0b52017-12-28 17:58:54 -0500677 RelevantType::Tuple(elems) => {
678 tuple_visit(elems, lookup, kind, name)
679 }
David Tolnay3d772182017-12-28 17:18:53 -0500680 RelevantType::Simple(item) => {
681 let mut res = simple_visit(item, kind, name);
682 Some(if item.eos_full {
683 format!("full!({res})", res = res)
684 } else {
685 res
686 })
687 }
David Tolnay1e01f9c2017-12-28 20:16:19 -0500688 RelevantType::Token(ty) => {
689 Some(token_visit(ty, kind, name))
David Tolnaycc0f0372017-12-28 19:11:04 -0500690 }
David Tolnay3d772182017-12-28 17:18:53 -0500691 RelevantType::Pass => {
692 None
Nika Layzell27726662017-10-24 23:16:35 -0400693 }
694 }
Nika Layzell27726662017-10-24 23:16:35 -0400695 }
696
697 pub fn generate(state: &mut State, lookup: &Lookup, s: &AstItem) {
David Tolnay3d772182017-12-28 17:18:53 -0500698 let under_name = under_name(s.ast.ident);
Nika Layzell27726662017-10-24 23:16:35 -0400699
700 state.visit_trait.push_str(&format!(
701 "{features}\n\
Nika Layzellc86173a2017-11-18 13:55:22 -0500702 fn visit_{under_name}(&mut self, i: &'ast {ty}) {{ \
David Tolnayd67fb752017-12-27 13:50:29 -0500703 visit_{under_name}(self, i) \
Nika Layzell27726662017-10-24 23:16:35 -0400704 }}\n",
705 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_trait.push_str(&format!(
710 "{features}\n\
Nika Layzella6f46c42017-10-26 15:26:16 -0400711 fn visit_{under_name}_mut(&mut self, i: &mut {ty}) {{ \
David Tolnayd67fb752017-12-27 13:50:29 -0500712 visit_{under_name}_mut(self, i) \
Nika Layzell27726662017-10-24 23:16:35 -0400713 }}\n",
714 features = s.features,
715 under_name = under_name,
David Tolnay3d772182017-12-28 17:18:53 -0500716 ty = s.ast.ident,
Nika Layzell27726662017-10-24 23:16:35 -0400717 ));
718 state.fold_trait.push_str(&format!(
719 "{features}\n\
720 fn fold_{under_name}(&mut self, i: {ty}) -> {ty} {{ \
David Tolnayd67fb752017-12-27 13:50:29 -0500721 fold_{under_name}(self, i) \
Nika Layzell27726662017-10-24 23:16:35 -0400722 }}\n",
723 features = s.features,
724 under_name = under_name,
David Tolnay3d772182017-12-28 17:18:53 -0500725 ty = s.ast.ident,
Nika Layzell27726662017-10-24 23:16:35 -0400726 ));
727
728 state.visit_impl.push_str(&format!(
729 "{features}\n\
Nika Layzellc86173a2017-11-18 13:55:22 -0500730 pub fn visit_{under_name}<'ast, V: Visitor<'ast> + ?Sized>(\
David Tolnayd67fb752017-12-27 13:50:29 -0500731 _visitor: &mut V, _i: &'ast {ty}) {{\n",
Nika Layzell27726662017-10-24 23:16:35 -0400732 features = s.features,
733 under_name = under_name,
David Tolnay3d772182017-12-28 17:18:53 -0500734 ty = s.ast.ident,
Nika Layzell27726662017-10-24 23:16:35 -0400735 ));
736 state.visit_mut_impl.push_str(&format!(
737 "{features}\n\
Nika Layzella6f46c42017-10-26 15:26:16 -0400738 pub fn visit_{under_name}_mut<V: VisitorMut + ?Sized>(\
David Tolnayd67fb752017-12-27 13:50:29 -0500739 _visitor: &mut V, _i: &mut {ty}) {{\n",
Nika Layzell27726662017-10-24 23:16:35 -0400740 features = s.features,
741 under_name = under_name,
David Tolnay3d772182017-12-28 17:18:53 -0500742 ty = s.ast.ident,
Nika Layzell27726662017-10-24 23:16:35 -0400743 ));
David Tolnayd0adf522017-12-29 01:30:07 -0500744 let before_fold_impl_len = state.fold_impl.len();
Nika Layzell27726662017-10-24 23:16:35 -0400745 state.fold_impl.push_str(&format!(
746 "{features}\n\
Nika Layzella6f46c42017-10-26 15:26:16 -0400747 pub fn fold_{under_name}<V: Folder + ?Sized>(\
David Tolnayd67fb752017-12-27 13:50:29 -0500748 _visitor: &mut V, _i: {ty}) -> {ty} {{\n",
Nika Layzell27726662017-10-24 23:16:35 -0400749 features = s.features,
750 under_name = under_name,
David Tolnay3d772182017-12-28 17:18:53 -0500751 ty = s.ast.ident,
Nika Layzell27726662017-10-24 23:16:35 -0400752 ));
753
754 // XXX: This part is a disaster - I'm not sure how to make it cleaner though :'(
David Tolnaye3d41b72017-12-31 15:24:00 -0500755 match s.ast.data {
756 Data::Enum(ref e) => {
Nika Layzell27726662017-10-24 23:16:35 -0400757 state.visit_impl.push_str(" match *_i {\n");
758 state.visit_mut_impl.push_str(" match *_i {\n");
759 state.fold_impl.push_str(" match _i {\n");
760 for variant in &e.variants {
David Tolnay6eff4da2018-01-01 20:27:45 -0800761 let fields: Vec<(&Field, Tokens)> = match variant.fields {
David Tolnaye3d41b72017-12-31 15:24:00 -0500762 Fields::Named(..) => panic!("Doesn't support enum struct variants"),
763 Fields::Unnamed(ref fields) => {
David Tolnay6eff4da2018-01-01 20:27:45 -0800764 let binding = format!(" {}::{}(", s.ast.ident, variant.ident);
Nika Layzell27726662017-10-24 23:16:35 -0400765 state.visit_impl.push_str(&binding);
766 state.visit_mut_impl.push_str(&binding);
767 state.fold_impl.push_str(&binding);
768
David Tolnaybdafb102018-01-01 19:39:10 -0800769 let res = fields.unnamed
David Tolnayd67fb752017-12-27 13:50:29 -0500770 .iter()
771 .enumerate()
772 .map(|(idx, el)| {
773 let name = format!("_binding_{}", idx);
Nika Layzell27726662017-10-24 23:16:35 -0400774
David Tolnayd67fb752017-12-27 13:50:29 -0500775 state.visit_impl.push_str("ref ");
776 state.visit_mut_impl.push_str("ref mut ");
Nika Layzell27726662017-10-24 23:16:35 -0400777
David Tolnayd67fb752017-12-27 13:50:29 -0500778 state.visit_impl.push_str(&name);
779 state.visit_mut_impl.push_str(&name);
780 state.fold_impl.push_str(&name);
781 state.visit_impl.push_str(", ");
782 state.visit_mut_impl.push_str(", ");
783 state.fold_impl.push_str(", ");
Nika Layzell27726662017-10-24 23:16:35 -0400784
David Tolnayd67fb752017-12-27 13:50:29 -0500785 let mut tokens = quote!();
786 Ident::from(name).to_tokens(&mut tokens);
Nika Layzell27726662017-10-24 23:16:35 -0400787
David Tolnay6eff4da2018-01-01 20:27:45 -0800788 (el, tokens)
David Tolnayd67fb752017-12-27 13:50:29 -0500789 })
790 .collect();
Nika Layzell27726662017-10-24 23:16:35 -0400791
792 state.visit_impl.push_str(") => {\n");
793 state.visit_mut_impl.push_str(") => {\n");
794 state.fold_impl.push_str(") => {\n");
795
796 res
797 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500798 Fields::Unit => {
David Tolnayd67fb752017-12-27 13:50:29 -0500799 state
800 .visit_impl
David Tolnay6eff4da2018-01-01 20:27:45 -0800801 .push_str(&format!(" {0}::{1} => {{ }}\n", s.ast.ident, variant.ident));
David Tolnayd67fb752017-12-27 13:50:29 -0500802 state
803 .visit_mut_impl
David Tolnay6eff4da2018-01-01 20:27:45 -0800804 .push_str(&format!(" {0}::{1} => {{ }}\n", s.ast.ident, variant.ident));
Nika Layzell27726662017-10-24 23:16:35 -0400805 state.fold_impl.push_str(&format!(
David Tolnay6702ade2017-12-30 23:38:15 -0500806 " {0}::{1} => {{ {0}::{1} }}\n",
807 s.ast.ident,
David Tolnay6eff4da2018-01-01 20:27:45 -0800808 variant.ident
Nika Layzell27726662017-10-24 23:16:35 -0400809 ));
David Tolnayd67fb752017-12-27 13:50:29 -0500810 continue;
Nika Layzell27726662017-10-24 23:16:35 -0400811 }
812 };
813
814 if fields.is_empty() {
815 state.visit_impl.push_str(" {}");
816 state.visit_mut_impl.push_str(") => {\n");
817 state.fold_impl.push_str(") => {\n");
818 }
David Tolnayd67fb752017-12-27 13:50:29 -0500819 state
820 .fold_impl
David Tolnay6eff4da2018-01-01 20:27:45 -0800821 .push_str(&format!(" {}::{} (\n", s.ast.ident, variant.ident,));
Nika Layzell27726662017-10-24 23:16:35 -0400822 for (field, binding) in fields {
823 state.visit_impl.push_str(&format!(
824 " {};\n",
David Tolnayd67fb752017-12-27 13:50:29 -0500825 visit(
826 &field.ty,
827 lookup,
David Tolnay83db9272017-12-28 17:02:31 -0500828 Visit,
829 &Borrowed(binding.clone())
David Tolnay4a918742017-12-28 16:54:41 -0500830 ).unwrap_or_else(|| noop_visit(
David Tolnay83db9272017-12-28 17:02:31 -0500831 Visit,
832 &Borrowed(binding.clone())
David Tolnay4a918742017-12-28 16:54:41 -0500833 )),
Nika Layzell27726662017-10-24 23:16:35 -0400834 ));
835 state.visit_mut_impl.push_str(&format!(
836 " {};\n",
David Tolnayd67fb752017-12-27 13:50:29 -0500837 visit(
838 &field.ty,
839 lookup,
David Tolnay83db9272017-12-28 17:02:31 -0500840 VisitMut,
841 &Borrowed(binding.clone())
David Tolnay4a918742017-12-28 16:54:41 -0500842 ).unwrap_or_else(|| noop_visit(
David Tolnay83db9272017-12-28 17:02:31 -0500843 VisitMut,
844 &Borrowed(binding.clone())
David Tolnay4a918742017-12-28 16:54:41 -0500845 )),
Nika Layzell27726662017-10-24 23:16:35 -0400846 ));
847 state.fold_impl.push_str(&format!(
848 " {},\n",
David Tolnay83db9272017-12-28 17:02:31 -0500849 visit(&field.ty, lookup, Fold, &Owned(binding.clone()))
David Tolnay4a918742017-12-28 16:54:41 -0500850 .unwrap_or_else(|| noop_visit(
David Tolnay83db9272017-12-28 17:02:31 -0500851 Fold,
852 &Owned(binding)
David Tolnay4a918742017-12-28 16:54:41 -0500853 )),
Nika Layzell27726662017-10-24 23:16:35 -0400854 ));
855 }
856 state.fold_impl.push_str(" )\n");
857
858 state.visit_impl.push_str(" }\n");
859 state.visit_mut_impl.push_str(" }\n");
860 state.fold_impl.push_str(" }\n");
861 }
862 state.visit_impl.push_str(" }\n");
863 state.visit_mut_impl.push_str(" }\n");
864 state.fold_impl.push_str(" }\n");
865 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500866 Data::Struct(ref v) => {
867 let fields: Vec<(&Field, Tokens)> = match v.fields {
868 Fields::Named(ref fields) => {
David Tolnayd67fb752017-12-27 13:50:29 -0500869 state
870 .fold_impl
David Tolnay3d772182017-12-28 17:18:53 -0500871 .push_str(&format!(" {} {{\n", s.ast.ident));
David Tolnaybdafb102018-01-01 19:39:10 -0800872 fields.named
David Tolnayd67fb752017-12-27 13:50:29 -0500873 .iter()
874 .map(|el| {
David Tolnay6eff4da2018-01-01 20:27:45 -0800875 let id = el.ident;
876 (el, quote!(_i.#id))
David Tolnayd67fb752017-12-27 13:50:29 -0500877 })
878 .collect()
Nika Layzell27726662017-10-24 23:16:35 -0400879 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500880 Fields::Unnamed(ref fields) => {
David Tolnayd67fb752017-12-27 13:50:29 -0500881 state
882 .fold_impl
David Tolnay3d772182017-12-28 17:18:53 -0500883 .push_str(&format!(" {} (\n", s.ast.ident));
David Tolnaybdafb102018-01-01 19:39:10 -0800884 fields.unnamed
David Tolnayd67fb752017-12-27 13:50:29 -0500885 .iter()
886 .enumerate()
887 .map(|(idx, el)| {
David Tolnay14982012017-12-29 00:49:51 -0500888 let id = Index::from(idx);
David Tolnay6eff4da2018-01-01 20:27:45 -0800889 (el, quote!(_i.#id))
David Tolnayd67fb752017-12-27 13:50:29 -0500890 })
891 .collect()
Nika Layzell27726662017-10-24 23:16:35 -0400892 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500893 Fields::Unit => {
Nika Layzellefb83ba2017-12-19 18:23:55 -0500894 state.fold_impl.push_str(" _i\n");
895 vec![]
896 }
Nika Layzell27726662017-10-24 23:16:35 -0400897 };
898
899 for (field, ref_toks) in fields {
David Tolnay83db9272017-12-28 17:02:31 -0500900 let ref_toks = Owned(ref_toks);
Nika Layzell27726662017-10-24 23:16:35 -0400901 state.visit_impl.push_str(&format!(
David Tolnayd67fb752017-12-27 13:50:29 -0500902 " {};\n",
David Tolnay83db9272017-12-28 17:02:31 -0500903 visit(&field.ty, lookup, Visit, &ref_toks)
David Tolnay4a918742017-12-28 16:54:41 -0500904 .unwrap_or_else(|| noop_visit(
David Tolnay83db9272017-12-28 17:02:31 -0500905 Visit,
David Tolnay4a918742017-12-28 16:54:41 -0500906 &ref_toks,
907 ))
Nika Layzell27726662017-10-24 23:16:35 -0400908 ));
909 state.visit_mut_impl.push_str(&format!(
David Tolnayd67fb752017-12-27 13:50:29 -0500910 " {};\n",
David Tolnay83db9272017-12-28 17:02:31 -0500911 visit(&field.ty, lookup, VisitMut, &ref_toks)
David Tolnay4a918742017-12-28 16:54:41 -0500912 .unwrap_or_else(|| noop_visit(
David Tolnay83db9272017-12-28 17:02:31 -0500913 VisitMut,
David Tolnay4a918742017-12-28 16:54:41 -0500914 &ref_toks,
915 ))
Nika Layzell27726662017-10-24 23:16:35 -0400916 ));
David Tolnay83db9272017-12-28 17:02:31 -0500917 let fold = visit(&field.ty, lookup, Fold, &ref_toks)
David Tolnay4a918742017-12-28 16:54:41 -0500918 .unwrap_or_else(|| noop_visit(
David Tolnay83db9272017-12-28 17:02:31 -0500919 Fold,
David Tolnay4a918742017-12-28 16:54:41 -0500920 &ref_toks,
921 ));
Nika Layzell27726662017-10-24 23:16:35 -0400922 if let Some(ref name) = field.ident {
David Tolnayd67fb752017-12-27 13:50:29 -0500923 state
924 .fold_impl
925 .push_str(&format!(" {}: {},\n", name, fold));
Nika Layzell27726662017-10-24 23:16:35 -0400926 } else {
927 state.fold_impl.push_str(&format!(" {},\n", fold));
928 }
929 }
930
David Tolnaye3d41b72017-12-31 15:24:00 -0500931 match v.fields {
932 Fields::Named(..) => state.fold_impl.push_str(" }\n"),
933 Fields::Unnamed(..) => state.fold_impl.push_str(" )\n"),
934 Fields::Unit => {}
Nika Layzell27726662017-10-24 23:16:35 -0400935 };
936 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500937 Data::Union(..) => panic!("Union not supported"),
Nika Layzell27726662017-10-24 23:16:35 -0400938 }
939
940 // Close the impl body
941 state.visit_impl.push_str("}\n");
942 state.visit_mut_impl.push_str("}\n");
943 state.fold_impl.push_str("}\n");
David Tolnayd0adf522017-12-29 01:30:07 -0500944
David Tolnay360efd22018-01-04 23:35:26 -0800945 if let Data::Struct(ref data) = s.ast.data {
946 if let Fields::Named(ref fields) = data.fields {
947 if fields.named.iter().any(|field| field.vis == Visibility::Inherited) {
948 // Discard the generated impl if there are private fields.
949 // These have to be handwritten.
950 state.fold_impl.truncate(before_fold_impl_len);
951 }
952 }
David Tolnayd0adf522017-12-29 01:30:07 -0500953 }
Nika Layzell27726662017-10-24 23:16:35 -0400954 }
955}
956
957fn main() {
958 let mut lookup = BTreeMap::new();
David Tolnayea9ae892017-12-26 01:44:32 -0500959 load_file(SYN_CRATE_ROOT, &quote!(), &mut lookup).unwrap();
Nika Layzell27726662017-10-24 23:16:35 -0400960
Nika Layzellefb83ba2017-12-19 18:23:55 -0500961 // Load in any terminal types
962 for &tt in TERMINAL_TYPES {
963 use syn::*;
David Tolnayd67fb752017-12-27 13:50:29 -0500964 lookup.insert(
965 Ident::from(tt),
966 AstItem {
David Tolnay3d772182017-12-28 17:18:53 -0500967 ast: DeriveInput {
David Tolnayd67fb752017-12-27 13:50:29 -0500968 ident: Ident::from(tt),
969 vis: Visibility::Public(VisPublic {
970 pub_token: Default::default(),
971 }),
972 attrs: vec![],
973 generics: Default::default(),
David Tolnaye3d41b72017-12-31 15:24:00 -0500974 data: Data::Struct(DataStruct {
975 fields: Fields::Unit,
David Tolnayd67fb752017-12-27 13:50:29 -0500976 struct_token: Default::default(),
977 semi_token: None,
978 }),
979 },
980 features: Default::default(),
981 eos_full: false,
Nika Layzellefb83ba2017-12-19 18:23:55 -0500982 },
David Tolnayd67fb752017-12-27 13:50:29 -0500983 );
Nika Layzellefb83ba2017-12-19 18:23:55 -0500984 }
985
Nika Layzell27726662017-10-24 23:16:35 -0400986 let mut state = Default::default();
987 for s in lookup.values() {
988 codegen::generate(&mut state, &lookup, s);
989 }
990
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400991 let full_macro = "
992#[cfg(feature = \"full\")]
993macro_rules! full {
994 ($e:expr) => { $e }
995}
996
David Tolnay0a0d78c2018-01-05 15:24:01 -0800997#[cfg(all(feature = \"derive\", not(feature = \"full\")))]
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400998macro_rules! full {
999 ($e:expr) => { unreachable!() }
1000}
1001";
1002
Nika Layzell27726662017-10-24 23:16:35 -04001003 let mut fold_file = File::create(FOLD_SRC).unwrap();
David Tolnayd67fb752017-12-27 13:50:29 -05001004 write!(
1005 fold_file,
1006 "\
Nika Layzell27726662017-10-24 23:16:35 -04001007// THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT
1008
1009//! A Folder represents an AST->AST fold; it accepts an AST piece,
1010//! and returns a piece of the same type.
1011
David Tolnay0afc9b32017-12-27 13:38:24 -05001012#![cfg_attr(rustfmt, rustfmt_skip)]
1013
Nika Layzell27726662017-10-24 23:16:35 -04001014// Unreachable code is generated sometimes without the full feature.
1015#![allow(unreachable_code)]
David Tolnayf0d63bf2017-12-26 12:29:47 -05001016#![cfg_attr(feature = \"cargo-clippy\", allow(needless_pass_by_value))]
Nika Layzell27726662017-10-24 23:16:35 -04001017
Nika Layzella6f46c42017-10-26 15:26:16 -04001018use *;
David Tolnay0a0d78c2018-01-05 15:24:01 -08001019#[cfg(any(feature = \"full\", feature = \"derive\"))]
David Tolnay1e01f9c2017-12-28 20:16:19 -05001020use token::{{Brace, Bracket, Paren, Group}};
David Tolnay98942562017-12-26 21:24:35 -05001021use proc_macro2::Span;
David Tolnay0a0d78c2018-01-05 15:24:01 -08001022#[cfg(any(feature = \"full\", feature = \"derive\"))]
David Tolnayf60f4262017-12-28 19:17:58 -05001023use gen::helper::fold::*;
Nika Layzell27726662017-10-24 23:16:35 -04001024
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001025{full_macro}
1026
Nika Layzell27726662017-10-24 23:16:35 -04001027/// AST->AST fold.
1028///
1029/// Each method of the Folder trait is a hook to be potentially overridden. Each
1030/// method's default implementation recursively visits the substructure of the
1031/// input via the `walk` functions, which perform an \"identity fold\", that
1032/// is, they return the same structure that they are given (for example the
1033/// `fold_file` method by default calls `fold::walk_file`).
1034///
1035/// If you want to ensure that your code handles every variant
1036/// explicitly, you need to override each method. (And you also need
1037/// to monitor future changes to `Folder` in case a new method with a
1038/// new default implementation gets introduced.)
1039pub trait Folder {{
1040{fold_trait}
1041}}
1042
David Tolnay360efd22018-01-04 23:35:26 -08001043macro_rules! fold_span_only {{
1044 ($f:ident : $t:ident) => {{
1045 pub fn $f<V: Folder + ?Sized>(_visitor: &mut V, mut _i: $t) -> $t {{
1046 _i.span = _visitor.fold_span(_i.span);
1047 _i
1048 }}
1049 }}
David Tolnayd0adf522017-12-29 01:30:07 -05001050}}
1051
David Tolnay360efd22018-01-04 23:35:26 -08001052fold_span_only!(fold_ident: Ident);
David Tolnay0a0d78c2018-01-05 15:24:01 -08001053#[cfg(any(feature = \"full\", feature = \"derive\"))]
David Tolnay360efd22018-01-04 23:35:26 -08001054fold_span_only!(fold_lifetime: Lifetime);
1055#[cfg(any(feature = \"full\", feature = \"derive\"))]
1056fold_span_only!(fold_lit_byte: LitByte);
1057#[cfg(any(feature = \"full\", feature = \"derive\"))]
1058fold_span_only!(fold_lit_byte_str: LitByteStr);
1059#[cfg(any(feature = \"full\", feature = \"derive\"))]
1060fold_span_only!(fold_lit_char: LitChar);
1061#[cfg(any(feature = \"full\", feature = \"derive\"))]
1062fold_span_only!(fold_lit_float: LitFloat);
1063#[cfg(any(feature = \"full\", feature = \"derive\"))]
1064fold_span_only!(fold_lit_int: LitInt);
1065#[cfg(any(feature = \"full\", feature = \"derive\"))]
1066fold_span_only!(fold_lit_str: LitStr);
David Tolnayd0adf522017-12-29 01:30:07 -05001067
Nika Layzell27726662017-10-24 23:16:35 -04001068{fold_impl}
1069",
David Tolnayd67fb752017-12-27 13:50:29 -05001070 full_macro = full_macro,
1071 fold_trait = state.fold_trait,
1072 fold_impl = state.fold_impl
1073 ).unwrap();
Nika Layzell27726662017-10-24 23:16:35 -04001074
1075 let mut visit_file = File::create(VISIT_SRC).unwrap();
David Tolnayd67fb752017-12-27 13:50:29 -05001076 write!(
1077 visit_file,
1078 "\
Nika Layzell27726662017-10-24 23:16:35 -04001079// THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT
1080
1081//! AST walker. Each overridden visit method has full control over what
1082//! happens with its node, it can do its own traversal of the node's children,
1083//! call `visit::walk_*` to apply the default traversal algorithm, or prevent
1084//! deeper traversal by doing nothing.
1085
David Tolnay0afc9b32017-12-27 13:38:24 -05001086#![cfg_attr(rustfmt, rustfmt_skip)]
1087
David Tolnayf0d63bf2017-12-26 12:29:47 -05001088#![cfg_attr(feature = \"cargo-clippy\", allow(match_same_arms))]
1089
Nika Layzella6f46c42017-10-26 15:26:16 -04001090use *;
David Tolnay0a0d78c2018-01-05 15:24:01 -08001091#[cfg(any(feature = \"full\", feature = \"derive\"))]
David Tolnay6eff4da2018-01-01 20:27:45 -08001092use punctuated::Punctuated;
David Tolnay98942562017-12-26 21:24:35 -05001093use proc_macro2::Span;
David Tolnay0a0d78c2018-01-05 15:24:01 -08001094#[cfg(any(feature = \"full\", feature = \"derive\"))]
David Tolnaycc0f0372017-12-28 19:11:04 -05001095use gen::helper::visit::*;
Nika Layzell27726662017-10-24 23:16:35 -04001096
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001097{full_macro}
1098
Nika Layzell27726662017-10-24 23:16:35 -04001099/// Each method of the Visitor trait is a hook to be potentially
1100/// overridden. Each method's default implementation recursively visits
1101/// the substructure of the input via the corresponding `walk` method;
1102/// e.g. the `visit_mod` method by default calls `visit::walk_mod`.
1103///
1104/// If you want to ensure that your code handles every variant
1105/// explicitly, you need to override each method. (And you also need
1106/// to monitor future changes to `Visitor` in case a new method with a
1107/// new default implementation gets introduced.)
Nika Layzellc86173a2017-11-18 13:55:22 -05001108pub trait Visitor<'ast> {{
Nika Layzell27726662017-10-24 23:16:35 -04001109{visit_trait}
1110}}
1111
1112{visit_impl}
1113",
David Tolnayd67fb752017-12-27 13:50:29 -05001114 full_macro = full_macro,
1115 visit_trait = state.visit_trait,
1116 visit_impl = state.visit_impl
1117 ).unwrap();
Nika Layzell27726662017-10-24 23:16:35 -04001118
1119 let mut visit_mut_file = File::create(VISIT_MUT_SRC).unwrap();
David Tolnayd67fb752017-12-27 13:50:29 -05001120 write!(
1121 visit_mut_file,
1122 "\
Nika Layzell27726662017-10-24 23:16:35 -04001123// THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT
1124
1125//! AST walker. Each overridden visit method has full control over what
1126//! happens with its node, it can do its own traversal of the node's children,
1127//! call `visit::walk_*` to apply the default traversal algorithm, or prevent
1128//! deeper traversal by doing nothing.
1129
David Tolnay0afc9b32017-12-27 13:38:24 -05001130#![cfg_attr(rustfmt, rustfmt_skip)]
1131
David Tolnayf0d63bf2017-12-26 12:29:47 -05001132#![cfg_attr(feature = \"cargo-clippy\", allow(match_same_arms))]
1133
Nika Layzella6f46c42017-10-26 15:26:16 -04001134use *;
David Tolnay0a0d78c2018-01-05 15:24:01 -08001135#[cfg(any(feature = \"full\", feature = \"derive\"))]
David Tolnay6eff4da2018-01-01 20:27:45 -08001136use punctuated::Punctuated;
David Tolnay98942562017-12-26 21:24:35 -05001137use proc_macro2::Span;
David Tolnay0a0d78c2018-01-05 15:24:01 -08001138#[cfg(any(feature = \"full\", feature = \"derive\"))]
David Tolnaycc0f0372017-12-28 19:11:04 -05001139use gen::helper::visit_mut::*;
Nika Layzell27726662017-10-24 23:16:35 -04001140
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001141{full_macro}
1142
Nika Layzell27726662017-10-24 23:16:35 -04001143/// Each method of the VisitorMut trait is a hook to be potentially
1144/// overridden. Each method's default implementation recursively visits
1145/// the substructure of the input via the corresponding `walk` method;
1146/// e.g. the `visit_mod` method by default calls `visit::walk_mod`.
1147///
1148/// If you want to ensure that your code handles every variant
1149/// explicitly, you need to override each method. (And you also need
1150/// to monitor future changes to `VisitorMut` in case a new method with a
1151/// new default implementation gets introduced.)
1152pub trait VisitorMut {{
1153{visit_mut_trait}
1154}}
1155
1156{visit_mut_impl}
1157",
David Tolnayd67fb752017-12-27 13:50:29 -05001158 full_macro = full_macro,
1159 visit_mut_trait = state.visit_mut_trait,
1160 visit_mut_impl = state.visit_mut_impl
1161 ).unwrap();
Nika Layzell27726662017-10-24 23:16:35 -04001162}