blob: c7b2a87c9262bf26fdd224a5d35763171d8e3a88 [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 Tolnayc8d3e4a2018-11-21 01:36:58 -080014#![allow(
15 clippy::needless_pass_by_value,
16 clippy::redundant_closure,
17 clippy::write_with_newline,
David Tolnay6b46a702018-08-01 23:51:32 -070018)]
David Tolnayea9ae892017-12-26 01:44:32 -050019
David Tolnayd67fb752017-12-27 13:50:29 -050020#[macro_use]
21extern crate failure;
Nika Layzell27726662017-10-24 23:16:35 -040022extern crate inflections;
David Tolnay2e0dba12017-12-27 01:54:40 -050023extern crate proc_macro2;
David Tolnayd67fb752017-12-27 13:50:29 -050024#[macro_use]
25extern crate quote;
David Tolnay5c4c0b52017-12-28 17:58:54 -050026#[macro_use]
David Tolnayd67fb752017-12-27 13:50:29 -050027extern crate syn;
David Tolnay8c81f622018-07-31 23:34:35 -070028extern crate rustfmt_nightly as rustfmt;
Nika Layzell27726662017-10-24 23:16:35 -040029
David Tolnayd67fb752017-12-27 13:50:29 -050030use failure::{err_msg, Error};
David Tolnaye303b7c2018-05-20 16:46:35 -070031use proc_macro2::{Span, TokenStream};
David Tolnay01ed0ce2018-05-20 20:18:14 -070032use quote::ToTokens;
33use syn::{Attribute, Data, DataStruct, DeriveInput, Ident, Item};
Nika Layzell27726662017-10-24 23:16:35 -040034
David Tolnay01ed0ce2018-05-20 20:18:14 -070035use std::collections::BTreeMap;
David Tolnayf0d63bf2017-12-26 12:29:47 -050036use std::fmt::{self, Debug};
Nika Layzell27726662017-10-24 23:16:35 -040037use std::fs::File;
David Tolnay6af48992018-08-01 11:16:28 -070038use std::io::{Read, Write};
Nika Layzell27726662017-10-24 23:16:35 -040039use std::path::Path;
Nika Layzell27726662017-10-24 23:16:35 -040040
41const SYN_CRATE_ROOT: &str = "../src/lib.rs";
42
43const FOLD_SRC: &str = "../src/gen/fold.rs";
44const VISIT_SRC: &str = "../src/gen/visit.rs";
45const VISIT_MUT_SRC: &str = "../src/gen/visit_mut.rs";
46
David Tolnayd67fb752017-12-27 13:50:29 -050047const IGNORED_MODS: &[&str] = &["fold", "visit", "visit_mut"];
Nika Layzell27726662017-10-24 23:16:35 -040048
Alex Crichton131308c2018-05-18 14:00:24 -070049const EXTRA_TYPES: &[&str] = &["Lifetime"];
David Tolnay4ba63a02017-12-28 15:53:05 -050050
Alex Crichtond261d092018-05-18 13:47:35 -070051const TERMINAL_TYPES: &[&str] = &["Span", "Ident"];
Nika Layzellefb83ba2017-12-19 18:23:55 -050052
Alex Crichtona74a1c82018-05-16 10:20:44 -070053fn path_eq(a: &syn::Path, b: &str) -> bool {
David Tolnay78b16142018-09-01 16:53:07 -070054 a.leading_colon.is_none()
55 && a.segments.len() == 1
56 && a.segments[0].ident == b
Nika Layzell27726662017-10-24 23:16:35 -040057}
58
Alex Crichton715862b2018-05-17 12:31:49 -070059fn get_features(attrs: &[Attribute], mut features: TokenStream) -> TokenStream {
Nika Layzell27726662017-10-24 23:16:35 -040060 for attr in attrs {
Alex Crichtona74a1c82018-05-16 10:20:44 -070061 if path_eq(&attr.path, "cfg") {
Nika Layzell27726662017-10-24 23:16:35 -040062 attr.to_tokens(&mut features);
63 }
64 }
65 features
66}
67
68#[derive(Clone)]
69pub struct AstItem {
David Tolnay3d772182017-12-28 17:18:53 -050070 ast: DeriveInput,
Alex Crichton715862b2018-05-17 12:31:49 -070071 features: TokenStream,
Nika Layzell27726662017-10-24 23:16:35 -040072 // True if this is an ast_enum_of_structs! item with a #full annotation.
73 eos_full: bool,
74}
75
David Tolnayf0d63bf2017-12-26 12:29:47 -050076impl Debug for AstItem {
Nika Layzell27726662017-10-24 23:16:35 -040077 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
78 f.debug_struct("AstItem")
David Tolnay3d772182017-12-28 17:18:53 -050079 .field("ast", &self.ast)
Nika Layzell27726662017-10-24 23:16:35 -040080 .field("features", &self.features.to_string())
81 .finish()
82 }
83}
84
85// NOTE: BTreeMap is used here instead of HashMap to have deterministic output.
David Tolnayc47f8422017-12-28 17:30:46 -050086type Lookup = BTreeMap<Ident, AstItem>;
Nika Layzell27726662017-10-24 23:16:35 -040087
David Tolnay01ed0ce2018-05-20 20:18:14 -070088fn load_file<P: AsRef<Path>>(
89 name: P,
90 features: &TokenStream,
91 lookup: &mut Lookup,
92) -> Result<(), Error> {
Nika Layzell27726662017-10-24 23:16:35 -040093 let name = name.as_ref();
David Tolnayea9ae892017-12-26 01:44:32 -050094 let parent = name.parent().ok_or_else(|| err_msg("no parent path"))?;
Nika Layzell27726662017-10-24 23:16:35 -040095
96 let mut f = File::open(name)?;
97 let mut src = String::new();
98 f.read_to_string(&mut src)?;
99
100 // Parse the file
David Tolnayd67fb752017-12-27 13:50:29 -0500101 let file =
102 syn::parse_file(&src).map_err(|_| format_err!("failed to parse {}", name.display()))?;
Nika Layzell27726662017-10-24 23:16:35 -0400103
104 // Collect all of the interesting AstItems declared in this file or submodules.
David Tolnay4ba63a02017-12-28 15:53:05 -0500105 'items: for item in file.items {
106 match item {
107 Item::Mod(item) => {
Nika Layzell27726662017-10-24 23:16:35 -0400108 // Don't inspect inline modules.
David Tolnayc6b55bc2017-11-09 22:48:38 -0800109 if item.content.is_some() {
Nika Layzell27726662017-10-24 23:16:35 -0400110 continue;
111 }
112
113 // We don't want to try to load the generated rust files and
114 // parse them, so we ignore them here.
115 for name in IGNORED_MODS {
David Tolnay446f7d62018-05-20 17:58:15 -0700116 if item.ident == name {
Nika Layzell27726662017-10-24 23:16:35 -0400117 continue 'items;
118 }
119 }
120
121 // Lookup any #[cfg()] attributes on the module and add them to
122 // the feature set.
David Tolnay0a0d78c2018-01-05 15:24:01 -0800123 //
124 // The derive module is weird because it is built with either
125 // `full` or `derive` but exported only under `derive`.
David Tolnay446f7d62018-05-20 17:58:15 -0700126 let features = if item.ident == "derive" {
David Tolnay0a0d78c2018-01-05 15:24:01 -0800127 quote!(#[cfg(feature = "derive")])
128 } else {
129 get_features(&item.attrs, features.clone())
130 };
Nika Layzell27726662017-10-24 23:16:35 -0400131
132 // Look up the submodule file, and recursively parse it.
133 // XXX: Only handles same-directory .rs file submodules.
Alex Crichtona74a1c82018-05-16 10:20:44 -0700134 let path = parent.join(&format!("{}.rs", item.ident));
David Tolnayea9ae892017-12-26 01:44:32 -0500135 load_file(path, &features, lookup)?;
Nika Layzell27726662017-10-24 23:16:35 -0400136 }
David Tolnay4ba63a02017-12-28 15:53:05 -0500137 Item::Macro(item) => {
Nika Layzell27726662017-10-24 23:16:35 -0400138 // Lookip any #[cfg()] attributes directly on the macro
139 // invocation, and add them to the feature set.
140 let features = get_features(&item.attrs, features.clone());
141
142 // Try to parse the AstItem declaration out of the item.
David Tolnay01a77582018-01-01 20:00:51 -0800143 let tts = &item.mac.tts;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700144 let found = if path_eq(&item.mac.path, "ast_struct") {
David Tolnay64f03842018-08-30 21:34:40 -0700145 syn::parse2::<parsing::AstStruct>(quote!(#tts))
David Tolnayd67fb752017-12-27 13:50:29 -0500146 .map_err(|_| err_msg("failed to parse ast_struct"))?
147 .0
Alex Crichtona74a1c82018-05-16 10:20:44 -0700148 } else if path_eq(&item.mac.path, "ast_enum") {
David Tolnay64f03842018-08-30 21:34:40 -0700149 syn::parse2::<parsing::AstEnum>(quote!(#tts))
David Tolnayd67fb752017-12-27 13:50:29 -0500150 .map_err(|_| err_msg("failed to parse ast_enum"))?
151 .0
Alex Crichtona74a1c82018-05-16 10:20:44 -0700152 } else if path_eq(&item.mac.path, "ast_enum_of_structs") {
David Tolnay64f03842018-08-30 21:34:40 -0700153 syn::parse2::<parsing::AstEnumOfStructs>(quote!(#tts))
David Tolnay1cf80912017-12-31 18:35:12 -0500154 .map_err(|_| err_msg("failed to parse ast_enum_of_structs"))?
David Tolnayd67fb752017-12-27 13:50:29 -0500155 .0
Nika Layzell27726662017-10-24 23:16:35 -0400156 } else {
David Tolnayd67fb752017-12-27 13:50:29 -0500157 continue;
Nika Layzell27726662017-10-24 23:16:35 -0400158 };
159
160 // Record our features on the parsed AstItems.
161 for mut item in found {
162 features.to_tokens(&mut item.features);
Alex Crichtona74a1c82018-05-16 10:20:44 -0700163 lookup.insert(item.ast.ident.clone(), item);
Nika Layzell27726662017-10-24 23:16:35 -0400164 }
165 }
David Tolnay4ba63a02017-12-28 15:53:05 -0500166 Item::Struct(item) => {
167 let ident = item.ident;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700168 if EXTRA_TYPES.contains(&&ident.to_string()[..]) {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700169 lookup.insert(
170 ident.clone(),
171 AstItem {
172 ast: DeriveInput {
David Tolnay6b46a702018-08-01 23:51:32 -0700173 ident,
David Tolnay01ed0ce2018-05-20 20:18:14 -0700174 vis: item.vis,
175 attrs: item.attrs,
176 generics: item.generics,
177 data: Data::Struct(DataStruct {
178 fields: item.fields,
179 struct_token: item.struct_token,
180 semi_token: item.semi_token,
181 }),
182 },
183 features: features.clone(),
184 eos_full: false,
David Tolnay4ba63a02017-12-28 15:53:05 -0500185 },
David Tolnay01ed0ce2018-05-20 20:18:14 -0700186 );
David Tolnay4ba63a02017-12-28 15:53:05 -0500187 }
188 }
Nika Layzell27726662017-10-24 23:16:35 -0400189 _ => {}
190 }
191 }
192 Ok(())
193}
194
195mod parsing {
196 use super::AstItem;
197
David Tolnay01ed0ce2018-05-20 20:18:14 -0700198 use proc_macro2::TokenStream;
David Tolnay1cf80912017-12-31 18:35:12 -0500199 use syn;
David Tolnay64f03842018-08-30 21:34:40 -0700200 use syn::parse::{Parse, ParseStream, Result};
Nika Layzell27726662017-10-24 23:16:35 -0400201 use syn::*;
Nika Layzell27726662017-10-24 23:16:35 -0400202
David Tolnay64f03842018-08-30 21:34:40 -0700203 fn peek_tag(input: ParseStream, tag: &str) -> bool {
204 let ahead = input.fork();
David Tolnay86cb9452018-08-31 10:35:09 -0700205 ahead.parse::<Token![#]>().is_ok() && ahead
206 .parse::<Ident>()
207 .map(|ident| ident == tag)
208 .unwrap_or(false)
David Tolnay64f03842018-08-30 21:34:40 -0700209 }
210
Nika Layzell27726662017-10-24 23:16:35 -0400211 // Parses #full - returns #[cfg(feature = "full")] if it is present, and
212 // nothing otherwise.
David Tolnay64f03842018-08-30 21:34:40 -0700213 fn full(input: ParseStream) -> (TokenStream, bool) {
214 if peek_tag(input, "full") {
215 input.parse::<Token![#]>().unwrap();
216 input.parse::<Ident>().unwrap();
217 (quote!(#[cfg(feature = "full")]), true)
218 } else {
219 (quote!(), false)
220 }
221 }
Nika Layzell27726662017-10-24 23:16:35 -0400222
David Tolnay64f03842018-08-30 21:34:40 -0700223 fn skip_manual_extra_traits(input: ParseStream) {
224 if peek_tag(input, "manual_extra_traits") {
225 input.parse::<Token![#]>().unwrap();
226 input.parse::<Ident>().unwrap();
227 }
228 }
David Tolnay28c5a462017-12-27 01:59:30 -0500229
Nika Layzell27726662017-10-24 23:16:35 -0400230 // Parses a simple AstStruct without the `pub struct` prefix.
David Tolnay64f03842018-08-30 21:34:40 -0700231 fn ast_struct_inner(input: ParseStream) -> Result<AstItem> {
232 let ident: Ident = input.parse()?;
233 let (features, eos_full) = full(input);
234 skip_manual_extra_traits(input);
235 let rest: TokenStream = input.parse()?;
236 Ok(AstItem {
237 ast: syn::parse2(quote! {
238 pub struct #ident #rest
239 })?,
240 features: features,
241 eos_full: eos_full,
Nika Layzell27726662017-10-24 23:16:35 -0400242 })
David Tolnay64f03842018-08-30 21:34:40 -0700243 }
Nika Layzell27726662017-10-24 23:16:35 -0400244
245 // ast_struct! parsing
246 pub struct AstStruct(pub Vec<AstItem>);
David Tolnay64f03842018-08-30 21:34:40 -0700247 impl Parse for AstStruct {
248 fn parse(input: ParseStream) -> Result<Self> {
249 input.call(Attribute::parse_outer)?;
250 input.parse::<Token![pub]>()?;
251 input.parse::<Token![struct]>()?;
252 let res = input.call(ast_struct_inner)?;
253 Ok(AstStruct(vec![res]))
254 }
Nika Layzell27726662017-10-24 23:16:35 -0400255 }
256
David Tolnay64f03842018-08-30 21:34:40 -0700257 fn no_visit(input: ParseStream) -> bool {
258 if peek_tag(input, "no_visit") {
259 input.parse::<Token![#]>().unwrap();
260 input.parse::<Ident>().unwrap();
261 true
262 } else {
263 false
264 }
265 }
David Tolnay360efd22018-01-04 23:35:26 -0800266
Nika Layzell27726662017-10-24 23:16:35 -0400267 // ast_enum! parsing
268 pub struct AstEnum(pub Vec<AstItem>);
David Tolnay64f03842018-08-30 21:34:40 -0700269 impl Parse for AstEnum {
270 fn parse(input: ParseStream) -> Result<Self> {
271 input.call(Attribute::parse_outer)?;
272 input.parse::<Token![pub]>()?;
273 input.parse::<Token![enum]>()?;
274 let ident: Ident = input.parse()?;
275 let no_visit = no_visit(input);
276 let rest: TokenStream = input.parse()?;
277 Ok(AstEnum(if no_visit {
David Tolnay360efd22018-01-04 23:35:26 -0800278 vec![]
279 } else {
280 vec![AstItem {
David Tolnay64f03842018-08-30 21:34:40 -0700281 ast: syn::parse2(quote! {
282 pub enum #ident #rest
283 })?,
David Tolnay360efd22018-01-04 23:35:26 -0800284 features: quote!(),
285 eos_full: false,
286 }]
287 }))
David Tolnay64f03842018-08-30 21:34:40 -0700288 }
Nika Layzell27726662017-10-24 23:16:35 -0400289 }
290
291 // A single variant of an ast_enum_of_structs!
292 struct EosVariant {
293 name: Ident,
David Tolnayfcfb9002017-12-28 22:04:29 -0500294 member: Option<Path>,
Nika Layzell27726662017-10-24 23:16:35 -0400295 inner: Option<AstItem>,
296 }
David Tolnay64f03842018-08-30 21:34:40 -0700297 fn eos_variant(input: ParseStream) -> Result<EosVariant> {
298 input.call(Attribute::parse_outer)?;
299 input.parse::<Token![pub]>()?;
300 let variant: Ident = input.parse()?;
301 let (member, inner) = if input.peek(token::Paren) {
302 let content;
303 parenthesized!(content in input);
304 if content.fork().call(ast_struct_inner).is_ok() {
305 let item = content.call(ast_struct_inner)?;
306 (Some(Path::from(item.ast.ident.clone())), Some(item))
307 } else {
308 let path: Path = content.parse()?;
309 (Some(path), None)
310 }
311 } else {
312 (None, None)
313 };
314 input.parse::<Token![,]>()?;
315 Ok(EosVariant {
Nika Layzell27726662017-10-24 23:16:35 -0400316 name: variant,
David Tolnay64f03842018-08-30 21:34:40 -0700317 member: member,
318 inner: inner,
Nika Layzell27726662017-10-24 23:16:35 -0400319 })
David Tolnay64f03842018-08-30 21:34:40 -0700320 }
Nika Layzell27726662017-10-24 23:16:35 -0400321
322 // ast_enum_of_structs! parsing
323 pub struct AstEnumOfStructs(pub Vec<AstItem>);
David Tolnay64f03842018-08-30 21:34:40 -0700324 impl Parse for AstEnumOfStructs {
325 fn parse(input: ParseStream) -> Result<Self> {
326 input.call(Attribute::parse_outer)?;
327 input.parse::<Token![pub]>()?;
328 input.parse::<Token![enum]>()?;
329 let ident: Ident = input.parse()?;
330
331 let content;
332 braced!(content in input);
333 let mut variants = Vec::new();
334 while !content.is_empty() {
335 variants.push(content.call(eos_variant)?);
336 }
337
338 if let Some(ident) = input.parse::<Option<Ident>>()? {
339 assert_eq!(ident, "do_not_generate_to_tokens");
340 }
341
342 let enum_item = {
343 let variants = variants.iter().map(|v| {
344 let name = v.name.clone();
345 match v.member {
346 Some(ref member) => quote!(#name(#member)),
347 None => quote!(#name),
David Tolnayb7ccc4f2018-08-02 00:41:32 -0700348 }
David Tolnay64f03842018-08-30 21:34:40 -0700349 });
350 parse_quote! {
351 pub enum #ident {
352 #(#variants),*
353 }
354 }
355 };
356 let mut items = vec![AstItem {
357 ast: enum_item,
358 features: quote!(),
David Tolnay86cb9452018-08-31 10:35:09 -0700359 eos_full: false,
David Tolnay64f03842018-08-30 21:34:40 -0700360 }];
361 items.extend(variants.into_iter().filter_map(|v| v.inner));
362 Ok(AstEnumOfStructs(items))
363 }
Nika Layzell27726662017-10-24 23:16:35 -0400364 }
365}
366
367mod codegen {
368 use super::{AstItem, Lookup};
David Tolnay6b46a702018-08-01 23:51:32 -0700369 use inflections::Inflect;
David Tolnay01ed0ce2018-05-20 20:18:14 -0700370 use proc_macro2::{Span, TokenStream};
David Tolnay6af48992018-08-01 11:16:28 -0700371 use quote::{ToTokens, TokenStreamExt};
David Tolnay86cb9452018-08-31 10:35:09 -0700372 use syn::ext::IdentExt;
373 use syn::parse::Parser;
David Tolnay01ed0ce2018-05-20 20:18:14 -0700374 use syn::punctuated::Punctuated;
375 use syn::*;
Nika Layzell27726662017-10-24 23:16:35 -0400376
377 #[derive(Default)]
378 pub struct State {
David Tolnay6af48992018-08-01 11:16:28 -0700379 pub visit_trait: TokenStream,
380 pub visit_impl: TokenStream,
381 pub visit_mut_trait: TokenStream,
382 pub visit_mut_impl: TokenStream,
383 pub fold_trait: TokenStream,
384 pub fold_impl: TokenStream,
Nika Layzell27726662017-10-24 23:16:35 -0400385 }
386
David Tolnay4a918742017-12-28 16:54:41 -0500387 fn under_name(name: Ident) -> Ident {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700388 Ident::new(&name.to_string().to_snake_case(), Span::call_site())
Nika Layzell27726662017-10-24 23:16:35 -0400389 }
390
David Tolnay3d772182017-12-28 17:18:53 -0500391 enum RelevantType<'a> {
392 Box(&'a Type),
393 Vec(&'a Type),
David Tolnayf2cfd722017-12-31 18:02:51 -0500394 Punctuated(&'a Type),
David Tolnay3d772182017-12-28 17:18:53 -0500395 Option(&'a Type),
David Tolnayf2cfd722017-12-31 18:02:51 -0500396 Tuple(&'a Punctuated<Type, Token![,]>),
David Tolnay3d772182017-12-28 17:18:53 -0500397 Simple(&'a AstItem),
David Tolnay7ac699c2018-08-24 14:00:58 -0400398 TokenPunct(TokenStream),
399 TokenKeyword(TokenStream),
400 TokenGroup(Ident),
David Tolnay3d772182017-12-28 17:18:53 -0500401 Pass,
402 }
Nika Layzell27726662017-10-24 23:16:35 -0400403
David Tolnay3d772182017-12-28 17:18:53 -0500404 fn classify<'a>(ty: &'a Type, lookup: &'a Lookup) -> RelevantType<'a> {
405 match *ty {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700406 Type::Path(TypePath {
407 qself: None,
408 ref path,
409 }) => {
David Tolnay56080682018-01-06 14:01:52 -0800410 let last = path.segments.last().unwrap().into_value();
Alex Crichtona74a1c82018-05-16 10:20:44 -0700411 match &last.ident.to_string()[..] {
David Tolnay3d772182017-12-28 17:18:53 -0500412 "Box" => RelevantType::Box(first_arg(&last.arguments)),
413 "Vec" => RelevantType::Vec(first_arg(&last.arguments)),
David Tolnayf2cfd722017-12-31 18:02:51 -0500414 "Punctuated" => RelevantType::Punctuated(first_arg(&last.arguments)),
David Tolnay3d772182017-12-28 17:18:53 -0500415 "Option" => RelevantType::Option(first_arg(&last.arguments)),
David Tolnay1e01f9c2017-12-28 20:16:19 -0500416 "Brace" | "Bracket" | "Paren" | "Group" => {
David Tolnay7ac699c2018-08-24 14:00:58 -0400417 RelevantType::TokenGroup(last.ident.clone())
David Tolnay1e01f9c2017-12-28 20:16:19 -0500418 }
David Tolnay3d772182017-12-28 17:18:53 -0500419 _ => {
420 if let Some(item) = lookup.get(&last.ident) {
421 RelevantType::Simple(item)
422 } else {
423 RelevantType::Pass
424 }
425 }
426 }
Nika Layzell27726662017-10-24 23:16:35 -0400427 }
David Tolnay01ed0ce2018-05-20 20:18:14 -0700428 Type::Tuple(TypeTuple { ref elems, .. }) => RelevantType::Tuple(elems),
429 Type::Macro(TypeMacro { ref mac })
430 if mac.path.segments.last().unwrap().into_value().ident == "Token" =>
431 {
David Tolnay86cb9452018-08-31 10:35:09 -0700432 let is_ident = Ident::parse_any.parse2(mac.tts.clone()).is_ok();
David Tolnay7ac699c2018-08-24 14:00:58 -0400433 let is_underscore = parse2::<Token![_]>(mac.tts.clone()).is_ok();
434 if is_ident && !is_underscore {
435 RelevantType::TokenKeyword(mac.into_token_stream())
436 } else {
437 RelevantType::TokenPunct(mac.into_token_stream())
438 }
David Tolnaycc0f0372017-12-28 19:11:04 -0500439 }
David Tolnay3d772182017-12-28 17:18:53 -0500440 _ => RelevantType::Pass,
Nika Layzell27726662017-10-24 23:16:35 -0400441 }
442 }
443
444 #[derive(Debug, Eq, PartialEq, Copy, Clone)]
445 enum Kind {
446 Visit,
447 VisitMut,
448 Fold,
449 }
450
David Tolnayf0d63bf2017-12-26 12:29:47 -0500451 enum Operand {
Alex Crichton715862b2018-05-17 12:31:49 -0700452 Borrowed(TokenStream),
453 Owned(TokenStream),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500454 }
455
David Tolnay83db9272017-12-28 17:02:31 -0500456 use self::Kind::*;
David Tolnay01ed0ce2018-05-20 20:18:14 -0700457 use self::Operand::*;
David Tolnay83db9272017-12-28 17:02:31 -0500458
David Tolnayf0d63bf2017-12-26 12:29:47 -0500459 impl Operand {
Alex Crichton715862b2018-05-17 12:31:49 -0700460 fn tokens(&self) -> &TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500461 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500462 Borrowed(ref n) | Owned(ref n) => n,
David Tolnayf0d63bf2017-12-26 12:29:47 -0500463 }
464 }
465
Alex Crichton715862b2018-05-17 12:31:49 -0700466 fn ref_tokens(&self) -> TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500467 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500468 Borrowed(ref n) => n.clone(),
469 Owned(ref n) => quote!(&#n),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500470 }
471 }
472
Alex Crichton715862b2018-05-17 12:31:49 -0700473 fn ref_mut_tokens(&self) -> TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500474 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500475 Borrowed(ref n) => n.clone(),
476 Owned(ref n) => quote!(&mut #n),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500477 }
478 }
479
Alex Crichton715862b2018-05-17 12:31:49 -0700480 fn owned_tokens(&self) -> TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500481 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500482 Borrowed(ref n) => quote!(*#n),
483 Owned(ref n) => n.clone(),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500484 }
485 }
486 }
487
Nika Layzellc08227a2017-12-04 16:30:17 -0500488 fn first_arg(params: &PathArguments) -> &Type {
Nika Layzell27726662017-10-24 23:16:35 -0400489 let data = match *params {
Nika Layzellc08227a2017-12-04 16:30:17 -0500490 PathArguments::AngleBracketed(ref data) => data,
491 _ => panic!("Expected at least 1 type argument here"),
Nika Layzell27726662017-10-24 23:16:35 -0400492 };
493
David Tolnay01ed0ce2018-05-20 20:18:14 -0700494 match **data
495 .args
David Tolnayd67fb752017-12-27 13:50:29 -0500496 .first()
497 .expect("Expected at least 1 type argument here")
David Tolnay56080682018-01-06 14:01:52 -0800498 .value()
David Tolnayd67fb752017-12-27 13:50:29 -0500499 {
David Tolnayea9ae892017-12-26 01:44:32 -0500500 GenericArgument::Type(ref ty) => ty,
Nika Layzellc08227a2017-12-04 16:30:17 -0500501 _ => panic!("Expected at least 1 type argument here"),
Nika Layzell357885a2017-12-04 15:47:07 -0500502 }
Nika Layzell27726662017-10-24 23:16:35 -0400503 }
504
David Tolnay6af48992018-08-01 11:16:28 -0700505 fn simple_visit(item: &AstItem, kind: Kind, name: &Operand) -> TokenStream {
506 let ident = under_name(item.ast.ident.clone());
507
David Tolnay4a918742017-12-28 16:54:41 -0500508 match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700509 Visit => {
510 let method = Ident::new(&format!("visit_{}", ident), Span::call_site());
511 let name = name.ref_tokens();
512 quote! {
513 _visitor.#method(#name)
514 }
515 }
516 VisitMut => {
517 let method = Ident::new(&format!("visit_{}_mut", ident), Span::call_site());
518 let name = name.ref_mut_tokens();
519 quote! {
520 _visitor.#method(#name)
521 }
522 }
523 Fold => {
524 let method = Ident::new(&format!("fold_{}", ident), Span::call_site());
525 let name = name.owned_tokens();
526 quote! {
527 _visitor.#method(#name)
528 }
529 }
Nika Layzell27726662017-10-24 23:16:35 -0400530 }
531 }
532
David Tolnay6af48992018-08-01 11:16:28 -0700533 fn box_visit(elem: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<TokenStream> {
David Tolnay4a918742017-12-28 16:54:41 -0500534 let name = name.owned_tokens();
David Tolnay39d0a202017-12-28 18:19:00 -0500535 let res = visit(elem, lookup, kind, &Owned(quote!(*#name)))?;
David Tolnay4a918742017-12-28 16:54:41 -0500536 Some(match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700537 Fold => quote! {
538 Box::new(#res)
539 },
David Tolnay83db9272017-12-28 17:02:31 -0500540 Visit | VisitMut => res,
David Tolnay4a918742017-12-28 16:54:41 -0500541 })
Nika Layzell27726662017-10-24 23:16:35 -0400542 }
543
David Tolnay6af48992018-08-01 11:16:28 -0700544 fn vec_visit(elem: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<TokenStream> {
David Tolnay4a918742017-12-28 16:54:41 -0500545 let operand = match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500546 Visit | VisitMut => Borrowed(quote!(it)),
547 Fold => Owned(quote!(it)),
David Tolnay4a918742017-12-28 16:54:41 -0500548 };
David Tolnay39d0a202017-12-28 18:19:00 -0500549 let val = visit(elem, lookup, kind, &operand)?;
David Tolnay4a918742017-12-28 16:54:41 -0500550 Some(match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700551 Visit => {
552 let name = name.ref_tokens();
553 quote! {
554 for it in #name {
555 #val
556 }
557 }
558 }
559 VisitMut => {
560 let name = name.ref_mut_tokens();
561 quote! {
562 for it in #name {
563 #val
564 }
565 }
566 }
567 Fold => {
568 let name = name.owned_tokens();
569 quote! {
570 FoldHelper::lift(#name, |it| { #val })
571 }
572 }
David Tolnay3d772182017-12-28 17:18:53 -0500573 })
574 }
575
David Tolnay6eff4da2018-01-01 20:27:45 -0800576 fn punctuated_visit(
David Tolnay3d772182017-12-28 17:18:53 -0500577 elem: &Type,
578 lookup: &Lookup,
579 kind: Kind,
580 name: &Operand,
David Tolnay6af48992018-08-01 11:16:28 -0700581 ) -> Option<TokenStream> {
David Tolnay3d772182017-12-28 17:18:53 -0500582 let operand = match kind {
583 Visit | VisitMut => Borrowed(quote!(it)),
584 Fold => Owned(quote!(it)),
585 };
David Tolnay39d0a202017-12-28 18:19:00 -0500586 let val = visit(elem, lookup, kind, &operand)?;
David Tolnay3d772182017-12-28 17:18:53 -0500587 Some(match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700588 Visit => {
589 let name = name.ref_tokens();
590 quote! {
591 for el in Punctuated::pairs(#name) {
592 let it = el.value();
593 #val
594 }
595 }
596 }
597 VisitMut => {
598 let name = name.ref_mut_tokens();
599 quote! {
600 for mut el in Punctuated::pairs_mut(#name) {
601 let it = el.value_mut();
602 #val
603 }
604 }
605 }
606 Fold => {
607 let name = name.owned_tokens();
608 quote! {
609 FoldHelper::lift(#name, |it| { #val })
610 }
611 }
David Tolnay4a918742017-12-28 16:54:41 -0500612 })
Nika Layzell27726662017-10-24 23:16:35 -0400613 }
614
David Tolnay6af48992018-08-01 11:16:28 -0700615 fn option_visit(
616 elem: &Type,
617 lookup: &Lookup,
618 kind: Kind,
619 name: &Operand,
620 ) -> Option<TokenStream> {
David Tolnay4a918742017-12-28 16:54:41 -0500621 let it = match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500622 Visit | VisitMut => Borrowed(quote!(it)),
623 Fold => Owned(quote!(it)),
David Tolnay4a918742017-12-28 16:54:41 -0500624 };
David Tolnay39d0a202017-12-28 18:19:00 -0500625 let val = visit(elem, lookup, kind, &it)?;
David Tolnay6af48992018-08-01 11:16:28 -0700626 let name = name.owned_tokens();
David Tolnay4a918742017-12-28 16:54:41 -0500627 Some(match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700628 Visit => quote! {
629 if let Some(ref it) = #name {
630 #val
631 }
632 },
633 VisitMut => quote! {
634 if let Some(ref mut it) = #name {
635 #val
636 }
637 },
638 Fold => quote! {
639 (#name).map(|it| { #val })
640 },
David Tolnay4a918742017-12-28 16:54:41 -0500641 })
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400642 }
643
David Tolnay5c4c0b52017-12-28 17:58:54 -0500644 fn tuple_visit(
David Tolnayf2cfd722017-12-31 18:02:51 -0500645 elems: &Punctuated<Type, Token![,]>,
David Tolnay5c4c0b52017-12-28 17:58:54 -0500646 lookup: &Lookup,
647 kind: Kind,
648 name: &Operand,
David Tolnay6af48992018-08-01 11:16:28 -0700649 ) -> Option<TokenStream> {
650 if elems.is_empty() {
651 return None;
652 }
653
654 let mut code = TokenStream::new();
David Tolnayf047c7a2017-12-31 02:29:04 -0500655 for (i, elem) in elems.iter().enumerate() {
David Tolnay5c4c0b52017-12-28 17:58:54 -0500656 let name = name.tokens();
David Tolnay14982012017-12-29 00:49:51 -0500657 let i = Index::from(i);
David Tolnay5c4c0b52017-12-28 17:58:54 -0500658 let it = Owned(quote!((#name).#i));
David Tolnay01ed0ce2018-05-20 20:18:14 -0700659 let val = visit(elem, lookup, kind, &it).unwrap_or_else(|| noop_visit(kind, &it));
David Tolnay6af48992018-08-01 11:16:28 -0700660 code.append_all(val);
David Tolnay5c4c0b52017-12-28 17:58:54 -0500661 match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700662 Fold => code.append_all(quote!(,)),
663 Visit | VisitMut => code.append_all(quote!(;)),
David Tolnay5c4c0b52017-12-28 17:58:54 -0500664 }
David Tolnay5c4c0b52017-12-28 17:58:54 -0500665 }
David Tolnay6af48992018-08-01 11:16:28 -0700666 Some(match kind {
667 Fold => quote! {
668 (#code)
669 },
670 Visit | VisitMut => code,
671 })
David Tolnay5c4c0b52017-12-28 17:58:54 -0500672 }
673
David Tolnay7ac699c2018-08-24 14:00:58 -0400674 fn token_punct_visit(ty: TokenStream, kind: Kind, name: &Operand) -> TokenStream {
David Tolnay6af48992018-08-01 11:16:28 -0700675 let name = name.tokens();
David Tolnaycc0f0372017-12-28 19:11:04 -0500676 match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700677 Fold => quote! {
David Tolnay7ac699c2018-08-24 14:00:58 -0400678 #ty(tokens_helper(_visitor, &#name.spans))
David Tolnay6af48992018-08-01 11:16:28 -0700679 },
680 Visit => quote! {
David Tolnay7ac699c2018-08-24 14:00:58 -0400681 tokens_helper(_visitor, &#name.spans)
David Tolnay6af48992018-08-01 11:16:28 -0700682 },
683 VisitMut => quote! {
David Tolnay7ac699c2018-08-24 14:00:58 -0400684 tokens_helper(_visitor, &mut #name.spans)
685 },
686 }
687 }
688
689 fn token_keyword_visit(ty: TokenStream, kind: Kind, name: &Operand) -> TokenStream {
690 let name = name.tokens();
691 match kind {
692 Fold => quote! {
693 #ty(tokens_helper(_visitor, &#name.span))
694 },
695 Visit => quote! {
696 tokens_helper(_visitor, &#name.span)
697 },
698 VisitMut => quote! {
699 tokens_helper(_visitor, &mut #name.span)
700 },
701 }
702 }
703
704 fn token_group_visit(ty: Ident, kind: Kind, name: &Operand) -> TokenStream {
705 let name = name.tokens();
706 match kind {
707 Fold => quote! {
708 #ty(tokens_helper(_visitor, &#name.span))
709 },
710 Visit => quote! {
711 tokens_helper(_visitor, &#name.span)
712 },
713 VisitMut => quote! {
714 tokens_helper(_visitor, &mut #name.span)
David Tolnay6af48992018-08-01 11:16:28 -0700715 },
David Tolnaycc0f0372017-12-28 19:11:04 -0500716 }
717 }
718
David Tolnay6af48992018-08-01 11:16:28 -0700719 fn noop_visit(kind: Kind, name: &Operand) -> TokenStream {
David Tolnay4a918742017-12-28 16:54:41 -0500720 match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700721 Fold => name.owned_tokens(),
722 Visit | VisitMut => {
723 let name = name.tokens();
724 quote! {
725 skip!(#name)
726 }
727 }
David Tolnay4a918742017-12-28 16:54:41 -0500728 }
729 }
730
David Tolnay6af48992018-08-01 11:16:28 -0700731 fn visit(ty: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<TokenStream> {
David Tolnay3d772182017-12-28 17:18:53 -0500732 match classify(ty, lookup) {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700733 RelevantType::Box(elem) => box_visit(elem, lookup, kind, name),
734 RelevantType::Vec(elem) => vec_visit(elem, lookup, kind, name),
735 RelevantType::Punctuated(elem) => punctuated_visit(elem, lookup, kind, name),
736 RelevantType::Option(elem) => option_visit(elem, lookup, kind, name),
737 RelevantType::Tuple(elems) => tuple_visit(elems, lookup, kind, name),
David Tolnay3d772182017-12-28 17:18:53 -0500738 RelevantType::Simple(item) => {
739 let mut res = simple_visit(item, kind, name);
740 Some(if item.eos_full {
David Tolnay6af48992018-08-01 11:16:28 -0700741 quote! {
742 full!(#res)
743 }
David Tolnay3d772182017-12-28 17:18:53 -0500744 } else {
745 res
746 })
747 }
David Tolnay7ac699c2018-08-24 14:00:58 -0400748 RelevantType::TokenPunct(ty) => Some(token_punct_visit(ty, kind, name)),
749 RelevantType::TokenKeyword(ty) => Some(token_keyword_visit(ty, kind, name)),
750 RelevantType::TokenGroup(ty) => Some(token_group_visit(ty, kind, name)),
David Tolnay01ed0ce2018-05-20 20:18:14 -0700751 RelevantType::Pass => None,
Nika Layzell27726662017-10-24 23:16:35 -0400752 }
Nika Layzell27726662017-10-24 23:16:35 -0400753 }
754
755 pub fn generate(state: &mut State, lookup: &Lookup, s: &AstItem) {
David Tolnay6af48992018-08-01 11:16:28 -0700756 let features = &s.features;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700757 let under_name = under_name(s.ast.ident.clone());
David Tolnay6af48992018-08-01 11:16:28 -0700758 let ty = &s.ast.ident;
759 let visit_fn = Ident::new(&format!("visit_{}", under_name), Span::call_site());
760 let visit_mut_fn = Ident::new(&format!("visit_{}_mut", under_name), Span::call_site());
761 let fold_fn = Ident::new(&format!("fold_{}", under_name), Span::call_site());
Nika Layzell27726662017-10-24 23:16:35 -0400762
David Tolnay6af48992018-08-01 11:16:28 -0700763 let mut visit_impl = TokenStream::new();
764 let mut visit_mut_impl = TokenStream::new();
765 let mut fold_impl = TokenStream::new();
Nika Layzell27726662017-10-24 23:16:35 -0400766
David Tolnaye3d41b72017-12-31 15:24:00 -0500767 match s.ast.data {
768 Data::Enum(ref e) => {
David Tolnay6af48992018-08-01 11:16:28 -0700769 let mut visit_variants = TokenStream::new();
770 let mut visit_mut_variants = TokenStream::new();
771 let mut fold_variants = TokenStream::new();
772
Nika Layzell27726662017-10-24 23:16:35 -0400773 for variant in &e.variants {
David Tolnay6af48992018-08-01 11:16:28 -0700774 let variant_ident = &variant.ident;
775
776 match variant.fields {
David Tolnaye3d41b72017-12-31 15:24:00 -0500777 Fields::Named(..) => panic!("Doesn't support enum struct variants"),
778 Fields::Unnamed(ref fields) => {
David Tolnay6af48992018-08-01 11:16:28 -0700779 let mut bind_visit_fields = TokenStream::new();
780 let mut bind_visit_mut_fields = TokenStream::new();
781 let mut bind_fold_fields = TokenStream::new();
Nika Layzell27726662017-10-24 23:16:35 -0400782
David Tolnay6af48992018-08-01 11:16:28 -0700783 let mut visit_fields = TokenStream::new();
784 let mut visit_mut_fields = TokenStream::new();
785 let mut fold_fields = TokenStream::new();
Nika Layzell27726662017-10-24 23:16:35 -0400786
David Tolnay6af48992018-08-01 11:16:28 -0700787 for (idx, field) in fields.unnamed.iter().enumerate() {
788 let name = format!("_binding_{}", idx);
789 let binding = Ident::new(&name, Span::call_site());
Nika Layzell27726662017-10-24 23:16:35 -0400790
David Tolnay6af48992018-08-01 11:16:28 -0700791 bind_visit_fields.append_all(quote! {
792 ref #binding,
793 });
794 bind_visit_mut_fields.append_all(quote! {
795 ref mut #binding,
796 });
797 bind_fold_fields.append_all(quote! {
798 #binding,
799 });
Nika Layzell27726662017-10-24 23:16:35 -0400800
David Tolnay6af48992018-08-01 11:16:28 -0700801 let borrowed_binding = Borrowed(quote!(#binding));
802 let owned_binding = Owned(quote!(#binding));
Nika Layzell27726662017-10-24 23:16:35 -0400803
David Tolnay6af48992018-08-01 11:16:28 -0700804 visit_fields.append_all(
805 visit(&field.ty, lookup, Visit, &borrowed_binding)
806 .unwrap_or_else(|| noop_visit(Visit, &borrowed_binding)),
807 );
808 visit_mut_fields.append_all(
809 visit(&field.ty, lookup, VisitMut, &borrowed_binding)
810 .unwrap_or_else(|| noop_visit(VisitMut, &borrowed_binding)),
811 );
812 fold_fields.append_all(
813 visit(&field.ty, lookup, Fold, &owned_binding)
814 .unwrap_or_else(|| noop_visit(Fold, &owned_binding)),
815 );
Nika Layzell27726662017-10-24 23:16:35 -0400816
David Tolnay6af48992018-08-01 11:16:28 -0700817 visit_fields.append_all(quote!(;));
818 visit_mut_fields.append_all(quote!(;));
819 fold_fields.append_all(quote!(,));
820 }
Nika Layzell27726662017-10-24 23:16:35 -0400821
David Tolnay6af48992018-08-01 11:16:28 -0700822 visit_variants.append_all(quote! {
823 #ty::#variant_ident(#bind_visit_fields) => {
824 #visit_fields
825 }
826 });
827
828 visit_mut_variants.append_all(quote! {
829 #ty::#variant_ident(#bind_visit_mut_fields) => {
830 #visit_mut_fields
831 }
832 });
833
834 fold_variants.append_all(quote! {
835 #ty::#variant_ident(#bind_fold_fields) => {
836 #ty::#variant_ident(
837 #fold_fields
838 )
839 }
840 });
Nika Layzell27726662017-10-24 23:16:35 -0400841 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500842 Fields::Unit => {
David Tolnay6af48992018-08-01 11:16:28 -0700843 visit_variants.append_all(quote! {
844 #ty::#variant_ident => {}
845 });
846 visit_mut_variants.append_all(quote! {
847 #ty::#variant_ident => {}
848 });
849 fold_variants.append_all(quote! {
850 #ty::#variant_ident => {
851 #ty::#variant_ident
852 }
853 });
Nika Layzell27726662017-10-24 23:16:35 -0400854 }
Nika Layzell27726662017-10-24 23:16:35 -0400855 }
Nika Layzell27726662017-10-24 23:16:35 -0400856 }
David Tolnay6af48992018-08-01 11:16:28 -0700857
858 visit_impl.append_all(quote! {
859 match *_i {
860 #visit_variants
861 }
862 });
863
864 visit_mut_impl.append_all(quote! {
865 match *_i {
866 #visit_mut_variants
867 }
868 });
869
870 fold_impl.append_all(quote! {
871 match _i {
872 #fold_variants
873 }
874 });
Nika Layzell27726662017-10-24 23:16:35 -0400875 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500876 Data::Struct(ref v) => {
David Tolnay6af48992018-08-01 11:16:28 -0700877 let mut fold_fields = TokenStream::new();
878
David Tolnaybc5b2c02018-08-02 00:18:23 -0700879 for (idx, field) in v.fields.iter().enumerate() {
880 let id = match field.ident {
881 Some(ref ident) => Member::Named(ident.clone()),
882 None => Member::Unnamed(Index::from(idx)),
883 };
884 let ref_toks = Owned(quote!(_i.#id));
David Tolnay6af48992018-08-01 11:16:28 -0700885 let visit_field = visit(&field.ty, lookup, Visit, &ref_toks)
886 .unwrap_or_else(|| noop_visit(Visit, &ref_toks));
887 visit_impl.append_all(quote! {
888 #visit_field;
889 });
890 let visit_mut_field = visit(&field.ty, lookup, VisitMut, &ref_toks)
891 .unwrap_or_else(|| noop_visit(VisitMut, &ref_toks));
892 visit_mut_impl.append_all(quote! {
893 #visit_mut_field;
894 });
David Tolnay83db9272017-12-28 17:02:31 -0500895 let fold = visit(&field.ty, lookup, Fold, &ref_toks)
David Tolnay01ed0ce2018-05-20 20:18:14 -0700896 .unwrap_or_else(|| noop_visit(Fold, &ref_toks));
Nika Layzell27726662017-10-24 23:16:35 -0400897 if let Some(ref name) = field.ident {
David Tolnay6af48992018-08-01 11:16:28 -0700898 fold_fields.append_all(quote! {
899 #name: #fold,
900 });
Nika Layzell27726662017-10-24 23:16:35 -0400901 } else {
David Tolnay6af48992018-08-01 11:16:28 -0700902 fold_fields.append_all(quote! {
903 #fold,
904 });
Nika Layzell27726662017-10-24 23:16:35 -0400905 }
906 }
907
David Tolnaye3d41b72017-12-31 15:24:00 -0500908 match v.fields {
David Tolnay6af48992018-08-01 11:16:28 -0700909 Fields::Named(..) | Fields::Unnamed(..) => fold_impl.append_all(quote! {
910 #ty {
911 #fold_fields
912 }
913 }),
914 Fields::Unit => {
915 if ty == "Ident" {
916 fold_impl.append_all(quote! {
917 let mut _i = _i;
918 let span = _visitor.fold_span(_i.span());
919 _i.set_span(span);
920 });
921 }
922 fold_impl.append_all(quote! {
923 _i
924 });
925 }
Nika Layzell27726662017-10-24 23:16:35 -0400926 };
927 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500928 Data::Union(..) => panic!("Union not supported"),
Nika Layzell27726662017-10-24 23:16:35 -0400929 }
930
David Tolnay6af48992018-08-01 11:16:28 -0700931 let mut include_fold_impl = true;
David Tolnay360efd22018-01-04 23:35:26 -0800932 if let Data::Struct(ref data) = s.ast.data {
933 if let Fields::Named(ref fields) = data.fields {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700934 if fields
935 .named
936 .iter()
937 .any(|field| field.vis == Visibility::Inherited)
938 {
David Tolnay360efd22018-01-04 23:35:26 -0800939 // Discard the generated impl if there are private fields.
940 // These have to be handwritten.
David Tolnay6af48992018-08-01 11:16:28 -0700941 include_fold_impl = false;
David Tolnay360efd22018-01-04 23:35:26 -0800942 }
943 }
David Tolnayd0adf522017-12-29 01:30:07 -0500944 }
David Tolnay6af48992018-08-01 11:16:28 -0700945
946 state.visit_trait.append_all(quote! {
947 #features
948 fn #visit_fn(&mut self, i: &'ast #ty) {
949 #visit_fn(self, i)
950 }
951 });
952
953 state.visit_impl.append_all(quote! {
954 #features
955 pub fn #visit_fn<'ast, V: Visit<'ast> + ?Sized>(
956 _visitor: &mut V, _i: &'ast #ty
957 ) {
958 #visit_impl
959 }
960 });
961
962 state.visit_mut_trait.append_all(quote! {
963 #features
964 fn #visit_mut_fn(&mut self, i: &mut #ty) {
965 #visit_mut_fn(self, i)
966 }
967 });
968
969 state.visit_mut_impl.append_all(quote! {
970 #features
971 pub fn #visit_mut_fn<V: VisitMut + ?Sized>(
972 _visitor: &mut V, _i: &mut #ty
973 ) {
974 #visit_mut_impl
975 }
976 });
977
978 state.fold_trait.append_all(quote! {
979 #features
980 fn #fold_fn(&mut self, i: #ty) -> #ty {
981 #fold_fn(self, i)
982 }
983 });
984
985 if include_fold_impl {
986 state.fold_impl.append_all(quote! {
987 #features
988 pub fn #fold_fn<V: Fold + ?Sized>(
989 _visitor: &mut V, _i: #ty
990 ) -> #ty {
991 #fold_impl
992 }
993 });
994 }
Nika Layzell27726662017-10-24 23:16:35 -0400995 }
996}
997
David Tolnay6af48992018-08-01 11:16:28 -0700998fn write_file(path: &str, content: TokenStream) {
David Tolnay8c81f622018-07-31 23:34:35 -0700999 let mut file = File::create(path).unwrap();
David Tolnay6af48992018-08-01 11:16:28 -07001000 write!(
1001 file,
1002 "// THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT\n\n"
1003 ).unwrap();
David Tolnay8c81f622018-07-31 23:34:35 -07001004 let mut config = rustfmt::Config::default();
1005 config.set().emit_mode(rustfmt::EmitMode::Stdout);
1006 config.set().verbose(rustfmt::Verbosity::Quiet);
David Tolnay280202f2018-08-02 00:29:54 -07001007 config.set().format_macro_matchers(true);
David Tolnay8c81f622018-07-31 23:34:35 -07001008 let mut session = rustfmt::Session::new(config, Some(&mut file));
David Tolnay6af48992018-08-01 11:16:28 -07001009 session
1010 .format(rustfmt::Input::Text(content.to_string()))
1011 .unwrap();
David Tolnay8c81f622018-07-31 23:34:35 -07001012}
1013
Nika Layzell27726662017-10-24 23:16:35 -04001014fn main() {
1015 let mut lookup = BTreeMap::new();
David Tolnayea9ae892017-12-26 01:44:32 -05001016 load_file(SYN_CRATE_ROOT, &quote!(), &mut lookup).unwrap();
Nika Layzell27726662017-10-24 23:16:35 -04001017
Nika Layzellefb83ba2017-12-19 18:23:55 -05001018 // Load in any terminal types
1019 for &tt in TERMINAL_TYPES {
1020 use syn::*;
David Tolnayd67fb752017-12-27 13:50:29 -05001021 lookup.insert(
Alex Crichtona74a1c82018-05-16 10:20:44 -07001022 Ident::new(&tt, Span::call_site()),
David Tolnayd67fb752017-12-27 13:50:29 -05001023 AstItem {
David Tolnay3d772182017-12-28 17:18:53 -05001024 ast: DeriveInput {
Alex Crichtona74a1c82018-05-16 10:20:44 -07001025 ident: Ident::new(tt, Span::call_site()),
David Tolnayd67fb752017-12-27 13:50:29 -05001026 vis: Visibility::Public(VisPublic {
David Tolnay6af48992018-08-01 11:16:28 -07001027 pub_token: <Token![pub]>::default(),
David Tolnayd67fb752017-12-27 13:50:29 -05001028 }),
1029 attrs: vec![],
David Tolnay6af48992018-08-01 11:16:28 -07001030 generics: Generics::default(),
David Tolnaye3d41b72017-12-31 15:24:00 -05001031 data: Data::Struct(DataStruct {
1032 fields: Fields::Unit,
David Tolnay6af48992018-08-01 11:16:28 -07001033 struct_token: <Token![struct]>::default(),
David Tolnayd67fb752017-12-27 13:50:29 -05001034 semi_token: None,
1035 }),
1036 },
hcplaa511792018-05-29 07:13:01 +03001037 features: TokenStream::new(),
David Tolnayd67fb752017-12-27 13:50:29 -05001038 eos_full: false,
Nika Layzellefb83ba2017-12-19 18:23:55 -05001039 },
David Tolnayd67fb752017-12-27 13:50:29 -05001040 );
Nika Layzellefb83ba2017-12-19 18:23:55 -05001041 }
1042
David Tolnay6af48992018-08-01 11:16:28 -07001043 let mut state = codegen::State::default();
Nika Layzell27726662017-10-24 23:16:35 -04001044 for s in lookup.values() {
1045 codegen::generate(&mut state, &lookup, s);
1046 }
1047
David Tolnay6af48992018-08-01 11:16:28 -07001048 let full_macro = quote! {
1049 #[cfg(feature = "full")]
1050 macro_rules! full {
1051 ($e:expr) => {
1052 $e
1053 };
1054 }
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001055
David Tolnay6af48992018-08-01 11:16:28 -07001056 #[cfg(all(feature = "derive", not(feature = "full")))]
1057 macro_rules! full {
1058 ($e:expr) => {
1059 unreachable!()
1060 };
1061 }
1062 };
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001063
David Tolnay6af48992018-08-01 11:16:28 -07001064 let skip_macro = quote! {
1065 #[cfg(any(feature = "full", feature = "derive"))]
1066 macro_rules! skip {
1067 ($($tt:tt)*) => {};
1068 }
1069 };
1070
1071 let fold_trait = state.fold_trait;
1072 let fold_impl = state.fold_impl;
David Tolnayae0009e2018-08-01 00:40:00 -07001073 write_file(
1074 FOLD_SRC,
David Tolnay6af48992018-08-01 11:16:28 -07001075 quote! {
1076 // Unreachable code is generated sometimes without the full feature.
1077 #![allow(unreachable_code)]
1078 #![cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))]
Nika Layzell27726662017-10-24 23:16:35 -04001079
David Tolnay6af48992018-08-01 11:16:28 -07001080 use *;
1081 #[cfg(any(feature = "full", feature = "derive"))]
1082 use token::{Brace, Bracket, Paren, Group};
1083 use proc_macro2::Span;
1084 #[cfg(any(feature = "full", feature = "derive"))]
1085 use gen::helper::fold::*;
Nika Layzell27726662017-10-24 23:16:35 -04001086
David Tolnay6af48992018-08-01 11:16:28 -07001087 #full_macro
Nika Layzell27726662017-10-24 23:16:35 -04001088
David Tolnay6af48992018-08-01 11:16:28 -07001089 /// Syntax tree traversal to transform the nodes of an owned syntax tree.
1090 ///
1091 /// See the [module documentation] for details.
1092 ///
1093 /// [module documentation]: index.html
1094 ///
1095 /// *This trait is available if Syn is built with the `"fold"` feature.*
1096 pub trait Fold {
1097 #fold_trait
1098 }
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001099
David Tolnay6af48992018-08-01 11:16:28 -07001100 #[cfg(any(feature = "full", feature = "derive"))]
1101 macro_rules! fold_span_only {
1102 ($f:ident : $t:ident) => {
1103 pub fn $f<V: Fold + ?Sized>(_visitor: &mut V, mut _i: $t) -> $t {
1104 let span = _visitor.fold_span(_i.span());
1105 _i.set_span(span);
1106 _i
1107 }
1108 }
1109 }
Nika Layzell27726662017-10-24 23:16:35 -04001110
David Tolnay6af48992018-08-01 11:16:28 -07001111 #[cfg(any(feature = "full", feature = "derive"))]
1112 fold_span_only!(fold_lit_byte: LitByte);
1113 #[cfg(any(feature = "full", feature = "derive"))]
1114 fold_span_only!(fold_lit_byte_str: LitByteStr);
1115 #[cfg(any(feature = "full", feature = "derive"))]
1116 fold_span_only!(fold_lit_char: LitChar);
1117 #[cfg(any(feature = "full", feature = "derive"))]
1118 fold_span_only!(fold_lit_float: LitFloat);
1119 #[cfg(any(feature = "full", feature = "derive"))]
1120 fold_span_only!(fold_lit_int: LitInt);
1121 #[cfg(any(feature = "full", feature = "derive"))]
1122 fold_span_only!(fold_lit_str: LitStr);
David Tolnayd0adf522017-12-29 01:30:07 -05001123
David Tolnay6af48992018-08-01 11:16:28 -07001124 #fold_impl
1125 },
David Tolnayae0009e2018-08-01 00:40:00 -07001126 );
Nika Layzell27726662017-10-24 23:16:35 -04001127
David Tolnay6af48992018-08-01 11:16:28 -07001128 let visit_trait = state.visit_trait;
1129 let visit_impl = state.visit_impl;
David Tolnayae0009e2018-08-01 00:40:00 -07001130 write_file(
1131 VISIT_SRC,
David Tolnay6af48992018-08-01 11:16:28 -07001132 quote! {
1133 #![cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
Nika Layzell27726662017-10-24 23:16:35 -04001134
David Tolnay6af48992018-08-01 11:16:28 -07001135 use *;
1136 #[cfg(any(feature = "full", feature = "derive"))]
1137 use punctuated::Punctuated;
1138 use proc_macro2::Span;
1139 #[cfg(any(feature = "full", feature = "derive"))]
1140 use gen::helper::visit::*;
David Tolnayf0d63bf2017-12-26 12:29:47 -05001141
David Tolnay6af48992018-08-01 11:16:28 -07001142 #full_macro
1143 #skip_macro
Nika Layzell27726662017-10-24 23:16:35 -04001144
David Tolnay6af48992018-08-01 11:16:28 -07001145 /// Syntax tree traversal to walk a shared borrow of a syntax tree.
1146 ///
1147 /// See the [module documentation] for details.
1148 ///
1149 /// [module documentation]: index.html
1150 ///
1151 /// *This trait is available if Syn is built with the `"visit"` feature.*
1152 pub trait Visit<'ast> {
1153 #visit_trait
1154 }
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001155
David Tolnay6af48992018-08-01 11:16:28 -07001156 #visit_impl
1157 },
David Tolnayae0009e2018-08-01 00:40:00 -07001158 );
Nika Layzell27726662017-10-24 23:16:35 -04001159
David Tolnay6af48992018-08-01 11:16:28 -07001160 let visit_mut_trait = state.visit_mut_trait;
1161 let visit_mut_impl = state.visit_mut_impl;
David Tolnayae0009e2018-08-01 00:40:00 -07001162 write_file(
1163 VISIT_MUT_SRC,
David Tolnay6af48992018-08-01 11:16:28 -07001164 quote! {
1165 #![cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
Nika Layzell27726662017-10-24 23:16:35 -04001166
David Tolnay6af48992018-08-01 11:16:28 -07001167 use *;
1168 #[cfg(any(feature = "full", feature = "derive"))]
1169 use punctuated::Punctuated;
1170 use proc_macro2::Span;
1171 #[cfg(any(feature = "full", feature = "derive"))]
1172 use gen::helper::visit_mut::*;
David Tolnayf0d63bf2017-12-26 12:29:47 -05001173
David Tolnay6af48992018-08-01 11:16:28 -07001174 #full_macro
1175 #skip_macro
Nika Layzell27726662017-10-24 23:16:35 -04001176
David Tolnay6af48992018-08-01 11:16:28 -07001177 /// Syntax tree traversal to mutate an exclusive borrow of a syntax tree in
1178 /// place.
1179 ///
1180 /// See the [module documentation] for details.
1181 ///
1182 /// [module documentation]: index.html
1183 ///
1184 /// *This trait is available if Syn is built with the `"visit-mut"` feature.*
1185 pub trait VisitMut {
1186 #visit_mut_trait
1187 }
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001188
David Tolnay6af48992018-08-01 11:16:28 -07001189 #visit_mut_impl
1190 },
David Tolnayae0009e2018-08-01 00:40:00 -07001191 );
Nika Layzell27726662017-10-24 23:16:35 -04001192}