blob: 89eec815b291898eb71931c50306a62561882e7b [file] [log] [blame]
David Tolnay4b4c4b62018-01-06 13:48:05 -08001//! This crate automatically generates the definition of the `Visit`,
2//! `VisitMut`, and `Fold` traits in `syn` based on the `syn` source. It
Nika Layzell27726662017-10-24 23:16:35 -04003//! discovers structs and enums declared with the `ast_*` macros and generates
4//! the functions for those types.
5//!
6//! It makes a few assumptions about the target crate:
7//! 1. All structs which are discovered must be re-exported in the root of the
8//! crate, even if they were declared in a submodule.
9//! 2. This code cannot discover submodules which are located in subdirectories
10//! - only submodules located in the same directory.
11//! 3. The path to `syn` is hardcoded.
12
David Tolnayea9ae892017-12-26 01:44:32 -050013#![cfg_attr(feature = "cargo-clippy", allow(redundant_closure))]
14
David Tolnayd67fb752017-12-27 13:50:29 -050015#[macro_use]
16extern crate failure;
Nika Layzell27726662017-10-24 23:16:35 -040017extern crate inflections;
David Tolnay2e0dba12017-12-27 01:54:40 -050018extern crate proc_macro2;
David Tolnayd67fb752017-12-27 13:50:29 -050019#[macro_use]
20extern crate quote;
David Tolnay5c4c0b52017-12-28 17:58:54 -050021#[macro_use]
David Tolnayd67fb752017-12-27 13:50:29 -050022extern crate syn;
David Tolnay8c81f622018-07-31 23:34:35 -070023extern crate rustfmt_nightly as rustfmt;
Nika Layzell27726662017-10-24 23:16:35 -040024
David Tolnayd67fb752017-12-27 13:50:29 -050025use failure::{err_msg, Error};
David Tolnaye303b7c2018-05-20 16:46:35 -070026use proc_macro2::{Span, TokenStream};
David Tolnay01ed0ce2018-05-20 20:18:14 -070027use quote::ToTokens;
28use syn::{Attribute, Data, DataStruct, DeriveInput, Ident, Item};
Nika Layzell27726662017-10-24 23:16:35 -040029
David Tolnay01ed0ce2018-05-20 20:18:14 -070030use std::collections::BTreeMap;
David Tolnayf0d63bf2017-12-26 12:29:47 -050031use std::fmt::{self, Debug};
Nika Layzell27726662017-10-24 23:16:35 -040032use std::fs::File;
David Tolnay8c81f622018-07-31 23:34:35 -070033use std::io::Read;
Nika Layzell27726662017-10-24 23:16:35 -040034use std::path::Path;
Nika Layzell27726662017-10-24 23:16:35 -040035
36const SYN_CRATE_ROOT: &str = "../src/lib.rs";
37
38const FOLD_SRC: &str = "../src/gen/fold.rs";
39const VISIT_SRC: &str = "../src/gen/visit.rs";
40const VISIT_MUT_SRC: &str = "../src/gen/visit_mut.rs";
41
David Tolnayd67fb752017-12-27 13:50:29 -050042const IGNORED_MODS: &[&str] = &["fold", "visit", "visit_mut"];
Nika Layzell27726662017-10-24 23:16:35 -040043
Alex Crichton131308c2018-05-18 14:00:24 -070044const EXTRA_TYPES: &[&str] = &["Lifetime"];
David Tolnay4ba63a02017-12-28 15:53:05 -050045
Alex Crichtond261d092018-05-18 13:47:35 -070046const TERMINAL_TYPES: &[&str] = &["Span", "Ident"];
Nika Layzellefb83ba2017-12-19 18:23:55 -050047
Alex Crichtona74a1c82018-05-16 10:20:44 -070048fn path_eq(a: &syn::Path, b: &str) -> bool {
49 if a.global() {
David Tolnay01ed0ce2018-05-20 20:18:14 -070050 return false;
Nika Layzell27726662017-10-24 23:16:35 -040051 }
Alex Crichtona74a1c82018-05-16 10:20:44 -070052 if a.segments.len() != 1 {
David Tolnay01ed0ce2018-05-20 20:18:14 -070053 return false;
Alex Crichtona74a1c82018-05-16 10:20:44 -070054 }
David Tolnay446f7d62018-05-20 17:58:15 -070055 a.segments[0].ident == b
Nika Layzell27726662017-10-24 23:16:35 -040056}
57
Alex Crichton715862b2018-05-17 12:31:49 -070058fn get_features(attrs: &[Attribute], mut features: TokenStream) -> TokenStream {
Nika Layzell27726662017-10-24 23:16:35 -040059 for attr in attrs {
Alex Crichtona74a1c82018-05-16 10:20:44 -070060 if path_eq(&attr.path, "cfg") {
Nika Layzell27726662017-10-24 23:16:35 -040061 attr.to_tokens(&mut features);
62 }
63 }
64 features
65}
66
67#[derive(Clone)]
68pub struct AstItem {
David Tolnay3d772182017-12-28 17:18:53 -050069 ast: DeriveInput,
Alex Crichton715862b2018-05-17 12:31:49 -070070 features: TokenStream,
Nika Layzell27726662017-10-24 23:16:35 -040071 // True if this is an ast_enum_of_structs! item with a #full annotation.
72 eos_full: bool,
73}
74
David Tolnayf0d63bf2017-12-26 12:29:47 -050075impl Debug for AstItem {
Nika Layzell27726662017-10-24 23:16:35 -040076 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
77 f.debug_struct("AstItem")
David Tolnay3d772182017-12-28 17:18:53 -050078 .field("ast", &self.ast)
Nika Layzell27726662017-10-24 23:16:35 -040079 .field("features", &self.features.to_string())
80 .finish()
81 }
82}
83
84// NOTE: BTreeMap is used here instead of HashMap to have deterministic output.
David Tolnayc47f8422017-12-28 17:30:46 -050085type Lookup = BTreeMap<Ident, AstItem>;
Nika Layzell27726662017-10-24 23:16:35 -040086
David Tolnay01ed0ce2018-05-20 20:18:14 -070087fn load_file<P: AsRef<Path>>(
88 name: P,
89 features: &TokenStream,
90 lookup: &mut Lookup,
91) -> Result<(), Error> {
Nika Layzell27726662017-10-24 23:16:35 -040092 let name = name.as_ref();
David Tolnayea9ae892017-12-26 01:44:32 -050093 let parent = name.parent().ok_or_else(|| err_msg("no parent path"))?;
Nika Layzell27726662017-10-24 23:16:35 -040094
95 let mut f = File::open(name)?;
96 let mut src = String::new();
97 f.read_to_string(&mut src)?;
98
99 // Parse the file
David Tolnayd67fb752017-12-27 13:50:29 -0500100 let file =
101 syn::parse_file(&src).map_err(|_| format_err!("failed to parse {}", name.display()))?;
Nika Layzell27726662017-10-24 23:16:35 -0400102
103 // Collect all of the interesting AstItems declared in this file or submodules.
David Tolnay4ba63a02017-12-28 15:53:05 -0500104 'items: for item in file.items {
105 match item {
106 Item::Mod(item) => {
Nika Layzell27726662017-10-24 23:16:35 -0400107 // Don't inspect inline modules.
David Tolnayc6b55bc2017-11-09 22:48:38 -0800108 if item.content.is_some() {
Nika Layzell27726662017-10-24 23:16:35 -0400109 continue;
110 }
111
112 // We don't want to try to load the generated rust files and
113 // parse them, so we ignore them here.
114 for name in IGNORED_MODS {
David Tolnay446f7d62018-05-20 17:58:15 -0700115 if item.ident == name {
Nika Layzell27726662017-10-24 23:16:35 -0400116 continue 'items;
117 }
118 }
119
120 // Lookup any #[cfg()] attributes on the module and add them to
121 // the feature set.
David Tolnay0a0d78c2018-01-05 15:24:01 -0800122 //
123 // The derive module is weird because it is built with either
124 // `full` or `derive` but exported only under `derive`.
David Tolnay446f7d62018-05-20 17:58:15 -0700125 let features = if item.ident == "derive" {
David Tolnay0a0d78c2018-01-05 15:24:01 -0800126 quote!(#[cfg(feature = "derive")])
127 } else {
128 get_features(&item.attrs, features.clone())
129 };
Nika Layzell27726662017-10-24 23:16:35 -0400130
131 // Look up the submodule file, and recursively parse it.
132 // XXX: Only handles same-directory .rs file submodules.
Alex Crichtona74a1c82018-05-16 10:20:44 -0700133 let path = parent.join(&format!("{}.rs", item.ident));
David Tolnayea9ae892017-12-26 01:44:32 -0500134 load_file(path, &features, lookup)?;
Nika Layzell27726662017-10-24 23:16:35 -0400135 }
David Tolnay4ba63a02017-12-28 15:53:05 -0500136 Item::Macro(item) => {
Nika Layzell27726662017-10-24 23:16:35 -0400137 // Lookip any #[cfg()] attributes directly on the macro
138 // invocation, and add them to the feature set.
139 let features = get_features(&item.attrs, features.clone());
140
141 // Try to parse the AstItem declaration out of the item.
David Tolnay01a77582018-01-01 20:00:51 -0800142 let tts = &item.mac.tts;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700143 let found = if path_eq(&item.mac.path, "ast_struct") {
David Tolnay01a77582018-01-01 20:00:51 -0800144 syn::parse_str::<parsing::AstStruct>(&quote!(#tts).to_string())
David Tolnayd67fb752017-12-27 13:50:29 -0500145 .map_err(|_| err_msg("failed to parse ast_struct"))?
146 .0
Alex Crichtona74a1c82018-05-16 10:20:44 -0700147 } else if path_eq(&item.mac.path, "ast_enum") {
David Tolnay01a77582018-01-01 20:00:51 -0800148 syn::parse_str::<parsing::AstEnum>(&quote!(#tts).to_string())
David Tolnayd67fb752017-12-27 13:50:29 -0500149 .map_err(|_| err_msg("failed to parse ast_enum"))?
150 .0
Alex Crichtona74a1c82018-05-16 10:20:44 -0700151 } else if path_eq(&item.mac.path, "ast_enum_of_structs") {
David Tolnay01a77582018-01-01 20:00:51 -0800152 syn::parse_str::<parsing::AstEnumOfStructs>(&quote!(#tts).to_string())
David Tolnay1cf80912017-12-31 18:35:12 -0500153 .map_err(|_| err_msg("failed to parse ast_enum_of_structs"))?
David Tolnayd67fb752017-12-27 13:50:29 -0500154 .0
Nika Layzell27726662017-10-24 23:16:35 -0400155 } else {
David Tolnayd67fb752017-12-27 13:50:29 -0500156 continue;
Nika Layzell27726662017-10-24 23:16:35 -0400157 };
158
159 // Record our features on the parsed AstItems.
160 for mut item in found {
161 features.to_tokens(&mut item.features);
Alex Crichtona74a1c82018-05-16 10:20:44 -0700162 lookup.insert(item.ast.ident.clone(), item);
Nika Layzell27726662017-10-24 23:16:35 -0400163 }
164 }
David Tolnay4ba63a02017-12-28 15:53:05 -0500165 Item::Struct(item) => {
166 let ident = item.ident;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700167 if EXTRA_TYPES.contains(&&ident.to_string()[..]) {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700168 lookup.insert(
169 ident.clone(),
170 AstItem {
171 ast: DeriveInput {
172 ident: ident,
173 vis: item.vis,
174 attrs: item.attrs,
175 generics: item.generics,
176 data: Data::Struct(DataStruct {
177 fields: item.fields,
178 struct_token: item.struct_token,
179 semi_token: item.semi_token,
180 }),
181 },
182 features: features.clone(),
183 eos_full: false,
David Tolnay4ba63a02017-12-28 15:53:05 -0500184 },
David Tolnay01ed0ce2018-05-20 20:18:14 -0700185 );
David Tolnay4ba63a02017-12-28 15:53:05 -0500186 }
187 }
Nika Layzell27726662017-10-24 23:16:35 -0400188 _ => {}
189 }
190 }
191 Ok(())
192}
193
194mod parsing {
195 use super::AstItem;
196
David Tolnay01ed0ce2018-05-20 20:18:14 -0700197 use proc_macro2::TokenStream;
David Tolnay1cf80912017-12-31 18:35:12 -0500198 use syn;
David Tolnayc5ab8c62017-12-26 16:43:39 -0500199 use syn::synom::*;
Nika Layzell27726662017-10-24 23:16:35 -0400200 use syn::*;
Nika Layzell27726662017-10-24 23:16:35 -0400201
202 // Parses #full - returns #[cfg(feature = "full")] if it is present, and
203 // nothing otherwise.
Alex Crichton715862b2018-05-17 12:31:49 -0700204 named!(full -> (TokenStream, bool), map!(option!(do_parse!(
David Tolnayf8db7ba2017-11-11 22:52:16 -0800205 punct!(#) >>
Nika Layzell27726662017-10-24 23:16:35 -0400206 id: syn!(Ident) >>
David Tolnay446f7d62018-05-20 17:58:15 -0700207 cond_reduce!(id == "full") >>
David Tolnayea9ae892017-12-26 01:44:32 -0500208 ()
Nika Layzell27726662017-10-24 23:16:35 -0400209 )), |s| if s.is_some() {
210 (quote!(#[cfg(feature = "full")]), true)
211 } else {
212 (quote!(), false)
213 }));
214
David Tolnay28c5a462017-12-27 01:59:30 -0500215 named!(manual_extra_traits -> (), do_parse!(
216 punct!(#) >>
217 id: syn!(Ident) >>
David Tolnay446f7d62018-05-20 17:58:15 -0700218 cond_reduce!(id == "manual_extra_traits") >>
David Tolnay28c5a462017-12-27 01:59:30 -0500219 ()
220 ));
221
Nika Layzell27726662017-10-24 23:16:35 -0400222 // Parses a simple AstStruct without the `pub struct` prefix.
223 named!(ast_struct_inner -> AstItem, do_parse!(
224 id: syn!(Ident) >>
225 features: full >>
David Tolnay28c5a462017-12-27 01:59:30 -0500226 option!(manual_extra_traits) >>
David Tolnay2e0dba12017-12-27 01:54:40 -0500227 rest: syn!(TokenStream) >>
Nika Layzell27726662017-10-24 23:16:35 -0400228 (AstItem {
David Tolnay01a77582018-01-01 20:00:51 -0800229 ast: syn::parse_str(&quote! {
David Tolnay2e0dba12017-12-27 01:54:40 -0500230 pub struct #id #rest
David Tolnay01a77582018-01-01 20:00:51 -0800231 }.to_string())?,
Nika Layzell27726662017-10-24 23:16:35 -0400232 features: features.0,
233 eos_full: features.1,
234 })
235 ));
236
237 // ast_struct! parsing
238 pub struct AstStruct(pub Vec<AstItem>);
239 impl Synom for AstStruct {
David Tolnayab919512017-12-30 23:31:51 -0500240 named!(parse -> Self, do_parse!(
David Tolnay2c136452017-12-27 14:13:32 -0500241 many0!(Attribute::parse_outer) >>
David Tolnayf8db7ba2017-11-11 22:52:16 -0800242 keyword!(pub) >>
243 keyword!(struct) >>
Nika Layzell27726662017-10-24 23:16:35 -0400244 res: call!(ast_struct_inner) >>
David Tolnayab919512017-12-30 23:31:51 -0500245 (AstStruct(vec![res]))
246 ));
Nika Layzell27726662017-10-24 23:16:35 -0400247 }
248
David Tolnay360efd22018-01-04 23:35:26 -0800249 named!(no_visit -> (), do_parse!(
250 punct!(#) >>
251 id: syn!(Ident) >>
David Tolnay446f7d62018-05-20 17:58:15 -0700252 cond_reduce!(id == "no_visit") >>
David Tolnay360efd22018-01-04 23:35:26 -0800253 ()
254 ));
255
Nika Layzell27726662017-10-24 23:16:35 -0400256 // ast_enum! parsing
257 pub struct AstEnum(pub Vec<AstItem>);
258 impl Synom for AstEnum {
David Tolnay360efd22018-01-04 23:35:26 -0800259 named!(parse -> Self, do_parse!(
260 many0!(Attribute::parse_outer) >>
261 keyword!(pub) >>
262 keyword!(enum) >>
263 id: syn!(Ident) >>
264 no_visit: option!(no_visit) >>
265 rest: syn!(TokenStream) >>
266 (AstEnum(if no_visit.is_some() {
267 vec![]
268 } else {
269 vec![AstItem {
270 ast: syn::parse_str(&quote! {
271 pub enum #id #rest
272 }.to_string())?,
273 features: quote!(),
274 eos_full: false,
275 }]
276 }))
277 ));
Nika Layzell27726662017-10-24 23:16:35 -0400278 }
279
280 // A single variant of an ast_enum_of_structs!
281 struct EosVariant {
282 name: Ident,
David Tolnayfcfb9002017-12-28 22:04:29 -0500283 member: Option<Path>,
Nika Layzell27726662017-10-24 23:16:35 -0400284 inner: Option<AstItem>,
285 }
286 named!(eos_variant -> EosVariant, do_parse!(
David Tolnay2c136452017-12-27 14:13:32 -0500287 many0!(Attribute::parse_outer) >>
David Tolnayf8db7ba2017-11-11 22:52:16 -0800288 keyword!(pub) >>
Nika Layzell27726662017-10-24 23:16:35 -0400289 variant: syn!(Ident) >>
David Tolnayfcfb9002017-12-28 22:04:29 -0500290 member: option!(map!(parens!(alt!(
Alex Crichtona74a1c82018-05-16 10:20:44 -0700291 call!(ast_struct_inner) => { |x: AstItem| (Path::from(x.ast.ident.clone()), Some(x)) }
Nika Layzell27726662017-10-24 23:16:35 -0400292 |
293 syn!(Path) => { |x| (x, None) }
David Tolnay8875fca2017-12-31 13:52:37 -0500294 )), |x| x.1)) >>
David Tolnayf8db7ba2017-11-11 22:52:16 -0800295 punct!(,) >>
Nika Layzell27726662017-10-24 23:16:35 -0400296 (EosVariant {
297 name: variant,
David Tolnayfcfb9002017-12-28 22:04:29 -0500298 member: member.clone().map(|x| x.0),
299 inner: member.map(|x| x.1).unwrap_or_default(),
Nika Layzell27726662017-10-24 23:16:35 -0400300 })
301 ));
302
303 // ast_enum_of_structs! parsing
304 pub struct AstEnumOfStructs(pub Vec<AstItem>);
305 impl Synom for AstEnumOfStructs {
David Tolnayab919512017-12-30 23:31:51 -0500306 named!(parse -> Self, do_parse!(
David Tolnay2c136452017-12-27 14:13:32 -0500307 many0!(Attribute::parse_outer) >>
David Tolnayf8db7ba2017-11-11 22:52:16 -0800308 keyword!(pub) >>
309 keyword!(enum) >>
Nika Layzell27726662017-10-24 23:16:35 -0400310 id: syn!(Ident) >>
David Tolnaye3d41b72017-12-31 15:24:00 -0500311 variants: braces!(many0!(eos_variant)) >>
Nika Layzell27726662017-10-24 23:16:35 -0400312 option!(syn!(Ident)) >> // do_not_generate_to_tokens
313 ({
314 // XXX: This is really gross - we shouldn't have to convert the
315 // tokens to strings to re-parse them.
316 let enum_item = {
David Tolnaye3d41b72017-12-31 15:24:00 -0500317 let variants = variants.1.iter().map(|v| {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700318 let name = v.name.clone();
David Tolnayfcfb9002017-12-28 22:04:29 -0500319 match v.member {
320 Some(ref member) => quote!(#name(#member)),
321 None => quote!(#name),
322 }
Nika Layzell27726662017-10-24 23:16:35 -0400323 });
David Tolnay01a77582018-01-01 20:00:51 -0800324 syn::parse_str(&quote! {
Nika Layzell27726662017-10-24 23:16:35 -0400325 pub enum #id { #(#variants),* }
David Tolnay01a77582018-01-01 20:00:51 -0800326 }.to_string())?
Nika Layzell27726662017-10-24 23:16:35 -0400327 };
328 let mut items = vec![AstItem {
David Tolnay3d772182017-12-28 17:18:53 -0500329 ast: enum_item,
Nika Layzell27726662017-10-24 23:16:35 -0400330 features: quote!(),
331 eos_full: false,
332 }];
David Tolnaye3d41b72017-12-31 15:24:00 -0500333 items.extend(variants.1.into_iter().filter_map(|v| v.inner));
Nika Layzell27726662017-10-24 23:16:35 -0400334 AstEnumOfStructs(items)
335 })
David Tolnayab919512017-12-30 23:31:51 -0500336 ));
Nika Layzell27726662017-10-24 23:16:35 -0400337 }
338}
339
340mod codegen {
341 use super::{AstItem, Lookup};
David Tolnay01ed0ce2018-05-20 20:18:14 -0700342 use proc_macro2::{Span, TokenStream};
Alex Crichton715862b2018-05-17 12:31:49 -0700343 use quote::ToTokens;
David Tolnayf0d63bf2017-12-26 12:29:47 -0500344 use std::fmt::{self, Display};
David Tolnay01ed0ce2018-05-20 20:18:14 -0700345 use syn::punctuated::Punctuated;
346 use syn::*;
Nika Layzell27726662017-10-24 23:16:35 -0400347
348 #[derive(Default)]
349 pub struct State {
350 pub visit_trait: String,
351 pub visit_impl: String,
352 pub visit_mut_trait: String,
353 pub visit_mut_impl: String,
354 pub fold_trait: String,
355 pub fold_impl: String,
356 }
357
David Tolnay4a918742017-12-28 16:54:41 -0500358 fn under_name(name: Ident) -> Ident {
Nika Layzell27726662017-10-24 23:16:35 -0400359 use inflections::Inflect;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700360 Ident::new(&name.to_string().to_snake_case(), Span::call_site())
Nika Layzell27726662017-10-24 23:16:35 -0400361 }
362
David Tolnay3d772182017-12-28 17:18:53 -0500363 enum RelevantType<'a> {
364 Box(&'a Type),
365 Vec(&'a Type),
David Tolnayf2cfd722017-12-31 18:02:51 -0500366 Punctuated(&'a Type),
David Tolnay3d772182017-12-28 17:18:53 -0500367 Option(&'a Type),
David Tolnayf2cfd722017-12-31 18:02:51 -0500368 Tuple(&'a Punctuated<Type, Token![,]>),
David Tolnay3d772182017-12-28 17:18:53 -0500369 Simple(&'a AstItem),
Alex Crichton715862b2018-05-17 12:31:49 -0700370 Token(TokenStream),
David Tolnay3d772182017-12-28 17:18:53 -0500371 Pass,
372 }
Nika Layzell27726662017-10-24 23:16:35 -0400373
David Tolnay3d772182017-12-28 17:18:53 -0500374 fn classify<'a>(ty: &'a Type, lookup: &'a Lookup) -> RelevantType<'a> {
375 match *ty {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700376 Type::Path(TypePath {
377 qself: None,
378 ref path,
379 }) => {
David Tolnay56080682018-01-06 14:01:52 -0800380 let last = path.segments.last().unwrap().into_value();
Alex Crichtona74a1c82018-05-16 10:20:44 -0700381 match &last.ident.to_string()[..] {
David Tolnay3d772182017-12-28 17:18:53 -0500382 "Box" => RelevantType::Box(first_arg(&last.arguments)),
383 "Vec" => RelevantType::Vec(first_arg(&last.arguments)),
David Tolnayf2cfd722017-12-31 18:02:51 -0500384 "Punctuated" => RelevantType::Punctuated(first_arg(&last.arguments)),
David Tolnay3d772182017-12-28 17:18:53 -0500385 "Option" => RelevantType::Option(first_arg(&last.arguments)),
David Tolnay1e01f9c2017-12-28 20:16:19 -0500386 "Brace" | "Bracket" | "Paren" | "Group" => {
Alex Crichton715862b2018-05-17 12:31:49 -0700387 RelevantType::Token(last.ident.clone().into_token_stream())
David Tolnay1e01f9c2017-12-28 20:16:19 -0500388 }
David Tolnay3d772182017-12-28 17:18:53 -0500389 _ => {
390 if let Some(item) = lookup.get(&last.ident) {
391 RelevantType::Simple(item)
392 } else {
393 RelevantType::Pass
394 }
395 }
396 }
Nika Layzell27726662017-10-24 23:16:35 -0400397 }
David Tolnay01ed0ce2018-05-20 20:18:14 -0700398 Type::Tuple(TypeTuple { ref elems, .. }) => RelevantType::Tuple(elems),
399 Type::Macro(TypeMacro { ref mac })
400 if mac.path.segments.last().unwrap().into_value().ident == "Token" =>
401 {
Alex Crichton715862b2018-05-17 12:31:49 -0700402 RelevantType::Token(mac.into_token_stream())
David Tolnaycc0f0372017-12-28 19:11:04 -0500403 }
David Tolnay3d772182017-12-28 17:18:53 -0500404 _ => RelevantType::Pass,
Nika Layzell27726662017-10-24 23:16:35 -0400405 }
406 }
407
408 #[derive(Debug, Eq, PartialEq, Copy, Clone)]
409 enum Kind {
410 Visit,
411 VisitMut,
412 Fold,
413 }
414
David Tolnayf0d63bf2017-12-26 12:29:47 -0500415 enum Operand {
Alex Crichton715862b2018-05-17 12:31:49 -0700416 Borrowed(TokenStream),
417 Owned(TokenStream),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500418 }
419
David Tolnay83db9272017-12-28 17:02:31 -0500420 use self::Kind::*;
David Tolnay01ed0ce2018-05-20 20:18:14 -0700421 use self::Operand::*;
David Tolnay83db9272017-12-28 17:02:31 -0500422
David Tolnayf0d63bf2017-12-26 12:29:47 -0500423 impl Operand {
Alex Crichton715862b2018-05-17 12:31:49 -0700424 fn tokens(&self) -> &TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500425 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500426 Borrowed(ref n) | Owned(ref n) => n,
David Tolnayf0d63bf2017-12-26 12:29:47 -0500427 }
428 }
429
Alex Crichton715862b2018-05-17 12:31:49 -0700430 fn ref_tokens(&self) -> TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500431 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500432 Borrowed(ref n) => n.clone(),
433 Owned(ref n) => quote!(&#n),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500434 }
435 }
436
Alex Crichton715862b2018-05-17 12:31:49 -0700437 fn ref_mut_tokens(&self) -> TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500438 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500439 Borrowed(ref n) => n.clone(),
440 Owned(ref n) => quote!(&mut #n),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500441 }
442 }
443
Alex Crichton715862b2018-05-17 12:31:49 -0700444 fn owned_tokens(&self) -> TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500445 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500446 Borrowed(ref n) => quote!(*#n),
447 Owned(ref n) => n.clone(),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500448 }
449 }
450 }
451
452 impl Display for Operand {
453 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
David Tolnay4e52d8a2017-12-28 15:54:50 -0500454 Display::fmt(self.tokens(), formatter)
David Tolnayf0d63bf2017-12-26 12:29:47 -0500455 }
456 }
457
Nika Layzellc08227a2017-12-04 16:30:17 -0500458 fn first_arg(params: &PathArguments) -> &Type {
Nika Layzell27726662017-10-24 23:16:35 -0400459 let data = match *params {
Nika Layzellc08227a2017-12-04 16:30:17 -0500460 PathArguments::AngleBracketed(ref data) => data,
461 _ => panic!("Expected at least 1 type argument here"),
Nika Layzell27726662017-10-24 23:16:35 -0400462 };
463
David Tolnay01ed0ce2018-05-20 20:18:14 -0700464 match **data
465 .args
David Tolnayd67fb752017-12-27 13:50:29 -0500466 .first()
467 .expect("Expected at least 1 type argument here")
David Tolnay56080682018-01-06 14:01:52 -0800468 .value()
David Tolnayd67fb752017-12-27 13:50:29 -0500469 {
David Tolnayea9ae892017-12-26 01:44:32 -0500470 GenericArgument::Type(ref ty) => ty,
Nika Layzellc08227a2017-12-04 16:30:17 -0500471 _ => panic!("Expected at least 1 type argument here"),
Nika Layzell357885a2017-12-04 15:47:07 -0500472 }
Nika Layzell27726662017-10-24 23:16:35 -0400473 }
474
David Tolnay01ed0ce2018-05-20 20:18:14 -0700475 fn simple_visit(item: &AstItem, kind: Kind, name: &Operand) -> String {
David Tolnay4a918742017-12-28 16:54:41 -0500476 match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500477 Visit => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500478 "_visitor.visit_{under_name}({name})",
Alex Crichtona74a1c82018-05-16 10:20:44 -0700479 under_name = under_name(item.ast.ident.clone()),
David Tolnay4a918742017-12-28 16:54:41 -0500480 name = name.ref_tokens(),
481 ),
David Tolnay83db9272017-12-28 17:02:31 -0500482 VisitMut => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500483 "_visitor.visit_{under_name}_mut({name})",
Alex Crichtona74a1c82018-05-16 10:20:44 -0700484 under_name = under_name(item.ast.ident.clone()),
David Tolnay4a918742017-12-28 16:54:41 -0500485 name = name.ref_mut_tokens(),
486 ),
David Tolnay83db9272017-12-28 17:02:31 -0500487 Fold => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500488 "_visitor.fold_{under_name}({name})",
Alex Crichtona74a1c82018-05-16 10:20:44 -0700489 under_name = under_name(item.ast.ident.clone()),
David Tolnay4a918742017-12-28 16:54:41 -0500490 name = name.owned_tokens(),
491 ),
Nika Layzell27726662017-10-24 23:16:35 -0400492 }
493 }
494
David Tolnay01ed0ce2018-05-20 20:18:14 -0700495 fn box_visit(elem: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<String> {
David Tolnay4a918742017-12-28 16:54:41 -0500496 let name = name.owned_tokens();
David Tolnay39d0a202017-12-28 18:19:00 -0500497 let res = visit(elem, lookup, kind, &Owned(quote!(*#name)))?;
David Tolnay4a918742017-12-28 16:54:41 -0500498 Some(match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500499 Fold => format!("Box::new({})", res),
500 Visit | VisitMut => res,
David Tolnay4a918742017-12-28 16:54:41 -0500501 })
Nika Layzell27726662017-10-24 23:16:35 -0400502 }
503
David Tolnay01ed0ce2018-05-20 20:18:14 -0700504 fn vec_visit(elem: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<String> {
David Tolnay4a918742017-12-28 16:54:41 -0500505 let operand = match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500506 Visit | VisitMut => Borrowed(quote!(it)),
507 Fold => Owned(quote!(it)),
David Tolnay4a918742017-12-28 16:54:41 -0500508 };
David Tolnay39d0a202017-12-28 18:19:00 -0500509 let val = visit(elem, lookup, kind, &operand)?;
David Tolnay4a918742017-12-28 16:54:41 -0500510 Some(match kind {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700511 Visit => format!(
512 "for it in {name} {{ {val} }}",
513 name = name.ref_tokens(),
514 val = val,
515 ),
516 VisitMut => format!(
517 "for it in {name} {{ {val} }}",
518 name = name.ref_mut_tokens(),
519 val = val,
520 ),
David Tolnay3d772182017-12-28 17:18:53 -0500521 Fold => format!(
522 "FoldHelper::lift({name}, |it| {{ {val} }})",
523 name = name.owned_tokens(),
524 val = val,
525 ),
526 })
527 }
528
David Tolnay6eff4da2018-01-01 20:27:45 -0800529 fn punctuated_visit(
David Tolnay3d772182017-12-28 17:18:53 -0500530 elem: &Type,
531 lookup: &Lookup,
532 kind: Kind,
533 name: &Operand,
534 ) -> Option<String> {
535 let operand = match kind {
536 Visit | VisitMut => Borrowed(quote!(it)),
537 Fold => Owned(quote!(it)),
538 };
David Tolnay39d0a202017-12-28 18:19:00 -0500539 let val = visit(elem, lookup, kind, &operand)?;
David Tolnay3d772182017-12-28 17:18:53 -0500540 Some(match kind {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700541 Visit => format!(
542 "for el in Punctuated::pairs({name}) {{ \
543 let it = el.value(); \
544 {val} \
545 }}",
546 name = name.ref_tokens(),
547 val = val,
548 ),
549 VisitMut => format!(
550 "for mut el in Punctuated::pairs_mut({name}) {{ \
551 let it = el.value_mut(); \
552 {val} \
553 }}",
554 name = name.ref_mut_tokens(),
555 val = val,
556 ),
David Tolnay83db9272017-12-28 17:02:31 -0500557 Fold => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500558 "FoldHelper::lift({name}, |it| {{ {val} }})",
559 name = name.owned_tokens(),
560 val = val,
561 ),
562 })
Nika Layzell27726662017-10-24 23:16:35 -0400563 }
564
David Tolnay01ed0ce2018-05-20 20:18:14 -0700565 fn option_visit(elem: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<String> {
David Tolnay4a918742017-12-28 16:54:41 -0500566 let it = match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500567 Visit | VisitMut => Borrowed(quote!(it)),
568 Fold => Owned(quote!(it)),
David Tolnay4a918742017-12-28 16:54:41 -0500569 };
David Tolnay39d0a202017-12-28 18:19:00 -0500570 let val = visit(elem, lookup, kind, &it)?;
David Tolnay4a918742017-12-28 16:54:41 -0500571 Some(match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500572 Visit => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500573 "if let Some(ref it) = {name} {{ {val} }}",
574 name = name.owned_tokens(),
575 val = val,
576 ),
David Tolnay83db9272017-12-28 17:02:31 -0500577 VisitMut => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500578 "if let Some(ref mut it) = {name} {{ {val} }}",
579 name = name.owned_tokens(),
580 val = val,
581 ),
David Tolnay83db9272017-12-28 17:02:31 -0500582 Fold => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500583 "({name}).map(|it| {{ {val} }})",
584 name = name.owned_tokens(),
585 val = val,
586 ),
587 })
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400588 }
589
David Tolnay5c4c0b52017-12-28 17:58:54 -0500590 fn tuple_visit(
David Tolnayf2cfd722017-12-31 18:02:51 -0500591 elems: &Punctuated<Type, Token![,]>,
David Tolnay5c4c0b52017-12-28 17:58:54 -0500592 lookup: &Lookup,
593 kind: Kind,
594 name: &Operand,
595 ) -> Option<String> {
596 let mut code = String::new();
David Tolnayf047c7a2017-12-31 02:29:04 -0500597 for (i, elem) in elems.iter().enumerate() {
David Tolnay5c4c0b52017-12-28 17:58:54 -0500598 let name = name.tokens();
David Tolnay14982012017-12-29 00:49:51 -0500599 let i = Index::from(i);
David Tolnay5c4c0b52017-12-28 17:58:54 -0500600 let it = Owned(quote!((#name).#i));
David Tolnay01ed0ce2018-05-20 20:18:14 -0700601 let val = visit(elem, lookup, kind, &it).unwrap_or_else(|| noop_visit(kind, &it));
David Tolnay5c4c0b52017-12-28 17:58:54 -0500602 code.push_str(&format!(" {}", val));
603 match kind {
604 Fold => code.push(','),
605 Visit | VisitMut => code.push(';'),
606 }
607 code.push('\n');
608 }
609 if code.is_empty() {
610 None
611 } else {
612 Some(match kind {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700613 Fold => format!("(\n{} )", code),
614 Visit | VisitMut => format!("\n{} ", code),
David Tolnay5c4c0b52017-12-28 17:58:54 -0500615 })
616 }
617 }
618
Alex Crichton715862b2018-05-17 12:31:49 -0700619 fn token_visit(ty: TokenStream, kind: Kind, name: &Operand) -> String {
David Tolnaycc0f0372017-12-28 19:11:04 -0500620 match kind {
621 Fold => format!(
David Tolnay1e01f9c2017-12-28 20:16:19 -0500622 "{ty}(tokens_helper(_visitor, &({name}).0))",
623 ty = ty,
David Tolnayd228b332018-06-27 23:56:05 -0700624 name = name.tokens(),
David Tolnaycc0f0372017-12-28 19:11:04 -0500625 ),
626 Visit => format!(
627 "tokens_helper(_visitor, &({name}).0)",
David Tolnayd228b332018-06-27 23:56:05 -0700628 name = name.tokens(),
David Tolnaycc0f0372017-12-28 19:11:04 -0500629 ),
630 VisitMut => format!(
631 "tokens_helper(_visitor, &mut ({name}).0)",
David Tolnayd228b332018-06-27 23:56:05 -0700632 name = name.tokens(),
David Tolnaycc0f0372017-12-28 19:11:04 -0500633 ),
634 }
635 }
636
David Tolnay4a918742017-12-28 16:54:41 -0500637 fn noop_visit(kind: Kind, name: &Operand) -> String {
638 match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500639 Fold => name.owned_tokens().to_string(),
640 Visit | VisitMut => format!("// Skipped field {}", name),
David Tolnay4a918742017-12-28 16:54:41 -0500641 }
642 }
643
644 fn visit(ty: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<String> {
David Tolnay3d772182017-12-28 17:18:53 -0500645 match classify(ty, lookup) {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700646 RelevantType::Box(elem) => box_visit(elem, lookup, kind, name),
647 RelevantType::Vec(elem) => vec_visit(elem, lookup, kind, name),
648 RelevantType::Punctuated(elem) => punctuated_visit(elem, lookup, kind, name),
649 RelevantType::Option(elem) => option_visit(elem, lookup, kind, name),
650 RelevantType::Tuple(elems) => tuple_visit(elems, lookup, kind, name),
David Tolnay3d772182017-12-28 17:18:53 -0500651 RelevantType::Simple(item) => {
652 let mut res = simple_visit(item, kind, name);
653 Some(if item.eos_full {
654 format!("full!({res})", res = res)
655 } else {
656 res
657 })
658 }
David Tolnay01ed0ce2018-05-20 20:18:14 -0700659 RelevantType::Token(ty) => Some(token_visit(ty, kind, name)),
660 RelevantType::Pass => None,
Nika Layzell27726662017-10-24 23:16:35 -0400661 }
Nika Layzell27726662017-10-24 23:16:35 -0400662 }
663
664 pub fn generate(state: &mut State, lookup: &Lookup, s: &AstItem) {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700665 let under_name = under_name(s.ast.ident.clone());
Nika Layzell27726662017-10-24 23:16:35 -0400666
667 state.visit_trait.push_str(&format!(
668 "{features}\n\
Nika Layzellc86173a2017-11-18 13:55:22 -0500669 fn visit_{under_name}(&mut self, i: &'ast {ty}) {{ \
David Tolnayd67fb752017-12-27 13:50:29 -0500670 visit_{under_name}(self, i) \
Nika Layzell27726662017-10-24 23:16:35 -0400671 }}\n",
672 features = s.features,
673 under_name = under_name,
David Tolnay3d772182017-12-28 17:18:53 -0500674 ty = s.ast.ident,
Nika Layzell27726662017-10-24 23:16:35 -0400675 ));
676 state.visit_mut_trait.push_str(&format!(
677 "{features}\n\
Nika Layzella6f46c42017-10-26 15:26:16 -0400678 fn visit_{under_name}_mut(&mut self, i: &mut {ty}) {{ \
David Tolnayd67fb752017-12-27 13:50:29 -0500679 visit_{under_name}_mut(self, i) \
Nika Layzell27726662017-10-24 23:16:35 -0400680 }}\n",
681 features = s.features,
682 under_name = under_name,
David Tolnay3d772182017-12-28 17:18:53 -0500683 ty = s.ast.ident,
Nika Layzell27726662017-10-24 23:16:35 -0400684 ));
685 state.fold_trait.push_str(&format!(
686 "{features}\n\
687 fn fold_{under_name}(&mut self, i: {ty}) -> {ty} {{ \
David Tolnayd67fb752017-12-27 13:50:29 -0500688 fold_{under_name}(self, i) \
Nika Layzell27726662017-10-24 23:16:35 -0400689 }}\n",
690 features = s.features,
691 under_name = under_name,
David Tolnay3d772182017-12-28 17:18:53 -0500692 ty = s.ast.ident,
Nika Layzell27726662017-10-24 23:16:35 -0400693 ));
694
695 state.visit_impl.push_str(&format!(
696 "{features}\n\
David Tolnay4b4c4b62018-01-06 13:48:05 -0800697 pub fn visit_{under_name}<'ast, V: Visit<'ast> + ?Sized>(\
David Tolnayd67fb752017-12-27 13:50:29 -0500698 _visitor: &mut V, _i: &'ast {ty}) {{\n",
Nika Layzell27726662017-10-24 23:16:35 -0400699 features = s.features,
700 under_name = under_name,
David Tolnay3d772182017-12-28 17:18:53 -0500701 ty = s.ast.ident,
Nika Layzell27726662017-10-24 23:16:35 -0400702 ));
703 state.visit_mut_impl.push_str(&format!(
704 "{features}\n\
David Tolnay4b4c4b62018-01-06 13:48:05 -0800705 pub fn visit_{under_name}_mut<V: VisitMut + ?Sized>(\
David Tolnayd67fb752017-12-27 13:50:29 -0500706 _visitor: &mut V, _i: &mut {ty}) {{\n",
Nika Layzell27726662017-10-24 23:16:35 -0400707 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 ));
David Tolnayd0adf522017-12-29 01:30:07 -0500711 let before_fold_impl_len = state.fold_impl.len();
Nika Layzell27726662017-10-24 23:16:35 -0400712 state.fold_impl.push_str(&format!(
713 "{features}\n\
David Tolnay4b4c4b62018-01-06 13:48:05 -0800714 pub fn fold_{under_name}<V: Fold + ?Sized>(\
David Tolnayd67fb752017-12-27 13:50:29 -0500715 _visitor: &mut V, _i: {ty}) -> {ty} {{\n",
Nika Layzell27726662017-10-24 23:16:35 -0400716 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 // XXX: This part is a disaster - I'm not sure how to make it cleaner though :'(
David Tolnaye3d41b72017-12-31 15:24:00 -0500722 match s.ast.data {
723 Data::Enum(ref e) => {
Nika Layzell27726662017-10-24 23:16:35 -0400724 state.visit_impl.push_str(" match *_i {\n");
725 state.visit_mut_impl.push_str(" match *_i {\n");
726 state.fold_impl.push_str(" match _i {\n");
727 for variant in &e.variants {
Alex Crichton715862b2018-05-17 12:31:49 -0700728 let fields: Vec<(&Field, TokenStream)> = match variant.fields {
David Tolnaye3d41b72017-12-31 15:24:00 -0500729 Fields::Named(..) => panic!("Doesn't support enum struct variants"),
730 Fields::Unnamed(ref fields) => {
David Tolnay6eff4da2018-01-01 20:27:45 -0800731 let binding = format!(" {}::{}(", s.ast.ident, variant.ident);
Nika Layzell27726662017-10-24 23:16:35 -0400732 state.visit_impl.push_str(&binding);
733 state.visit_mut_impl.push_str(&binding);
734 state.fold_impl.push_str(&binding);
735
David Tolnay01ed0ce2018-05-20 20:18:14 -0700736 let res = fields
737 .unnamed
David Tolnayd67fb752017-12-27 13:50:29 -0500738 .iter()
739 .enumerate()
740 .map(|(idx, el)| {
741 let name = format!("_binding_{}", idx);
Nika Layzell27726662017-10-24 23:16:35 -0400742
David Tolnayd67fb752017-12-27 13:50:29 -0500743 state.visit_impl.push_str("ref ");
744 state.visit_mut_impl.push_str("ref mut ");
Nika Layzell27726662017-10-24 23:16:35 -0400745
David Tolnayd67fb752017-12-27 13:50:29 -0500746 state.visit_impl.push_str(&name);
747 state.visit_mut_impl.push_str(&name);
748 state.fold_impl.push_str(&name);
749 state.visit_impl.push_str(", ");
750 state.visit_mut_impl.push_str(", ");
751 state.fold_impl.push_str(", ");
Nika Layzell27726662017-10-24 23:16:35 -0400752
David Tolnayd67fb752017-12-27 13:50:29 -0500753 let mut tokens = quote!();
Alex Crichtona74a1c82018-05-16 10:20:44 -0700754 Ident::new(&name, Span::call_site()).to_tokens(&mut tokens);
Nika Layzell27726662017-10-24 23:16:35 -0400755
David Tolnay6eff4da2018-01-01 20:27:45 -0800756 (el, tokens)
David Tolnayd67fb752017-12-27 13:50:29 -0500757 })
758 .collect();
Nika Layzell27726662017-10-24 23:16:35 -0400759
760 state.visit_impl.push_str(") => {\n");
761 state.visit_mut_impl.push_str(") => {\n");
762 state.fold_impl.push_str(") => {\n");
763
764 res
765 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500766 Fields::Unit => {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700767 state.visit_impl.push_str(&format!(
768 " {0}::{1} => {{ }}\n",
769 s.ast.ident, variant.ident
770 ));
771 state.visit_mut_impl.push_str(&format!(
772 " {0}::{1} => {{ }}\n",
773 s.ast.ident, variant.ident
774 ));
Nika Layzell27726662017-10-24 23:16:35 -0400775 state.fold_impl.push_str(&format!(
David Tolnay6702ade2017-12-30 23:38:15 -0500776 " {0}::{1} => {{ {0}::{1} }}\n",
David Tolnay01ed0ce2018-05-20 20:18:14 -0700777 s.ast.ident, variant.ident
Nika Layzell27726662017-10-24 23:16:35 -0400778 ));
David Tolnayd67fb752017-12-27 13:50:29 -0500779 continue;
Nika Layzell27726662017-10-24 23:16:35 -0400780 }
781 };
782
783 if fields.is_empty() {
784 state.visit_impl.push_str(" {}");
785 state.visit_mut_impl.push_str(") => {\n");
786 state.fold_impl.push_str(") => {\n");
787 }
David Tolnay01ed0ce2018-05-20 20:18:14 -0700788 state.fold_impl.push_str(&format!(
789 " {}::{} (\n",
790 s.ast.ident, variant.ident,
791 ));
Nika Layzell27726662017-10-24 23:16:35 -0400792 for (field, binding) in fields {
793 state.visit_impl.push_str(&format!(
794 " {};\n",
David Tolnay01ed0ce2018-05-20 20:18:14 -0700795 visit(&field.ty, lookup, Visit, &Borrowed(binding.clone()))
796 .unwrap_or_else(|| noop_visit(Visit, &Borrowed(binding.clone()))),
Nika Layzell27726662017-10-24 23:16:35 -0400797 ));
798 state.visit_mut_impl.push_str(&format!(
799 " {};\n",
David Tolnay01ed0ce2018-05-20 20:18:14 -0700800 visit(&field.ty, lookup, VisitMut, &Borrowed(binding.clone()))
801 .unwrap_or_else(|| noop_visit(
802 VisitMut,
803 &Borrowed(binding.clone())
804 )),
Nika Layzell27726662017-10-24 23:16:35 -0400805 ));
806 state.fold_impl.push_str(&format!(
807 " {},\n",
David Tolnay83db9272017-12-28 17:02:31 -0500808 visit(&field.ty, lookup, Fold, &Owned(binding.clone()))
David Tolnay01ed0ce2018-05-20 20:18:14 -0700809 .unwrap_or_else(|| noop_visit(Fold, &Owned(binding))),
Nika Layzell27726662017-10-24 23:16:35 -0400810 ));
811 }
812 state.fold_impl.push_str(" )\n");
813
814 state.visit_impl.push_str(" }\n");
815 state.visit_mut_impl.push_str(" }\n");
816 state.fold_impl.push_str(" }\n");
817 }
818 state.visit_impl.push_str(" }\n");
819 state.visit_mut_impl.push_str(" }\n");
820 state.fold_impl.push_str(" }\n");
821 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500822 Data::Struct(ref v) => {
Alex Crichton715862b2018-05-17 12:31:49 -0700823 let fields: Vec<(&Field, TokenStream)> = match v.fields {
David Tolnaye3d41b72017-12-31 15:24:00 -0500824 Fields::Named(ref fields) => {
David Tolnayd67fb752017-12-27 13:50:29 -0500825 state
826 .fold_impl
David Tolnay3d772182017-12-28 17:18:53 -0500827 .push_str(&format!(" {} {{\n", s.ast.ident));
David Tolnay01ed0ce2018-05-20 20:18:14 -0700828 fields
829 .named
David Tolnayd67fb752017-12-27 13:50:29 -0500830 .iter()
831 .map(|el| {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700832 let id = el.ident.clone();
David Tolnay6eff4da2018-01-01 20:27:45 -0800833 (el, quote!(_i.#id))
David Tolnayd67fb752017-12-27 13:50:29 -0500834 })
835 .collect()
Nika Layzell27726662017-10-24 23:16:35 -0400836 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500837 Fields::Unnamed(ref fields) => {
David Tolnayd67fb752017-12-27 13:50:29 -0500838 state
839 .fold_impl
David Tolnay3d772182017-12-28 17:18:53 -0500840 .push_str(&format!(" {} (\n", s.ast.ident));
David Tolnay01ed0ce2018-05-20 20:18:14 -0700841 fields
842 .unnamed
David Tolnayd67fb752017-12-27 13:50:29 -0500843 .iter()
844 .enumerate()
845 .map(|(idx, el)| {
David Tolnay14982012017-12-29 00:49:51 -0500846 let id = Index::from(idx);
David Tolnay6eff4da2018-01-01 20:27:45 -0800847 (el, quote!(_i.#id))
David Tolnayd67fb752017-12-27 13:50:29 -0500848 })
849 .collect()
Nika Layzell27726662017-10-24 23:16:35 -0400850 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500851 Fields::Unit => {
David Tolnay18837692018-05-20 20:17:20 -0700852 if s.ast.ident == "Ident" {
David Tolnaya23b4432018-05-20 20:38:44 -0700853 state.fold_impl.push_str(" let mut _i = _i;\n");
854 state.fold_impl.push_str(" let span = _visitor.fold_span(_i.span());\n");
855 state.fold_impl.push_str(" _i.set_span(span);\n");
David Tolnay18837692018-05-20 20:17:20 -0700856 }
David Tolnaya23b4432018-05-20 20:38:44 -0700857 state.fold_impl.push_str(" _i\n");
Nika Layzellefb83ba2017-12-19 18:23:55 -0500858 vec![]
859 }
Nika Layzell27726662017-10-24 23:16:35 -0400860 };
861
862 for (field, ref_toks) in fields {
David Tolnay83db9272017-12-28 17:02:31 -0500863 let ref_toks = Owned(ref_toks);
Nika Layzell27726662017-10-24 23:16:35 -0400864 state.visit_impl.push_str(&format!(
David Tolnayd67fb752017-12-27 13:50:29 -0500865 " {};\n",
David Tolnay83db9272017-12-28 17:02:31 -0500866 visit(&field.ty, lookup, Visit, &ref_toks)
David Tolnay01ed0ce2018-05-20 20:18:14 -0700867 .unwrap_or_else(|| noop_visit(Visit, &ref_toks,))
Nika Layzell27726662017-10-24 23:16:35 -0400868 ));
869 state.visit_mut_impl.push_str(&format!(
David Tolnayd67fb752017-12-27 13:50:29 -0500870 " {};\n",
David Tolnay83db9272017-12-28 17:02:31 -0500871 visit(&field.ty, lookup, VisitMut, &ref_toks)
David Tolnay01ed0ce2018-05-20 20:18:14 -0700872 .unwrap_or_else(|| noop_visit(VisitMut, &ref_toks,))
Nika Layzell27726662017-10-24 23:16:35 -0400873 ));
David Tolnay83db9272017-12-28 17:02:31 -0500874 let fold = visit(&field.ty, lookup, Fold, &ref_toks)
David Tolnay01ed0ce2018-05-20 20:18:14 -0700875 .unwrap_or_else(|| noop_visit(Fold, &ref_toks));
Nika Layzell27726662017-10-24 23:16:35 -0400876 if let Some(ref name) = field.ident {
David Tolnayd67fb752017-12-27 13:50:29 -0500877 state
878 .fold_impl
879 .push_str(&format!(" {}: {},\n", name, fold));
Nika Layzell27726662017-10-24 23:16:35 -0400880 } else {
881 state.fold_impl.push_str(&format!(" {},\n", fold));
882 }
883 }
884
David Tolnaye3d41b72017-12-31 15:24:00 -0500885 match v.fields {
886 Fields::Named(..) => state.fold_impl.push_str(" }\n"),
887 Fields::Unnamed(..) => state.fold_impl.push_str(" )\n"),
888 Fields::Unit => {}
Nika Layzell27726662017-10-24 23:16:35 -0400889 };
890 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500891 Data::Union(..) => panic!("Union not supported"),
Nika Layzell27726662017-10-24 23:16:35 -0400892 }
893
894 // Close the impl body
895 state.visit_impl.push_str("}\n");
896 state.visit_mut_impl.push_str("}\n");
897 state.fold_impl.push_str("}\n");
David Tolnayd0adf522017-12-29 01:30:07 -0500898
David Tolnay360efd22018-01-04 23:35:26 -0800899 if let Data::Struct(ref data) = s.ast.data {
900 if let Fields::Named(ref fields) = data.fields {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700901 if fields
902 .named
903 .iter()
904 .any(|field| field.vis == Visibility::Inherited)
905 {
David Tolnay360efd22018-01-04 23:35:26 -0800906 // Discard the generated impl if there are private fields.
907 // These have to be handwritten.
908 state.fold_impl.truncate(before_fold_impl_len);
909 }
910 }
David Tolnayd0adf522017-12-29 01:30:07 -0500911 }
Nika Layzell27726662017-10-24 23:16:35 -0400912 }
913}
914
David Tolnay8c81f622018-07-31 23:34:35 -0700915fn write_file(path: &str, content: String) {
916 let mut file = File::create(path).unwrap();
917 let mut config = rustfmt::Config::default();
918 config.set().emit_mode(rustfmt::EmitMode::Stdout);
919 config.set().verbose(rustfmt::Verbosity::Quiet);
920 let mut session = rustfmt::Session::new(config, Some(&mut file));
921 session.format(rustfmt::Input::Text(content)).unwrap();
922}
923
Nika Layzell27726662017-10-24 23:16:35 -0400924fn main() {
925 let mut lookup = BTreeMap::new();
David Tolnayea9ae892017-12-26 01:44:32 -0500926 load_file(SYN_CRATE_ROOT, &quote!(), &mut lookup).unwrap();
Nika Layzell27726662017-10-24 23:16:35 -0400927
Nika Layzellefb83ba2017-12-19 18:23:55 -0500928 // Load in any terminal types
929 for &tt in TERMINAL_TYPES {
930 use syn::*;
David Tolnayd67fb752017-12-27 13:50:29 -0500931 lookup.insert(
Alex Crichtona74a1c82018-05-16 10:20:44 -0700932 Ident::new(&tt, Span::call_site()),
David Tolnayd67fb752017-12-27 13:50:29 -0500933 AstItem {
David Tolnay3d772182017-12-28 17:18:53 -0500934 ast: DeriveInput {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700935 ident: Ident::new(tt, Span::call_site()),
David Tolnayd67fb752017-12-27 13:50:29 -0500936 vis: Visibility::Public(VisPublic {
937 pub_token: Default::default(),
938 }),
939 attrs: vec![],
940 generics: Default::default(),
David Tolnaye3d41b72017-12-31 15:24:00 -0500941 data: Data::Struct(DataStruct {
942 fields: Fields::Unit,
David Tolnayd67fb752017-12-27 13:50:29 -0500943 struct_token: Default::default(),
944 semi_token: None,
945 }),
946 },
hcplaa511792018-05-29 07:13:01 +0300947 features: TokenStream::new(),
David Tolnayd67fb752017-12-27 13:50:29 -0500948 eos_full: false,
Nika Layzellefb83ba2017-12-19 18:23:55 -0500949 },
David Tolnayd67fb752017-12-27 13:50:29 -0500950 );
Nika Layzellefb83ba2017-12-19 18:23:55 -0500951 }
952
Nika Layzell27726662017-10-24 23:16:35 -0400953 let mut state = Default::default();
954 for s in lookup.values() {
955 codegen::generate(&mut state, &lookup, s);
956 }
957
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400958 let full_macro = "
959#[cfg(feature = \"full\")]
960macro_rules! full {
961 ($e:expr) => { $e }
962}
963
David Tolnay0a0d78c2018-01-05 15:24:01 -0800964#[cfg(all(feature = \"derive\", not(feature = \"full\")))]
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400965macro_rules! full {
966 ($e:expr) => { unreachable!() }
967}
968";
969
David Tolnay8c81f622018-07-31 23:34:35 -0700970 write_file(FOLD_SRC, format!(
David Tolnayd67fb752017-12-27 13:50:29 -0500971 "\
Nika Layzell27726662017-10-24 23:16:35 -0400972// THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT
973
Nika Layzell27726662017-10-24 23:16:35 -0400974// Unreachable code is generated sometimes without the full feature.
975#![allow(unreachable_code)]
David Tolnayf0d63bf2017-12-26 12:29:47 -0500976#![cfg_attr(feature = \"cargo-clippy\", allow(needless_pass_by_value))]
Nika Layzell27726662017-10-24 23:16:35 -0400977
Nika Layzella6f46c42017-10-26 15:26:16 -0400978use *;
David Tolnay0a0d78c2018-01-05 15:24:01 -0800979#[cfg(any(feature = \"full\", feature = \"derive\"))]
David Tolnay1e01f9c2017-12-28 20:16:19 -0500980use token::{{Brace, Bracket, Paren, Group}};
David Tolnaye303b7c2018-05-20 16:46:35 -0700981use proc_macro2::Span;
David Tolnay0a0d78c2018-01-05 15:24:01 -0800982#[cfg(any(feature = \"full\", feature = \"derive\"))]
David Tolnayf60f4262017-12-28 19:17:58 -0500983use gen::helper::fold::*;
Nika Layzell27726662017-10-24 23:16:35 -0400984
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400985{full_macro}
986
David Tolnayded2d682018-01-06 18:53:53 -0800987/// Syntax tree traversal to transform the nodes of an owned syntax tree.
Nika Layzell27726662017-10-24 23:16:35 -0400988///
David Tolnayded2d682018-01-06 18:53:53 -0800989/// See the [module documentation] for details.
Nika Layzell27726662017-10-24 23:16:35 -0400990///
David Tolnayded2d682018-01-06 18:53:53 -0800991/// [module documentation]: index.html
David Tolnay461d98e2018-01-07 11:07:19 -0800992///
993/// *This trait is available if Syn is built with the `\"fold\"` feature.*
David Tolnay4b4c4b62018-01-06 13:48:05 -0800994pub trait Fold {{
Nika Layzell27726662017-10-24 23:16:35 -0400995{fold_trait}
996}}
997
David Tolnayd3f32142018-05-20 20:21:12 -0700998#[cfg(any(feature = \"full\", feature = \"derive\"))]
David Tolnay360efd22018-01-04 23:35:26 -0800999macro_rules! fold_span_only {{
1000 ($f:ident : $t:ident) => {{
David Tolnay4b4c4b62018-01-06 13:48:05 -08001001 pub fn $f<V: Fold + ?Sized>(_visitor: &mut V, mut _i: $t) -> $t {{
Alex Crichton9a4dca22018-03-28 06:32:19 -07001002 let span = _visitor.fold_span(_i.span());
1003 _i.set_span(span);
David Tolnay360efd22018-01-04 23:35:26 -08001004 _i
1005 }}
1006 }}
David Tolnayd0adf522017-12-29 01:30:07 -05001007}}
1008
David Tolnay360efd22018-01-04 23:35:26 -08001009#[cfg(any(feature = \"full\", feature = \"derive\"))]
1010fold_span_only!(fold_lit_byte: LitByte);
1011#[cfg(any(feature = \"full\", feature = \"derive\"))]
1012fold_span_only!(fold_lit_byte_str: LitByteStr);
1013#[cfg(any(feature = \"full\", feature = \"derive\"))]
1014fold_span_only!(fold_lit_char: LitChar);
1015#[cfg(any(feature = \"full\", feature = \"derive\"))]
1016fold_span_only!(fold_lit_float: LitFloat);
1017#[cfg(any(feature = \"full\", feature = \"derive\"))]
1018fold_span_only!(fold_lit_int: LitInt);
1019#[cfg(any(feature = \"full\", feature = \"derive\"))]
1020fold_span_only!(fold_lit_str: LitStr);
David Tolnayd0adf522017-12-29 01:30:07 -05001021
Nika Layzell27726662017-10-24 23:16:35 -04001022{fold_impl}
1023",
David Tolnayd67fb752017-12-27 13:50:29 -05001024 full_macro = full_macro,
1025 fold_trait = state.fold_trait,
1026 fold_impl = state.fold_impl
David Tolnay8c81f622018-07-31 23:34:35 -07001027 ));
Nika Layzell27726662017-10-24 23:16:35 -04001028
David Tolnay8c81f622018-07-31 23:34:35 -07001029 write_file(VISIT_SRC, format!(
David Tolnayd67fb752017-12-27 13:50:29 -05001030 "\
Nika Layzell27726662017-10-24 23:16:35 -04001031// THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT
1032
David Tolnayf0d63bf2017-12-26 12:29:47 -05001033#![cfg_attr(feature = \"cargo-clippy\", allow(match_same_arms))]
1034
Nika Layzella6f46c42017-10-26 15:26:16 -04001035use *;
David Tolnay0a0d78c2018-01-05 15:24:01 -08001036#[cfg(any(feature = \"full\", feature = \"derive\"))]
David Tolnay6eff4da2018-01-01 20:27:45 -08001037use punctuated::Punctuated;
David Tolnaye303b7c2018-05-20 16:46:35 -07001038use proc_macro2::Span;
David Tolnay0a0d78c2018-01-05 15:24:01 -08001039#[cfg(any(feature = \"full\", feature = \"derive\"))]
David Tolnaycc0f0372017-12-28 19:11:04 -05001040use gen::helper::visit::*;
Nika Layzell27726662017-10-24 23:16:35 -04001041
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001042{full_macro}
1043
David Tolnayded2d682018-01-06 18:53:53 -08001044/// Syntax tree traversal to walk a shared borrow of a syntax tree.
Nika Layzell27726662017-10-24 23:16:35 -04001045///
David Tolnayded2d682018-01-06 18:53:53 -08001046/// See the [module documentation] for details.
1047///
1048/// [module documentation]: index.html
David Tolnay461d98e2018-01-07 11:07:19 -08001049///
1050/// *This trait is available if Syn is built with the `\"visit\"` feature.*
David Tolnay4b4c4b62018-01-06 13:48:05 -08001051pub trait Visit<'ast> {{
Nika Layzell27726662017-10-24 23:16:35 -04001052{visit_trait}
1053}}
1054
1055{visit_impl}
1056",
David Tolnayd67fb752017-12-27 13:50:29 -05001057 full_macro = full_macro,
1058 visit_trait = state.visit_trait,
1059 visit_impl = state.visit_impl
David Tolnay8c81f622018-07-31 23:34:35 -07001060 ));
Nika Layzell27726662017-10-24 23:16:35 -04001061
David Tolnay8c81f622018-07-31 23:34:35 -07001062 write_file(VISIT_MUT_SRC, format!(
David Tolnayd67fb752017-12-27 13:50:29 -05001063 "\
Nika Layzell27726662017-10-24 23:16:35 -04001064// THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT
1065
David Tolnayf0d63bf2017-12-26 12:29:47 -05001066#![cfg_attr(feature = \"cargo-clippy\", allow(match_same_arms))]
1067
Nika Layzella6f46c42017-10-26 15:26:16 -04001068use *;
David Tolnay0a0d78c2018-01-05 15:24:01 -08001069#[cfg(any(feature = \"full\", feature = \"derive\"))]
David Tolnay6eff4da2018-01-01 20:27:45 -08001070use punctuated::Punctuated;
David Tolnaye303b7c2018-05-20 16:46:35 -07001071use proc_macro2::Span;
David Tolnay0a0d78c2018-01-05 15:24:01 -08001072#[cfg(any(feature = \"full\", feature = \"derive\"))]
David Tolnaycc0f0372017-12-28 19:11:04 -05001073use gen::helper::visit_mut::*;
Nika Layzell27726662017-10-24 23:16:35 -04001074
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001075{full_macro}
1076
David Tolnayded2d682018-01-06 18:53:53 -08001077/// Syntax tree traversal to mutate an exclusive borrow of a syntax tree in
1078/// place.
Nika Layzell27726662017-10-24 23:16:35 -04001079///
David Tolnayded2d682018-01-06 18:53:53 -08001080/// See the [module documentation] for details.
1081///
1082/// [module documentation]: index.html
David Tolnay461d98e2018-01-07 11:07:19 -08001083///
1084/// *This trait is available if Syn is built with the `\"visit-mut\"` feature.*
David Tolnay4b4c4b62018-01-06 13:48:05 -08001085pub trait VisitMut {{
Nika Layzell27726662017-10-24 23:16:35 -04001086{visit_mut_trait}
1087}}
1088
1089{visit_mut_impl}
1090",
David Tolnayd67fb752017-12-27 13:50:29 -05001091 full_macro = full_macro,
1092 visit_mut_trait = state.visit_mut_trait,
1093 visit_mut_impl = state.visit_mut_impl
David Tolnay8c81f622018-07-31 23:34:35 -07001094 ));
Nika Layzell27726662017-10-24 23:16:35 -04001095}