blob: 01027430ba7caa562eafed85397c30887fce6be4 [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 Tolnay6eff4da2018-01-01 20:27:45 -080053 .all(|(a, b)| a.ident.as_ref() == b.ident.as_ref())
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 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 Tolnay01a77582018-01-01 20:00:51 -0800129 let tts = &item.mac.tts;
David Tolnayc6b55bc2017-11-09 22:48:38 -0800130 let found = if path_eq(&item.mac.path, &"ast_struct".into()) {
David Tolnay01a77582018-01-01 20:00:51 -0800131 syn::parse_str::<parsing::AstStruct>(&quote!(#tts).to_string())
David Tolnayd67fb752017-12-27 13:50:29 -0500132 .map_err(|_| err_msg("failed to parse ast_struct"))?
133 .0
David Tolnayc6b55bc2017-11-09 22:48:38 -0800134 } else if path_eq(&item.mac.path, &"ast_enum".into()) {
David Tolnay01a77582018-01-01 20:00:51 -0800135 syn::parse_str::<parsing::AstEnum>(&quote!(#tts).to_string())
David Tolnayd67fb752017-12-27 13:50:29 -0500136 .map_err(|_| err_msg("failed to parse ast_enum"))?
137 .0
David Tolnayc6b55bc2017-11-09 22:48:38 -0800138 } else if path_eq(&item.mac.path, &"ast_enum_of_structs".into()) {
David Tolnay01a77582018-01-01 20:00:51 -0800139 syn::parse_str::<parsing::AstEnumOfStructs>(&quote!(#tts).to_string())
David Tolnay1cf80912017-12-31 18:35:12 -0500140 .map_err(|_| err_msg("failed to parse ast_enum_of_structs"))?
David Tolnayd67fb752017-12-27 13:50:29 -0500141 .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,
David Tolnaye3d41b72017-12-31 15:24:00 -0500161 data: Data::Struct(DataStruct {
162 fields: item.fields,
David Tolnay4ba63a02017-12-28 15:53:05 -0500163 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 Tolnay1cf80912017-12-31 18:35:12 -0500181 use syn;
David Tolnayc5ab8c62017-12-26 16:43:39 -0500182 use syn::synom::*;
Nika Layzell27726662017-10-24 23:16:35 -0400183 use syn::*;
Nika Layzell27726662017-10-24 23:16:35 -0400184 use quote::Tokens;
David Tolnay2e0dba12017-12-27 01:54:40 -0500185 use proc_macro2::TokenStream;
Nika Layzell27726662017-10-24 23:16:35 -0400186
187 // Parses #full - returns #[cfg(feature = "full")] if it is present, and
188 // nothing otherwise.
189 named!(full -> (Tokens, bool), map!(option!(do_parse!(
David Tolnayf8db7ba2017-11-11 22:52:16 -0800190 punct!(#) >>
Nika Layzell27726662017-10-24 23:16:35 -0400191 id: syn!(Ident) >>
David Tolnay28c5a462017-12-27 01:59:30 -0500192 cond_reduce!(id == "full", epsilon!()) >>
David Tolnayea9ae892017-12-26 01:44:32 -0500193 ()
Nika Layzell27726662017-10-24 23:16:35 -0400194 )), |s| if s.is_some() {
195 (quote!(#[cfg(feature = "full")]), true)
196 } else {
197 (quote!(), false)
198 }));
199
David Tolnay28c5a462017-12-27 01:59:30 -0500200 named!(manual_extra_traits -> (), do_parse!(
201 punct!(#) >>
202 id: syn!(Ident) >>
203 cond_reduce!(id == "manual_extra_traits", epsilon!()) >>
204 ()
205 ));
206
Nika Layzell27726662017-10-24 23:16:35 -0400207 // Parses a simple AstStruct without the `pub struct` prefix.
208 named!(ast_struct_inner -> AstItem, do_parse!(
209 id: syn!(Ident) >>
210 features: full >>
David Tolnay28c5a462017-12-27 01:59:30 -0500211 option!(manual_extra_traits) >>
David Tolnay2e0dba12017-12-27 01:54:40 -0500212 rest: syn!(TokenStream) >>
Nika Layzell27726662017-10-24 23:16:35 -0400213 (AstItem {
David Tolnay01a77582018-01-01 20:00:51 -0800214 ast: syn::parse_str(&quote! {
David Tolnay2e0dba12017-12-27 01:54:40 -0500215 pub struct #id #rest
David Tolnay01a77582018-01-01 20:00:51 -0800216 }.to_string())?,
Nika Layzell27726662017-10-24 23:16:35 -0400217 features: features.0,
218 eos_full: features.1,
219 })
220 ));
221
222 // ast_struct! parsing
223 pub struct AstStruct(pub Vec<AstItem>);
224 impl Synom for AstStruct {
David Tolnayab919512017-12-30 23:31:51 -0500225 named!(parse -> Self, do_parse!(
David Tolnay2c136452017-12-27 14:13:32 -0500226 many0!(Attribute::parse_outer) >>
David Tolnayf8db7ba2017-11-11 22:52:16 -0800227 keyword!(pub) >>
228 keyword!(struct) >>
Nika Layzell27726662017-10-24 23:16:35 -0400229 res: call!(ast_struct_inner) >>
David Tolnayab919512017-12-30 23:31:51 -0500230 (AstStruct(vec![res]))
231 ));
Nika Layzell27726662017-10-24 23:16:35 -0400232 }
233
David Tolnay360efd22018-01-04 23:35:26 -0800234 named!(no_visit -> (), do_parse!(
235 punct!(#) >>
236 id: syn!(Ident) >>
237 cond_reduce!(id == "no_visit", epsilon!()) >>
238 ()
239 ));
240
Nika Layzell27726662017-10-24 23:16:35 -0400241 // ast_enum! parsing
242 pub struct AstEnum(pub Vec<AstItem>);
243 impl Synom for AstEnum {
David Tolnay360efd22018-01-04 23:35:26 -0800244 named!(parse -> Self, do_parse!(
245 many0!(Attribute::parse_outer) >>
246 keyword!(pub) >>
247 keyword!(enum) >>
248 id: syn!(Ident) >>
249 no_visit: option!(no_visit) >>
250 rest: syn!(TokenStream) >>
251 (AstEnum(if no_visit.is_some() {
252 vec![]
253 } else {
254 vec![AstItem {
255 ast: syn::parse_str(&quote! {
256 pub enum #id #rest
257 }.to_string())?,
258 features: quote!(),
259 eos_full: false,
260 }]
261 }))
262 ));
Nika Layzell27726662017-10-24 23:16:35 -0400263 }
264
265 // A single variant of an ast_enum_of_structs!
266 struct EosVariant {
267 name: Ident,
David Tolnayfcfb9002017-12-28 22:04:29 -0500268 member: Option<Path>,
Nika Layzell27726662017-10-24 23:16:35 -0400269 inner: Option<AstItem>,
270 }
271 named!(eos_variant -> EosVariant, do_parse!(
David Tolnay2c136452017-12-27 14:13:32 -0500272 many0!(Attribute::parse_outer) >>
David Tolnayf8db7ba2017-11-11 22:52:16 -0800273 keyword!(pub) >>
Nika Layzell27726662017-10-24 23:16:35 -0400274 variant: syn!(Ident) >>
David Tolnayfcfb9002017-12-28 22:04:29 -0500275 member: option!(map!(parens!(alt!(
David Tolnay3d772182017-12-28 17:18:53 -0500276 call!(ast_struct_inner) => { |x: AstItem| (Path::from(x.ast.ident), Some(x)) }
Nika Layzell27726662017-10-24 23:16:35 -0400277 |
278 syn!(Path) => { |x| (x, None) }
David Tolnay8875fca2017-12-31 13:52:37 -0500279 )), |x| x.1)) >>
David Tolnayf8db7ba2017-11-11 22:52:16 -0800280 punct!(,) >>
Nika Layzell27726662017-10-24 23:16:35 -0400281 (EosVariant {
282 name: variant,
David Tolnayfcfb9002017-12-28 22:04:29 -0500283 member: member.clone().map(|x| x.0),
284 inner: member.map(|x| x.1).unwrap_or_default(),
Nika Layzell27726662017-10-24 23:16:35 -0400285 })
286 ));
287
288 // ast_enum_of_structs! parsing
289 pub struct AstEnumOfStructs(pub Vec<AstItem>);
290 impl Synom for AstEnumOfStructs {
David Tolnayab919512017-12-30 23:31:51 -0500291 named!(parse -> Self, do_parse!(
David Tolnay2c136452017-12-27 14:13:32 -0500292 many0!(Attribute::parse_outer) >>
David Tolnayf8db7ba2017-11-11 22:52:16 -0800293 keyword!(pub) >>
294 keyword!(enum) >>
Nika Layzell27726662017-10-24 23:16:35 -0400295 id: syn!(Ident) >>
David Tolnaye3d41b72017-12-31 15:24:00 -0500296 variants: braces!(many0!(eos_variant)) >>
Nika Layzell27726662017-10-24 23:16:35 -0400297 option!(syn!(Ident)) >> // do_not_generate_to_tokens
298 ({
299 // XXX: This is really gross - we shouldn't have to convert the
300 // tokens to strings to re-parse them.
301 let enum_item = {
David Tolnaye3d41b72017-12-31 15:24:00 -0500302 let variants = variants.1.iter().map(|v| {
Nika Layzell27726662017-10-24 23:16:35 -0400303 let name = v.name;
David Tolnayfcfb9002017-12-28 22:04:29 -0500304 match v.member {
305 Some(ref member) => quote!(#name(#member)),
306 None => quote!(#name),
307 }
Nika Layzell27726662017-10-24 23:16:35 -0400308 });
David Tolnay01a77582018-01-01 20:00:51 -0800309 syn::parse_str(&quote! {
Nika Layzell27726662017-10-24 23:16:35 -0400310 pub enum #id { #(#variants),* }
David Tolnay01a77582018-01-01 20:00:51 -0800311 }.to_string())?
Nika Layzell27726662017-10-24 23:16:35 -0400312 };
313 let mut items = vec![AstItem {
David Tolnay3d772182017-12-28 17:18:53 -0500314 ast: enum_item,
Nika Layzell27726662017-10-24 23:16:35 -0400315 features: quote!(),
316 eos_full: false,
317 }];
David Tolnaye3d41b72017-12-31 15:24:00 -0500318 items.extend(variants.1.into_iter().filter_map(|v| v.inner));
Nika Layzell27726662017-10-24 23:16:35 -0400319 AstEnumOfStructs(items)
320 })
David Tolnayab919512017-12-30 23:31:51 -0500321 ));
Nika Layzell27726662017-10-24 23:16:35 -0400322 }
323}
324
325mod codegen {
326 use super::{AstItem, Lookup};
327 use syn::*;
David Tolnayf2cfd722017-12-31 18:02:51 -0500328 use syn::punctuated::Punctuated;
David Tolnayd67fb752017-12-27 13:50:29 -0500329 use quote::{ToTokens, Tokens};
David Tolnayf0d63bf2017-12-26 12:29:47 -0500330 use std::fmt::{self, Display};
Nika Layzell27726662017-10-24 23:16:35 -0400331
332 #[derive(Default)]
333 pub struct State {
334 pub visit_trait: String,
335 pub visit_impl: String,
336 pub visit_mut_trait: String,
337 pub visit_mut_impl: String,
338 pub fold_trait: String,
339 pub fold_impl: String,
340 }
341
David Tolnay4a918742017-12-28 16:54:41 -0500342 fn under_name(name: Ident) -> Ident {
Nika Layzell27726662017-10-24 23:16:35 -0400343 use inflections::Inflect;
344 name.as_ref().to_snake_case().into()
345 }
346
David Tolnay3d772182017-12-28 17:18:53 -0500347 enum RelevantType<'a> {
348 Box(&'a Type),
349 Vec(&'a Type),
David Tolnayf2cfd722017-12-31 18:02:51 -0500350 Punctuated(&'a Type),
David Tolnay3d772182017-12-28 17:18:53 -0500351 Option(&'a Type),
David Tolnayf2cfd722017-12-31 18:02:51 -0500352 Tuple(&'a Punctuated<Type, Token![,]>),
David Tolnay3d772182017-12-28 17:18:53 -0500353 Simple(&'a AstItem),
David Tolnay1e01f9c2017-12-28 20:16:19 -0500354 Token(Tokens),
David Tolnay3d772182017-12-28 17:18:53 -0500355 Pass,
356 }
Nika Layzell27726662017-10-24 23:16:35 -0400357
David Tolnay3d772182017-12-28 17:18:53 -0500358 fn classify<'a>(ty: &'a Type, lookup: &'a Lookup) -> RelevantType<'a> {
359 match *ty {
360 Type::Path(TypePath { qself: None, ref path }) => {
361 let last = path.segments.last().unwrap().into_item();
362 match last.ident.as_ref() {
363 "Box" => RelevantType::Box(first_arg(&last.arguments)),
364 "Vec" => RelevantType::Vec(first_arg(&last.arguments)),
David Tolnayf2cfd722017-12-31 18:02:51 -0500365 "Punctuated" => RelevantType::Punctuated(first_arg(&last.arguments)),
David Tolnay3d772182017-12-28 17:18:53 -0500366 "Option" => RelevantType::Option(first_arg(&last.arguments)),
David Tolnay1e01f9c2017-12-28 20:16:19 -0500367 "Brace" | "Bracket" | "Paren" | "Group" => {
368 RelevantType::Token(last.ident.into_tokens())
369 }
David Tolnay3d772182017-12-28 17:18:53 -0500370 _ => {
371 if let Some(item) = lookup.get(&last.ident) {
372 RelevantType::Simple(item)
373 } else {
374 RelevantType::Pass
375 }
376 }
377 }
Nika Layzell27726662017-10-24 23:16:35 -0400378 }
David Tolnayeadbda32017-12-29 02:33:47 -0500379 Type::Tuple(TypeTuple { ref elems, .. }) => {
380 RelevantType::Tuple(elems)
David Tolnay5c4c0b52017-12-28 17:58:54 -0500381 }
David Tolnay323279a2017-12-29 11:26:32 -0500382 Type::Macro(TypeMacro { ref mac }) if mac.path.segments.last().unwrap().into_item().ident == "Token" => {
David Tolnay1e01f9c2017-12-28 20:16:19 -0500383 RelevantType::Token(mac.into_tokens())
David Tolnaycc0f0372017-12-28 19:11:04 -0500384 }
David Tolnay3d772182017-12-28 17:18:53 -0500385 _ => RelevantType::Pass,
Nika Layzell27726662017-10-24 23:16:35 -0400386 }
387 }
388
389 #[derive(Debug, Eq, PartialEq, Copy, Clone)]
390 enum Kind {
391 Visit,
392 VisitMut,
393 Fold,
394 }
395
David Tolnayf0d63bf2017-12-26 12:29:47 -0500396 enum Operand {
397 Borrowed(Tokens),
398 Owned(Tokens),
399 }
400
David Tolnay83db9272017-12-28 17:02:31 -0500401 use self::Operand::*;
402 use self::Kind::*;
403
David Tolnayf0d63bf2017-12-26 12:29:47 -0500404 impl Operand {
405 fn tokens(&self) -> &Tokens {
406 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500407 Borrowed(ref n) | Owned(ref n) => n,
David Tolnayf0d63bf2017-12-26 12:29:47 -0500408 }
409 }
410
411 fn ref_tokens(&self) -> Tokens {
412 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500413 Borrowed(ref n) => n.clone(),
414 Owned(ref n) => quote!(&#n),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500415 }
416 }
417
418 fn ref_mut_tokens(&self) -> Tokens {
419 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500420 Borrowed(ref n) => n.clone(),
421 Owned(ref n) => quote!(&mut #n),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500422 }
423 }
424
425 fn owned_tokens(&self) -> Tokens {
426 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500427 Borrowed(ref n) => quote!(*#n),
428 Owned(ref n) => n.clone(),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500429 }
430 }
431 }
432
433 impl Display for Operand {
434 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
David Tolnay4e52d8a2017-12-28 15:54:50 -0500435 Display::fmt(self.tokens(), formatter)
David Tolnayf0d63bf2017-12-26 12:29:47 -0500436 }
437 }
438
Nika Layzellc08227a2017-12-04 16:30:17 -0500439 fn first_arg(params: &PathArguments) -> &Type {
Nika Layzell27726662017-10-24 23:16:35 -0400440 let data = match *params {
Nika Layzellc08227a2017-12-04 16:30:17 -0500441 PathArguments::AngleBracketed(ref data) => data,
442 _ => panic!("Expected at least 1 type argument here"),
Nika Layzell27726662017-10-24 23:16:35 -0400443 };
444
David Tolnayd67fb752017-12-27 13:50:29 -0500445 match **data.args
446 .first()
447 .expect("Expected at least 1 type argument here")
448 .item()
449 {
David Tolnayea9ae892017-12-26 01:44:32 -0500450 GenericArgument::Type(ref ty) => ty,
Nika Layzellc08227a2017-12-04 16:30:17 -0500451 _ => panic!("Expected at least 1 type argument here"),
Nika Layzell357885a2017-12-04 15:47:07 -0500452 }
Nika Layzell27726662017-10-24 23:16:35 -0400453 }
454
455 fn simple_visit(
David Tolnay3d772182017-12-28 17:18:53 -0500456 item: &AstItem,
Nika Layzell27726662017-10-24 23:16:35 -0400457 kind: Kind,
David Tolnayf0d63bf2017-12-26 12:29:47 -0500458 name: &Operand,
David Tolnay4a918742017-12-28 16:54:41 -0500459 ) -> String {
460 match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500461 Visit => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500462 "_visitor.visit_{under_name}({name})",
David Tolnay3d772182017-12-28 17:18:53 -0500463 under_name = under_name(item.ast.ident),
David Tolnay4a918742017-12-28 16:54:41 -0500464 name = name.ref_tokens(),
465 ),
David Tolnay83db9272017-12-28 17:02:31 -0500466 VisitMut => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500467 "_visitor.visit_{under_name}_mut({name})",
David Tolnay3d772182017-12-28 17:18:53 -0500468 under_name = under_name(item.ast.ident),
David Tolnay4a918742017-12-28 16:54:41 -0500469 name = name.ref_mut_tokens(),
470 ),
David Tolnay83db9272017-12-28 17:02:31 -0500471 Fold => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500472 "_visitor.fold_{under_name}({name})",
David Tolnay3d772182017-12-28 17:18:53 -0500473 under_name = under_name(item.ast.ident),
David Tolnay4a918742017-12-28 16:54:41 -0500474 name = name.owned_tokens(),
475 ),
Nika Layzell27726662017-10-24 23:16:35 -0400476 }
477 }
478
479 fn box_visit(
David Tolnay3d772182017-12-28 17:18:53 -0500480 elem: &Type,
Nika Layzell27726662017-10-24 23:16:35 -0400481 lookup: &Lookup,
482 kind: Kind,
David Tolnayf0d63bf2017-12-26 12:29:47 -0500483 name: &Operand,
Nika Layzell27726662017-10-24 23:16:35 -0400484 ) -> Option<String> {
David Tolnay4a918742017-12-28 16:54:41 -0500485 let name = name.owned_tokens();
David Tolnay39d0a202017-12-28 18:19:00 -0500486 let res = visit(elem, lookup, kind, &Owned(quote!(*#name)))?;
David Tolnay4a918742017-12-28 16:54:41 -0500487 Some(match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500488 Fold => format!("Box::new({})", res),
489 Visit | VisitMut => res,
David Tolnay4a918742017-12-28 16:54:41 -0500490 })
Nika Layzell27726662017-10-24 23:16:35 -0400491 }
492
493 fn vec_visit(
David Tolnay3d772182017-12-28 17:18:53 -0500494 elem: &Type,
Nika Layzell27726662017-10-24 23:16:35 -0400495 lookup: &Lookup,
496 kind: Kind,
David Tolnayf0d63bf2017-12-26 12:29:47 -0500497 name: &Operand,
Nika Layzell27726662017-10-24 23:16:35 -0400498 ) -> Option<String> {
David Tolnay4a918742017-12-28 16:54:41 -0500499 let operand = match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500500 Visit | VisitMut => Borrowed(quote!(it)),
501 Fold => Owned(quote!(it)),
David Tolnay4a918742017-12-28 16:54:41 -0500502 };
David Tolnay39d0a202017-12-28 18:19:00 -0500503 let val = visit(elem, lookup, kind, &operand)?;
David Tolnay4a918742017-12-28 16:54:41 -0500504 Some(match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500505 Visit => {
David Tolnay3d772182017-12-28 17:18:53 -0500506 format!(
507 "for it in {name} {{ {val} }}",
508 name = name.ref_tokens(),
509 val = val,
510 )
Nika Layzell27726662017-10-24 23:16:35 -0400511 }
David Tolnay83db9272017-12-28 17:02:31 -0500512 VisitMut => {
David Tolnay3d772182017-12-28 17:18:53 -0500513 format!(
514 "for it in {name} {{ {val} }}",
515 name = name.ref_mut_tokens(),
516 val = val,
517 )
518 }
519 Fold => format!(
520 "FoldHelper::lift({name}, |it| {{ {val} }})",
521 name = name.owned_tokens(),
522 val = val,
523 ),
524 })
525 }
526
David Tolnay6eff4da2018-01-01 20:27:45 -0800527 fn punctuated_visit(
David Tolnay3d772182017-12-28 17:18:53 -0500528 elem: &Type,
529 lookup: &Lookup,
530 kind: Kind,
531 name: &Operand,
532 ) -> Option<String> {
533 let operand = match kind {
534 Visit | VisitMut => Borrowed(quote!(it)),
535 Fold => Owned(quote!(it)),
536 };
David Tolnay39d0a202017-12-28 18:19:00 -0500537 let val = visit(elem, lookup, kind, &operand)?;
David Tolnay3d772182017-12-28 17:18:53 -0500538 Some(match kind {
539 Visit => {
540 format!(
David Tolnay6eff4da2018-01-01 20:27:45 -0800541 "for el in Punctuated::elements({name}) {{ \
David Tolnay3d772182017-12-28 17:18:53 -0500542 let it = el.item(); \
543 {val} \
544 }}",
545 name = name.ref_tokens(),
546 val = val,
547 )
548 }
549 VisitMut => {
550 format!(
David Tolnay6eff4da2018-01-01 20:27:45 -0800551 "for mut el in Punctuated::elements_mut({name}) {{ \
David Tolnay3d772182017-12-28 17:18:53 -0500552 let it = el.item_mut(); \
553 {val} \
554 }}",
555 name = name.ref_mut_tokens(),
556 val = val,
557 )
David Tolnay4a918742017-12-28 16:54:41 -0500558 }
David Tolnay83db9272017-12-28 17:02:31 -0500559 Fold => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500560 "FoldHelper::lift({name}, |it| {{ {val} }})",
561 name = name.owned_tokens(),
562 val = val,
563 ),
564 })
Nika Layzell27726662017-10-24 23:16:35 -0400565 }
566
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400567 fn option_visit(
David Tolnay3d772182017-12-28 17:18:53 -0500568 elem: &Type,
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400569 lookup: &Lookup,
570 kind: Kind,
David Tolnayf0d63bf2017-12-26 12:29:47 -0500571 name: &Operand,
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400572 ) -> Option<String> {
David Tolnay4a918742017-12-28 16:54:41 -0500573 let it = match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500574 Visit | VisitMut => Borrowed(quote!(it)),
575 Fold => Owned(quote!(it)),
David Tolnay4a918742017-12-28 16:54:41 -0500576 };
David Tolnay39d0a202017-12-28 18:19:00 -0500577 let val = visit(elem, lookup, kind, &it)?;
David Tolnay4a918742017-12-28 16:54:41 -0500578 Some(match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500579 Visit => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500580 "if let Some(ref it) = {name} {{ {val} }}",
581 name = name.owned_tokens(),
582 val = val,
583 ),
David Tolnay83db9272017-12-28 17:02:31 -0500584 VisitMut => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500585 "if let Some(ref mut it) = {name} {{ {val} }}",
586 name = name.owned_tokens(),
587 val = val,
588 ),
David Tolnay83db9272017-12-28 17:02:31 -0500589 Fold => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500590 "({name}).map(|it| {{ {val} }})",
591 name = name.owned_tokens(),
592 val = val,
593 ),
594 })
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400595 }
596
David Tolnay5c4c0b52017-12-28 17:58:54 -0500597 fn tuple_visit(
David Tolnayf2cfd722017-12-31 18:02:51 -0500598 elems: &Punctuated<Type, Token![,]>,
David Tolnay5c4c0b52017-12-28 17:58:54 -0500599 lookup: &Lookup,
600 kind: Kind,
601 name: &Operand,
602 ) -> Option<String> {
603 let mut code = String::new();
David Tolnayf047c7a2017-12-31 02:29:04 -0500604 for (i, elem) in elems.iter().enumerate() {
David Tolnay5c4c0b52017-12-28 17:58:54 -0500605 let name = name.tokens();
David Tolnay14982012017-12-29 00:49:51 -0500606 let i = Index::from(i);
David Tolnay5c4c0b52017-12-28 17:58:54 -0500607 let it = Owned(quote!((#name).#i));
David Tolnay6eff4da2018-01-01 20:27:45 -0800608 let val = visit(elem, lookup, kind, &it)
David Tolnay5c4c0b52017-12-28 17:58:54 -0500609 .unwrap_or_else(|| noop_visit(kind, &it));
610 code.push_str(&format!(" {}", val));
611 match kind {
612 Fold => code.push(','),
613 Visit | VisitMut => code.push(';'),
614 }
615 code.push('\n');
616 }
617 if code.is_empty() {
618 None
619 } else {
620 Some(match kind {
621 Fold => {
622 format!("(\n{} )", code)
623 }
624 Visit | VisitMut => {
625 format!("\n{} ", code)
626 }
627 })
628 }
629 }
630
David Tolnay1e01f9c2017-12-28 20:16:19 -0500631 fn token_visit(ty: Tokens, kind: Kind, name: &Operand) -> String {
David Tolnaycc0f0372017-12-28 19:11:04 -0500632 match kind {
633 Fold => format!(
David Tolnay1e01f9c2017-12-28 20:16:19 -0500634 "{ty}(tokens_helper(_visitor, &({name}).0))",
635 ty = ty,
David Tolnaycc0f0372017-12-28 19:11:04 -0500636 name = name.owned_tokens(),
637 ),
638 Visit => format!(
639 "tokens_helper(_visitor, &({name}).0)",
640 name = name.ref_tokens(),
641 ),
642 VisitMut => format!(
643 "tokens_helper(_visitor, &mut ({name}).0)",
644 name = name.ref_mut_tokens(),
645 ),
646 }
647 }
648
David Tolnay4a918742017-12-28 16:54:41 -0500649 fn noop_visit(kind: Kind, name: &Operand) -> String {
650 match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500651 Fold => name.owned_tokens().to_string(),
652 Visit | VisitMut => format!("// Skipped field {}", name),
David Tolnay4a918742017-12-28 16:54:41 -0500653 }
654 }
655
656 fn visit(ty: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<String> {
David Tolnay3d772182017-12-28 17:18:53 -0500657 match classify(ty, lookup) {
658 RelevantType::Box(elem) => {
659 box_visit(elem, lookup, kind, name)
David Tolnay4a918742017-12-28 16:54:41 -0500660 }
David Tolnay3d772182017-12-28 17:18:53 -0500661 RelevantType::Vec(elem) => {
662 vec_visit(elem, lookup, kind, name)
David Tolnay4a918742017-12-28 16:54:41 -0500663 }
David Tolnayf2cfd722017-12-31 18:02:51 -0500664 RelevantType::Punctuated(elem) => {
David Tolnay6eff4da2018-01-01 20:27:45 -0800665 punctuated_visit(elem, lookup, kind, name)
David Tolnay4a918742017-12-28 16:54:41 -0500666 }
David Tolnay3d772182017-12-28 17:18:53 -0500667 RelevantType::Option(elem) => {
668 option_visit(elem, lookup, kind, name)
669 }
David Tolnay5c4c0b52017-12-28 17:58:54 -0500670 RelevantType::Tuple(elems) => {
671 tuple_visit(elems, lookup, kind, name)
672 }
David Tolnay3d772182017-12-28 17:18:53 -0500673 RelevantType::Simple(item) => {
674 let mut res = simple_visit(item, kind, name);
675 Some(if item.eos_full {
676 format!("full!({res})", res = res)
677 } else {
678 res
679 })
680 }
David Tolnay1e01f9c2017-12-28 20:16:19 -0500681 RelevantType::Token(ty) => {
682 Some(token_visit(ty, kind, name))
David Tolnaycc0f0372017-12-28 19:11:04 -0500683 }
David Tolnay3d772182017-12-28 17:18:53 -0500684 RelevantType::Pass => {
685 None
Nika Layzell27726662017-10-24 23:16:35 -0400686 }
687 }
Nika Layzell27726662017-10-24 23:16:35 -0400688 }
689
690 pub fn generate(state: &mut State, lookup: &Lookup, s: &AstItem) {
David Tolnay3d772182017-12-28 17:18:53 -0500691 let under_name = under_name(s.ast.ident);
Nika Layzell27726662017-10-24 23:16:35 -0400692
693 state.visit_trait.push_str(&format!(
694 "{features}\n\
Nika Layzellc86173a2017-11-18 13:55:22 -0500695 fn visit_{under_name}(&mut self, i: &'ast {ty}) {{ \
David Tolnayd67fb752017-12-27 13:50:29 -0500696 visit_{under_name}(self, i) \
Nika Layzell27726662017-10-24 23:16:35 -0400697 }}\n",
698 features = s.features,
699 under_name = under_name,
David Tolnay3d772182017-12-28 17:18:53 -0500700 ty = s.ast.ident,
Nika Layzell27726662017-10-24 23:16:35 -0400701 ));
702 state.visit_mut_trait.push_str(&format!(
703 "{features}\n\
Nika Layzella6f46c42017-10-26 15:26:16 -0400704 fn visit_{under_name}_mut(&mut self, i: &mut {ty}) {{ \
David Tolnayd67fb752017-12-27 13:50:29 -0500705 visit_{under_name}_mut(self, i) \
Nika Layzell27726662017-10-24 23:16:35 -0400706 }}\n",
707 features = s.features,
708 under_name = under_name,
David Tolnay3d772182017-12-28 17:18:53 -0500709 ty = s.ast.ident,
Nika Layzell27726662017-10-24 23:16:35 -0400710 ));
711 state.fold_trait.push_str(&format!(
712 "{features}\n\
713 fn fold_{under_name}(&mut self, i: {ty}) -> {ty} {{ \
David Tolnayd67fb752017-12-27 13:50:29 -0500714 fold_{under_name}(self, i) \
Nika Layzell27726662017-10-24 23:16:35 -0400715 }}\n",
716 features = s.features,
717 under_name = under_name,
David Tolnay3d772182017-12-28 17:18:53 -0500718 ty = s.ast.ident,
Nika Layzell27726662017-10-24 23:16:35 -0400719 ));
720
721 state.visit_impl.push_str(&format!(
722 "{features}\n\
Nika Layzellc86173a2017-11-18 13:55:22 -0500723 pub fn visit_{under_name}<'ast, V: Visitor<'ast> + ?Sized>(\
David Tolnayd67fb752017-12-27 13:50:29 -0500724 _visitor: &mut V, _i: &'ast {ty}) {{\n",
Nika Layzell27726662017-10-24 23:16:35 -0400725 features = s.features,
726 under_name = under_name,
David Tolnay3d772182017-12-28 17:18:53 -0500727 ty = s.ast.ident,
Nika Layzell27726662017-10-24 23:16:35 -0400728 ));
729 state.visit_mut_impl.push_str(&format!(
730 "{features}\n\
Nika Layzella6f46c42017-10-26 15:26:16 -0400731 pub fn visit_{under_name}_mut<V: VisitorMut + ?Sized>(\
David Tolnayd67fb752017-12-27 13:50:29 -0500732 _visitor: &mut V, _i: &mut {ty}) {{\n",
Nika Layzell27726662017-10-24 23:16:35 -0400733 features = s.features,
734 under_name = under_name,
David Tolnay3d772182017-12-28 17:18:53 -0500735 ty = s.ast.ident,
Nika Layzell27726662017-10-24 23:16:35 -0400736 ));
David Tolnayd0adf522017-12-29 01:30:07 -0500737 let before_fold_impl_len = state.fold_impl.len();
Nika Layzell27726662017-10-24 23:16:35 -0400738 state.fold_impl.push_str(&format!(
739 "{features}\n\
Nika Layzella6f46c42017-10-26 15:26:16 -0400740 pub fn fold_{under_name}<V: Folder + ?Sized>(\
David Tolnayd67fb752017-12-27 13:50:29 -0500741 _visitor: &mut V, _i: {ty}) -> {ty} {{\n",
Nika Layzell27726662017-10-24 23:16:35 -0400742 features = s.features,
743 under_name = under_name,
David Tolnay3d772182017-12-28 17:18:53 -0500744 ty = s.ast.ident,
Nika Layzell27726662017-10-24 23:16:35 -0400745 ));
746
747 // XXX: This part is a disaster - I'm not sure how to make it cleaner though :'(
David Tolnaye3d41b72017-12-31 15:24:00 -0500748 match s.ast.data {
749 Data::Enum(ref e) => {
Nika Layzell27726662017-10-24 23:16:35 -0400750 state.visit_impl.push_str(" match *_i {\n");
751 state.visit_mut_impl.push_str(" match *_i {\n");
752 state.fold_impl.push_str(" match _i {\n");
753 for variant in &e.variants {
David Tolnay6eff4da2018-01-01 20:27:45 -0800754 let fields: Vec<(&Field, Tokens)> = match variant.fields {
David Tolnaye3d41b72017-12-31 15:24:00 -0500755 Fields::Named(..) => panic!("Doesn't support enum struct variants"),
756 Fields::Unnamed(ref fields) => {
David Tolnay6eff4da2018-01-01 20:27:45 -0800757 let binding = format!(" {}::{}(", s.ast.ident, variant.ident);
Nika Layzell27726662017-10-24 23:16:35 -0400758 state.visit_impl.push_str(&binding);
759 state.visit_mut_impl.push_str(&binding);
760 state.fold_impl.push_str(&binding);
761
David Tolnaybdafb102018-01-01 19:39:10 -0800762 let res = fields.unnamed
David Tolnayd67fb752017-12-27 13:50:29 -0500763 .iter()
764 .enumerate()
765 .map(|(idx, el)| {
766 let name = format!("_binding_{}", idx);
Nika Layzell27726662017-10-24 23:16:35 -0400767
David Tolnayd67fb752017-12-27 13:50:29 -0500768 state.visit_impl.push_str("ref ");
769 state.visit_mut_impl.push_str("ref mut ");
Nika Layzell27726662017-10-24 23:16:35 -0400770
David Tolnayd67fb752017-12-27 13:50:29 -0500771 state.visit_impl.push_str(&name);
772 state.visit_mut_impl.push_str(&name);
773 state.fold_impl.push_str(&name);
774 state.visit_impl.push_str(", ");
775 state.visit_mut_impl.push_str(", ");
776 state.fold_impl.push_str(", ");
Nika Layzell27726662017-10-24 23:16:35 -0400777
David Tolnayd67fb752017-12-27 13:50:29 -0500778 let mut tokens = quote!();
779 Ident::from(name).to_tokens(&mut tokens);
Nika Layzell27726662017-10-24 23:16:35 -0400780
David Tolnay6eff4da2018-01-01 20:27:45 -0800781 (el, tokens)
David Tolnayd67fb752017-12-27 13:50:29 -0500782 })
783 .collect();
Nika Layzell27726662017-10-24 23:16:35 -0400784
785 state.visit_impl.push_str(") => {\n");
786 state.visit_mut_impl.push_str(") => {\n");
787 state.fold_impl.push_str(") => {\n");
788
789 res
790 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500791 Fields::Unit => {
David Tolnayd67fb752017-12-27 13:50:29 -0500792 state
793 .visit_impl
David Tolnay6eff4da2018-01-01 20:27:45 -0800794 .push_str(&format!(" {0}::{1} => {{ }}\n", s.ast.ident, variant.ident));
David Tolnayd67fb752017-12-27 13:50:29 -0500795 state
796 .visit_mut_impl
David Tolnay6eff4da2018-01-01 20:27:45 -0800797 .push_str(&format!(" {0}::{1} => {{ }}\n", s.ast.ident, variant.ident));
Nika Layzell27726662017-10-24 23:16:35 -0400798 state.fold_impl.push_str(&format!(
David Tolnay6702ade2017-12-30 23:38:15 -0500799 " {0}::{1} => {{ {0}::{1} }}\n",
800 s.ast.ident,
David Tolnay6eff4da2018-01-01 20:27:45 -0800801 variant.ident
Nika Layzell27726662017-10-24 23:16:35 -0400802 ));
David Tolnayd67fb752017-12-27 13:50:29 -0500803 continue;
Nika Layzell27726662017-10-24 23:16:35 -0400804 }
805 };
806
807 if fields.is_empty() {
808 state.visit_impl.push_str(" {}");
809 state.visit_mut_impl.push_str(") => {\n");
810 state.fold_impl.push_str(") => {\n");
811 }
David Tolnayd67fb752017-12-27 13:50:29 -0500812 state
813 .fold_impl
David Tolnay6eff4da2018-01-01 20:27:45 -0800814 .push_str(&format!(" {}::{} (\n", s.ast.ident, variant.ident,));
Nika Layzell27726662017-10-24 23:16:35 -0400815 for (field, binding) in fields {
816 state.visit_impl.push_str(&format!(
817 " {};\n",
David Tolnayd67fb752017-12-27 13:50:29 -0500818 visit(
819 &field.ty,
820 lookup,
David Tolnay83db9272017-12-28 17:02:31 -0500821 Visit,
822 &Borrowed(binding.clone())
David Tolnay4a918742017-12-28 16:54:41 -0500823 ).unwrap_or_else(|| noop_visit(
David Tolnay83db9272017-12-28 17:02:31 -0500824 Visit,
825 &Borrowed(binding.clone())
David Tolnay4a918742017-12-28 16:54:41 -0500826 )),
Nika Layzell27726662017-10-24 23:16:35 -0400827 ));
828 state.visit_mut_impl.push_str(&format!(
829 " {};\n",
David Tolnayd67fb752017-12-27 13:50:29 -0500830 visit(
831 &field.ty,
832 lookup,
David Tolnay83db9272017-12-28 17:02:31 -0500833 VisitMut,
834 &Borrowed(binding.clone())
David Tolnay4a918742017-12-28 16:54:41 -0500835 ).unwrap_or_else(|| noop_visit(
David Tolnay83db9272017-12-28 17:02:31 -0500836 VisitMut,
837 &Borrowed(binding.clone())
David Tolnay4a918742017-12-28 16:54:41 -0500838 )),
Nika Layzell27726662017-10-24 23:16:35 -0400839 ));
840 state.fold_impl.push_str(&format!(
841 " {},\n",
David Tolnay83db9272017-12-28 17:02:31 -0500842 visit(&field.ty, lookup, Fold, &Owned(binding.clone()))
David Tolnay4a918742017-12-28 16:54:41 -0500843 .unwrap_or_else(|| noop_visit(
David Tolnay83db9272017-12-28 17:02:31 -0500844 Fold,
845 &Owned(binding)
David Tolnay4a918742017-12-28 16:54:41 -0500846 )),
Nika Layzell27726662017-10-24 23:16:35 -0400847 ));
848 }
849 state.fold_impl.push_str(" )\n");
850
851 state.visit_impl.push_str(" }\n");
852 state.visit_mut_impl.push_str(" }\n");
853 state.fold_impl.push_str(" }\n");
854 }
855 state.visit_impl.push_str(" }\n");
856 state.visit_mut_impl.push_str(" }\n");
857 state.fold_impl.push_str(" }\n");
858 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500859 Data::Struct(ref v) => {
860 let fields: Vec<(&Field, Tokens)> = match v.fields {
861 Fields::Named(ref fields) => {
David Tolnayd67fb752017-12-27 13:50:29 -0500862 state
863 .fold_impl
David Tolnay3d772182017-12-28 17:18:53 -0500864 .push_str(&format!(" {} {{\n", s.ast.ident));
David Tolnaybdafb102018-01-01 19:39:10 -0800865 fields.named
David Tolnayd67fb752017-12-27 13:50:29 -0500866 .iter()
867 .map(|el| {
David Tolnay6eff4da2018-01-01 20:27:45 -0800868 let id = el.ident;
869 (el, quote!(_i.#id))
David Tolnayd67fb752017-12-27 13:50:29 -0500870 })
871 .collect()
Nika Layzell27726662017-10-24 23:16:35 -0400872 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500873 Fields::Unnamed(ref fields) => {
David Tolnayd67fb752017-12-27 13:50:29 -0500874 state
875 .fold_impl
David Tolnay3d772182017-12-28 17:18:53 -0500876 .push_str(&format!(" {} (\n", s.ast.ident));
David Tolnaybdafb102018-01-01 19:39:10 -0800877 fields.unnamed
David Tolnayd67fb752017-12-27 13:50:29 -0500878 .iter()
879 .enumerate()
880 .map(|(idx, el)| {
David Tolnay14982012017-12-29 00:49:51 -0500881 let id = Index::from(idx);
David Tolnay6eff4da2018-01-01 20:27:45 -0800882 (el, quote!(_i.#id))
David Tolnayd67fb752017-12-27 13:50:29 -0500883 })
884 .collect()
Nika Layzell27726662017-10-24 23:16:35 -0400885 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500886 Fields::Unit => {
Nika Layzellefb83ba2017-12-19 18:23:55 -0500887 state.fold_impl.push_str(" _i\n");
888 vec![]
889 }
Nika Layzell27726662017-10-24 23:16:35 -0400890 };
891
892 for (field, ref_toks) in fields {
David Tolnay83db9272017-12-28 17:02:31 -0500893 let ref_toks = Owned(ref_toks);
Nika Layzell27726662017-10-24 23:16:35 -0400894 state.visit_impl.push_str(&format!(
David Tolnayd67fb752017-12-27 13:50:29 -0500895 " {};\n",
David Tolnay83db9272017-12-28 17:02:31 -0500896 visit(&field.ty, lookup, Visit, &ref_toks)
David Tolnay4a918742017-12-28 16:54:41 -0500897 .unwrap_or_else(|| noop_visit(
David Tolnay83db9272017-12-28 17:02:31 -0500898 Visit,
David Tolnay4a918742017-12-28 16:54:41 -0500899 &ref_toks,
900 ))
Nika Layzell27726662017-10-24 23:16:35 -0400901 ));
902 state.visit_mut_impl.push_str(&format!(
David Tolnayd67fb752017-12-27 13:50:29 -0500903 " {};\n",
David Tolnay83db9272017-12-28 17:02:31 -0500904 visit(&field.ty, lookup, VisitMut, &ref_toks)
David Tolnay4a918742017-12-28 16:54:41 -0500905 .unwrap_or_else(|| noop_visit(
David Tolnay83db9272017-12-28 17:02:31 -0500906 VisitMut,
David Tolnay4a918742017-12-28 16:54:41 -0500907 &ref_toks,
908 ))
Nika Layzell27726662017-10-24 23:16:35 -0400909 ));
David Tolnay83db9272017-12-28 17:02:31 -0500910 let fold = visit(&field.ty, lookup, Fold, &ref_toks)
David Tolnay4a918742017-12-28 16:54:41 -0500911 .unwrap_or_else(|| noop_visit(
David Tolnay83db9272017-12-28 17:02:31 -0500912 Fold,
David Tolnay4a918742017-12-28 16:54:41 -0500913 &ref_toks,
914 ));
Nika Layzell27726662017-10-24 23:16:35 -0400915 if let Some(ref name) = field.ident {
David Tolnayd67fb752017-12-27 13:50:29 -0500916 state
917 .fold_impl
918 .push_str(&format!(" {}: {},\n", name, fold));
Nika Layzell27726662017-10-24 23:16:35 -0400919 } else {
920 state.fold_impl.push_str(&format!(" {},\n", fold));
921 }
922 }
923
David Tolnaye3d41b72017-12-31 15:24:00 -0500924 match v.fields {
925 Fields::Named(..) => state.fold_impl.push_str(" }\n"),
926 Fields::Unnamed(..) => state.fold_impl.push_str(" )\n"),
927 Fields::Unit => {}
Nika Layzell27726662017-10-24 23:16:35 -0400928 };
929 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500930 Data::Union(..) => panic!("Union not supported"),
Nika Layzell27726662017-10-24 23:16:35 -0400931 }
932
933 // Close the impl body
934 state.visit_impl.push_str("}\n");
935 state.visit_mut_impl.push_str("}\n");
936 state.fold_impl.push_str("}\n");
David Tolnayd0adf522017-12-29 01:30:07 -0500937
David Tolnay360efd22018-01-04 23:35:26 -0800938 if let Data::Struct(ref data) = s.ast.data {
939 if let Fields::Named(ref fields) = data.fields {
940 if fields.named.iter().any(|field| field.vis == Visibility::Inherited) {
941 // Discard the generated impl if there are private fields.
942 // These have to be handwritten.
943 state.fold_impl.truncate(before_fold_impl_len);
944 }
945 }
David Tolnayd0adf522017-12-29 01:30:07 -0500946 }
Nika Layzell27726662017-10-24 23:16:35 -0400947 }
948}
949
950fn main() {
951 let mut lookup = BTreeMap::new();
David Tolnayea9ae892017-12-26 01:44:32 -0500952 load_file(SYN_CRATE_ROOT, &quote!(), &mut lookup).unwrap();
Nika Layzell27726662017-10-24 23:16:35 -0400953
Nika Layzellefb83ba2017-12-19 18:23:55 -0500954 // Load in any terminal types
955 for &tt in TERMINAL_TYPES {
956 use syn::*;
David Tolnayd67fb752017-12-27 13:50:29 -0500957 lookup.insert(
958 Ident::from(tt),
959 AstItem {
David Tolnay3d772182017-12-28 17:18:53 -0500960 ast: DeriveInput {
David Tolnayd67fb752017-12-27 13:50:29 -0500961 ident: Ident::from(tt),
962 vis: Visibility::Public(VisPublic {
963 pub_token: Default::default(),
964 }),
965 attrs: vec![],
966 generics: Default::default(),
David Tolnaye3d41b72017-12-31 15:24:00 -0500967 data: Data::Struct(DataStruct {
968 fields: Fields::Unit,
David Tolnayd67fb752017-12-27 13:50:29 -0500969 struct_token: Default::default(),
970 semi_token: None,
971 }),
972 },
973 features: Default::default(),
974 eos_full: false,
Nika Layzellefb83ba2017-12-19 18:23:55 -0500975 },
David Tolnayd67fb752017-12-27 13:50:29 -0500976 );
Nika Layzellefb83ba2017-12-19 18:23:55 -0500977 }
978
Nika Layzell27726662017-10-24 23:16:35 -0400979 let mut state = Default::default();
980 for s in lookup.values() {
981 codegen::generate(&mut state, &lookup, s);
982 }
983
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400984 let full_macro = "
985#[cfg(feature = \"full\")]
986macro_rules! full {
987 ($e:expr) => { $e }
988}
989
990#[cfg(not(feature = \"full\"))]
991macro_rules! full {
992 ($e:expr) => { unreachable!() }
993}
994";
995
Nika Layzell27726662017-10-24 23:16:35 -0400996 let mut fold_file = File::create(FOLD_SRC).unwrap();
David Tolnayd67fb752017-12-27 13:50:29 -0500997 write!(
998 fold_file,
999 "\
Nika Layzell27726662017-10-24 23:16:35 -04001000// THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT
1001
1002//! A Folder represents an AST->AST fold; it accepts an AST piece,
1003//! and returns a piece of the same type.
1004
David Tolnay0afc9b32017-12-27 13:38:24 -05001005#![cfg_attr(rustfmt, rustfmt_skip)]
1006
Nika Layzell27726662017-10-24 23:16:35 -04001007// Unreachable code is generated sometimes without the full feature.
1008#![allow(unreachable_code)]
David Tolnayf0d63bf2017-12-26 12:29:47 -05001009#![cfg_attr(feature = \"cargo-clippy\", allow(needless_pass_by_value))]
Nika Layzell27726662017-10-24 23:16:35 -04001010
Nika Layzella6f46c42017-10-26 15:26:16 -04001011use *;
David Tolnay1e01f9c2017-12-28 20:16:19 -05001012use token::{{Brace, Bracket, Paren, Group}};
David Tolnay98942562017-12-26 21:24:35 -05001013use proc_macro2::Span;
David Tolnayf60f4262017-12-28 19:17:58 -05001014use gen::helper::fold::*;
Nika Layzell27726662017-10-24 23:16:35 -04001015
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001016{full_macro}
1017
Nika Layzell27726662017-10-24 23:16:35 -04001018/// AST->AST fold.
1019///
1020/// Each method of the Folder trait is a hook to be potentially overridden. Each
1021/// method's default implementation recursively visits the substructure of the
1022/// input via the `walk` functions, which perform an \"identity fold\", that
1023/// is, they return the same structure that they are given (for example the
1024/// `fold_file` method by default calls `fold::walk_file`).
1025///
1026/// If you want to ensure that your code handles every variant
1027/// explicitly, you need to override each method. (And you also need
1028/// to monitor future changes to `Folder` in case a new method with a
1029/// new default implementation gets introduced.)
1030pub trait Folder {{
1031{fold_trait}
1032}}
1033
David Tolnay360efd22018-01-04 23:35:26 -08001034macro_rules! fold_span_only {{
1035 ($f:ident : $t:ident) => {{
1036 pub fn $f<V: Folder + ?Sized>(_visitor: &mut V, mut _i: $t) -> $t {{
1037 _i.span = _visitor.fold_span(_i.span);
1038 _i
1039 }}
1040 }}
David Tolnayd0adf522017-12-29 01:30:07 -05001041}}
1042
David Tolnay360efd22018-01-04 23:35:26 -08001043fold_span_only!(fold_ident: Ident);
1044fold_span_only!(fold_lifetime: Lifetime);
1045#[cfg(any(feature = \"full\", feature = \"derive\"))]
1046fold_span_only!(fold_lit_byte: LitByte);
1047#[cfg(any(feature = \"full\", feature = \"derive\"))]
1048fold_span_only!(fold_lit_byte_str: LitByteStr);
1049#[cfg(any(feature = \"full\", feature = \"derive\"))]
1050fold_span_only!(fold_lit_char: LitChar);
1051#[cfg(any(feature = \"full\", feature = \"derive\"))]
1052fold_span_only!(fold_lit_float: LitFloat);
1053#[cfg(any(feature = \"full\", feature = \"derive\"))]
1054fold_span_only!(fold_lit_int: LitInt);
1055#[cfg(any(feature = \"full\", feature = \"derive\"))]
1056fold_span_only!(fold_lit_str: LitStr);
David Tolnayd0adf522017-12-29 01:30:07 -05001057
Nika Layzell27726662017-10-24 23:16:35 -04001058{fold_impl}
1059",
David Tolnayd67fb752017-12-27 13:50:29 -05001060 full_macro = full_macro,
1061 fold_trait = state.fold_trait,
1062 fold_impl = state.fold_impl
1063 ).unwrap();
Nika Layzell27726662017-10-24 23:16:35 -04001064
1065 let mut visit_file = File::create(VISIT_SRC).unwrap();
David Tolnayd67fb752017-12-27 13:50:29 -05001066 write!(
1067 visit_file,
1068 "\
Nika Layzell27726662017-10-24 23:16:35 -04001069// THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT
1070
1071//! AST walker. Each overridden visit method has full control over what
1072//! happens with its node, it can do its own traversal of the node's children,
1073//! call `visit::walk_*` to apply the default traversal algorithm, or prevent
1074//! deeper traversal by doing nothing.
1075
David Tolnay0afc9b32017-12-27 13:38:24 -05001076#![cfg_attr(rustfmt, rustfmt_skip)]
1077
David Tolnayf0d63bf2017-12-26 12:29:47 -05001078#![cfg_attr(feature = \"cargo-clippy\", allow(match_same_arms))]
1079
Nika Layzella6f46c42017-10-26 15:26:16 -04001080use *;
David Tolnay6eff4da2018-01-01 20:27:45 -08001081use punctuated::Punctuated;
David Tolnay98942562017-12-26 21:24:35 -05001082use proc_macro2::Span;
David Tolnaycc0f0372017-12-28 19:11:04 -05001083use gen::helper::visit::*;
Nika Layzell27726662017-10-24 23:16:35 -04001084
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001085{full_macro}
1086
Nika Layzell27726662017-10-24 23:16:35 -04001087/// Each method of the Visitor trait is a hook to be potentially
1088/// overridden. Each method's default implementation recursively visits
1089/// the substructure of the input via the corresponding `walk` method;
1090/// e.g. the `visit_mod` method by default calls `visit::walk_mod`.
1091///
1092/// If you want to ensure that your code handles every variant
1093/// explicitly, you need to override each method. (And you also need
1094/// to monitor future changes to `Visitor` in case a new method with a
1095/// new default implementation gets introduced.)
Nika Layzellc86173a2017-11-18 13:55:22 -05001096pub trait Visitor<'ast> {{
Nika Layzell27726662017-10-24 23:16:35 -04001097{visit_trait}
1098}}
1099
1100{visit_impl}
1101",
David Tolnayd67fb752017-12-27 13:50:29 -05001102 full_macro = full_macro,
1103 visit_trait = state.visit_trait,
1104 visit_impl = state.visit_impl
1105 ).unwrap();
Nika Layzell27726662017-10-24 23:16:35 -04001106
1107 let mut visit_mut_file = File::create(VISIT_MUT_SRC).unwrap();
David Tolnayd67fb752017-12-27 13:50:29 -05001108 write!(
1109 visit_mut_file,
1110 "\
Nika Layzell27726662017-10-24 23:16:35 -04001111// THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT
1112
1113//! AST walker. Each overridden visit method has full control over what
1114//! happens with its node, it can do its own traversal of the node's children,
1115//! call `visit::walk_*` to apply the default traversal algorithm, or prevent
1116//! deeper traversal by doing nothing.
1117
David Tolnay0afc9b32017-12-27 13:38:24 -05001118#![cfg_attr(rustfmt, rustfmt_skip)]
1119
David Tolnayf0d63bf2017-12-26 12:29:47 -05001120#![cfg_attr(feature = \"cargo-clippy\", allow(match_same_arms))]
1121
Nika Layzella6f46c42017-10-26 15:26:16 -04001122use *;
David Tolnay6eff4da2018-01-01 20:27:45 -08001123use punctuated::Punctuated;
David Tolnay98942562017-12-26 21:24:35 -05001124use proc_macro2::Span;
David Tolnaycc0f0372017-12-28 19:11:04 -05001125use gen::helper::visit_mut::*;
Nika Layzell27726662017-10-24 23:16:35 -04001126
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001127{full_macro}
1128
Nika Layzell27726662017-10-24 23:16:35 -04001129/// Each method of the VisitorMut trait is a hook to be potentially
1130/// overridden. Each method's default implementation recursively visits
1131/// the substructure of the input via the corresponding `walk` method;
1132/// e.g. the `visit_mod` method by default calls `visit::walk_mod`.
1133///
1134/// If you want to ensure that your code handles every variant
1135/// explicitly, you need to override each method. (And you also need
1136/// to monitor future changes to `VisitorMut` in case a new method with a
1137/// new default implementation gets introduced.)
1138pub trait VisitorMut {{
1139{visit_mut_trait}
1140}}
1141
1142{visit_mut_impl}
1143",
David Tolnayd67fb752017-12-27 13:50:29 -05001144 full_macro = full_macro,
1145 visit_mut_trait = state.visit_mut_trait,
1146 visit_mut_impl = state.visit_mut_impl
1147 ).unwrap();
Nika Layzell27726662017-10-24 23:16:35 -04001148}