blob: f155fbf395bcdb2b058a26e383be3865d7186218 [file] [log] [blame]
David Tolnay4b4c4b62018-01-06 13:48:05 -08001//! This crate automatically generates the definition of the `Visit`,
2//! `VisitMut`, and `Fold` traits in `syn` based on the `syn` source. It
Nika Layzell27726662017-10-24 23:16:35 -04003//! discovers structs and enums declared with the `ast_*` macros and generates
4//! the functions for those types.
5//!
6//! It makes a few assumptions about the target crate:
7//! 1. All structs which are discovered must be re-exported in the root of the
8//! crate, even if they were declared in a submodule.
9//! 2. This code cannot discover submodules which are located in subdirectories
10//! - only submodules located in the same directory.
11//! 3. The path to `syn` is hardcoded.
12
David Tolnayea9ae892017-12-26 01:44:32 -050013#![cfg_attr(feature = "cargo-clippy", allow(redundant_closure))]
14
David Tolnayd67fb752017-12-27 13:50:29 -050015#[macro_use]
16extern crate failure;
Nika Layzell27726662017-10-24 23:16:35 -040017extern crate inflections;
David Tolnay2e0dba12017-12-27 01:54:40 -050018extern crate proc_macro2;
David Tolnayd67fb752017-12-27 13:50:29 -050019#[macro_use]
20extern crate quote;
David Tolnay5c4c0b52017-12-28 17:58:54 -050021#[macro_use]
David Tolnayd67fb752017-12-27 13:50:29 -050022extern crate syn;
Nika Layzell27726662017-10-24 23:16:35 -040023
Alex Crichton715862b2018-05-17 12:31:49 -070024use quote::ToTokens;
David Tolnaye303b7c2018-05-20 16:46:35 -070025use syn::{Attribute, Data, DataStruct, DeriveInput, Ident, Item};
David Tolnayd67fb752017-12-27 13:50:29 -050026use failure::{err_msg, Error};
David Tolnaye303b7c2018-05-20 16:46:35 -070027use proc_macro2::{Span, TokenStream};
Nika Layzell27726662017-10-24 23:16:35 -040028
David Tolnay8ed806a2017-12-26 01:28:28 -050029use std::io::{Read, Write};
David Tolnayf0d63bf2017-12-26 12:29:47 -050030use std::fmt::{self, Debug};
Nika Layzell27726662017-10-24 23:16:35 -040031use std::fs::File;
32use std::path::Path;
33use std::collections::BTreeMap;
34
35const SYN_CRATE_ROOT: &str = "../src/lib.rs";
36
37const FOLD_SRC: &str = "../src/gen/fold.rs";
38const VISIT_SRC: &str = "../src/gen/visit.rs";
39const VISIT_MUT_SRC: &str = "../src/gen/visit_mut.rs";
40
David Tolnayd67fb752017-12-27 13:50:29 -050041const IGNORED_MODS: &[&str] = &["fold", "visit", "visit_mut"];
Nika Layzell27726662017-10-24 23:16:35 -040042
Alex Crichton131308c2018-05-18 14:00:24 -070043const EXTRA_TYPES: &[&str] = &["Lifetime"];
David Tolnay4ba63a02017-12-28 15:53:05 -050044
Alex Crichtond261d092018-05-18 13:47:35 -070045const TERMINAL_TYPES: &[&str] = &["Span", "Ident"];
Nika Layzellefb83ba2017-12-19 18:23:55 -050046
Alex Crichtona74a1c82018-05-16 10:20:44 -070047fn path_eq(a: &syn::Path, b: &str) -> bool {
48 if a.global() {
49 return false
Nika Layzell27726662017-10-24 23:16:35 -040050 }
Alex Crichtona74a1c82018-05-16 10:20:44 -070051 if a.segments.len() != 1 {
52 return false
53 }
David Tolnay446f7d62018-05-20 17:58:15 -070054 a.segments[0].ident == b
Nika Layzell27726662017-10-24 23:16:35 -040055}
56
Alex Crichton715862b2018-05-17 12:31:49 -070057fn get_features(attrs: &[Attribute], mut features: TokenStream) -> TokenStream {
Nika Layzell27726662017-10-24 23:16:35 -040058 for attr in attrs {
Alex Crichtona74a1c82018-05-16 10:20:44 -070059 if path_eq(&attr.path, "cfg") {
Nika Layzell27726662017-10-24 23:16:35 -040060 attr.to_tokens(&mut features);
61 }
62 }
63 features
64}
65
66#[derive(Clone)]
67pub struct AstItem {
David Tolnay3d772182017-12-28 17:18:53 -050068 ast: DeriveInput,
Alex Crichton715862b2018-05-17 12:31:49 -070069 features: TokenStream,
Nika Layzell27726662017-10-24 23:16:35 -040070 // True if this is an ast_enum_of_structs! item with a #full annotation.
71 eos_full: bool,
72}
73
David Tolnayf0d63bf2017-12-26 12:29:47 -050074impl Debug for AstItem {
Nika Layzell27726662017-10-24 23:16:35 -040075 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
76 f.debug_struct("AstItem")
David Tolnay3d772182017-12-28 17:18:53 -050077 .field("ast", &self.ast)
Nika Layzell27726662017-10-24 23:16:35 -040078 .field("features", &self.features.to_string())
79 .finish()
80 }
81}
82
83// NOTE: BTreeMap is used here instead of HashMap to have deterministic output.
David Tolnayc47f8422017-12-28 17:30:46 -050084type Lookup = BTreeMap<Ident, AstItem>;
Nika Layzell27726662017-10-24 23:16:35 -040085
Alex Crichton715862b2018-05-17 12:31:49 -070086fn load_file<P: AsRef<Path>>(name: P, features: &TokenStream, lookup: &mut Lookup) -> Result<(), Error> {
Nika Layzell27726662017-10-24 23:16:35 -040087 let name = name.as_ref();
David Tolnayea9ae892017-12-26 01:44:32 -050088 let parent = name.parent().ok_or_else(|| err_msg("no parent path"))?;
Nika Layzell27726662017-10-24 23:16:35 -040089
90 let mut f = File::open(name)?;
91 let mut src = String::new();
92 f.read_to_string(&mut src)?;
93
94 // Parse the file
David Tolnayd67fb752017-12-27 13:50:29 -050095 let file =
96 syn::parse_file(&src).map_err(|_| format_err!("failed to parse {}", name.display()))?;
Nika Layzell27726662017-10-24 23:16:35 -040097
98 // Collect all of the interesting AstItems declared in this file or submodules.
David Tolnay4ba63a02017-12-28 15:53:05 -050099 'items: for item in file.items {
100 match item {
101 Item::Mod(item) => {
Nika Layzell27726662017-10-24 23:16:35 -0400102 // Don't inspect inline modules.
David Tolnayc6b55bc2017-11-09 22:48:38 -0800103 if item.content.is_some() {
Nika Layzell27726662017-10-24 23:16:35 -0400104 continue;
105 }
106
107 // We don't want to try to load the generated rust files and
108 // parse them, so we ignore them here.
109 for name in IGNORED_MODS {
David Tolnay446f7d62018-05-20 17:58:15 -0700110 if item.ident == name {
Nika Layzell27726662017-10-24 23:16:35 -0400111 continue 'items;
112 }
113 }
114
115 // Lookup any #[cfg()] attributes on the module and add them to
116 // the feature set.
David Tolnay0a0d78c2018-01-05 15:24:01 -0800117 //
118 // The derive module is weird because it is built with either
119 // `full` or `derive` but exported only under `derive`.
David Tolnay446f7d62018-05-20 17:58:15 -0700120 let features = if item.ident == "derive" {
David Tolnay0a0d78c2018-01-05 15:24:01 -0800121 quote!(#[cfg(feature = "derive")])
122 } else {
123 get_features(&item.attrs, features.clone())
124 };
Nika Layzell27726662017-10-24 23:16:35 -0400125
126 // Look up the submodule file, and recursively parse it.
127 // XXX: Only handles same-directory .rs file submodules.
Alex Crichtona74a1c82018-05-16 10:20:44 -0700128 let path = parent.join(&format!("{}.rs", item.ident));
David Tolnayea9ae892017-12-26 01:44:32 -0500129 load_file(path, &features, lookup)?;
Nika Layzell27726662017-10-24 23:16:35 -0400130 }
David Tolnay4ba63a02017-12-28 15:53:05 -0500131 Item::Macro(item) => {
Nika Layzell27726662017-10-24 23:16:35 -0400132 // Lookip any #[cfg()] attributes directly on the macro
133 // invocation, and add them to the feature set.
134 let features = get_features(&item.attrs, features.clone());
135
136 // Try to parse the AstItem declaration out of the item.
David Tolnay01a77582018-01-01 20:00:51 -0800137 let tts = &item.mac.tts;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700138 let found = if path_eq(&item.mac.path, "ast_struct") {
David Tolnay01a77582018-01-01 20:00:51 -0800139 syn::parse_str::<parsing::AstStruct>(&quote!(#tts).to_string())
David Tolnayd67fb752017-12-27 13:50:29 -0500140 .map_err(|_| err_msg("failed to parse ast_struct"))?
141 .0
Alex Crichtona74a1c82018-05-16 10:20:44 -0700142 } else if path_eq(&item.mac.path, "ast_enum") {
David Tolnay01a77582018-01-01 20:00:51 -0800143 syn::parse_str::<parsing::AstEnum>(&quote!(#tts).to_string())
David Tolnayd67fb752017-12-27 13:50:29 -0500144 .map_err(|_| err_msg("failed to parse ast_enum"))?
145 .0
Alex Crichtona74a1c82018-05-16 10:20:44 -0700146 } else if path_eq(&item.mac.path, "ast_enum_of_structs") {
David Tolnay01a77582018-01-01 20:00:51 -0800147 syn::parse_str::<parsing::AstEnumOfStructs>(&quote!(#tts).to_string())
David Tolnay1cf80912017-12-31 18:35:12 -0500148 .map_err(|_| err_msg("failed to parse ast_enum_of_structs"))?
David Tolnayd67fb752017-12-27 13:50:29 -0500149 .0
Nika Layzell27726662017-10-24 23:16:35 -0400150 } else {
David Tolnayd67fb752017-12-27 13:50:29 -0500151 continue;
Nika Layzell27726662017-10-24 23:16:35 -0400152 };
153
154 // Record our features on the parsed AstItems.
155 for mut item in found {
156 features.to_tokens(&mut item.features);
Alex Crichtona74a1c82018-05-16 10:20:44 -0700157 lookup.insert(item.ast.ident.clone(), item);
Nika Layzell27726662017-10-24 23:16:35 -0400158 }
159 }
David Tolnay4ba63a02017-12-28 15:53:05 -0500160 Item::Struct(item) => {
161 let ident = item.ident;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700162 if EXTRA_TYPES.contains(&&ident.to_string()[..]) {
163 lookup.insert(ident.clone(), AstItem {
David Tolnay3d772182017-12-28 17:18:53 -0500164 ast: DeriveInput {
David Tolnay4ba63a02017-12-28 15:53:05 -0500165 ident: ident,
166 vis: item.vis,
167 attrs: item.attrs,
168 generics: item.generics,
David Tolnaye3d41b72017-12-31 15:24:00 -0500169 data: Data::Struct(DataStruct {
170 fields: item.fields,
David Tolnay4ba63a02017-12-28 15:53:05 -0500171 struct_token: item.struct_token,
172 semi_token: item.semi_token,
173 }),
174 },
175 features: features.clone(),
176 eos_full: false,
177 });
178 }
179 }
Nika Layzell27726662017-10-24 23:16:35 -0400180 _ => {}
181 }
182 }
183 Ok(())
184}
185
186mod parsing {
187 use super::AstItem;
188
David Tolnay1cf80912017-12-31 18:35:12 -0500189 use syn;
David Tolnayc5ab8c62017-12-26 16:43:39 -0500190 use syn::synom::*;
Nika Layzell27726662017-10-24 23:16:35 -0400191 use syn::*;
David Tolnaye303b7c2018-05-20 16:46:35 -0700192 use proc_macro2::TokenStream;
Nika Layzell27726662017-10-24 23:16:35 -0400193
194 // Parses #full - returns #[cfg(feature = "full")] if it is present, and
195 // nothing otherwise.
Alex Crichton715862b2018-05-17 12:31:49 -0700196 named!(full -> (TokenStream, bool), map!(option!(do_parse!(
David Tolnayf8db7ba2017-11-11 22:52:16 -0800197 punct!(#) >>
Nika Layzell27726662017-10-24 23:16:35 -0400198 id: syn!(Ident) >>
David Tolnay446f7d62018-05-20 17:58:15 -0700199 cond_reduce!(id == "full") >>
David Tolnayea9ae892017-12-26 01:44:32 -0500200 ()
Nika Layzell27726662017-10-24 23:16:35 -0400201 )), |s| if s.is_some() {
202 (quote!(#[cfg(feature = "full")]), true)
203 } else {
204 (quote!(), false)
205 }));
206
David Tolnay28c5a462017-12-27 01:59:30 -0500207 named!(manual_extra_traits -> (), do_parse!(
208 punct!(#) >>
209 id: syn!(Ident) >>
David Tolnay446f7d62018-05-20 17:58:15 -0700210 cond_reduce!(id == "manual_extra_traits") >>
David Tolnay28c5a462017-12-27 01:59:30 -0500211 ()
212 ));
213
Nika Layzell27726662017-10-24 23:16:35 -0400214 // Parses a simple AstStruct without the `pub struct` prefix.
215 named!(ast_struct_inner -> AstItem, do_parse!(
216 id: syn!(Ident) >>
217 features: full >>
David Tolnay28c5a462017-12-27 01:59:30 -0500218 option!(manual_extra_traits) >>
David Tolnay2e0dba12017-12-27 01:54:40 -0500219 rest: syn!(TokenStream) >>
Nika Layzell27726662017-10-24 23:16:35 -0400220 (AstItem {
David Tolnay01a77582018-01-01 20:00:51 -0800221 ast: syn::parse_str(&quote! {
David Tolnay2e0dba12017-12-27 01:54:40 -0500222 pub struct #id #rest
David Tolnay01a77582018-01-01 20:00:51 -0800223 }.to_string())?,
Nika Layzell27726662017-10-24 23:16:35 -0400224 features: features.0,
225 eos_full: features.1,
226 })
227 ));
228
229 // ast_struct! parsing
230 pub struct AstStruct(pub Vec<AstItem>);
231 impl Synom for AstStruct {
David Tolnayab919512017-12-30 23:31:51 -0500232 named!(parse -> Self, do_parse!(
David Tolnay2c136452017-12-27 14:13:32 -0500233 many0!(Attribute::parse_outer) >>
David Tolnayf8db7ba2017-11-11 22:52:16 -0800234 keyword!(pub) >>
235 keyword!(struct) >>
Nika Layzell27726662017-10-24 23:16:35 -0400236 res: call!(ast_struct_inner) >>
David Tolnayab919512017-12-30 23:31:51 -0500237 (AstStruct(vec![res]))
238 ));
Nika Layzell27726662017-10-24 23:16:35 -0400239 }
240
David Tolnay360efd22018-01-04 23:35:26 -0800241 named!(no_visit -> (), do_parse!(
242 punct!(#) >>
243 id: syn!(Ident) >>
David Tolnay446f7d62018-05-20 17:58:15 -0700244 cond_reduce!(id == "no_visit") >>
David Tolnay360efd22018-01-04 23:35:26 -0800245 ()
246 ));
247
Nika Layzell27726662017-10-24 23:16:35 -0400248 // ast_enum! parsing
249 pub struct AstEnum(pub Vec<AstItem>);
250 impl Synom for AstEnum {
David Tolnay360efd22018-01-04 23:35:26 -0800251 named!(parse -> Self, do_parse!(
252 many0!(Attribute::parse_outer) >>
253 keyword!(pub) >>
254 keyword!(enum) >>
255 id: syn!(Ident) >>
256 no_visit: option!(no_visit) >>
257 rest: syn!(TokenStream) >>
258 (AstEnum(if no_visit.is_some() {
259 vec![]
260 } else {
261 vec![AstItem {
262 ast: syn::parse_str(&quote! {
263 pub enum #id #rest
264 }.to_string())?,
265 features: quote!(),
266 eos_full: false,
267 }]
268 }))
269 ));
Nika Layzell27726662017-10-24 23:16:35 -0400270 }
271
272 // A single variant of an ast_enum_of_structs!
273 struct EosVariant {
274 name: Ident,
David Tolnayfcfb9002017-12-28 22:04:29 -0500275 member: Option<Path>,
Nika Layzell27726662017-10-24 23:16:35 -0400276 inner: Option<AstItem>,
277 }
278 named!(eos_variant -> EosVariant, do_parse!(
David Tolnay2c136452017-12-27 14:13:32 -0500279 many0!(Attribute::parse_outer) >>
David Tolnayf8db7ba2017-11-11 22:52:16 -0800280 keyword!(pub) >>
Nika Layzell27726662017-10-24 23:16:35 -0400281 variant: syn!(Ident) >>
David Tolnayfcfb9002017-12-28 22:04:29 -0500282 member: option!(map!(parens!(alt!(
Alex Crichtona74a1c82018-05-16 10:20:44 -0700283 call!(ast_struct_inner) => { |x: AstItem| (Path::from(x.ast.ident.clone()), Some(x)) }
Nika Layzell27726662017-10-24 23:16:35 -0400284 |
285 syn!(Path) => { |x| (x, None) }
David Tolnay8875fca2017-12-31 13:52:37 -0500286 )), |x| x.1)) >>
David Tolnayf8db7ba2017-11-11 22:52:16 -0800287 punct!(,) >>
Nika Layzell27726662017-10-24 23:16:35 -0400288 (EosVariant {
289 name: variant,
David Tolnayfcfb9002017-12-28 22:04:29 -0500290 member: member.clone().map(|x| x.0),
291 inner: member.map(|x| x.1).unwrap_or_default(),
Nika Layzell27726662017-10-24 23:16:35 -0400292 })
293 ));
294
295 // ast_enum_of_structs! parsing
296 pub struct AstEnumOfStructs(pub Vec<AstItem>);
297 impl Synom for AstEnumOfStructs {
David Tolnayab919512017-12-30 23:31:51 -0500298 named!(parse -> Self, do_parse!(
David Tolnay2c136452017-12-27 14:13:32 -0500299 many0!(Attribute::parse_outer) >>
David Tolnayf8db7ba2017-11-11 22:52:16 -0800300 keyword!(pub) >>
301 keyword!(enum) >>
Nika Layzell27726662017-10-24 23:16:35 -0400302 id: syn!(Ident) >>
David Tolnaye3d41b72017-12-31 15:24:00 -0500303 variants: braces!(many0!(eos_variant)) >>
Nika Layzell27726662017-10-24 23:16:35 -0400304 option!(syn!(Ident)) >> // do_not_generate_to_tokens
305 ({
306 // XXX: This is really gross - we shouldn't have to convert the
307 // tokens to strings to re-parse them.
308 let enum_item = {
David Tolnaye3d41b72017-12-31 15:24:00 -0500309 let variants = variants.1.iter().map(|v| {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700310 let name = v.name.clone();
David Tolnayfcfb9002017-12-28 22:04:29 -0500311 match v.member {
312 Some(ref member) => quote!(#name(#member)),
313 None => quote!(#name),
314 }
Nika Layzell27726662017-10-24 23:16:35 -0400315 });
David Tolnay01a77582018-01-01 20:00:51 -0800316 syn::parse_str(&quote! {
Nika Layzell27726662017-10-24 23:16:35 -0400317 pub enum #id { #(#variants),* }
David Tolnay01a77582018-01-01 20:00:51 -0800318 }.to_string())?
Nika Layzell27726662017-10-24 23:16:35 -0400319 };
320 let mut items = vec![AstItem {
David Tolnay3d772182017-12-28 17:18:53 -0500321 ast: enum_item,
Nika Layzell27726662017-10-24 23:16:35 -0400322 features: quote!(),
323 eos_full: false,
324 }];
David Tolnaye3d41b72017-12-31 15:24:00 -0500325 items.extend(variants.1.into_iter().filter_map(|v| v.inner));
Nika Layzell27726662017-10-24 23:16:35 -0400326 AstEnumOfStructs(items)
327 })
David Tolnayab919512017-12-30 23:31:51 -0500328 ));
Nika Layzell27726662017-10-24 23:16:35 -0400329 }
330}
331
332mod codegen {
333 use super::{AstItem, Lookup};
334 use syn::*;
David Tolnayf2cfd722017-12-31 18:02:51 -0500335 use syn::punctuated::Punctuated;
Alex Crichton715862b2018-05-17 12:31:49 -0700336 use quote::ToTokens;
David Tolnayf0d63bf2017-12-26 12:29:47 -0500337 use std::fmt::{self, Display};
David Tolnaye303b7c2018-05-20 16:46:35 -0700338 use proc_macro2::{Span, TokenStream};
Nika Layzell27726662017-10-24 23:16:35 -0400339
340 #[derive(Default)]
341 pub struct State {
342 pub visit_trait: String,
343 pub visit_impl: String,
344 pub visit_mut_trait: String,
345 pub visit_mut_impl: String,
346 pub fold_trait: String,
347 pub fold_impl: String,
348 }
349
David Tolnay4a918742017-12-28 16:54:41 -0500350 fn under_name(name: Ident) -> Ident {
Nika Layzell27726662017-10-24 23:16:35 -0400351 use inflections::Inflect;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700352 Ident::new(&name.to_string().to_snake_case(), Span::call_site())
Nika Layzell27726662017-10-24 23:16:35 -0400353 }
354
David Tolnay3d772182017-12-28 17:18:53 -0500355 enum RelevantType<'a> {
356 Box(&'a Type),
357 Vec(&'a Type),
David Tolnayf2cfd722017-12-31 18:02:51 -0500358 Punctuated(&'a Type),
David Tolnay3d772182017-12-28 17:18:53 -0500359 Option(&'a Type),
David Tolnayf2cfd722017-12-31 18:02:51 -0500360 Tuple(&'a Punctuated<Type, Token![,]>),
David Tolnay3d772182017-12-28 17:18:53 -0500361 Simple(&'a AstItem),
Alex Crichton715862b2018-05-17 12:31:49 -0700362 Token(TokenStream),
David Tolnay3d772182017-12-28 17:18:53 -0500363 Pass,
364 }
Nika Layzell27726662017-10-24 23:16:35 -0400365
David Tolnay3d772182017-12-28 17:18:53 -0500366 fn classify<'a>(ty: &'a Type, lookup: &'a Lookup) -> RelevantType<'a> {
367 match *ty {
368 Type::Path(TypePath { qself: None, ref path }) => {
David Tolnay56080682018-01-06 14:01:52 -0800369 let last = path.segments.last().unwrap().into_value();
Alex Crichtona74a1c82018-05-16 10:20:44 -0700370 match &last.ident.to_string()[..] {
David Tolnay3d772182017-12-28 17:18:53 -0500371 "Box" => RelevantType::Box(first_arg(&last.arguments)),
372 "Vec" => RelevantType::Vec(first_arg(&last.arguments)),
David Tolnayf2cfd722017-12-31 18:02:51 -0500373 "Punctuated" => RelevantType::Punctuated(first_arg(&last.arguments)),
David Tolnay3d772182017-12-28 17:18:53 -0500374 "Option" => RelevantType::Option(first_arg(&last.arguments)),
David Tolnay1e01f9c2017-12-28 20:16:19 -0500375 "Brace" | "Bracket" | "Paren" | "Group" => {
Alex Crichton715862b2018-05-17 12:31:49 -0700376 RelevantType::Token(last.ident.clone().into_token_stream())
David Tolnay1e01f9c2017-12-28 20:16:19 -0500377 }
David Tolnay3d772182017-12-28 17:18:53 -0500378 _ => {
379 if let Some(item) = lookup.get(&last.ident) {
380 RelevantType::Simple(item)
381 } else {
382 RelevantType::Pass
383 }
384 }
385 }
Nika Layzell27726662017-10-24 23:16:35 -0400386 }
David Tolnayeadbda32017-12-29 02:33:47 -0500387 Type::Tuple(TypeTuple { ref elems, .. }) => {
388 RelevantType::Tuple(elems)
David Tolnay5c4c0b52017-12-28 17:58:54 -0500389 }
David Tolnay446f7d62018-05-20 17:58:15 -0700390 Type::Macro(TypeMacro { ref mac }) if mac.path.segments.last().unwrap().into_value().ident == "Token" => {
Alex Crichton715862b2018-05-17 12:31:49 -0700391 RelevantType::Token(mac.into_token_stream())
David Tolnaycc0f0372017-12-28 19:11:04 -0500392 }
David Tolnay3d772182017-12-28 17:18:53 -0500393 _ => RelevantType::Pass,
Nika Layzell27726662017-10-24 23:16:35 -0400394 }
395 }
396
397 #[derive(Debug, Eq, PartialEq, Copy, Clone)]
398 enum Kind {
399 Visit,
400 VisitMut,
401 Fold,
402 }
403
David Tolnayf0d63bf2017-12-26 12:29:47 -0500404 enum Operand {
Alex Crichton715862b2018-05-17 12:31:49 -0700405 Borrowed(TokenStream),
406 Owned(TokenStream),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500407 }
408
David Tolnay83db9272017-12-28 17:02:31 -0500409 use self::Operand::*;
410 use self::Kind::*;
411
David Tolnayf0d63bf2017-12-26 12:29:47 -0500412 impl Operand {
Alex Crichton715862b2018-05-17 12:31:49 -0700413 fn tokens(&self) -> &TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500414 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500415 Borrowed(ref n) | Owned(ref n) => n,
David Tolnayf0d63bf2017-12-26 12:29:47 -0500416 }
417 }
418
Alex Crichton715862b2018-05-17 12:31:49 -0700419 fn ref_tokens(&self) -> TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500420 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500421 Borrowed(ref n) => n.clone(),
422 Owned(ref n) => quote!(&#n),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500423 }
424 }
425
Alex Crichton715862b2018-05-17 12:31:49 -0700426 fn ref_mut_tokens(&self) -> TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500427 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500428 Borrowed(ref n) => n.clone(),
429 Owned(ref n) => quote!(&mut #n),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500430 }
431 }
432
Alex Crichton715862b2018-05-17 12:31:49 -0700433 fn owned_tokens(&self) -> TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500434 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500435 Borrowed(ref n) => quote!(*#n),
436 Owned(ref n) => n.clone(),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500437 }
438 }
439 }
440
441 impl Display for Operand {
442 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
David Tolnay4e52d8a2017-12-28 15:54:50 -0500443 Display::fmt(self.tokens(), formatter)
David Tolnayf0d63bf2017-12-26 12:29:47 -0500444 }
445 }
446
Nika Layzellc08227a2017-12-04 16:30:17 -0500447 fn first_arg(params: &PathArguments) -> &Type {
Nika Layzell27726662017-10-24 23:16:35 -0400448 let data = match *params {
Nika Layzellc08227a2017-12-04 16:30:17 -0500449 PathArguments::AngleBracketed(ref data) => data,
450 _ => panic!("Expected at least 1 type argument here"),
Nika Layzell27726662017-10-24 23:16:35 -0400451 };
452
David Tolnayd67fb752017-12-27 13:50:29 -0500453 match **data.args
454 .first()
455 .expect("Expected at least 1 type argument here")
David Tolnay56080682018-01-06 14:01:52 -0800456 .value()
David Tolnayd67fb752017-12-27 13:50:29 -0500457 {
David Tolnayea9ae892017-12-26 01:44:32 -0500458 GenericArgument::Type(ref ty) => ty,
Nika Layzellc08227a2017-12-04 16:30:17 -0500459 _ => panic!("Expected at least 1 type argument here"),
Nika Layzell357885a2017-12-04 15:47:07 -0500460 }
Nika Layzell27726662017-10-24 23:16:35 -0400461 }
462
463 fn simple_visit(
David Tolnay3d772182017-12-28 17:18:53 -0500464 item: &AstItem,
Nika Layzell27726662017-10-24 23:16:35 -0400465 kind: Kind,
David Tolnayf0d63bf2017-12-26 12:29:47 -0500466 name: &Operand,
David Tolnay4a918742017-12-28 16:54:41 -0500467 ) -> String {
468 match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500469 Visit => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500470 "_visitor.visit_{under_name}({name})",
Alex Crichtona74a1c82018-05-16 10:20:44 -0700471 under_name = under_name(item.ast.ident.clone()),
David Tolnay4a918742017-12-28 16:54:41 -0500472 name = name.ref_tokens(),
473 ),
David Tolnay83db9272017-12-28 17:02:31 -0500474 VisitMut => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500475 "_visitor.visit_{under_name}_mut({name})",
Alex Crichtona74a1c82018-05-16 10:20:44 -0700476 under_name = under_name(item.ast.ident.clone()),
David Tolnay4a918742017-12-28 16:54:41 -0500477 name = name.ref_mut_tokens(),
478 ),
David Tolnay83db9272017-12-28 17:02:31 -0500479 Fold => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500480 "_visitor.fold_{under_name}({name})",
Alex Crichtona74a1c82018-05-16 10:20:44 -0700481 under_name = under_name(item.ast.ident.clone()),
David Tolnay4a918742017-12-28 16:54:41 -0500482 name = name.owned_tokens(),
483 ),
Nika Layzell27726662017-10-24 23:16:35 -0400484 }
485 }
486
487 fn box_visit(
David Tolnay3d772182017-12-28 17:18:53 -0500488 elem: &Type,
Nika Layzell27726662017-10-24 23:16:35 -0400489 lookup: &Lookup,
490 kind: Kind,
David Tolnayf0d63bf2017-12-26 12:29:47 -0500491 name: &Operand,
Nika Layzell27726662017-10-24 23:16:35 -0400492 ) -> Option<String> {
David Tolnay4a918742017-12-28 16:54:41 -0500493 let name = name.owned_tokens();
David Tolnay39d0a202017-12-28 18:19:00 -0500494 let res = visit(elem, lookup, kind, &Owned(quote!(*#name)))?;
David Tolnay4a918742017-12-28 16:54:41 -0500495 Some(match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500496 Fold => format!("Box::new({})", res),
497 Visit | VisitMut => res,
David Tolnay4a918742017-12-28 16:54:41 -0500498 })
Nika Layzell27726662017-10-24 23:16:35 -0400499 }
500
501 fn vec_visit(
David Tolnay3d772182017-12-28 17:18:53 -0500502 elem: &Type,
Nika Layzell27726662017-10-24 23:16:35 -0400503 lookup: &Lookup,
504 kind: Kind,
David Tolnayf0d63bf2017-12-26 12:29:47 -0500505 name: &Operand,
Nika Layzell27726662017-10-24 23:16:35 -0400506 ) -> Option<String> {
David Tolnay4a918742017-12-28 16:54:41 -0500507 let operand = match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500508 Visit | VisitMut => Borrowed(quote!(it)),
509 Fold => Owned(quote!(it)),
David Tolnay4a918742017-12-28 16:54:41 -0500510 };
David Tolnay39d0a202017-12-28 18:19:00 -0500511 let val = visit(elem, lookup, kind, &operand)?;
David Tolnay4a918742017-12-28 16:54:41 -0500512 Some(match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500513 Visit => {
David Tolnay3d772182017-12-28 17:18:53 -0500514 format!(
515 "for it in {name} {{ {val} }}",
516 name = name.ref_tokens(),
517 val = val,
518 )
Nika Layzell27726662017-10-24 23:16:35 -0400519 }
David Tolnay83db9272017-12-28 17:02:31 -0500520 VisitMut => {
David Tolnay3d772182017-12-28 17:18:53 -0500521 format!(
522 "for it in {name} {{ {val} }}",
523 name = name.ref_mut_tokens(),
524 val = val,
525 )
526 }
527 Fold => format!(
528 "FoldHelper::lift({name}, |it| {{ {val} }})",
529 name = name.owned_tokens(),
530 val = val,
531 ),
532 })
533 }
534
David Tolnay6eff4da2018-01-01 20:27:45 -0800535 fn punctuated_visit(
David Tolnay3d772182017-12-28 17:18:53 -0500536 elem: &Type,
537 lookup: &Lookup,
538 kind: Kind,
539 name: &Operand,
540 ) -> Option<String> {
541 let operand = match kind {
542 Visit | VisitMut => Borrowed(quote!(it)),
543 Fold => Owned(quote!(it)),
544 };
David Tolnay39d0a202017-12-28 18:19:00 -0500545 let val = visit(elem, lookup, kind, &operand)?;
David Tolnay3d772182017-12-28 17:18:53 -0500546 Some(match kind {
547 Visit => {
548 format!(
David Tolnay56080682018-01-06 14:01:52 -0800549 "for el in Punctuated::pairs({name}) {{ \
550 let it = el.value(); \
David Tolnay3d772182017-12-28 17:18:53 -0500551 {val} \
552 }}",
553 name = name.ref_tokens(),
554 val = val,
555 )
556 }
557 VisitMut => {
558 format!(
David Tolnay56080682018-01-06 14:01:52 -0800559 "for mut el in Punctuated::pairs_mut({name}) {{ \
560 let it = el.value_mut(); \
David Tolnay3d772182017-12-28 17:18:53 -0500561 {val} \
562 }}",
563 name = name.ref_mut_tokens(),
564 val = val,
565 )
David Tolnay4a918742017-12-28 16:54:41 -0500566 }
David Tolnay83db9272017-12-28 17:02:31 -0500567 Fold => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500568 "FoldHelper::lift({name}, |it| {{ {val} }})",
569 name = name.owned_tokens(),
570 val = val,
571 ),
572 })
Nika Layzell27726662017-10-24 23:16:35 -0400573 }
574
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400575 fn option_visit(
David Tolnay3d772182017-12-28 17:18:53 -0500576 elem: &Type,
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400577 lookup: &Lookup,
578 kind: Kind,
David Tolnayf0d63bf2017-12-26 12:29:47 -0500579 name: &Operand,
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400580 ) -> Option<String> {
David Tolnay4a918742017-12-28 16:54:41 -0500581 let it = match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500582 Visit | VisitMut => Borrowed(quote!(it)),
583 Fold => Owned(quote!(it)),
David Tolnay4a918742017-12-28 16:54:41 -0500584 };
David Tolnay39d0a202017-12-28 18:19:00 -0500585 let val = visit(elem, lookup, kind, &it)?;
David Tolnay4a918742017-12-28 16:54:41 -0500586 Some(match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500587 Visit => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500588 "if let Some(ref it) = {name} {{ {val} }}",
589 name = name.owned_tokens(),
590 val = val,
591 ),
David Tolnay83db9272017-12-28 17:02:31 -0500592 VisitMut => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500593 "if let Some(ref mut it) = {name} {{ {val} }}",
594 name = name.owned_tokens(),
595 val = val,
596 ),
David Tolnay83db9272017-12-28 17:02:31 -0500597 Fold => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500598 "({name}).map(|it| {{ {val} }})",
599 name = name.owned_tokens(),
600 val = val,
601 ),
602 })
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400603 }
604
David Tolnay5c4c0b52017-12-28 17:58:54 -0500605 fn tuple_visit(
David Tolnayf2cfd722017-12-31 18:02:51 -0500606 elems: &Punctuated<Type, Token![,]>,
David Tolnay5c4c0b52017-12-28 17:58:54 -0500607 lookup: &Lookup,
608 kind: Kind,
609 name: &Operand,
610 ) -> Option<String> {
611 let mut code = String::new();
David Tolnayf047c7a2017-12-31 02:29:04 -0500612 for (i, elem) in elems.iter().enumerate() {
David Tolnay5c4c0b52017-12-28 17:58:54 -0500613 let name = name.tokens();
David Tolnay14982012017-12-29 00:49:51 -0500614 let i = Index::from(i);
David Tolnay5c4c0b52017-12-28 17:58:54 -0500615 let it = Owned(quote!((#name).#i));
David Tolnay6eff4da2018-01-01 20:27:45 -0800616 let val = visit(elem, lookup, kind, &it)
David Tolnay5c4c0b52017-12-28 17:58:54 -0500617 .unwrap_or_else(|| noop_visit(kind, &it));
618 code.push_str(&format!(" {}", val));
619 match kind {
620 Fold => code.push(','),
621 Visit | VisitMut => code.push(';'),
622 }
623 code.push('\n');
624 }
625 if code.is_empty() {
626 None
627 } else {
628 Some(match kind {
629 Fold => {
630 format!("(\n{} )", code)
631 }
632 Visit | VisitMut => {
633 format!("\n{} ", code)
634 }
635 })
636 }
637 }
638
Alex Crichton715862b2018-05-17 12:31:49 -0700639 fn token_visit(ty: TokenStream, kind: Kind, name: &Operand) -> String {
David Tolnaycc0f0372017-12-28 19:11:04 -0500640 match kind {
641 Fold => format!(
David Tolnay1e01f9c2017-12-28 20:16:19 -0500642 "{ty}(tokens_helper(_visitor, &({name}).0))",
643 ty = ty,
David Tolnaycc0f0372017-12-28 19:11:04 -0500644 name = name.owned_tokens(),
645 ),
646 Visit => format!(
647 "tokens_helper(_visitor, &({name}).0)",
648 name = name.ref_tokens(),
649 ),
650 VisitMut => format!(
651 "tokens_helper(_visitor, &mut ({name}).0)",
652 name = name.ref_mut_tokens(),
653 ),
654 }
655 }
656
David Tolnay4a918742017-12-28 16:54:41 -0500657 fn noop_visit(kind: Kind, name: &Operand) -> String {
658 match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500659 Fold => name.owned_tokens().to_string(),
660 Visit | VisitMut => format!("// Skipped field {}", name),
David Tolnay4a918742017-12-28 16:54:41 -0500661 }
662 }
663
664 fn visit(ty: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<String> {
David Tolnay3d772182017-12-28 17:18:53 -0500665 match classify(ty, lookup) {
666 RelevantType::Box(elem) => {
667 box_visit(elem, lookup, kind, name)
David Tolnay4a918742017-12-28 16:54:41 -0500668 }
David Tolnay3d772182017-12-28 17:18:53 -0500669 RelevantType::Vec(elem) => {
670 vec_visit(elem, lookup, kind, name)
David Tolnay4a918742017-12-28 16:54:41 -0500671 }
David Tolnayf2cfd722017-12-31 18:02:51 -0500672 RelevantType::Punctuated(elem) => {
David Tolnay6eff4da2018-01-01 20:27:45 -0800673 punctuated_visit(elem, lookup, kind, name)
David Tolnay4a918742017-12-28 16:54:41 -0500674 }
David Tolnay3d772182017-12-28 17:18:53 -0500675 RelevantType::Option(elem) => {
676 option_visit(elem, lookup, kind, name)
677 }
David Tolnay5c4c0b52017-12-28 17:58:54 -0500678 RelevantType::Tuple(elems) => {
679 tuple_visit(elems, lookup, kind, name)
680 }
David Tolnay3d772182017-12-28 17:18:53 -0500681 RelevantType::Simple(item) => {
682 let mut res = simple_visit(item, kind, name);
683 Some(if item.eos_full {
684 format!("full!({res})", res = res)
685 } else {
686 res
687 })
688 }
David Tolnay1e01f9c2017-12-28 20:16:19 -0500689 RelevantType::Token(ty) => {
690 Some(token_visit(ty, kind, name))
David Tolnaycc0f0372017-12-28 19:11:04 -0500691 }
David Tolnay3d772182017-12-28 17:18:53 -0500692 RelevantType::Pass => {
693 None
Nika Layzell27726662017-10-24 23:16:35 -0400694 }
695 }
Nika Layzell27726662017-10-24 23:16:35 -0400696 }
697
698 pub fn generate(state: &mut State, lookup: &Lookup, s: &AstItem) {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700699 let under_name = under_name(s.ast.ident.clone());
Nika Layzell27726662017-10-24 23:16:35 -0400700
701 state.visit_trait.push_str(&format!(
702 "{features}\n\
Nika Layzellc86173a2017-11-18 13:55:22 -0500703 fn visit_{under_name}(&mut self, i: &'ast {ty}) {{ \
David Tolnayd67fb752017-12-27 13:50:29 -0500704 visit_{under_name}(self, i) \
Nika Layzell27726662017-10-24 23:16:35 -0400705 }}\n",
706 features = s.features,
707 under_name = under_name,
David Tolnay3d772182017-12-28 17:18:53 -0500708 ty = s.ast.ident,
Nika Layzell27726662017-10-24 23:16:35 -0400709 ));
710 state.visit_mut_trait.push_str(&format!(
711 "{features}\n\
Nika Layzella6f46c42017-10-26 15:26:16 -0400712 fn visit_{under_name}_mut(&mut self, i: &mut {ty}) {{ \
David Tolnayd67fb752017-12-27 13:50:29 -0500713 visit_{under_name}_mut(self, i) \
Nika Layzell27726662017-10-24 23:16:35 -0400714 }}\n",
715 features = s.features,
716 under_name = under_name,
David Tolnay3d772182017-12-28 17:18:53 -0500717 ty = s.ast.ident,
Nika Layzell27726662017-10-24 23:16:35 -0400718 ));
719 state.fold_trait.push_str(&format!(
720 "{features}\n\
721 fn fold_{under_name}(&mut self, i: {ty}) -> {ty} {{ \
David Tolnayd67fb752017-12-27 13:50:29 -0500722 fold_{under_name}(self, i) \
Nika Layzell27726662017-10-24 23:16:35 -0400723 }}\n",
724 features = s.features,
725 under_name = under_name,
David Tolnay3d772182017-12-28 17:18:53 -0500726 ty = s.ast.ident,
Nika Layzell27726662017-10-24 23:16:35 -0400727 ));
728
729 state.visit_impl.push_str(&format!(
730 "{features}\n\
David Tolnay4b4c4b62018-01-06 13:48:05 -0800731 pub fn visit_{under_name}<'ast, V: Visit<'ast> + ?Sized>(\
David Tolnayd67fb752017-12-27 13:50:29 -0500732 _visitor: &mut V, _i: &'ast {ty}) {{\n",
Nika Layzell27726662017-10-24 23:16:35 -0400733 features = s.features,
734 under_name = under_name,
David Tolnay3d772182017-12-28 17:18:53 -0500735 ty = s.ast.ident,
Nika Layzell27726662017-10-24 23:16:35 -0400736 ));
737 state.visit_mut_impl.push_str(&format!(
738 "{features}\n\
David Tolnay4b4c4b62018-01-06 13:48:05 -0800739 pub fn visit_{under_name}_mut<V: VisitMut + ?Sized>(\
David Tolnayd67fb752017-12-27 13:50:29 -0500740 _visitor: &mut V, _i: &mut {ty}) {{\n",
Nika Layzell27726662017-10-24 23:16:35 -0400741 features = s.features,
742 under_name = under_name,
David Tolnay3d772182017-12-28 17:18:53 -0500743 ty = s.ast.ident,
Nika Layzell27726662017-10-24 23:16:35 -0400744 ));
David Tolnayd0adf522017-12-29 01:30:07 -0500745 let before_fold_impl_len = state.fold_impl.len();
Nika Layzell27726662017-10-24 23:16:35 -0400746 state.fold_impl.push_str(&format!(
747 "{features}\n\
David Tolnay4b4c4b62018-01-06 13:48:05 -0800748 pub fn fold_{under_name}<V: Fold + ?Sized>(\
David Tolnayd67fb752017-12-27 13:50:29 -0500749 _visitor: &mut V, _i: {ty}) -> {ty} {{\n",
Nika Layzell27726662017-10-24 23:16:35 -0400750 features = s.features,
751 under_name = under_name,
David Tolnay3d772182017-12-28 17:18:53 -0500752 ty = s.ast.ident,
Nika Layzell27726662017-10-24 23:16:35 -0400753 ));
754
755 // XXX: This part is a disaster - I'm not sure how to make it cleaner though :'(
David Tolnaye3d41b72017-12-31 15:24:00 -0500756 match s.ast.data {
757 Data::Enum(ref e) => {
Nika Layzell27726662017-10-24 23:16:35 -0400758 state.visit_impl.push_str(" match *_i {\n");
759 state.visit_mut_impl.push_str(" match *_i {\n");
760 state.fold_impl.push_str(" match _i {\n");
761 for variant in &e.variants {
Alex Crichton715862b2018-05-17 12:31:49 -0700762 let fields: Vec<(&Field, TokenStream)> = match variant.fields {
David Tolnaye3d41b72017-12-31 15:24:00 -0500763 Fields::Named(..) => panic!("Doesn't support enum struct variants"),
764 Fields::Unnamed(ref fields) => {
David Tolnay6eff4da2018-01-01 20:27:45 -0800765 let binding = format!(" {}::{}(", s.ast.ident, variant.ident);
Nika Layzell27726662017-10-24 23:16:35 -0400766 state.visit_impl.push_str(&binding);
767 state.visit_mut_impl.push_str(&binding);
768 state.fold_impl.push_str(&binding);
769
David Tolnaybdafb102018-01-01 19:39:10 -0800770 let res = fields.unnamed
David Tolnayd67fb752017-12-27 13:50:29 -0500771 .iter()
772 .enumerate()
773 .map(|(idx, el)| {
774 let name = format!("_binding_{}", idx);
Nika Layzell27726662017-10-24 23:16:35 -0400775
David Tolnayd67fb752017-12-27 13:50:29 -0500776 state.visit_impl.push_str("ref ");
777 state.visit_mut_impl.push_str("ref mut ");
Nika Layzell27726662017-10-24 23:16:35 -0400778
David Tolnayd67fb752017-12-27 13:50:29 -0500779 state.visit_impl.push_str(&name);
780 state.visit_mut_impl.push_str(&name);
781 state.fold_impl.push_str(&name);
782 state.visit_impl.push_str(", ");
783 state.visit_mut_impl.push_str(", ");
784 state.fold_impl.push_str(", ");
Nika Layzell27726662017-10-24 23:16:35 -0400785
David Tolnayd67fb752017-12-27 13:50:29 -0500786 let mut tokens = quote!();
Alex Crichtona74a1c82018-05-16 10:20:44 -0700787 Ident::new(&name, Span::call_site()).to_tokens(&mut tokens);
Nika Layzell27726662017-10-24 23:16:35 -0400788
David Tolnay6eff4da2018-01-01 20:27:45 -0800789 (el, tokens)
David Tolnayd67fb752017-12-27 13:50:29 -0500790 })
791 .collect();
Nika Layzell27726662017-10-24 23:16:35 -0400792
793 state.visit_impl.push_str(") => {\n");
794 state.visit_mut_impl.push_str(") => {\n");
795 state.fold_impl.push_str(") => {\n");
796
797 res
798 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500799 Fields::Unit => {
David Tolnayd67fb752017-12-27 13:50:29 -0500800 state
801 .visit_impl
David Tolnay6eff4da2018-01-01 20:27:45 -0800802 .push_str(&format!(" {0}::{1} => {{ }}\n", s.ast.ident, variant.ident));
David Tolnayd67fb752017-12-27 13:50:29 -0500803 state
804 .visit_mut_impl
David Tolnay6eff4da2018-01-01 20:27:45 -0800805 .push_str(&format!(" {0}::{1} => {{ }}\n", s.ast.ident, variant.ident));
Nika Layzell27726662017-10-24 23:16:35 -0400806 state.fold_impl.push_str(&format!(
David Tolnay6702ade2017-12-30 23:38:15 -0500807 " {0}::{1} => {{ {0}::{1} }}\n",
808 s.ast.ident,
David Tolnay6eff4da2018-01-01 20:27:45 -0800809 variant.ident
Nika Layzell27726662017-10-24 23:16:35 -0400810 ));
David Tolnayd67fb752017-12-27 13:50:29 -0500811 continue;
Nika Layzell27726662017-10-24 23:16:35 -0400812 }
813 };
814
815 if fields.is_empty() {
816 state.visit_impl.push_str(" {}");
817 state.visit_mut_impl.push_str(") => {\n");
818 state.fold_impl.push_str(") => {\n");
819 }
David Tolnayd67fb752017-12-27 13:50:29 -0500820 state
821 .fold_impl
David Tolnay6eff4da2018-01-01 20:27:45 -0800822 .push_str(&format!(" {}::{} (\n", s.ast.ident, variant.ident,));
Nika Layzell27726662017-10-24 23:16:35 -0400823 for (field, binding) in fields {
824 state.visit_impl.push_str(&format!(
825 " {};\n",
David Tolnayd67fb752017-12-27 13:50:29 -0500826 visit(
827 &field.ty,
828 lookup,
David Tolnay83db9272017-12-28 17:02:31 -0500829 Visit,
830 &Borrowed(binding.clone())
David Tolnay4a918742017-12-28 16:54:41 -0500831 ).unwrap_or_else(|| noop_visit(
David Tolnay83db9272017-12-28 17:02:31 -0500832 Visit,
833 &Borrowed(binding.clone())
David Tolnay4a918742017-12-28 16:54:41 -0500834 )),
Nika Layzell27726662017-10-24 23:16:35 -0400835 ));
836 state.visit_mut_impl.push_str(&format!(
837 " {};\n",
David Tolnayd67fb752017-12-27 13:50:29 -0500838 visit(
839 &field.ty,
840 lookup,
David Tolnay83db9272017-12-28 17:02:31 -0500841 VisitMut,
842 &Borrowed(binding.clone())
David Tolnay4a918742017-12-28 16:54:41 -0500843 ).unwrap_or_else(|| noop_visit(
David Tolnay83db9272017-12-28 17:02:31 -0500844 VisitMut,
845 &Borrowed(binding.clone())
David Tolnay4a918742017-12-28 16:54:41 -0500846 )),
Nika Layzell27726662017-10-24 23:16:35 -0400847 ));
848 state.fold_impl.push_str(&format!(
849 " {},\n",
David Tolnay83db9272017-12-28 17:02:31 -0500850 visit(&field.ty, lookup, Fold, &Owned(binding.clone()))
David Tolnay4a918742017-12-28 16:54:41 -0500851 .unwrap_or_else(|| noop_visit(
David Tolnay83db9272017-12-28 17:02:31 -0500852 Fold,
853 &Owned(binding)
David Tolnay4a918742017-12-28 16:54:41 -0500854 )),
Nika Layzell27726662017-10-24 23:16:35 -0400855 ));
856 }
857 state.fold_impl.push_str(" )\n");
858
859 state.visit_impl.push_str(" }\n");
860 state.visit_mut_impl.push_str(" }\n");
861 state.fold_impl.push_str(" }\n");
862 }
863 state.visit_impl.push_str(" }\n");
864 state.visit_mut_impl.push_str(" }\n");
865 state.fold_impl.push_str(" }\n");
866 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500867 Data::Struct(ref v) => {
Alex Crichton715862b2018-05-17 12:31:49 -0700868 let fields: Vec<(&Field, TokenStream)> = match v.fields {
David Tolnaye3d41b72017-12-31 15:24:00 -0500869 Fields::Named(ref fields) => {
David Tolnayd67fb752017-12-27 13:50:29 -0500870 state
871 .fold_impl
David Tolnay3d772182017-12-28 17:18:53 -0500872 .push_str(&format!(" {} {{\n", s.ast.ident));
David Tolnaybdafb102018-01-01 19:39:10 -0800873 fields.named
David Tolnayd67fb752017-12-27 13:50:29 -0500874 .iter()
875 .map(|el| {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700876 let id = el.ident.clone();
David Tolnay6eff4da2018-01-01 20:27:45 -0800877 (el, quote!(_i.#id))
David Tolnayd67fb752017-12-27 13:50:29 -0500878 })
879 .collect()
Nika Layzell27726662017-10-24 23:16:35 -0400880 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500881 Fields::Unnamed(ref fields) => {
David Tolnayd67fb752017-12-27 13:50:29 -0500882 state
883 .fold_impl
David Tolnay3d772182017-12-28 17:18:53 -0500884 .push_str(&format!(" {} (\n", s.ast.ident));
David Tolnaybdafb102018-01-01 19:39:10 -0800885 fields.unnamed
David Tolnayd67fb752017-12-27 13:50:29 -0500886 .iter()
887 .enumerate()
888 .map(|(idx, el)| {
David Tolnay14982012017-12-29 00:49:51 -0500889 let id = Index::from(idx);
David Tolnay6eff4da2018-01-01 20:27:45 -0800890 (el, quote!(_i.#id))
David Tolnayd67fb752017-12-27 13:50:29 -0500891 })
892 .collect()
Nika Layzell27726662017-10-24 23:16:35 -0400893 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500894 Fields::Unit => {
David Tolnay18837692018-05-20 20:17:20 -0700895 if s.ast.ident == "Ident" {
896 state.fold_impl.push_str(" Ident::new(&_i.to_string(), _visitor.fold_span(_i.span()))\n");
897 } else {
898 state.fold_impl.push_str(" _i\n");
899 }
Nika Layzellefb83ba2017-12-19 18:23:55 -0500900 vec![]
901 }
Nika Layzell27726662017-10-24 23:16:35 -0400902 };
903
904 for (field, ref_toks) in fields {
David Tolnay83db9272017-12-28 17:02:31 -0500905 let ref_toks = Owned(ref_toks);
Nika Layzell27726662017-10-24 23:16:35 -0400906 state.visit_impl.push_str(&format!(
David Tolnayd67fb752017-12-27 13:50:29 -0500907 " {};\n",
David Tolnay83db9272017-12-28 17:02:31 -0500908 visit(&field.ty, lookup, Visit, &ref_toks)
David Tolnay4a918742017-12-28 16:54:41 -0500909 .unwrap_or_else(|| noop_visit(
David Tolnay83db9272017-12-28 17:02:31 -0500910 Visit,
David Tolnay4a918742017-12-28 16:54:41 -0500911 &ref_toks,
912 ))
Nika Layzell27726662017-10-24 23:16:35 -0400913 ));
914 state.visit_mut_impl.push_str(&format!(
David Tolnayd67fb752017-12-27 13:50:29 -0500915 " {};\n",
David Tolnay83db9272017-12-28 17:02:31 -0500916 visit(&field.ty, lookup, VisitMut, &ref_toks)
David Tolnay4a918742017-12-28 16:54:41 -0500917 .unwrap_or_else(|| noop_visit(
David Tolnay83db9272017-12-28 17:02:31 -0500918 VisitMut,
David Tolnay4a918742017-12-28 16:54:41 -0500919 &ref_toks,
920 ))
Nika Layzell27726662017-10-24 23:16:35 -0400921 ));
David Tolnay83db9272017-12-28 17:02:31 -0500922 let fold = visit(&field.ty, lookup, Fold, &ref_toks)
David Tolnay4a918742017-12-28 16:54:41 -0500923 .unwrap_or_else(|| noop_visit(
David Tolnay83db9272017-12-28 17:02:31 -0500924 Fold,
David Tolnay4a918742017-12-28 16:54:41 -0500925 &ref_toks,
926 ));
Nika Layzell27726662017-10-24 23:16:35 -0400927 if let Some(ref name) = field.ident {
David Tolnayd67fb752017-12-27 13:50:29 -0500928 state
929 .fold_impl
930 .push_str(&format!(" {}: {},\n", name, fold));
Nika Layzell27726662017-10-24 23:16:35 -0400931 } else {
932 state.fold_impl.push_str(&format!(" {},\n", fold));
933 }
934 }
935
David Tolnaye3d41b72017-12-31 15:24:00 -0500936 match v.fields {
937 Fields::Named(..) => state.fold_impl.push_str(" }\n"),
938 Fields::Unnamed(..) => state.fold_impl.push_str(" )\n"),
939 Fields::Unit => {}
Nika Layzell27726662017-10-24 23:16:35 -0400940 };
941 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500942 Data::Union(..) => panic!("Union not supported"),
Nika Layzell27726662017-10-24 23:16:35 -0400943 }
944
945 // Close the impl body
946 state.visit_impl.push_str("}\n");
947 state.visit_mut_impl.push_str("}\n");
948 state.fold_impl.push_str("}\n");
David Tolnayd0adf522017-12-29 01:30:07 -0500949
David Tolnay360efd22018-01-04 23:35:26 -0800950 if let Data::Struct(ref data) = s.ast.data {
951 if let Fields::Named(ref fields) = data.fields {
952 if fields.named.iter().any(|field| field.vis == Visibility::Inherited) {
953 // Discard the generated impl if there are private fields.
954 // These have to be handwritten.
955 state.fold_impl.truncate(before_fold_impl_len);
956 }
957 }
David Tolnayd0adf522017-12-29 01:30:07 -0500958 }
Nika Layzell27726662017-10-24 23:16:35 -0400959 }
960}
961
962fn main() {
963 let mut lookup = BTreeMap::new();
David Tolnayea9ae892017-12-26 01:44:32 -0500964 load_file(SYN_CRATE_ROOT, &quote!(), &mut lookup).unwrap();
Nika Layzell27726662017-10-24 23:16:35 -0400965
Nika Layzellefb83ba2017-12-19 18:23:55 -0500966 // Load in any terminal types
967 for &tt in TERMINAL_TYPES {
968 use syn::*;
David Tolnayd67fb752017-12-27 13:50:29 -0500969 lookup.insert(
Alex Crichtona74a1c82018-05-16 10:20:44 -0700970 Ident::new(&tt, Span::call_site()),
David Tolnayd67fb752017-12-27 13:50:29 -0500971 AstItem {
David Tolnay3d772182017-12-28 17:18:53 -0500972 ast: DeriveInput {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700973 ident: Ident::new(tt, Span::call_site()),
David Tolnayd67fb752017-12-27 13:50:29 -0500974 vis: Visibility::Public(VisPublic {
975 pub_token: Default::default(),
976 }),
977 attrs: vec![],
978 generics: Default::default(),
David Tolnaye3d41b72017-12-31 15:24:00 -0500979 data: Data::Struct(DataStruct {
980 fields: Fields::Unit,
David Tolnayd67fb752017-12-27 13:50:29 -0500981 struct_token: Default::default(),
982 semi_token: None,
983 }),
984 },
Alex Crichton715862b2018-05-17 12:31:49 -0700985 features: TokenStream::empty(),
David Tolnayd67fb752017-12-27 13:50:29 -0500986 eos_full: false,
Nika Layzellefb83ba2017-12-19 18:23:55 -0500987 },
David Tolnayd67fb752017-12-27 13:50:29 -0500988 );
Nika Layzellefb83ba2017-12-19 18:23:55 -0500989 }
990
Nika Layzell27726662017-10-24 23:16:35 -0400991 let mut state = Default::default();
992 for s in lookup.values() {
993 codegen::generate(&mut state, &lookup, s);
994 }
995
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400996 let full_macro = "
997#[cfg(feature = \"full\")]
998macro_rules! full {
999 ($e:expr) => { $e }
1000}
1001
David Tolnay0a0d78c2018-01-05 15:24:01 -08001002#[cfg(all(feature = \"derive\", not(feature = \"full\")))]
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001003macro_rules! full {
1004 ($e:expr) => { unreachable!() }
1005}
1006";
1007
Nika Layzell27726662017-10-24 23:16:35 -04001008 let mut fold_file = File::create(FOLD_SRC).unwrap();
David Tolnayd67fb752017-12-27 13:50:29 -05001009 write!(
1010 fold_file,
1011 "\
Nika Layzell27726662017-10-24 23:16:35 -04001012// THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT
1013
David Tolnay0afc9b32017-12-27 13:38:24 -05001014#![cfg_attr(rustfmt, rustfmt_skip)]
1015
Nika Layzell27726662017-10-24 23:16:35 -04001016// Unreachable code is generated sometimes without the full feature.
1017#![allow(unreachable_code)]
David Tolnayf0d63bf2017-12-26 12:29:47 -05001018#![cfg_attr(feature = \"cargo-clippy\", allow(needless_pass_by_value))]
Nika Layzell27726662017-10-24 23:16:35 -04001019
Nika Layzella6f46c42017-10-26 15:26:16 -04001020use *;
David Tolnay0a0d78c2018-01-05 15:24:01 -08001021#[cfg(any(feature = \"full\", feature = \"derive\"))]
David Tolnay1e01f9c2017-12-28 20:16:19 -05001022use token::{{Brace, Bracket, Paren, Group}};
David Tolnaye303b7c2018-05-20 16:46:35 -07001023use proc_macro2::Span;
David Tolnay0a0d78c2018-01-05 15:24:01 -08001024#[cfg(any(feature = \"full\", feature = \"derive\"))]
David Tolnayf60f4262017-12-28 19:17:58 -05001025use gen::helper::fold::*;
Nika Layzell27726662017-10-24 23:16:35 -04001026
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001027{full_macro}
1028
David Tolnayded2d682018-01-06 18:53:53 -08001029/// Syntax tree traversal to transform the nodes of an owned syntax tree.
Nika Layzell27726662017-10-24 23:16:35 -04001030///
David Tolnayded2d682018-01-06 18:53:53 -08001031/// See the [module documentation] for details.
Nika Layzell27726662017-10-24 23:16:35 -04001032///
David Tolnayded2d682018-01-06 18:53:53 -08001033/// [module documentation]: index.html
David Tolnay461d98e2018-01-07 11:07:19 -08001034///
1035/// *This trait is available if Syn is built with the `\"fold\"` feature.*
David Tolnay4b4c4b62018-01-06 13:48:05 -08001036pub trait Fold {{
Nika Layzell27726662017-10-24 23:16:35 -04001037{fold_trait}
1038}}
1039
David Tolnay360efd22018-01-04 23:35:26 -08001040macro_rules! fold_span_only {{
1041 ($f:ident : $t:ident) => {{
David Tolnay4b4c4b62018-01-06 13:48:05 -08001042 pub fn $f<V: Fold + ?Sized>(_visitor: &mut V, mut _i: $t) -> $t {{
Alex Crichton9a4dca22018-03-28 06:32:19 -07001043 let span = _visitor.fold_span(_i.span());
1044 _i.set_span(span);
David Tolnay360efd22018-01-04 23:35:26 -08001045 _i
1046 }}
1047 }}
David Tolnayd0adf522017-12-29 01:30:07 -05001048}}
1049
David Tolnay360efd22018-01-04 23:35:26 -08001050#[cfg(any(feature = \"full\", feature = \"derive\"))]
1051fold_span_only!(fold_lit_byte: LitByte);
1052#[cfg(any(feature = \"full\", feature = \"derive\"))]
1053fold_span_only!(fold_lit_byte_str: LitByteStr);
1054#[cfg(any(feature = \"full\", feature = \"derive\"))]
1055fold_span_only!(fold_lit_char: LitChar);
1056#[cfg(any(feature = \"full\", feature = \"derive\"))]
1057fold_span_only!(fold_lit_float: LitFloat);
1058#[cfg(any(feature = \"full\", feature = \"derive\"))]
1059fold_span_only!(fold_lit_int: LitInt);
1060#[cfg(any(feature = \"full\", feature = \"derive\"))]
1061fold_span_only!(fold_lit_str: LitStr);
David Tolnayd0adf522017-12-29 01:30:07 -05001062
Nika Layzell27726662017-10-24 23:16:35 -04001063{fold_impl}
1064",
David Tolnayd67fb752017-12-27 13:50:29 -05001065 full_macro = full_macro,
1066 fold_trait = state.fold_trait,
1067 fold_impl = state.fold_impl
1068 ).unwrap();
Nika Layzell27726662017-10-24 23:16:35 -04001069
1070 let mut visit_file = File::create(VISIT_SRC).unwrap();
David Tolnayd67fb752017-12-27 13:50:29 -05001071 write!(
1072 visit_file,
1073 "\
Nika Layzell27726662017-10-24 23:16:35 -04001074// THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT
1075
David Tolnay0afc9b32017-12-27 13:38:24 -05001076#![cfg_attr(rustfmt, rustfmt_skip)]
1077
David Tolnayf0d63bf2017-12-26 12:29:47 -05001078#![cfg_attr(feature = \"cargo-clippy\", allow(match_same_arms))]
1079
Nika Layzella6f46c42017-10-26 15:26:16 -04001080use *;
David Tolnay0a0d78c2018-01-05 15:24:01 -08001081#[cfg(any(feature = \"full\", feature = \"derive\"))]
David Tolnay6eff4da2018-01-01 20:27:45 -08001082use punctuated::Punctuated;
David Tolnaye303b7c2018-05-20 16:46:35 -07001083use proc_macro2::Span;
David Tolnay0a0d78c2018-01-05 15:24:01 -08001084#[cfg(any(feature = \"full\", feature = \"derive\"))]
David Tolnaycc0f0372017-12-28 19:11:04 -05001085use gen::helper::visit::*;
Nika Layzell27726662017-10-24 23:16:35 -04001086
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001087{full_macro}
1088
David Tolnayded2d682018-01-06 18:53:53 -08001089/// Syntax tree traversal to walk a shared borrow of a syntax tree.
Nika Layzell27726662017-10-24 23:16:35 -04001090///
David Tolnayded2d682018-01-06 18:53:53 -08001091/// See the [module documentation] for details.
1092///
1093/// [module documentation]: index.html
David Tolnay461d98e2018-01-07 11:07:19 -08001094///
1095/// *This trait is available if Syn is built with the `\"visit\"` feature.*
David Tolnay4b4c4b62018-01-06 13:48:05 -08001096pub trait Visit<'ast> {{
Nika Layzell27726662017-10-24 23:16:35 -04001097{visit_trait}
1098}}
1099
1100{visit_impl}
1101",
David Tolnayd67fb752017-12-27 13:50:29 -05001102 full_macro = full_macro,
1103 visit_trait = state.visit_trait,
1104 visit_impl = state.visit_impl
1105 ).unwrap();
Nika Layzell27726662017-10-24 23:16:35 -04001106
1107 let mut visit_mut_file = File::create(VISIT_MUT_SRC).unwrap();
David Tolnayd67fb752017-12-27 13:50:29 -05001108 write!(
1109 visit_mut_file,
1110 "\
Nika Layzell27726662017-10-24 23:16:35 -04001111// THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT
1112
David Tolnay0afc9b32017-12-27 13:38:24 -05001113#![cfg_attr(rustfmt, rustfmt_skip)]
1114
David Tolnayf0d63bf2017-12-26 12:29:47 -05001115#![cfg_attr(feature = \"cargo-clippy\", allow(match_same_arms))]
1116
Nika Layzella6f46c42017-10-26 15:26:16 -04001117use *;
David Tolnay0a0d78c2018-01-05 15:24:01 -08001118#[cfg(any(feature = \"full\", feature = \"derive\"))]
David Tolnay6eff4da2018-01-01 20:27:45 -08001119use punctuated::Punctuated;
David Tolnaye303b7c2018-05-20 16:46:35 -07001120use proc_macro2::Span;
David Tolnay0a0d78c2018-01-05 15:24:01 -08001121#[cfg(any(feature = \"full\", feature = \"derive\"))]
David Tolnaycc0f0372017-12-28 19:11:04 -05001122use gen::helper::visit_mut::*;
Nika Layzell27726662017-10-24 23:16:35 -04001123
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001124{full_macro}
1125
David Tolnayded2d682018-01-06 18:53:53 -08001126/// Syntax tree traversal to mutate an exclusive borrow of a syntax tree in
1127/// place.
Nika Layzell27726662017-10-24 23:16:35 -04001128///
David Tolnayded2d682018-01-06 18:53:53 -08001129/// See the [module documentation] for details.
1130///
1131/// [module documentation]: index.html
David Tolnay461d98e2018-01-07 11:07:19 -08001132///
1133/// *This trait is available if Syn is built with the `\"visit-mut\"` feature.*
David Tolnay4b4c4b62018-01-06 13:48:05 -08001134pub trait VisitMut {{
Nika Layzell27726662017-10-24 23:16:35 -04001135{visit_mut_trait}
1136}}
1137
1138{visit_mut_impl}
1139",
David Tolnayd67fb752017-12-27 13:50:29 -05001140 full_macro = full_macro,
1141 visit_mut_trait = state.visit_mut_trait,
1142 visit_mut_impl = state.visit_mut_impl
1143 ).unwrap();
Nika Layzell27726662017-10-24 23:16:35 -04001144}