blob: 4a7ec38696ab41a09c5e9ae12b4930cda3649f2e [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 Tolnay6af48992018-08-01 11:16:28 -070013#![recursion_limit = "128"]
David Tolnay6b46a702018-08-01 23:51:32 -070014#![cfg_attr(
15 feature = "cargo-clippy",
16 allow(
17 needless_pass_by_value,
18 redundant_closure,
19 write_with_newline,
20 )
21)]
David Tolnayea9ae892017-12-26 01:44:32 -050022
David Tolnayd67fb752017-12-27 13:50:29 -050023#[macro_use]
24extern crate failure;
Nika Layzell27726662017-10-24 23:16:35 -040025extern crate inflections;
David Tolnay2e0dba12017-12-27 01:54:40 -050026extern crate proc_macro2;
David Tolnayd67fb752017-12-27 13:50:29 -050027#[macro_use]
28extern crate quote;
David Tolnay5c4c0b52017-12-28 17:58:54 -050029#[macro_use]
David Tolnayd67fb752017-12-27 13:50:29 -050030extern crate syn;
David Tolnay8c81f622018-07-31 23:34:35 -070031extern crate rustfmt_nightly as rustfmt;
Nika Layzell27726662017-10-24 23:16:35 -040032
David Tolnayd67fb752017-12-27 13:50:29 -050033use failure::{err_msg, Error};
David Tolnaye303b7c2018-05-20 16:46:35 -070034use proc_macro2::{Span, TokenStream};
David Tolnay01ed0ce2018-05-20 20:18:14 -070035use quote::ToTokens;
36use syn::{Attribute, Data, DataStruct, DeriveInput, Ident, Item};
Nika Layzell27726662017-10-24 23:16:35 -040037
David Tolnay01ed0ce2018-05-20 20:18:14 -070038use std::collections::BTreeMap;
David Tolnayf0d63bf2017-12-26 12:29:47 -050039use std::fmt::{self, Debug};
Nika Layzell27726662017-10-24 23:16:35 -040040use std::fs::File;
David Tolnay6af48992018-08-01 11:16:28 -070041use std::io::{Read, Write};
Nika Layzell27726662017-10-24 23:16:35 -040042use std::path::Path;
Nika Layzell27726662017-10-24 23:16:35 -040043
44const SYN_CRATE_ROOT: &str = "../src/lib.rs";
45
46const FOLD_SRC: &str = "../src/gen/fold.rs";
47const VISIT_SRC: &str = "../src/gen/visit.rs";
48const VISIT_MUT_SRC: &str = "../src/gen/visit_mut.rs";
49
David Tolnayd67fb752017-12-27 13:50:29 -050050const IGNORED_MODS: &[&str] = &["fold", "visit", "visit_mut"];
Nika Layzell27726662017-10-24 23:16:35 -040051
Alex Crichton131308c2018-05-18 14:00:24 -070052const EXTRA_TYPES: &[&str] = &["Lifetime"];
David Tolnay4ba63a02017-12-28 15:53:05 -050053
Alex Crichtond261d092018-05-18 13:47:35 -070054const TERMINAL_TYPES: &[&str] = &["Span", "Ident"];
Nika Layzellefb83ba2017-12-19 18:23:55 -050055
Alex Crichtona74a1c82018-05-16 10:20:44 -070056fn path_eq(a: &syn::Path, b: &str) -> bool {
57 if a.global() {
David Tolnay01ed0ce2018-05-20 20:18:14 -070058 return false;
Nika Layzell27726662017-10-24 23:16:35 -040059 }
Alex Crichtona74a1c82018-05-16 10:20:44 -070060 if a.segments.len() != 1 {
David Tolnay01ed0ce2018-05-20 20:18:14 -070061 return false;
Alex Crichtona74a1c82018-05-16 10:20:44 -070062 }
David Tolnay446f7d62018-05-20 17:58:15 -070063 a.segments[0].ident == b
Nika Layzell27726662017-10-24 23:16:35 -040064}
65
Alex Crichton715862b2018-05-17 12:31:49 -070066fn get_features(attrs: &[Attribute], mut features: TokenStream) -> TokenStream {
Nika Layzell27726662017-10-24 23:16:35 -040067 for attr in attrs {
Alex Crichtona74a1c82018-05-16 10:20:44 -070068 if path_eq(&attr.path, "cfg") {
Nika Layzell27726662017-10-24 23:16:35 -040069 attr.to_tokens(&mut features);
70 }
71 }
72 features
73}
74
75#[derive(Clone)]
76pub struct AstItem {
David Tolnay3d772182017-12-28 17:18:53 -050077 ast: DeriveInput,
Alex Crichton715862b2018-05-17 12:31:49 -070078 features: TokenStream,
Nika Layzell27726662017-10-24 23:16:35 -040079 // True if this is an ast_enum_of_structs! item with a #full annotation.
80 eos_full: bool,
81}
82
David Tolnayf0d63bf2017-12-26 12:29:47 -050083impl Debug for AstItem {
Nika Layzell27726662017-10-24 23:16:35 -040084 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
85 f.debug_struct("AstItem")
David Tolnay3d772182017-12-28 17:18:53 -050086 .field("ast", &self.ast)
Nika Layzell27726662017-10-24 23:16:35 -040087 .field("features", &self.features.to_string())
88 .finish()
89 }
90}
91
92// NOTE: BTreeMap is used here instead of HashMap to have deterministic output.
David Tolnayc47f8422017-12-28 17:30:46 -050093type Lookup = BTreeMap<Ident, AstItem>;
Nika Layzell27726662017-10-24 23:16:35 -040094
David Tolnay01ed0ce2018-05-20 20:18:14 -070095fn load_file<P: AsRef<Path>>(
96 name: P,
97 features: &TokenStream,
98 lookup: &mut Lookup,
99) -> Result<(), Error> {
Nika Layzell27726662017-10-24 23:16:35 -0400100 let name = name.as_ref();
David Tolnayea9ae892017-12-26 01:44:32 -0500101 let parent = name.parent().ok_or_else(|| err_msg("no parent path"))?;
Nika Layzell27726662017-10-24 23:16:35 -0400102
103 let mut f = File::open(name)?;
104 let mut src = String::new();
105 f.read_to_string(&mut src)?;
106
107 // Parse the file
David Tolnayd67fb752017-12-27 13:50:29 -0500108 let file =
109 syn::parse_file(&src).map_err(|_| format_err!("failed to parse {}", name.display()))?;
Nika Layzell27726662017-10-24 23:16:35 -0400110
111 // Collect all of the interesting AstItems declared in this file or submodules.
David Tolnay4ba63a02017-12-28 15:53:05 -0500112 'items: for item in file.items {
113 match item {
114 Item::Mod(item) => {
Nika Layzell27726662017-10-24 23:16:35 -0400115 // Don't inspect inline modules.
David Tolnayc6b55bc2017-11-09 22:48:38 -0800116 if item.content.is_some() {
Nika Layzell27726662017-10-24 23:16:35 -0400117 continue;
118 }
119
120 // We don't want to try to load the generated rust files and
121 // parse them, so we ignore them here.
122 for name in IGNORED_MODS {
David Tolnay446f7d62018-05-20 17:58:15 -0700123 if item.ident == name {
Nika Layzell27726662017-10-24 23:16:35 -0400124 continue 'items;
125 }
126 }
127
128 // Lookup any #[cfg()] attributes on the module and add them to
129 // the feature set.
David Tolnay0a0d78c2018-01-05 15:24:01 -0800130 //
131 // The derive module is weird because it is built with either
132 // `full` or `derive` but exported only under `derive`.
David Tolnay446f7d62018-05-20 17:58:15 -0700133 let features = if item.ident == "derive" {
David Tolnay0a0d78c2018-01-05 15:24:01 -0800134 quote!(#[cfg(feature = "derive")])
135 } else {
136 get_features(&item.attrs, features.clone())
137 };
Nika Layzell27726662017-10-24 23:16:35 -0400138
139 // Look up the submodule file, and recursively parse it.
140 // XXX: Only handles same-directory .rs file submodules.
Alex Crichtona74a1c82018-05-16 10:20:44 -0700141 let path = parent.join(&format!("{}.rs", item.ident));
David Tolnayea9ae892017-12-26 01:44:32 -0500142 load_file(path, &features, lookup)?;
Nika Layzell27726662017-10-24 23:16:35 -0400143 }
David Tolnay4ba63a02017-12-28 15:53:05 -0500144 Item::Macro(item) => {
Nika Layzell27726662017-10-24 23:16:35 -0400145 // Lookip any #[cfg()] attributes directly on the macro
146 // invocation, and add them to the feature set.
147 let features = get_features(&item.attrs, features.clone());
148
149 // Try to parse the AstItem declaration out of the item.
David Tolnay01a77582018-01-01 20:00:51 -0800150 let tts = &item.mac.tts;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700151 let found = if path_eq(&item.mac.path, "ast_struct") {
David Tolnay01a77582018-01-01 20:00:51 -0800152 syn::parse_str::<parsing::AstStruct>(&quote!(#tts).to_string())
David Tolnayd67fb752017-12-27 13:50:29 -0500153 .map_err(|_| err_msg("failed to parse ast_struct"))?
154 .0
Alex Crichtona74a1c82018-05-16 10:20:44 -0700155 } else if path_eq(&item.mac.path, "ast_enum") {
David Tolnay01a77582018-01-01 20:00:51 -0800156 syn::parse_str::<parsing::AstEnum>(&quote!(#tts).to_string())
David Tolnayd67fb752017-12-27 13:50:29 -0500157 .map_err(|_| err_msg("failed to parse ast_enum"))?
158 .0
Alex Crichtona74a1c82018-05-16 10:20:44 -0700159 } else if path_eq(&item.mac.path, "ast_enum_of_structs") {
David Tolnay01a77582018-01-01 20:00:51 -0800160 syn::parse_str::<parsing::AstEnumOfStructs>(&quote!(#tts).to_string())
David Tolnay1cf80912017-12-31 18:35:12 -0500161 .map_err(|_| err_msg("failed to parse ast_enum_of_structs"))?
David Tolnayd67fb752017-12-27 13:50:29 -0500162 .0
Nika Layzell27726662017-10-24 23:16:35 -0400163 } else {
David Tolnayd67fb752017-12-27 13:50:29 -0500164 continue;
Nika Layzell27726662017-10-24 23:16:35 -0400165 };
166
167 // Record our features on the parsed AstItems.
168 for mut item in found {
169 features.to_tokens(&mut item.features);
Alex Crichtona74a1c82018-05-16 10:20:44 -0700170 lookup.insert(item.ast.ident.clone(), item);
Nika Layzell27726662017-10-24 23:16:35 -0400171 }
172 }
David Tolnay4ba63a02017-12-28 15:53:05 -0500173 Item::Struct(item) => {
174 let ident = item.ident;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700175 if EXTRA_TYPES.contains(&&ident.to_string()[..]) {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700176 lookup.insert(
177 ident.clone(),
178 AstItem {
179 ast: DeriveInput {
David Tolnay6b46a702018-08-01 23:51:32 -0700180 ident,
David Tolnay01ed0ce2018-05-20 20:18:14 -0700181 vis: item.vis,
182 attrs: item.attrs,
183 generics: item.generics,
184 data: Data::Struct(DataStruct {
185 fields: item.fields,
186 struct_token: item.struct_token,
187 semi_token: item.semi_token,
188 }),
189 },
190 features: features.clone(),
191 eos_full: false,
David Tolnay4ba63a02017-12-28 15:53:05 -0500192 },
David Tolnay01ed0ce2018-05-20 20:18:14 -0700193 );
David Tolnay4ba63a02017-12-28 15:53:05 -0500194 }
195 }
Nika Layzell27726662017-10-24 23:16:35 -0400196 _ => {}
197 }
198 }
199 Ok(())
200}
201
202mod parsing {
203 use super::AstItem;
204
David Tolnay01ed0ce2018-05-20 20:18:14 -0700205 use proc_macro2::TokenStream;
David Tolnay1cf80912017-12-31 18:35:12 -0500206 use syn;
David Tolnayc5ab8c62017-12-26 16:43:39 -0500207 use syn::synom::*;
Nika Layzell27726662017-10-24 23:16:35 -0400208 use syn::*;
Nika Layzell27726662017-10-24 23:16:35 -0400209
210 // Parses #full - returns #[cfg(feature = "full")] if it is present, and
211 // nothing otherwise.
Alex Crichton715862b2018-05-17 12:31:49 -0700212 named!(full -> (TokenStream, bool), map!(option!(do_parse!(
David Tolnayf8db7ba2017-11-11 22:52:16 -0800213 punct!(#) >>
Nika Layzell27726662017-10-24 23:16:35 -0400214 id: syn!(Ident) >>
David Tolnay446f7d62018-05-20 17:58:15 -0700215 cond_reduce!(id == "full") >>
David Tolnayea9ae892017-12-26 01:44:32 -0500216 ()
Nika Layzell27726662017-10-24 23:16:35 -0400217 )), |s| if s.is_some() {
218 (quote!(#[cfg(feature = "full")]), true)
219 } else {
220 (quote!(), false)
221 }));
222
David Tolnay28c5a462017-12-27 01:59:30 -0500223 named!(manual_extra_traits -> (), do_parse!(
224 punct!(#) >>
225 id: syn!(Ident) >>
David Tolnay446f7d62018-05-20 17:58:15 -0700226 cond_reduce!(id == "manual_extra_traits") >>
David Tolnay28c5a462017-12-27 01:59:30 -0500227 ()
228 ));
229
Nika Layzell27726662017-10-24 23:16:35 -0400230 // Parses a simple AstStruct without the `pub struct` prefix.
231 named!(ast_struct_inner -> AstItem, do_parse!(
232 id: syn!(Ident) >>
233 features: full >>
David Tolnay28c5a462017-12-27 01:59:30 -0500234 option!(manual_extra_traits) >>
David Tolnay2e0dba12017-12-27 01:54:40 -0500235 rest: syn!(TokenStream) >>
Nika Layzell27726662017-10-24 23:16:35 -0400236 (AstItem {
David Tolnay01a77582018-01-01 20:00:51 -0800237 ast: syn::parse_str(&quote! {
David Tolnay2e0dba12017-12-27 01:54:40 -0500238 pub struct #id #rest
David Tolnay01a77582018-01-01 20:00:51 -0800239 }.to_string())?,
Nika Layzell27726662017-10-24 23:16:35 -0400240 features: features.0,
241 eos_full: features.1,
242 })
243 ));
244
245 // ast_struct! parsing
246 pub struct AstStruct(pub Vec<AstItem>);
247 impl Synom for AstStruct {
David Tolnayab919512017-12-30 23:31:51 -0500248 named!(parse -> Self, do_parse!(
David Tolnay2c136452017-12-27 14:13:32 -0500249 many0!(Attribute::parse_outer) >>
David Tolnayf8db7ba2017-11-11 22:52:16 -0800250 keyword!(pub) >>
251 keyword!(struct) >>
Nika Layzell27726662017-10-24 23:16:35 -0400252 res: call!(ast_struct_inner) >>
David Tolnayab919512017-12-30 23:31:51 -0500253 (AstStruct(vec![res]))
254 ));
Nika Layzell27726662017-10-24 23:16:35 -0400255 }
256
David Tolnay360efd22018-01-04 23:35:26 -0800257 named!(no_visit -> (), do_parse!(
258 punct!(#) >>
259 id: syn!(Ident) >>
David Tolnay446f7d62018-05-20 17:58:15 -0700260 cond_reduce!(id == "no_visit") >>
David Tolnay360efd22018-01-04 23:35:26 -0800261 ()
262 ));
263
Nika Layzell27726662017-10-24 23:16:35 -0400264 // ast_enum! parsing
265 pub struct AstEnum(pub Vec<AstItem>);
266 impl Synom for AstEnum {
David Tolnay360efd22018-01-04 23:35:26 -0800267 named!(parse -> Self, do_parse!(
268 many0!(Attribute::parse_outer) >>
269 keyword!(pub) >>
270 keyword!(enum) >>
271 id: syn!(Ident) >>
272 no_visit: option!(no_visit) >>
273 rest: syn!(TokenStream) >>
274 (AstEnum(if no_visit.is_some() {
275 vec![]
276 } else {
277 vec![AstItem {
278 ast: syn::parse_str(&quote! {
279 pub enum #id #rest
280 }.to_string())?,
281 features: quote!(),
282 eos_full: false,
283 }]
284 }))
285 ));
Nika Layzell27726662017-10-24 23:16:35 -0400286 }
287
288 // A single variant of an ast_enum_of_structs!
289 struct EosVariant {
290 name: Ident,
David Tolnayfcfb9002017-12-28 22:04:29 -0500291 member: Option<Path>,
Nika Layzell27726662017-10-24 23:16:35 -0400292 inner: Option<AstItem>,
293 }
294 named!(eos_variant -> EosVariant, do_parse!(
David Tolnay2c136452017-12-27 14:13:32 -0500295 many0!(Attribute::parse_outer) >>
David Tolnayf8db7ba2017-11-11 22:52:16 -0800296 keyword!(pub) >>
Nika Layzell27726662017-10-24 23:16:35 -0400297 variant: syn!(Ident) >>
David Tolnayfcfb9002017-12-28 22:04:29 -0500298 member: option!(map!(parens!(alt!(
Alex Crichtona74a1c82018-05-16 10:20:44 -0700299 call!(ast_struct_inner) => { |x: AstItem| (Path::from(x.ast.ident.clone()), Some(x)) }
Nika Layzell27726662017-10-24 23:16:35 -0400300 |
301 syn!(Path) => { |x| (x, None) }
David Tolnay8875fca2017-12-31 13:52:37 -0500302 )), |x| x.1)) >>
David Tolnayf8db7ba2017-11-11 22:52:16 -0800303 punct!(,) >>
Nika Layzell27726662017-10-24 23:16:35 -0400304 (EosVariant {
305 name: variant,
David Tolnayfcfb9002017-12-28 22:04:29 -0500306 member: member.clone().map(|x| x.0),
307 inner: member.map(|x| x.1).unwrap_or_default(),
Nika Layzell27726662017-10-24 23:16:35 -0400308 })
309 ));
310
311 // ast_enum_of_structs! parsing
312 pub struct AstEnumOfStructs(pub Vec<AstItem>);
313 impl Synom for AstEnumOfStructs {
David Tolnayab919512017-12-30 23:31:51 -0500314 named!(parse -> Self, do_parse!(
David Tolnay2c136452017-12-27 14:13:32 -0500315 many0!(Attribute::parse_outer) >>
David Tolnayf8db7ba2017-11-11 22:52:16 -0800316 keyword!(pub) >>
317 keyword!(enum) >>
Nika Layzell27726662017-10-24 23:16:35 -0400318 id: syn!(Ident) >>
David Tolnaye3d41b72017-12-31 15:24:00 -0500319 variants: braces!(many0!(eos_variant)) >>
Nika Layzell27726662017-10-24 23:16:35 -0400320 option!(syn!(Ident)) >> // do_not_generate_to_tokens
321 ({
Nika Layzell27726662017-10-24 23:16:35 -0400322 let enum_item = {
David Tolnaye3d41b72017-12-31 15:24:00 -0500323 let variants = variants.1.iter().map(|v| {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700324 let name = v.name.clone();
David Tolnayfcfb9002017-12-28 22:04:29 -0500325 match v.member {
326 Some(ref member) => quote!(#name(#member)),
327 None => quote!(#name),
328 }
Nika Layzell27726662017-10-24 23:16:35 -0400329 });
David Tolnayb7ccc4f2018-08-02 00:41:32 -0700330 parse_quote! {
331 pub enum #id {
332 #(#variants),*
333 }
334 }
Nika Layzell27726662017-10-24 23:16:35 -0400335 };
336 let mut items = vec![AstItem {
David Tolnay3d772182017-12-28 17:18:53 -0500337 ast: enum_item,
Nika Layzell27726662017-10-24 23:16:35 -0400338 features: quote!(),
339 eos_full: false,
340 }];
David Tolnaye3d41b72017-12-31 15:24:00 -0500341 items.extend(variants.1.into_iter().filter_map(|v| v.inner));
Nika Layzell27726662017-10-24 23:16:35 -0400342 AstEnumOfStructs(items)
343 })
David Tolnayab919512017-12-30 23:31:51 -0500344 ));
Nika Layzell27726662017-10-24 23:16:35 -0400345 }
346}
347
348mod codegen {
349 use super::{AstItem, Lookup};
David Tolnay6b46a702018-08-01 23:51:32 -0700350 use inflections::Inflect;
David Tolnay01ed0ce2018-05-20 20:18:14 -0700351 use proc_macro2::{Span, TokenStream};
David Tolnay6af48992018-08-01 11:16:28 -0700352 use quote::{ToTokens, TokenStreamExt};
David Tolnay01ed0ce2018-05-20 20:18:14 -0700353 use syn::punctuated::Punctuated;
354 use syn::*;
Nika Layzell27726662017-10-24 23:16:35 -0400355
356 #[derive(Default)]
357 pub struct State {
David Tolnay6af48992018-08-01 11:16:28 -0700358 pub visit_trait: TokenStream,
359 pub visit_impl: TokenStream,
360 pub visit_mut_trait: TokenStream,
361 pub visit_mut_impl: TokenStream,
362 pub fold_trait: TokenStream,
363 pub fold_impl: TokenStream,
Nika Layzell27726662017-10-24 23:16:35 -0400364 }
365
David Tolnay4a918742017-12-28 16:54:41 -0500366 fn under_name(name: Ident) -> Ident {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700367 Ident::new(&name.to_string().to_snake_case(), Span::call_site())
Nika Layzell27726662017-10-24 23:16:35 -0400368 }
369
David Tolnay3d772182017-12-28 17:18:53 -0500370 enum RelevantType<'a> {
371 Box(&'a Type),
372 Vec(&'a Type),
David Tolnayf2cfd722017-12-31 18:02:51 -0500373 Punctuated(&'a Type),
David Tolnay3d772182017-12-28 17:18:53 -0500374 Option(&'a Type),
David Tolnayf2cfd722017-12-31 18:02:51 -0500375 Tuple(&'a Punctuated<Type, Token![,]>),
David Tolnay3d772182017-12-28 17:18:53 -0500376 Simple(&'a AstItem),
Alex Crichton715862b2018-05-17 12:31:49 -0700377 Token(TokenStream),
David Tolnay3d772182017-12-28 17:18:53 -0500378 Pass,
379 }
Nika Layzell27726662017-10-24 23:16:35 -0400380
David Tolnay3d772182017-12-28 17:18:53 -0500381 fn classify<'a>(ty: &'a Type, lookup: &'a Lookup) -> RelevantType<'a> {
382 match *ty {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700383 Type::Path(TypePath {
384 qself: None,
385 ref path,
386 }) => {
David Tolnay56080682018-01-06 14:01:52 -0800387 let last = path.segments.last().unwrap().into_value();
Alex Crichtona74a1c82018-05-16 10:20:44 -0700388 match &last.ident.to_string()[..] {
David Tolnay3d772182017-12-28 17:18:53 -0500389 "Box" => RelevantType::Box(first_arg(&last.arguments)),
390 "Vec" => RelevantType::Vec(first_arg(&last.arguments)),
David Tolnayf2cfd722017-12-31 18:02:51 -0500391 "Punctuated" => RelevantType::Punctuated(first_arg(&last.arguments)),
David Tolnay3d772182017-12-28 17:18:53 -0500392 "Option" => RelevantType::Option(first_arg(&last.arguments)),
David Tolnay1e01f9c2017-12-28 20:16:19 -0500393 "Brace" | "Bracket" | "Paren" | "Group" => {
Alex Crichton715862b2018-05-17 12:31:49 -0700394 RelevantType::Token(last.ident.clone().into_token_stream())
David Tolnay1e01f9c2017-12-28 20:16:19 -0500395 }
David Tolnay3d772182017-12-28 17:18:53 -0500396 _ => {
397 if let Some(item) = lookup.get(&last.ident) {
398 RelevantType::Simple(item)
399 } else {
400 RelevantType::Pass
401 }
402 }
403 }
Nika Layzell27726662017-10-24 23:16:35 -0400404 }
David Tolnay01ed0ce2018-05-20 20:18:14 -0700405 Type::Tuple(TypeTuple { ref elems, .. }) => RelevantType::Tuple(elems),
406 Type::Macro(TypeMacro { ref mac })
407 if mac.path.segments.last().unwrap().into_value().ident == "Token" =>
408 {
Alex Crichton715862b2018-05-17 12:31:49 -0700409 RelevantType::Token(mac.into_token_stream())
David Tolnaycc0f0372017-12-28 19:11:04 -0500410 }
David Tolnay3d772182017-12-28 17:18:53 -0500411 _ => RelevantType::Pass,
Nika Layzell27726662017-10-24 23:16:35 -0400412 }
413 }
414
415 #[derive(Debug, Eq, PartialEq, Copy, Clone)]
416 enum Kind {
417 Visit,
418 VisitMut,
419 Fold,
420 }
421
David Tolnayf0d63bf2017-12-26 12:29:47 -0500422 enum Operand {
Alex Crichton715862b2018-05-17 12:31:49 -0700423 Borrowed(TokenStream),
424 Owned(TokenStream),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500425 }
426
David Tolnay83db9272017-12-28 17:02:31 -0500427 use self::Kind::*;
David Tolnay01ed0ce2018-05-20 20:18:14 -0700428 use self::Operand::*;
David Tolnay83db9272017-12-28 17:02:31 -0500429
David Tolnayf0d63bf2017-12-26 12:29:47 -0500430 impl Operand {
Alex Crichton715862b2018-05-17 12:31:49 -0700431 fn tokens(&self) -> &TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500432 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500433 Borrowed(ref n) | Owned(ref n) => n,
David Tolnayf0d63bf2017-12-26 12:29:47 -0500434 }
435 }
436
Alex Crichton715862b2018-05-17 12:31:49 -0700437 fn ref_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!(&#n),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500441 }
442 }
443
Alex Crichton715862b2018-05-17 12:31:49 -0700444 fn ref_mut_tokens(&self) -> TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500445 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500446 Borrowed(ref n) => n.clone(),
447 Owned(ref n) => quote!(&mut #n),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500448 }
449 }
450
Alex Crichton715862b2018-05-17 12:31:49 -0700451 fn owned_tokens(&self) -> TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500452 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500453 Borrowed(ref n) => quote!(*#n),
454 Owned(ref n) => n.clone(),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500455 }
456 }
457 }
458
Nika Layzellc08227a2017-12-04 16:30:17 -0500459 fn first_arg(params: &PathArguments) -> &Type {
Nika Layzell27726662017-10-24 23:16:35 -0400460 let data = match *params {
Nika Layzellc08227a2017-12-04 16:30:17 -0500461 PathArguments::AngleBracketed(ref data) => data,
462 _ => panic!("Expected at least 1 type argument here"),
Nika Layzell27726662017-10-24 23:16:35 -0400463 };
464
David Tolnay01ed0ce2018-05-20 20:18:14 -0700465 match **data
466 .args
David Tolnayd67fb752017-12-27 13:50:29 -0500467 .first()
468 .expect("Expected at least 1 type argument here")
David Tolnay56080682018-01-06 14:01:52 -0800469 .value()
David Tolnayd67fb752017-12-27 13:50:29 -0500470 {
David Tolnayea9ae892017-12-26 01:44:32 -0500471 GenericArgument::Type(ref ty) => ty,
Nika Layzellc08227a2017-12-04 16:30:17 -0500472 _ => panic!("Expected at least 1 type argument here"),
Nika Layzell357885a2017-12-04 15:47:07 -0500473 }
Nika Layzell27726662017-10-24 23:16:35 -0400474 }
475
David Tolnay6af48992018-08-01 11:16:28 -0700476 fn simple_visit(item: &AstItem, kind: Kind, name: &Operand) -> TokenStream {
477 let ident = under_name(item.ast.ident.clone());
478
David Tolnay4a918742017-12-28 16:54:41 -0500479 match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700480 Visit => {
481 let method = Ident::new(&format!("visit_{}", ident), Span::call_site());
482 let name = name.ref_tokens();
483 quote! {
484 _visitor.#method(#name)
485 }
486 }
487 VisitMut => {
488 let method = Ident::new(&format!("visit_{}_mut", ident), Span::call_site());
489 let name = name.ref_mut_tokens();
490 quote! {
491 _visitor.#method(#name)
492 }
493 }
494 Fold => {
495 let method = Ident::new(&format!("fold_{}", ident), Span::call_site());
496 let name = name.owned_tokens();
497 quote! {
498 _visitor.#method(#name)
499 }
500 }
Nika Layzell27726662017-10-24 23:16:35 -0400501 }
502 }
503
David Tolnay6af48992018-08-01 11:16:28 -0700504 fn box_visit(elem: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<TokenStream> {
David Tolnay4a918742017-12-28 16:54:41 -0500505 let name = name.owned_tokens();
David Tolnay39d0a202017-12-28 18:19:00 -0500506 let res = visit(elem, lookup, kind, &Owned(quote!(*#name)))?;
David Tolnay4a918742017-12-28 16:54:41 -0500507 Some(match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700508 Fold => quote! {
509 Box::new(#res)
510 },
David Tolnay83db9272017-12-28 17:02:31 -0500511 Visit | VisitMut => res,
David Tolnay4a918742017-12-28 16:54:41 -0500512 })
Nika Layzell27726662017-10-24 23:16:35 -0400513 }
514
David Tolnay6af48992018-08-01 11:16:28 -0700515 fn vec_visit(elem: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<TokenStream> {
David Tolnay4a918742017-12-28 16:54:41 -0500516 let operand = match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500517 Visit | VisitMut => Borrowed(quote!(it)),
518 Fold => Owned(quote!(it)),
David Tolnay4a918742017-12-28 16:54:41 -0500519 };
David Tolnay39d0a202017-12-28 18:19:00 -0500520 let val = visit(elem, lookup, kind, &operand)?;
David Tolnay4a918742017-12-28 16:54:41 -0500521 Some(match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700522 Visit => {
523 let name = name.ref_tokens();
524 quote! {
525 for it in #name {
526 #val
527 }
528 }
529 }
530 VisitMut => {
531 let name = name.ref_mut_tokens();
532 quote! {
533 for it in #name {
534 #val
535 }
536 }
537 }
538 Fold => {
539 let name = name.owned_tokens();
540 quote! {
541 FoldHelper::lift(#name, |it| { #val })
542 }
543 }
David Tolnay3d772182017-12-28 17:18:53 -0500544 })
545 }
546
David Tolnay6eff4da2018-01-01 20:27:45 -0800547 fn punctuated_visit(
David Tolnay3d772182017-12-28 17:18:53 -0500548 elem: &Type,
549 lookup: &Lookup,
550 kind: Kind,
551 name: &Operand,
David Tolnay6af48992018-08-01 11:16:28 -0700552 ) -> Option<TokenStream> {
David Tolnay3d772182017-12-28 17:18:53 -0500553 let operand = match kind {
554 Visit | VisitMut => Borrowed(quote!(it)),
555 Fold => Owned(quote!(it)),
556 };
David Tolnay39d0a202017-12-28 18:19:00 -0500557 let val = visit(elem, lookup, kind, &operand)?;
David Tolnay3d772182017-12-28 17:18:53 -0500558 Some(match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700559 Visit => {
560 let name = name.ref_tokens();
561 quote! {
562 for el in Punctuated::pairs(#name) {
563 let it = el.value();
564 #val
565 }
566 }
567 }
568 VisitMut => {
569 let name = name.ref_mut_tokens();
570 quote! {
571 for mut el in Punctuated::pairs_mut(#name) {
572 let it = el.value_mut();
573 #val
574 }
575 }
576 }
577 Fold => {
578 let name = name.owned_tokens();
579 quote! {
580 FoldHelper::lift(#name, |it| { #val })
581 }
582 }
David Tolnay4a918742017-12-28 16:54:41 -0500583 })
Nika Layzell27726662017-10-24 23:16:35 -0400584 }
585
David Tolnay6af48992018-08-01 11:16:28 -0700586 fn option_visit(
587 elem: &Type,
588 lookup: &Lookup,
589 kind: Kind,
590 name: &Operand,
591 ) -> Option<TokenStream> {
David Tolnay4a918742017-12-28 16:54:41 -0500592 let it = match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500593 Visit | VisitMut => Borrowed(quote!(it)),
594 Fold => Owned(quote!(it)),
David Tolnay4a918742017-12-28 16:54:41 -0500595 };
David Tolnay39d0a202017-12-28 18:19:00 -0500596 let val = visit(elem, lookup, kind, &it)?;
David Tolnay6af48992018-08-01 11:16:28 -0700597 let name = name.owned_tokens();
David Tolnay4a918742017-12-28 16:54:41 -0500598 Some(match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700599 Visit => quote! {
600 if let Some(ref it) = #name {
601 #val
602 }
603 },
604 VisitMut => quote! {
605 if let Some(ref mut it) = #name {
606 #val
607 }
608 },
609 Fold => quote! {
610 (#name).map(|it| { #val })
611 },
David Tolnay4a918742017-12-28 16:54:41 -0500612 })
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400613 }
614
David Tolnay5c4c0b52017-12-28 17:58:54 -0500615 fn tuple_visit(
David Tolnayf2cfd722017-12-31 18:02:51 -0500616 elems: &Punctuated<Type, Token![,]>,
David Tolnay5c4c0b52017-12-28 17:58:54 -0500617 lookup: &Lookup,
618 kind: Kind,
619 name: &Operand,
David Tolnay6af48992018-08-01 11:16:28 -0700620 ) -> Option<TokenStream> {
621 if elems.is_empty() {
622 return None;
623 }
624
625 let mut code = TokenStream::new();
David Tolnayf047c7a2017-12-31 02:29:04 -0500626 for (i, elem) in elems.iter().enumerate() {
David Tolnay5c4c0b52017-12-28 17:58:54 -0500627 let name = name.tokens();
David Tolnay14982012017-12-29 00:49:51 -0500628 let i = Index::from(i);
David Tolnay5c4c0b52017-12-28 17:58:54 -0500629 let it = Owned(quote!((#name).#i));
David Tolnay01ed0ce2018-05-20 20:18:14 -0700630 let val = visit(elem, lookup, kind, &it).unwrap_or_else(|| noop_visit(kind, &it));
David Tolnay6af48992018-08-01 11:16:28 -0700631 code.append_all(val);
David Tolnay5c4c0b52017-12-28 17:58:54 -0500632 match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700633 Fold => code.append_all(quote!(,)),
634 Visit | VisitMut => code.append_all(quote!(;)),
David Tolnay5c4c0b52017-12-28 17:58:54 -0500635 }
David Tolnay5c4c0b52017-12-28 17:58:54 -0500636 }
David Tolnay6af48992018-08-01 11:16:28 -0700637 Some(match kind {
638 Fold => quote! {
639 (#code)
640 },
641 Visit | VisitMut => code,
642 })
David Tolnay5c4c0b52017-12-28 17:58:54 -0500643 }
644
David Tolnay6af48992018-08-01 11:16:28 -0700645 fn token_visit(ty: TokenStream, kind: Kind, name: &Operand) -> TokenStream {
646 let name = name.tokens();
David Tolnaycc0f0372017-12-28 19:11:04 -0500647 match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700648 Fold => quote! {
649 #ty(tokens_helper(_visitor, &(#name).0))
650 },
651 Visit => quote! {
652 tokens_helper(_visitor, &(#name).0)
653 },
654 VisitMut => quote! {
655 tokens_helper(_visitor, &mut (#name).0)
656 },
David Tolnaycc0f0372017-12-28 19:11:04 -0500657 }
658 }
659
David Tolnay6af48992018-08-01 11:16:28 -0700660 fn noop_visit(kind: Kind, name: &Operand) -> TokenStream {
David Tolnay4a918742017-12-28 16:54:41 -0500661 match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700662 Fold => name.owned_tokens(),
663 Visit | VisitMut => {
664 let name = name.tokens();
665 quote! {
666 skip!(#name)
667 }
668 }
David Tolnay4a918742017-12-28 16:54:41 -0500669 }
670 }
671
David Tolnay6af48992018-08-01 11:16:28 -0700672 fn visit(ty: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<TokenStream> {
David Tolnay3d772182017-12-28 17:18:53 -0500673 match classify(ty, lookup) {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700674 RelevantType::Box(elem) => box_visit(elem, lookup, kind, name),
675 RelevantType::Vec(elem) => vec_visit(elem, lookup, kind, name),
676 RelevantType::Punctuated(elem) => punctuated_visit(elem, lookup, kind, name),
677 RelevantType::Option(elem) => option_visit(elem, lookup, kind, name),
678 RelevantType::Tuple(elems) => tuple_visit(elems, lookup, kind, name),
David Tolnay3d772182017-12-28 17:18:53 -0500679 RelevantType::Simple(item) => {
680 let mut res = simple_visit(item, kind, name);
681 Some(if item.eos_full {
David Tolnay6af48992018-08-01 11:16:28 -0700682 quote! {
683 full!(#res)
684 }
David Tolnay3d772182017-12-28 17:18:53 -0500685 } else {
686 res
687 })
688 }
David Tolnay01ed0ce2018-05-20 20:18:14 -0700689 RelevantType::Token(ty) => Some(token_visit(ty, kind, name)),
690 RelevantType::Pass => None,
Nika Layzell27726662017-10-24 23:16:35 -0400691 }
Nika Layzell27726662017-10-24 23:16:35 -0400692 }
693
694 pub fn generate(state: &mut State, lookup: &Lookup, s: &AstItem) {
David Tolnay6af48992018-08-01 11:16:28 -0700695 let features = &s.features;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700696 let under_name = under_name(s.ast.ident.clone());
David Tolnay6af48992018-08-01 11:16:28 -0700697 let ty = &s.ast.ident;
698 let visit_fn = Ident::new(&format!("visit_{}", under_name), Span::call_site());
699 let visit_mut_fn = Ident::new(&format!("visit_{}_mut", under_name), Span::call_site());
700 let fold_fn = Ident::new(&format!("fold_{}", under_name), Span::call_site());
Nika Layzell27726662017-10-24 23:16:35 -0400701
David Tolnay6af48992018-08-01 11:16:28 -0700702 let mut visit_impl = TokenStream::new();
703 let mut visit_mut_impl = TokenStream::new();
704 let mut fold_impl = TokenStream::new();
Nika Layzell27726662017-10-24 23:16:35 -0400705
David Tolnaye3d41b72017-12-31 15:24:00 -0500706 match s.ast.data {
707 Data::Enum(ref e) => {
David Tolnay6af48992018-08-01 11:16:28 -0700708 let mut visit_variants = TokenStream::new();
709 let mut visit_mut_variants = TokenStream::new();
710 let mut fold_variants = TokenStream::new();
711
Nika Layzell27726662017-10-24 23:16:35 -0400712 for variant in &e.variants {
David Tolnay6af48992018-08-01 11:16:28 -0700713 let variant_ident = &variant.ident;
714
715 match variant.fields {
David Tolnaye3d41b72017-12-31 15:24:00 -0500716 Fields::Named(..) => panic!("Doesn't support enum struct variants"),
717 Fields::Unnamed(ref fields) => {
David Tolnay6af48992018-08-01 11:16:28 -0700718 let mut bind_visit_fields = TokenStream::new();
719 let mut bind_visit_mut_fields = TokenStream::new();
720 let mut bind_fold_fields = TokenStream::new();
Nika Layzell27726662017-10-24 23:16:35 -0400721
David Tolnay6af48992018-08-01 11:16:28 -0700722 let mut visit_fields = TokenStream::new();
723 let mut visit_mut_fields = TokenStream::new();
724 let mut fold_fields = TokenStream::new();
Nika Layzell27726662017-10-24 23:16:35 -0400725
David Tolnay6af48992018-08-01 11:16:28 -0700726 for (idx, field) in fields.unnamed.iter().enumerate() {
727 let name = format!("_binding_{}", idx);
728 let binding = Ident::new(&name, Span::call_site());
Nika Layzell27726662017-10-24 23:16:35 -0400729
David Tolnay6af48992018-08-01 11:16:28 -0700730 bind_visit_fields.append_all(quote! {
731 ref #binding,
732 });
733 bind_visit_mut_fields.append_all(quote! {
734 ref mut #binding,
735 });
736 bind_fold_fields.append_all(quote! {
737 #binding,
738 });
Nika Layzell27726662017-10-24 23:16:35 -0400739
David Tolnay6af48992018-08-01 11:16:28 -0700740 let borrowed_binding = Borrowed(quote!(#binding));
741 let owned_binding = Owned(quote!(#binding));
Nika Layzell27726662017-10-24 23:16:35 -0400742
David Tolnay6af48992018-08-01 11:16:28 -0700743 visit_fields.append_all(
744 visit(&field.ty, lookup, Visit, &borrowed_binding)
745 .unwrap_or_else(|| noop_visit(Visit, &borrowed_binding)),
746 );
747 visit_mut_fields.append_all(
748 visit(&field.ty, lookup, VisitMut, &borrowed_binding)
749 .unwrap_or_else(|| noop_visit(VisitMut, &borrowed_binding)),
750 );
751 fold_fields.append_all(
752 visit(&field.ty, lookup, Fold, &owned_binding)
753 .unwrap_or_else(|| noop_visit(Fold, &owned_binding)),
754 );
Nika Layzell27726662017-10-24 23:16:35 -0400755
David Tolnay6af48992018-08-01 11:16:28 -0700756 visit_fields.append_all(quote!(;));
757 visit_mut_fields.append_all(quote!(;));
758 fold_fields.append_all(quote!(,));
759 }
Nika Layzell27726662017-10-24 23:16:35 -0400760
David Tolnay6af48992018-08-01 11:16:28 -0700761 visit_variants.append_all(quote! {
762 #ty::#variant_ident(#bind_visit_fields) => {
763 #visit_fields
764 }
765 });
766
767 visit_mut_variants.append_all(quote! {
768 #ty::#variant_ident(#bind_visit_mut_fields) => {
769 #visit_mut_fields
770 }
771 });
772
773 fold_variants.append_all(quote! {
774 #ty::#variant_ident(#bind_fold_fields) => {
775 #ty::#variant_ident(
776 #fold_fields
777 )
778 }
779 });
Nika Layzell27726662017-10-24 23:16:35 -0400780 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500781 Fields::Unit => {
David Tolnay6af48992018-08-01 11:16:28 -0700782 visit_variants.append_all(quote! {
783 #ty::#variant_ident => {}
784 });
785 visit_mut_variants.append_all(quote! {
786 #ty::#variant_ident => {}
787 });
788 fold_variants.append_all(quote! {
789 #ty::#variant_ident => {
790 #ty::#variant_ident
791 }
792 });
Nika Layzell27726662017-10-24 23:16:35 -0400793 }
Nika Layzell27726662017-10-24 23:16:35 -0400794 }
Nika Layzell27726662017-10-24 23:16:35 -0400795 }
David Tolnay6af48992018-08-01 11:16:28 -0700796
797 visit_impl.append_all(quote! {
798 match *_i {
799 #visit_variants
800 }
801 });
802
803 visit_mut_impl.append_all(quote! {
804 match *_i {
805 #visit_mut_variants
806 }
807 });
808
809 fold_impl.append_all(quote! {
810 match _i {
811 #fold_variants
812 }
813 });
Nika Layzell27726662017-10-24 23:16:35 -0400814 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500815 Data::Struct(ref v) => {
David Tolnay6af48992018-08-01 11:16:28 -0700816 let mut fold_fields = TokenStream::new();
817
David Tolnaybc5b2c02018-08-02 00:18:23 -0700818 for (idx, field) in v.fields.iter().enumerate() {
819 let id = match field.ident {
820 Some(ref ident) => Member::Named(ident.clone()),
821 None => Member::Unnamed(Index::from(idx)),
822 };
823 let ref_toks = Owned(quote!(_i.#id));
David Tolnay6af48992018-08-01 11:16:28 -0700824 let visit_field = visit(&field.ty, lookup, Visit, &ref_toks)
825 .unwrap_or_else(|| noop_visit(Visit, &ref_toks));
826 visit_impl.append_all(quote! {
827 #visit_field;
828 });
829 let visit_mut_field = visit(&field.ty, lookup, VisitMut, &ref_toks)
830 .unwrap_or_else(|| noop_visit(VisitMut, &ref_toks));
831 visit_mut_impl.append_all(quote! {
832 #visit_mut_field;
833 });
David Tolnay83db9272017-12-28 17:02:31 -0500834 let fold = visit(&field.ty, lookup, Fold, &ref_toks)
David Tolnay01ed0ce2018-05-20 20:18:14 -0700835 .unwrap_or_else(|| noop_visit(Fold, &ref_toks));
Nika Layzell27726662017-10-24 23:16:35 -0400836 if let Some(ref name) = field.ident {
David Tolnay6af48992018-08-01 11:16:28 -0700837 fold_fields.append_all(quote! {
838 #name: #fold,
839 });
Nika Layzell27726662017-10-24 23:16:35 -0400840 } else {
David Tolnay6af48992018-08-01 11:16:28 -0700841 fold_fields.append_all(quote! {
842 #fold,
843 });
Nika Layzell27726662017-10-24 23:16:35 -0400844 }
845 }
846
David Tolnaye3d41b72017-12-31 15:24:00 -0500847 match v.fields {
David Tolnay6af48992018-08-01 11:16:28 -0700848 Fields::Named(..) | Fields::Unnamed(..) => fold_impl.append_all(quote! {
849 #ty {
850 #fold_fields
851 }
852 }),
853 Fields::Unit => {
854 if ty == "Ident" {
855 fold_impl.append_all(quote! {
856 let mut _i = _i;
857 let span = _visitor.fold_span(_i.span());
858 _i.set_span(span);
859 });
860 }
861 fold_impl.append_all(quote! {
862 _i
863 });
864 }
Nika Layzell27726662017-10-24 23:16:35 -0400865 };
866 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500867 Data::Union(..) => panic!("Union not supported"),
Nika Layzell27726662017-10-24 23:16:35 -0400868 }
869
David Tolnay6af48992018-08-01 11:16:28 -0700870 let mut include_fold_impl = true;
David Tolnay360efd22018-01-04 23:35:26 -0800871 if let Data::Struct(ref data) = s.ast.data {
872 if let Fields::Named(ref fields) = data.fields {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700873 if fields
874 .named
875 .iter()
876 .any(|field| field.vis == Visibility::Inherited)
877 {
David Tolnay360efd22018-01-04 23:35:26 -0800878 // Discard the generated impl if there are private fields.
879 // These have to be handwritten.
David Tolnay6af48992018-08-01 11:16:28 -0700880 include_fold_impl = false;
David Tolnay360efd22018-01-04 23:35:26 -0800881 }
882 }
David Tolnayd0adf522017-12-29 01:30:07 -0500883 }
David Tolnay6af48992018-08-01 11:16:28 -0700884
885 state.visit_trait.append_all(quote! {
886 #features
887 fn #visit_fn(&mut self, i: &'ast #ty) {
888 #visit_fn(self, i)
889 }
890 });
891
892 state.visit_impl.append_all(quote! {
893 #features
894 pub fn #visit_fn<'ast, V: Visit<'ast> + ?Sized>(
895 _visitor: &mut V, _i: &'ast #ty
896 ) {
897 #visit_impl
898 }
899 });
900
901 state.visit_mut_trait.append_all(quote! {
902 #features
903 fn #visit_mut_fn(&mut self, i: &mut #ty) {
904 #visit_mut_fn(self, i)
905 }
906 });
907
908 state.visit_mut_impl.append_all(quote! {
909 #features
910 pub fn #visit_mut_fn<V: VisitMut + ?Sized>(
911 _visitor: &mut V, _i: &mut #ty
912 ) {
913 #visit_mut_impl
914 }
915 });
916
917 state.fold_trait.append_all(quote! {
918 #features
919 fn #fold_fn(&mut self, i: #ty) -> #ty {
920 #fold_fn(self, i)
921 }
922 });
923
924 if include_fold_impl {
925 state.fold_impl.append_all(quote! {
926 #features
927 pub fn #fold_fn<V: Fold + ?Sized>(
928 _visitor: &mut V, _i: #ty
929 ) -> #ty {
930 #fold_impl
931 }
932 });
933 }
Nika Layzell27726662017-10-24 23:16:35 -0400934 }
935}
936
David Tolnay6af48992018-08-01 11:16:28 -0700937fn write_file(path: &str, content: TokenStream) {
David Tolnay8c81f622018-07-31 23:34:35 -0700938 let mut file = File::create(path).unwrap();
David Tolnay6af48992018-08-01 11:16:28 -0700939 write!(
940 file,
941 "// THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT\n\n"
942 ).unwrap();
David Tolnay8c81f622018-07-31 23:34:35 -0700943 let mut config = rustfmt::Config::default();
944 config.set().emit_mode(rustfmt::EmitMode::Stdout);
945 config.set().verbose(rustfmt::Verbosity::Quiet);
David Tolnay280202f2018-08-02 00:29:54 -0700946 config.set().format_macro_matchers(true);
David Tolnay8c81f622018-07-31 23:34:35 -0700947 let mut session = rustfmt::Session::new(config, Some(&mut file));
David Tolnay6af48992018-08-01 11:16:28 -0700948 session
949 .format(rustfmt::Input::Text(content.to_string()))
950 .unwrap();
David Tolnay8c81f622018-07-31 23:34:35 -0700951}
952
Nika Layzell27726662017-10-24 23:16:35 -0400953fn main() {
954 let mut lookup = BTreeMap::new();
David Tolnayea9ae892017-12-26 01:44:32 -0500955 load_file(SYN_CRATE_ROOT, &quote!(), &mut lookup).unwrap();
Nika Layzell27726662017-10-24 23:16:35 -0400956
Nika Layzellefb83ba2017-12-19 18:23:55 -0500957 // Load in any terminal types
958 for &tt in TERMINAL_TYPES {
959 use syn::*;
David Tolnayd67fb752017-12-27 13:50:29 -0500960 lookup.insert(
Alex Crichtona74a1c82018-05-16 10:20:44 -0700961 Ident::new(&tt, Span::call_site()),
David Tolnayd67fb752017-12-27 13:50:29 -0500962 AstItem {
David Tolnay3d772182017-12-28 17:18:53 -0500963 ast: DeriveInput {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700964 ident: Ident::new(tt, Span::call_site()),
David Tolnayd67fb752017-12-27 13:50:29 -0500965 vis: Visibility::Public(VisPublic {
David Tolnay6af48992018-08-01 11:16:28 -0700966 pub_token: <Token![pub]>::default(),
David Tolnayd67fb752017-12-27 13:50:29 -0500967 }),
968 attrs: vec![],
David Tolnay6af48992018-08-01 11:16:28 -0700969 generics: Generics::default(),
David Tolnaye3d41b72017-12-31 15:24:00 -0500970 data: Data::Struct(DataStruct {
971 fields: Fields::Unit,
David Tolnay6af48992018-08-01 11:16:28 -0700972 struct_token: <Token![struct]>::default(),
David Tolnayd67fb752017-12-27 13:50:29 -0500973 semi_token: None,
974 }),
975 },
hcplaa511792018-05-29 07:13:01 +0300976 features: TokenStream::new(),
David Tolnayd67fb752017-12-27 13:50:29 -0500977 eos_full: false,
Nika Layzellefb83ba2017-12-19 18:23:55 -0500978 },
David Tolnayd67fb752017-12-27 13:50:29 -0500979 );
Nika Layzellefb83ba2017-12-19 18:23:55 -0500980 }
981
David Tolnay6af48992018-08-01 11:16:28 -0700982 let mut state = codegen::State::default();
Nika Layzell27726662017-10-24 23:16:35 -0400983 for s in lookup.values() {
984 codegen::generate(&mut state, &lookup, s);
985 }
986
David Tolnay6af48992018-08-01 11:16:28 -0700987 let full_macro = quote! {
988 #[cfg(feature = "full")]
989 macro_rules! full {
990 ($e:expr) => {
991 $e
992 };
993 }
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400994
David Tolnay6af48992018-08-01 11:16:28 -0700995 #[cfg(all(feature = "derive", not(feature = "full")))]
996 macro_rules! full {
997 ($e:expr) => {
998 unreachable!()
999 };
1000 }
1001 };
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001002
David Tolnay6af48992018-08-01 11:16:28 -07001003 let skip_macro = quote! {
1004 #[cfg(any(feature = "full", feature = "derive"))]
1005 macro_rules! skip {
1006 ($($tt:tt)*) => {};
1007 }
1008 };
1009
1010 let fold_trait = state.fold_trait;
1011 let fold_impl = state.fold_impl;
David Tolnayae0009e2018-08-01 00:40:00 -07001012 write_file(
1013 FOLD_SRC,
David Tolnay6af48992018-08-01 11:16:28 -07001014 quote! {
1015 // Unreachable code is generated sometimes without the full feature.
1016 #![allow(unreachable_code)]
1017 #![cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))]
Nika Layzell27726662017-10-24 23:16:35 -04001018
David Tolnay6af48992018-08-01 11:16:28 -07001019 use *;
1020 #[cfg(any(feature = "full", feature = "derive"))]
1021 use token::{Brace, Bracket, Paren, Group};
1022 use proc_macro2::Span;
1023 #[cfg(any(feature = "full", feature = "derive"))]
1024 use gen::helper::fold::*;
Nika Layzell27726662017-10-24 23:16:35 -04001025
David Tolnay6af48992018-08-01 11:16:28 -07001026 #full_macro
Nika Layzell27726662017-10-24 23:16:35 -04001027
David Tolnay6af48992018-08-01 11:16:28 -07001028 /// Syntax tree traversal to transform the nodes of an owned syntax tree.
1029 ///
1030 /// See the [module documentation] for details.
1031 ///
1032 /// [module documentation]: index.html
1033 ///
1034 /// *This trait is available if Syn is built with the `"fold"` feature.*
1035 pub trait Fold {
1036 #fold_trait
1037 }
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001038
David Tolnay6af48992018-08-01 11:16:28 -07001039 #[cfg(any(feature = "full", feature = "derive"))]
1040 macro_rules! fold_span_only {
1041 ($f:ident : $t:ident) => {
1042 pub fn $f<V: Fold + ?Sized>(_visitor: &mut V, mut _i: $t) -> $t {
1043 let span = _visitor.fold_span(_i.span());
1044 _i.set_span(span);
1045 _i
1046 }
1047 }
1048 }
Nika Layzell27726662017-10-24 23:16:35 -04001049
David Tolnay6af48992018-08-01 11:16:28 -07001050 #[cfg(any(feature = "full", feature = "derive"))]
1051 fold_span_only!(fold_lit_byte: LitByte);
1052 #[cfg(any(feature = "full", feature = "derive"))]
1053 fold_span_only!(fold_lit_byte_str: LitByteStr);
1054 #[cfg(any(feature = "full", feature = "derive"))]
1055 fold_span_only!(fold_lit_char: LitChar);
1056 #[cfg(any(feature = "full", feature = "derive"))]
1057 fold_span_only!(fold_lit_float: LitFloat);
1058 #[cfg(any(feature = "full", feature = "derive"))]
1059 fold_span_only!(fold_lit_int: LitInt);
1060 #[cfg(any(feature = "full", feature = "derive"))]
1061 fold_span_only!(fold_lit_str: LitStr);
David Tolnayd0adf522017-12-29 01:30:07 -05001062
David Tolnay6af48992018-08-01 11:16:28 -07001063 #fold_impl
1064 },
David Tolnayae0009e2018-08-01 00:40:00 -07001065 );
Nika Layzell27726662017-10-24 23:16:35 -04001066
David Tolnay6af48992018-08-01 11:16:28 -07001067 let visit_trait = state.visit_trait;
1068 let visit_impl = state.visit_impl;
David Tolnayae0009e2018-08-01 00:40:00 -07001069 write_file(
1070 VISIT_SRC,
David Tolnay6af48992018-08-01 11:16:28 -07001071 quote! {
1072 #![cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
Nika Layzell27726662017-10-24 23:16:35 -04001073
David Tolnay6af48992018-08-01 11:16:28 -07001074 use *;
1075 #[cfg(any(feature = "full", feature = "derive"))]
1076 use punctuated::Punctuated;
1077 use proc_macro2::Span;
1078 #[cfg(any(feature = "full", feature = "derive"))]
1079 use gen::helper::visit::*;
David Tolnayf0d63bf2017-12-26 12:29:47 -05001080
David Tolnay6af48992018-08-01 11:16:28 -07001081 #full_macro
1082 #skip_macro
Nika Layzell27726662017-10-24 23:16:35 -04001083
David Tolnay6af48992018-08-01 11:16:28 -07001084 /// Syntax tree traversal to walk a shared borrow of a syntax tree.
1085 ///
1086 /// See the [module documentation] for details.
1087 ///
1088 /// [module documentation]: index.html
1089 ///
1090 /// *This trait is available if Syn is built with the `"visit"` feature.*
1091 pub trait Visit<'ast> {
1092 #visit_trait
1093 }
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001094
David Tolnay6af48992018-08-01 11:16:28 -07001095 #visit_impl
1096 },
David Tolnayae0009e2018-08-01 00:40:00 -07001097 );
Nika Layzell27726662017-10-24 23:16:35 -04001098
David Tolnay6af48992018-08-01 11:16:28 -07001099 let visit_mut_trait = state.visit_mut_trait;
1100 let visit_mut_impl = state.visit_mut_impl;
David Tolnayae0009e2018-08-01 00:40:00 -07001101 write_file(
1102 VISIT_MUT_SRC,
David Tolnay6af48992018-08-01 11:16:28 -07001103 quote! {
1104 #![cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
Nika Layzell27726662017-10-24 23:16:35 -04001105
David Tolnay6af48992018-08-01 11:16:28 -07001106 use *;
1107 #[cfg(any(feature = "full", feature = "derive"))]
1108 use punctuated::Punctuated;
1109 use proc_macro2::Span;
1110 #[cfg(any(feature = "full", feature = "derive"))]
1111 use gen::helper::visit_mut::*;
David Tolnayf0d63bf2017-12-26 12:29:47 -05001112
David Tolnay6af48992018-08-01 11:16:28 -07001113 #full_macro
1114 #skip_macro
Nika Layzell27726662017-10-24 23:16:35 -04001115
David Tolnay6af48992018-08-01 11:16:28 -07001116 /// Syntax tree traversal to mutate an exclusive borrow of a syntax tree in
1117 /// place.
1118 ///
1119 /// See the [module documentation] for details.
1120 ///
1121 /// [module documentation]: index.html
1122 ///
1123 /// *This trait is available if Syn is built with the `"visit-mut"` feature.*
1124 pub trait VisitMut {
1125 #visit_mut_trait
1126 }
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001127
David Tolnay6af48992018-08-01 11:16:28 -07001128 #visit_mut_impl
1129 },
David Tolnayae0009e2018-08-01 00:40:00 -07001130 );
Nika Layzell27726662017-10-24 23:16:35 -04001131}